diff options
author | Stephen Kraus <8003332+stephenmk@users.noreply.github.com> | 2023-12-26 23:54:41 -0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-27 05:54:41 +0000 |
commit | adc17f4221a0e48da4450695c655632d37eee2a8 (patch) | |
tree | d8d56dc1dfae39e6d7ba47c872fabb7110375263 | |
parent | 8e95d99e6a1891eaf9331f6384fa3b1a1065b871 (diff) |
Add support for more HTML attributes and style declarations in structured content (#450)
* Add support for more HTMl attributes and style declarations
* Update test term to use new `margin` property
* Allow string values for 'padding' and 'margin' properties
* Remove newly added default 'unset' values from term bank schema
---------
Co-authored-by: stephenmk <stephenmk@users.noreply.github.com>
-rw-r--r-- | .eslintrc.json | 3 | ||||
-rw-r--r-- | ext/data/schemas/dictionary-term-bank-v3-schema.json | 69 | ||||
-rw-r--r-- | ext/js/display/sandbox/structured-content-generator.js | 45 | ||||
-rw-r--r-- | test/data/dictionaries/valid-dictionary1/term_bank_2.json | 739 | ||||
-rw-r--r-- | test/data/json.json | 6 | ||||
-rw-r--r-- | test/database.test.js | 22 | ||||
-rw-r--r-- | types/ext/structured-content.d.ts | 33 |
7 files changed, 904 insertions, 13 deletions
diff --git a/.eslintrc.json b/.eslintrc.json index abef43b1..6de43e2d 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -559,7 +559,8 @@ }, { "files": [ - "test/data/dictionaries/valid-dictionary1/term_bank_1.json" + "test/data/dictionaries/valid-dictionary1/term_bank_1.json", + "test/data/dictionaries/valid-dictionary1/term_bank_2.json" ], "rules": { "jsonc/array-element-newline": [ diff --git a/ext/data/schemas/dictionary-term-bank-v3-schema.json b/ext/data/schemas/dictionary-term-bank-v3-schema.json index 547bde49..f4b4faa5 100644 --- a/ext/data/schemas/dictionary-term-bank-v3-schema.json +++ b/ext/data/schemas/dictionary-term-bank-v3-schema.json @@ -115,6 +115,10 @@ "style": { "$ref": "#/definitions/structuredContentStyle" }, + "title": { + "type": "string", + "description": "Hover text for the element." + }, "lang": { "type": "string", "description": "Defines the language of an element in the format defined by RFC 5646." @@ -263,6 +267,12 @@ "type": "string", "default": "medium" }, + "color": { + "type": "string" + }, + "backgroundColor": { + "type": "string" + }, "textDecorationLine": { "oneOf": [ { @@ -280,6 +290,26 @@ } ] }, + "textDecorationStyle": { + "type": "string", + "enum": ["solid", "double", "dotted", "dashed", "wavy"], + "default": "solid" + }, + "textDecorationColor": { + "type": "string" + }, + "borderColor": { + "type": "string" + }, + "borderStyle": { + "type": "string" + }, + "borderRadius": { + "type": "string" + }, + "borderWidth": { + "type": "string" + }, "verticalAlign": { "type": "string", "enum": ["baseline", "sub", "super", "text-top", "text-bottom", "middle", "top", "bottom"], @@ -290,22 +320,53 @@ "enum": ["start", "end", "left", "right", "center", "justify", "justify-all", "match-parent"], "default": "start" }, + "margin": { + "type": "string" + }, "marginTop": { - "type": "number", + "type": ["number", "string"], "default": 0 }, "marginLeft": { - "type": "number", + "type": ["number", "string"], "default": 0 }, "marginRight": { - "type": "number", + "type": ["number", "string"], "default": 0 }, "marginBottom": { - "type": "number", + "type": ["number", "string"], "default": 0 }, + "padding": { + "type": "string" + }, + "paddingTop": { + "type": "string" + }, + "paddingLeft": { + "type": "string" + }, + "paddingRight": { + "type": "string" + }, + "paddingBottom": { + "type": "string" + }, + "wordBreak": { + "type": "string", + "enum": ["normal", "break-all", "keep-all"], + "default": "normal" + }, + "whiteSpace": { + "type": "string", + "default": "normal" + }, + "cursor": { + "type": "string", + "default": "auto" + }, "listStyleType": { "type": "string", "default": "disc" diff --git a/ext/js/display/sandbox/structured-content-generator.js b/ext/js/display/sandbox/structured-content-generator.js index ad63667a..b74674fc 100644 --- a/ext/js/display/sandbox/structured-content-generator.js +++ b/ext/js/display/sandbox/structured-content-generator.js @@ -321,10 +321,11 @@ export class StructuredContentGenerator { break; } if (hasStyle) { - const {style} = /** @type {import('structured-content').StyledElement} */ (content); + const {style, title} = /** @type {import('structured-content').StyledElement} */ (content); if (typeof style === 'object' && style !== null) { this._setStructuredContentElementStyle(node, style); } + if (typeof title === 'string') { node.title = title; } } if (hasChildren) { this._appendStructuredContent(node, content.content, dictionary, language); @@ -342,18 +343,37 @@ export class StructuredContentGenerator { fontStyle, fontWeight, fontSize, + color, + backgroundColor, textDecorationLine, + textDecorationStyle, + textDecorationColor, + borderColor, + borderStyle, + borderRadius, + borderWidth, verticalAlign, textAlign, + margin, marginTop, marginLeft, marginRight, marginBottom, + padding, + paddingTop, + paddingLeft, + paddingRight, + paddingBottom, + wordBreak, + whiteSpace, + cursor, listStyleType } = contentStyle; if (typeof fontStyle === 'string') { style.fontStyle = fontStyle; } if (typeof fontWeight === 'string') { style.fontWeight = fontWeight; } if (typeof fontSize === 'string') { style.fontSize = fontSize; } + if (typeof color === 'string') { style.color = color; } + if (typeof backgroundColor === 'string') { style.backgroundColor = backgroundColor; } if (typeof verticalAlign === 'string') { style.verticalAlign = verticalAlign; } if (typeof textAlign === 'string') { style.textAlign = textAlign; } if (typeof textDecorationLine === 'string') { @@ -361,10 +381,33 @@ export class StructuredContentGenerator { } else if (Array.isArray(textDecorationLine)) { style.textDecoration = textDecorationLine.join(' '); } + if (typeof textDecorationStyle === 'string') { + style.textDecorationStyle = textDecorationStyle; + } + if (typeof textDecorationColor === 'string') { + style.textDecorationColor = textDecorationColor; + } + if (typeof borderColor === 'string') { style.borderColor = borderColor; } + if (typeof borderStyle === 'string') { style.borderStyle = borderStyle; } + if (typeof borderRadius === 'string') { style.borderRadius = borderRadius; } + if (typeof borderWidth === 'string') { style.borderWidth = borderWidth; } + if (typeof margin === 'string') { style.margin = margin; } if (typeof marginTop === 'number') { style.marginTop = `${marginTop}em`; } + if (typeof marginTop === 'string') { style.marginTop = marginTop; } if (typeof marginLeft === 'number') { style.marginLeft = `${marginLeft}em`; } + if (typeof marginLeft === 'string') { style.marginLeft = marginLeft; } if (typeof marginRight === 'number') { style.marginRight = `${marginRight}em`; } + if (typeof marginRight === 'string') { style.marginRight = marginRight; } if (typeof marginBottom === 'number') { style.marginBottom = `${marginBottom}em`; } + if (typeof marginBottom === 'string') { style.marginBottom = marginBottom; } + if (typeof padding === 'string') { style.padding = padding; } + if (typeof paddingTop === 'string') { style.paddingTop = paddingTop; } + if (typeof paddingLeft === 'string') { style.paddingLeft = paddingLeft; } + if (typeof paddingRight === 'string') { style.paddingRight = paddingRight; } + if (typeof paddingBottom === 'string') { style.paddingBottom = paddingBottom; } + if (typeof wordBreak === 'string') { style.wordBreak = wordBreak; } + if (typeof whiteSpace === 'string') { style.whiteSpace = whiteSpace; } + if (typeof cursor === 'string') { style.cursor = cursor; } if (typeof listStyleType === 'string') { style.listStyleType = listStyleType; } } diff --git a/test/data/dictionaries/valid-dictionary1/term_bank_2.json b/test/data/dictionaries/valid-dictionary1/term_bank_2.json new file mode 100644 index 00000000..d46b4c14 --- /dev/null +++ b/test/data/dictionaries/valid-dictionary1/term_bank_2.json @@ -0,0 +1,739 @@ +[ + [ + "発条", + "ばね", + "", + "", + 0, + [ + { + "type": "structured-content", + "content": { + "tag": "ul", + "lang": "ja", + "style": { + "listStyleType": "\"*\"" + }, + "content": [ + { + "tag": "li", + "content": [ + { + "tag": "span", + "title": "noun (common) (futsuumeishi)", + "style": { + "fontSize": "0.7em", + "fontWeight": "bold", + "padding": "0.15em 0.3em 0.3em 0.3em", + "wordBreak": "keep-all", + "borderRadius": "0.3em", + "verticalAlign": "text-bottom", + "backgroundColor": "#565656", + "color": "white", + "cursor": "help", + "margin": "0em 0.25em 0em 0em" + }, + "data": { + "code": "n" + }, + "content": "noun" + }, + { + "tag": "span", + "title": "word usually written using kana alone", + "style": { + "fontSize": "0.7em", + "fontWeight": "bold", + "paddingTop": "0.15em", + "paddingRight": "0.3em", + "paddingBottom": "0.3em", + "paddingLeft": "0.3em", + "wordBreak": "keep-all", + "borderRadius": "0.3em", + "verticalAlign": "text-bottom", + "backgroundColor": "brown", + "color": "white", + "cursor": "help", + "marginRight": "0.25em" + }, + "data": { + "code": "uk" + }, + "content": "kana" + }, + { + "tag": "ol", + "content": { + "tag": "li", + "style": { + "listStyleType": "\"① \"" + }, + "data": { + "sense-number": "1" + }, + "content": [ + { + "tag": "ul", + "data": { + "content": "glossary" + }, + "content": { + "tag": "li", + "content": "spring" + } + }, + { + "tag": "ul", + "style": { + "marginBottom": 0.5 + }, + "content": [ + { + "tag": "li", + "lang": "ja", + "style": { + "listStyleType": "circle", + "fontSize": "120%" + }, + "data": { + "content": "example-sentence-a" + }, + "content": [ + "その", + { + "tag": "span", + "style": { + "textDecorationLine": "underline", + "textDecorationStyle": "wavy", + "textDecorationColor": "red" + }, + "content": "ばね" + }, + { + "tag": "ruby", + "content": [ + "1", + { + "tag": "rt", + "content": "いっ" + } + ] + }, + { + "tag": "ruby", + "content": [ + "個", + { + "tag": "rt", + "content": "こ" + } + ] + }, + "で", + { + "tag": "ruby", + "content": [ + "車", + { + "tag": "rt", + "content": "くるま" + } + ] + }, + "の", + { + "tag": "ruby", + "content": [ + "全", + { + "tag": "rt", + "content": "ぜん" + } + ] + }, + { + "tag": "ruby", + "content": [ + "重", + { + "tag": "rt", + "content": "じゅう" + } + ] + }, + { + "tag": "ruby", + "content": [ + "量", + { + "tag": "rt", + "content": "りょう" + } + ] + }, + "を", + { + "tag": "ruby", + "content": [ + "支", + { + "tag": "rt", + "content": "ささ" + } + ] + }, + "えている。" + ] + }, + { + "tag": "li", + "lang": "en", + "style": { + "listStyleType": "none", + "fontSize": "70%" + }, + "data": { + "content": "example-sentence-b" + }, + "content": "That one spring carries the whole weight of the car." + } + ] + } + ] + } + } + ] + }, + { + "tag": "li", + "content": [ + { + "tag": "span", + "title": "noun (common) (futsuumeishi)", + "style": { + "fontSize": "0.7em", + "fontWeight": "bold", + "padding": "0.15em 0.3em 0.3em 0.3em", + "wordBreak": "keep-all", + "borderRadius": "0.3em", + "verticalAlign": "text-bottom", + "backgroundColor": "#565656", + "color": "white", + "cursor": "help", + "marginRight": 0.25 + }, + "data": { + "code": "n" + }, + "content": "noun" + }, + { + "tag": "span", + "title": "word usually written using kana alone", + "style": { + "fontSize": "0.7em", + "fontWeight": "bold", + "padding": "0.15em 0.3em 0.3em 0.3em", + "wordBreak": "keep-all", + "borderRadius": "0.3em", + "verticalAlign": "text-bottom", + "backgroundColor": "brown", + "color": "white", + "cursor": "help", + "marginRight": 0.25 + }, + "data": { + "code": "uk" + }, + "content": "kana" + }, + { + "tag": "span", + "title": "valid only for these forms and/or readings", + "style": { + "cursor": "help" + }, + "content": [ + { + "tag": "span", + "style": { + "color": "red" + }, + "content": "[" + }, + "ばね・バネ only", + { + "tag": "span", + "style": { + "color": "red" + }, + "content": "]" + } + ] + }, + { + "tag": "ol", + "content": [ + { + "tag": "li", + "style": { + "listStyleType": "\"② \"" + }, + "data": { + "sense-number": "2" + }, + "content": { + "tag": "ul", + "data": { + "content": "glossary" + }, + "content": [ + { + "tag": "li", + "content": "spring (in one's legs)" + }, + { + "tag": "li", + "content": "bounce" + } + ] + } + }, + { + "tag": "li", + "style": { + "listStyleType": "\"③ \"" + }, + "data": { + "sense-number": "3" + }, + "content": { + "tag": "ul", + "data": { + "content": "glossary" + }, + "content": [ + { + "tag": "li", + "content": "springboard" + }, + { + "tag": "li", + "content": "impetus" + } + ] + } + } + ] + } + ] + }, + { + "tag": "li", + "data": { + "content": "forms" + }, + "content": [ + { + "tag": "span", + "title": "spelling and reading variants", + "style": { + "fontSize": "0.7em", + "fontWeight": "bold", + "padding": "0.15em 0.3em 0.3em 0.3em", + "wordBreak": "keep-all", + "borderRadius": "0.3em", + "verticalAlign": "text-bottom", + "backgroundColor": "#565656", + "color": "white", + "cursor": "help", + "marginRight": 0.25 + }, + "content": "forms" + }, + { + "tag": "table", + "content": [ + { + "tag": "tr", + "content": [ + { + "tag": "th", + "style": { + "textAlign": "center", + "fontWeight": "normal" + } + }, + { + "tag": "th", + "style": { + "textAlign": "center", + "fontWeight": "normal", + "whiteSpace": "nowrap" + }, + "content": "発条" + }, + { + "tag": "th", + "style": { + "textAlign": "center", + "fontWeight": "normal", + "whiteSpace": "nowrap" + }, + "content": "弾機" + }, + { + "tag": "th", + "style": { + "textAlign": "center", + "fontWeight": "normal", + "whiteSpace": "nowrap" + }, + "content": "撥条" + }, + { + "tag": "th", + "style": { + "textAlign": "center", + "fontWeight": "normal" + }, + "content": { + "tag": "span", + "title": "no associated kanji forms", + "style": { + "color": "red", + "cursor": "help" + }, + "content": "∅" + } + } + ] + }, + { + "tag": "tr", + "content": [ + { + "tag": "th", + "style": { + "fontWeight": "normal" + }, + "content": { + "tag": "span", + "title": "gikun (meaning as reading) or jukujikun (special kanji reading)", + "style": { + "cursor": "help" + }, + "content": [ + { + "tag": "span", + "style": { + "color": "red" + }, + "content": "{" + }, + "ばね", + { + "tag": "span", + "style": { + "color": "red" + }, + "content": "}" + } + ] + } + }, + { + "tag": "td", + "style": { + "textAlign": "center" + }, + "content": { + "tag": "span", + "title": "high priority form", + "style": { + "padding": "0.15em 0.2em", + "borderRadius": "100%", + "cursor": "help", + "backgroundColor": "green", + "color": "white" + }, + "content": "優" + } + }, + { + "tag": "td", + "style": { + "textAlign": "center" + }, + "content": { + "tag": "span", + "title": "valid form/reading combination", + "style": { + "padding": "0.15em 0.2em", + "borderRadius": "100%", + "cursor": "help", + "backgroundColor": "transparent", + "borderStyle": "solid", + "borderWidth": "0.75px" + }, + "content": "可" + } + }, + { + "tag": "td", + "style": { + "textAlign": "center" + }, + "content": { + "tag": "span", + "title": "rarely used form", + "style": { + "padding": "0.15em 0.2em", + "borderRadius": "100%", + "cursor": "help", + "backgroundColor": "purple", + "color": "white" + }, + "content": "稀" + } + }, + { + "tag": "td", + "style": { + "textAlign": "center" + } + } + ] + }, + { + "tag": "tr", + "content": [ + { + "tag": "th", + "style": { + "fontWeight": "normal" + }, + "content": "ぜんまい" + }, + { + "tag": "td", + "style": { + "textAlign": "center" + }, + "content": { + "tag": "span", + "title": "valid form/reading combination", + "style": { + "padding": "0.15em 0.2em", + "borderRadius": "100%", + "cursor": "help", + "backgroundColor": "transparent", + "borderStyle": "solid", + "borderWidth": "0.75px" + }, + "content": "可" + } + }, + { + "tag": "td", + "style": { + "textAlign": "center" + } + }, + { + "tag": "td", + "style": { + "textAlign": "center" + }, + "content": { + "tag": "span", + "title": "rarely used form", + "style": { + "padding": "0.15em 0.2em", + "borderRadius": "100%", + "cursor": "help", + "backgroundColor": "purple", + "color": "white" + }, + "content": "稀" + } + }, + { + "tag": "td", + "style": { + "textAlign": "center" + } + } + ] + }, + { + "tag": "tr", + "content": [ + { + "tag": "th", + "style": { + "fontWeight": "normal" + }, + "content": "はつじょう" + }, + { + "tag": "td", + "style": { + "textAlign": "center" + }, + "content": { + "tag": "span", + "title": "valid form/reading combination", + "style": { + "padding": "0.15em 0.2em", + "borderRadius": "100%", + "cursor": "help", + "backgroundColor": "transparent", + "borderStyle": "solid", + "borderWidth": "0.75px" + }, + "content": "可" + } + }, + { + "tag": "td", + "style": { + "textAlign": "center" + } + }, + { + "tag": "td", + "style": { + "textAlign": "center" + }, + "content": { + "tag": "span", + "title": "rarely used form", + "style": { + "padding": "0.15em 0.2em", + "borderRadius": "100%", + "cursor": "help", + "backgroundColor": "purple", + "color": "white" + }, + "content": "稀" + } + }, + { + "tag": "td", + "style": { + "textAlign": "center" + } + } + ] + }, + { + "tag": "tr", + "content": [ + { + "tag": "th", + "style": { + "fontWeight": "normal" + }, + "content": "だんき" + }, + { + "tag": "td", + "style": { + "textAlign": "center" + } + }, + { + "tag": "td", + "style": { + "textAlign": "center" + }, + "content": { + "tag": "span", + "title": "valid form/reading combination", + "style": { + "padding": "0.15em 0.2em", + "borderRadius": "100%", + "cursor": "help", + "backgroundColor": "transparent", + "borderStyle": "solid", + "borderWidth": "0.75px" + }, + "content": "可" + } + }, + { + "tag": "td", + "style": { + "textAlign": "center" + } + }, + { + "tag": "td", + "style": { + "textAlign": "center" + } + } + ] + }, + { + "tag": "tr", + "content": [ + { + "tag": "th", + "style": { + "fontWeight": "normal" + }, + "content": "バネ" + }, + { + "tag": "td", + "style": { + "textAlign": "center" + } + }, + { + "tag": "td", + "style": { + "textAlign": "center" + } + }, + { + "tag": "td", + "style": { + "textAlign": "center" + } + }, + { + "tag": "td", + "style": { + "textAlign": "center" + }, + "content": { + "tag": "span", + "title": "valid form/reading combination", + "style": { + "padding": "0.15em 0.2em", + "borderRadius": "100%", + "cursor": "help", + "backgroundColor": "transparent", + "borderStyle": "solid", + "borderWidth": "0.75px" + }, + "content": "可" + } + } + ] + } + ] + } + ] + } + ] + } + } + ], + 1099490, + "" + ] +] diff --git a/test/data/json.json b/test/data/json.json index 1f856033..abe2e339 100644 --- a/test/data/json.json +++ b/test/data/json.json @@ -166,6 +166,12 @@ "schema": "ext/data/schemas/dictionary-term-bank-v3-schema.json" }, { + "path": "test/data/dictionaries/valid-dictionary1/term_bank_2.json", + "typeFile": "types/ext/dictionary-data.d.ts", + "type": "TermV3Array", + "schema": "ext/data/schemas/dictionary-term-bank-v3-schema.json" + }, + { "path": "test/data/dictionaries/valid-dictionary1/term_meta_bank_1.json", "typeFile": "types/ext/dictionary-data.d.ts", "type": "TermMetaArray", diff --git a/test/database.test.js b/test/database.test.js index c99cf57c..7c3d5606 100644 --- a/test/database.test.js +++ b/test/database.test.js @@ -165,7 +165,7 @@ async function testDatabase1() { media: {total: 6}, tagMeta: {total: 15}, termMeta: {total: 38, freq: 31, pitch: 7}, - terms: {total: 21} + terms: {total: 22} } }; @@ -192,8 +192,8 @@ async function testDatabase1() { true ); expect(counts).toStrictEqual({ - counts: [{kanji: 2, kanjiMeta: 6, terms: 21, termMeta: 38, tagMeta: 15, media: 6}], - total: {kanji: 2, kanjiMeta: 6, terms: 21, termMeta: 38, tagMeta: 15, media: 6} + counts: [{kanji: 2, kanjiMeta: 6, terms: 22, termMeta: 38, tagMeta: 15, media: 6}], + total: {kanji: 2, kanjiMeta: 6, terms: 22, termMeta: 38, tagMeta: 15, media: 6} }); // Test find* functions @@ -559,6 +559,22 @@ async function testFindTermsBySequenceBulk1(database, mainDictionary) { { inputs: [ { + sequenceList: [1099490] + } + ], + expectedResults: { + total: 1, + terms: [ + ['発条', 1] + ], + readings: [ + ['ばね', 1] + ] + } + }, + { + inputs: [ + { sequenceList: [-1] } ], diff --git a/types/ext/structured-content.d.ts b/types/ext/structured-content.d.ts index 2144cd84..c9ad87f6 100644 --- a/types/ext/structured-content.d.ts +++ b/types/ext/structured-content.d.ts @@ -23,10 +23,14 @@ export type TextDecorationLine = 'underline' | 'overline' | 'line-through'; export type TextDecorationLineOrNone = 'none' | TextDecorationLine; +export type TextDecorationStyle = 'solid' | 'double' | 'dotted' | 'dashed' | 'wavy'; + export type FontStyle = 'normal' | 'italic'; export type FontWeight = 'normal' | 'bold'; +export type WordBreak = 'normal' | 'break-all' | 'keep-all'; + export type TextAlign = 'start' | 'end' | 'left' | 'right' | 'center' | 'justify' | 'justify-all' | 'match-parent'; export type SizeUnits = 'px' | 'em'; @@ -48,13 +52,30 @@ export type StructuredContentStyle = { fontStyle?: FontStyle; fontWeight?: FontWeight; fontSize?: string; + color?: string; + backgroundColor?: string; textDecorationLine?: TextDecorationLineOrNone | TextDecorationLine[]; + textDecorationStyle?: TextDecorationStyle; + textDecorationColor?: string; + borderColor?: string; + borderStyle?: string; + borderRadius?: string; + borderWidth?: string; verticalAlign?: VerticalAlign; textAlign?: TextAlign; - marginTop?: number; - marginLeft?: number; - marginRight?: number; - marginBottom?: number; + margin?: string; + marginTop?: number | string; + marginLeft?: number | string; + marginRight?: number | string; + marginBottom?: number | string; + padding?: string; + paddingTop?: string; + paddingLeft?: string; + paddingRight?: string; + paddingBottom?: string; + wordBreak?: WordBreak; + whiteSpace?: string; + cursor?: string; listStyleType?: string; }; @@ -100,6 +121,10 @@ export type StyledElement = { data?: Data; style?: StructuredContentStyle; /** + * Hover text for the element. + */ + title?: string; + /** * Defines the language of an element in the format defined by RFC 5646. */ lang?: string; |