Skip to content

Commit

Permalink
Allow customising formatter CSS classes
Browse files Browse the repository at this point in the history
## Changes

- allow customising CSS classes by passing the `cssClasses` option in the formatter's constructor
- `title` and `subtitle` classes are added to the rendered title and subtitle
- a `label-wrapper` is added to the table cell (`<td>`) that contains the `<h3 class="label">LABEL</h3>`
- *breaking change:* a `scopedCss` function is no longer exported by the formatters. Please use `new Formatter().cssString()`.

Resolves #399
  • Loading branch information
martijnversluis committed Jan 21, 2025
1 parent 2cf9544 commit 58fc190
Show file tree
Hide file tree
Showing 13 changed files with 798 additions and 272 deletions.
32 changes: 16 additions & 16 deletions src/formatter/formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@ class Formatter {
configuration: Configuration;

/**
* Instantiate
* @param {Object} [configuration={}] options
* @param {boolean} [configuration.evaluate=false] Whether or not to evaluate meta expressions.
* For more info about meta expressions, see: https://bit.ly/2SC9c2u
* @param {object} [configuration.metadata={}]
* @param {string} [configuration.metadata.separator=", "] The separator to be used when rendering a
* metadata value that has multiple values. See: https://bit.ly/2SC9c2u
* @param {Key|string} [configuration.key=null] The key to use for rendering. The chord sheet will be
* transposed from the song's original key (as indicated by the `{key}` directive) to the specified key.
* Note that transposing will only work if the original song key is set.
* @param {boolean} [configuration.expandChorusDirective=false] Whether or not to expand `{chorus}` directives
* by rendering the last defined chorus inline after the directive.
* @param {boolean} [configuration.useUnicodeModifiers=false] Whether or not to use unicode flat and sharp
* symbols.
* @param {boolean} [configuration.normalizeChords=true] Whether or not to automatically normalize chords
*/
* Instantiate
* @param {Object} [configuration={}] options
* @param {boolean} [configuration.evaluate=false] Whether or not to evaluate meta expressions.
* For more info about meta expressions, see: https://bit.ly/2SC9c2u
* @param {object} [configuration.metadata={}]
* @param {string} [configuration.metadata.separator=", "] The separator to be used when rendering a
* metadata value that has multiple values. See: https://bit.ly/2SC9c2u
* @param {Key|string} [configuration.key=null] The key to use for rendering. The chord sheet will be
* transposed from the song's original key (as indicated by the `{key}` directive) to the specified key.
* Note that transposing will only work if the original song key is set.
* @param {boolean} [configuration.expandChorusDirective=false] Whether or not to expand `{chorus}` directives
* by rendering the last defined chorus inline after the directive.
* @param {boolean} [configuration.useUnicodeModifiers=false] Whether or not to use unicode flat and sharp
* symbols.
* @param {boolean} [configuration.normalizeChords=true] Whether or not to automatically normalize chords
*/
constructor(configuration: ConfigurationProperties = {}) {
this.configuration = configure(configuration);
}
Expand Down
63 changes: 31 additions & 32 deletions src/formatter/html_div_formatter.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,35 @@
import HtmlFormatter, { CSS, Template } from './html_formatter';
import HtmlFormatter, { CSS, HtmlTemplateCssClasses, Template } from './html_formatter';
import template from './templates/html_div_formatter';
import { scopeCss } from '../utilities';

const defaultCss: CSS = {
'.chord:not(:last-child)': {
paddingRight: '10px',
},

'.paragraph': {
marginBottom: '1em',
},

'.row': {
display: 'flex',
},

'.chord:after': {
content: '\'\\200b\'',
},

'.lyrics:after': {
content: '\'\\200b\'',
},
};

/**
* Generates basic CSS, scoped within the provided selector, to use with output generated by {@link HtmlTableFormatter}
* @param scope the CSS scope to use, for example `.chordSheetViewer`
* @returns {string} the CSS string
*/
export function scopedCss(scope: string): string {
return scopeCss(defaultCss, scope);
function defaultCss(cssClasses: HtmlTemplateCssClasses): CSS {
const {
chord,
lyrics,
paragraph,
row,
} = cssClasses;

return {
[`.${chord}:not(:last-child)`]: {
paddingRight: '10px',
},

[`.${paragraph}`]: {
marginBottom: '1em',
},

[`.${row}`]: {
display: 'flex',
},

[`.${chord}:after`]: {
content: '\'\\200b\'',
},

[`.${lyrics}:after`]: {
content: '\'\\200b\'',
},
};
}

/**
Expand All @@ -42,7 +41,7 @@ class HtmlDivFormatter extends HtmlFormatter {
}

get defaultCss(): CSS {
return defaultCss;
return defaultCss(this.cssClasses);
}
}

Expand Down
83 changes: 60 additions & 23 deletions src/formatter/html_formatter.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import Formatter from './formatter';
import Configuration from './configuration';
import Configuration, { ConfigurationProperties } from './configuration';
import Song from '../chord_sheet/song';
import { scopeCss } from '../utilities';
import Paragraph from '../chord_sheet/paragraph';
import { Line } from '../index';
import Tag from '../chord_sheet/tag';

interface HtmlTemplateCssClasses {
annotation: string | ((_annotation: string) => string),
chord: string | ((_chord: string) => string),
chordSheet: string | ((_song: Song) => string),
column: string | (() => string),
comment: string | ((_comment: Tag) => string),
label: string | ((_label: string) => string),
line: string | ((_line: Line) => string),
literal: string | (() => string),
lyrics: string | (() => string),
paragraph: string | ((_paragraph: Paragraph) => string),
row: string | (() => string),
subtitle: string | (() => string),
title: string | (() => string),
export interface HtmlTemplateCssClasses {
annotation: string,
chord: string,
chordSheet: string,
column: string,
comment: string,
emptyLine: string,
label: string,
labelWrapper: string,
line: string,
literal: string,
literalContents: string,
lyrics: string,
paragraph: string,
row: string,
subtitle: string,
title: string,
}

export interface HtmlTemplateArgs {
Expand All @@ -33,15 +34,18 @@ export interface HtmlTemplateArgs {
export type Template = (_args: HtmlTemplateArgs) => string;
export type CSS = Record<string, Record<string, string>>;

const defaultCssClasses: HtmlTemplateCssClasses = {
export const defaultCssClasses: HtmlTemplateCssClasses = {
annotation: 'annotation',
chord: 'chord',
chordSheet: 'chord-sheet',
column: 'column',
comment: 'comment',
emptyLine: 'empty-line',
label: 'label',
labelWrapper: 'label-wrapper',
line: 'line',
literal: 'literal',
literalContents: 'contents',
lyrics: 'lyrics',
paragraph: 'paragraph',
row: 'row',
Expand All @@ -53,6 +57,42 @@ const defaultCssClasses: HtmlTemplateCssClasses = {
* Acts as a base class for HTML formatters
*/
abstract class HtmlFormatter extends Formatter {
cssClasses: HtmlTemplateCssClasses;

/**
* Instantiate the formatter. For all options see {@link Formatter}
* @param {Object} [configuration={}] options
* @param {object} [configuration.cssClasses={}] CSS classes to use in the HTML output. The default classes are
* defined in {@link defaultCssClasses}. You can override them by providing your own classes here:
* @example
* ```javascript
* {
* cssClasses: {
* annotation: 'my-annotation',
* chord: 'my-chord',
* chordSheet: 'my-chord-sheet',
* column: 'my-column',
* comment: 'my-comment',
* emptyLine: 'my-empty-line',
* label: 'my-label',
* labelWrapper: 'my-label-wrapper',
* line: 'my-line',
* literal: 'my-literal',
* literalContents: 'my-contents',
* lyrics: 'my-lyrics',
* paragraph: 'my-paragraph',
* row: 'my-row',
* subtitle: 'my-subtitle',
* title: 'my-title',
* }
* }
* ```
*/
constructor(configuration: ConfigurationProperties & { cssClasses?: Partial<HtmlTemplateCssClasses> } = {}) {
super(configuration);
this.cssClasses = { ...defaultCssClasses, ...configuration.cssClasses };
}

/**
* Formats a song into HTML.
* @param {Song} song The song to be formatted
Expand All @@ -66,10 +106,7 @@ abstract class HtmlFormatter extends Formatter {
song,
configuration: this.configuration,
bodyParagraphs: this.configuration.expandChorusDirective ? expandedBodyParagraphs : bodyParagraphs,
cssClasses: {
...defaultCssClasses,
...this.configuration.cssClasses,
},
cssClasses: this.cssClasses,
},
);
}
Expand All @@ -87,7 +124,7 @@ abstract class HtmlFormatter extends Formatter {
* @returns {string} the CSS string
*/
cssString(scope = ''): string {
return scopeCss(this.defaultCss, scope);
return scopeCss(this.cssObject, scope);
}

/**
Expand Down
69 changes: 38 additions & 31 deletions src/formatter/html_table_formatter.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,43 @@
import HtmlFormatter, { Template, CSS } from './html_formatter';
import HtmlFormatter, { Template, CSS, HtmlTemplateCssClasses } from './html_formatter';
import template from './templates/html_table_formatter';
import { scopeCss } from '../utilities';

const defaultCss: CSS = {
'h1': {
fontSize: '1.5em',
},
'h2': {
fontSize: '1.1em',
},
'table': {
borderSpacing: '0',
color: 'inherit',
},
'td': {
padding: '3px 0',
},
'.chord:not(:last-child)': {
paddingRight: '10px',
},
'.paragraph': {
marginBottom: '1em',
},
};
function defaultCss(cssClasses: HtmlTemplateCssClasses): CSS {
const {
annotation,
chord,
comment,
labelWrapper,
line,
literal,
literalContents,
lyrics,
paragraph,
row,
subtitle,
title,
} = cssClasses;

/**
* Generates basic CSS, scoped within the provided selector, to use with output generated by {@link HtmlTableFormatter}
* @param scope the CSS scope to use, for example `.chordSheetViewer`
* @returns {string} the CSS string
*/
export function scopedCss(scope: string): string {
return scopeCss(defaultCss, scope);
return {
[`.${title}`]: {
fontSize: '1.5em',
},
[`.${subtitle}`]: {
fontSize: '1.1em',
},
[`.${row}, .${line}, .${literal}`]: {
borderSpacing: '0',
color: 'inherit',
},
[`.${annotation}, .${chord}, .${comment}, .${literalContents}, .${labelWrapper}, .${literal}, .${lyrics}`]: {
padding: '3px 0',
},
[`.${chord}:not(:last-child)`]: {
paddingRight: '10px',
},
[`.${paragraph}`]: {
marginBottom: '1em',
},
};
}

/**
Expand All @@ -43,7 +50,7 @@ class HtmlTableFormatter extends HtmlFormatter {
}

get defaultCss(): CSS {
return defaultCss;
return defaultCss(this.cssClasses);
}
}

Expand Down
Loading

0 comments on commit 58fc190

Please # to comment.