diff options
| author | Stephen Kraus <8003332+stephenmk@users.noreply.github.com> | 2022-05-14 08:59:38 -0500 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-05-14 09:59:38 -0400 | 
| commit | 6a74746113c724e750620d10b58ad6bac94060c9 (patch) | |
| tree | 9e3cfbc7e7a5bc3b3c243ab1f2608aa46c99a193 | |
| parent | e9843f67cb6b3ca903015d85e4c1e2ed18557cb0 (diff) | |
Add new structured content features: lists and the HTML `lang` attribute (#2129)
* Add support for structured content lists and `list-style-type` style
A full list of supported style types is documented here:
https://developer.mozilla.org/en-US/docs/Web/CSS/list-style-type
There's nothing in this code preventing a term bank from assigning,
for example, a `list-style-type` style to a `div` element, but it
doesn't seem like browsers will complain about things like that.
* Add support for `lang` attribute in structured content
Support added for the following node types:
"ruby", "rt", "rp", "table", "thead", "tbody", "tfoot", "tr", "td",
"th", "span", "div", "ol", "ul", "li", "a"
I couldn't get it to work for the alt-hover text on "img" tags.
Tests are included in the file
"test/data/dictionaries/valid-dictionary/term_bank_1.json"
* Add styles for structured content lists
* Add override rules for new structured-content list styles
see: https://github.com/FooSoft/yomichan/pull/2129
Co-authored-by: stephenmk <stephenmk@users.noreply.github.com>
| -rw-r--r-- | dev/data/structured-content-overrides.css | 13 | ||||
| -rw-r--r-- | ext/css/structured-content.css | 18 | ||||
| -rw-r--r-- | ext/data/schemas/dictionary-term-bank-v3-schema.json | 22 | ||||
| -rw-r--r-- | ext/js/display/sandbox/structured-content-generator.js | 13 | ||||
| -rw-r--r-- | test/data/dictionaries/valid-dictionary1/term_bank_1.json | 99 | 
5 files changed, 162 insertions, 3 deletions
| diff --git a/dev/data/structured-content-overrides.css b/dev/data/structured-content-overrides.css index 9fd08f8f..811ed9cf 100644 --- a/dev/data/structured-content-overrides.css +++ b/dev/data/structured-content-overrides.css @@ -61,3 +61,16 @@      display: none;      /* remove-property background-color vertical-align width height margin-left background-color position */  } +.gloss-sc-ol, +.gloss-sc-ul { +    /* remove-property padding-left */ +} +:root[data-glossary-layout-mode=compact] .gloss-sc-ul[data-sc-content=glossary] { +    /* remove-rule */ +} +:root[data-glossary-layout-mode=compact] .gloss-sc-ul[data-sc-content=glossary] .gloss-sc-li { +    /* remove-rule */ +} +:root[data-glossary-layout-mode=compact] .gloss-sc-ul[data-sc-content=glossary] .gloss-sc-li:not(:first-child)::before { +    /* remove-rule */ +} diff --git a/ext/css/structured-content.css b/ext/css/structured-content.css index 485527e5..5e863318 100644 --- a/ext/css/structured-content.css +++ b/ext/css/structured-content.css @@ -235,3 +235,21 @@      padding: 0.25em;      vertical-align: top;  } +.gloss-sc-ol, +.gloss-sc-ul { +    padding-left: var(--list-padding2); +} +:root[data-glossary-layout-mode=compact] .gloss-sc-ul[data-sc-content=glossary] { +    display: inline; +    list-style: none; +    padding-left: 0; +} +:root[data-glossary-layout-mode=compact] .gloss-sc-ul[data-sc-content=glossary] .gloss-sc-li { +    display: inline; +} +:root[data-glossary-layout-mode=compact] .gloss-sc-ul[data-sc-content=glossary] .gloss-sc-li:not(:first-child)::before { +    white-space: pre-wrap; +    content: var(--compact-list-separator); +    display: inline; +    color: var(--text-color-light3); +} diff --git a/ext/data/schemas/dictionary-term-bank-v3-schema.json b/ext/data/schemas/dictionary-term-bank-v3-schema.json index 268a2c11..12c2e4f3 100644 --- a/ext/data/schemas/dictionary-term-bank-v3-schema.json +++ b/ext/data/schemas/dictionary-term-bank-v3-schema.json @@ -51,6 +51,10 @@                                  },                                  "data": {                                      "$ref": "#/definitions/structuredContentData" +                                }, +                                "lang": { +                                    "type": "string", +                                    "description": "Defines the language of an element in the format defined by RFC 5646."                                  }                              }                          }, @@ -82,6 +86,10 @@                                  },                                  "style": {                                      "$ref": "#/definitions/structuredContentStyle" +                                }, +                                "lang": { +                                    "type": "string", +                                    "description": "Defines the language of an element in the format defined by RFC 5646."                                  }                              }                          }, @@ -95,7 +103,7 @@                              "properties": {                                  "tag": {                                      "type": "string", -                                    "enum": ["span", "div"] +                                    "enum": ["span", "div", "ol", "ul", "li"]                                  },                                  "content": {                                      "$ref": "#/definitions/structuredContent" @@ -105,6 +113,10 @@                                  },                                  "style": {                                      "$ref": "#/definitions/structuredContentStyle" +                                }, +                                "lang": { +                                    "type": "string", +                                    "description": "Defines the language of an element in the format defined by RFC 5646."                                  }                              }                          }, @@ -206,6 +218,10 @@                                      "type": "string",                                      "description": "The URL for the link. URLs starting with a ? are treated as internal links to other dictionary content.",                                      "pattern": "^(?:https?:|\\?)[\\w\\W]*" +                                }, +                                "lang": { +                                    "type": "string", +                                    "description": "Defines the language of an element in the format defined by RFC 5646."                                  }                              }                          } @@ -276,6 +292,10 @@                  "marginBottom": {                      "type": "number",                      "default": 0 +                }, +                "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 eb847d07..6102cfdd 100644 --- a/ext/js/display/sandbox/structured-content-generator.js +++ b/ext/js/display/sandbox/structured-content-generator.js @@ -56,6 +56,9 @@ class StructuredContentGenerator {                  return this._createStructuredContentElement(tag, content, dictionary, 'table-cell', true, true);              case 'div':              case 'span': +            case 'ol': +            case 'ul': +            case 'li':                  return this._createStructuredContentElement(tag, content, dictionary, 'simple', true, true);              case 'img':                  return this.createDefinitionImage(content, dictionary); @@ -204,8 +207,9 @@ class StructuredContentGenerator {      _createStructuredContentElement(tag, content, dictionary, type, hasChildren, hasStyle) {          const node = this._createElement(tag, `gloss-sc-${tag}`); -        const {data} = content; +        const {data, lang} = content;          if (typeof data === 'object' && data !== null) { this._setElementDataset(node, data); } +        if (typeof lang === 'string') { node.lang = lang; }          switch (type) {              case 'table-cell':                  { @@ -239,7 +243,8 @@ class StructuredContentGenerator {              marginTop,              marginLeft,              marginRight, -            marginBottom +            marginBottom, +            listStyleType          } = contentStyle;          if (typeof fontStyle === 'string') { style.fontStyle = fontStyle; }          if (typeof fontWeight === 'string') { style.fontWeight = fontWeight; } @@ -254,6 +259,7 @@ class StructuredContentGenerator {          if (typeof marginLeft === 'number') { style.marginLeft = `${marginLeft}em`; }          if (typeof marginRight === 'number') { style.marginRight = `${marginRight}em`; }          if (typeof marginBottom === 'number') { style.marginBottom = `${marginBottom}em`; } +        if (typeof listStyleType === 'string') { style.listStyleType = listStyleType; }      }      _createLinkElement(content, dictionary) { @@ -269,6 +275,9 @@ class StructuredContentGenerator {          const text = this._createElement('span', 'gloss-link-text');          node.appendChild(text); +        const {lang} = content; +        if (typeof lang === 'string') { node.lang = lang; } +          const child = this.createStructuredContent(content.content, dictionary);          if (child !== null) { text.appendChild(child); } diff --git a/test/data/dictionaries/valid-dictionary1/term_bank_1.json b/test/data/dictionaries/valid-dictionary1/term_bank_1.json index e7506255..cdff4028 100644 --- a/test/data/dictionaries/valid-dictionary1/term_bank_1.json +++ b/test/data/dictionaries/valid-dictionary1/term_bank_1.json @@ -204,6 +204,105 @@                          "external link"                      ]                  } +            ]}, +            {"type": "structured-content", "content": [ +                {"tag": "ul", "content": [ +                    {"tag": "li", "content": "Unordered list item 1"}, +                    {"tag": "li", "content": "Unordered list item 2"}, +                    {"tag": "li", "content": "Unordered list item 3"} +                ]} +            ]}, +            {"type": "structured-content", "content": [ +                {"tag": "ol", "content": [ +                    {"tag": "li", "content": "Ordered list item 1"}, +                    {"tag": "li", "content": "Ordered list item 2"}, +                    {"tag": "li", "content": "Ordered list item 3"} +                ]} +            ]}, +            {"type": "structured-content", "content": [ +                {"tag": "ol", "style": {"listStyleType": "hiragana-iroha"}, "content": [ +                    {"tag": "li", "content": "List item i"}, +                    {"tag": "li", "content": "List item ro"}, +                    {"tag": "li", "content": "List item ha"} +                ]} +            ]}, +            {"type": "structured-content", "content": [ +                {"tag": "ul", "content": [ +                    {"tag": "li", "style": {"listStyleType": "'⇄'"}, "content": ["【", {"tag": "a", "href": "?query=よみ&wildcards=off", "content": ["Antonym"]}, "】"]}, +                    {"tag": "li", "style": {"listStyleType": "'🔄'"}, "content": ["【", {"tag": "a", "href": "?query=よみ&wildcards=off", "content": ["References and is referenced by"]}, "】"]}, +                    {"tag": "li", "style": {"listStyleType": "'➡'"}, "content": ["【", {"tag": "a", "href": "?query=よみ&wildcards=off", "content": ["References"]}, "】"]}, +                    {"tag": "li", "style": {"listStyleType": "'⬅'"}, "content": ["【", {"tag": "a", "href": "?query=よみ&wildcards=off", "content": ["Referenced by"]}, "】"]} +                ]} +            ]}, +            {"type": "structured-content", "content": [ +                {"tag": "ol", "content": [ +                    {"tag": "li", "style": {"listStyleType": "'①'"}, "content": "まるいち"}, +                    {"tag": "li", "style": {"listStyleType": "'②'"}, "content": "まるに"}, +                    {"tag": "li", "style": {"listStyleType": "'③'"}, "content": "まるさん"}, +                    {"tag": "li", "style": {"listStyleType": "'④'"}, "content": "まるよん"} +                ]} +            ]}, +            {"type": "structured-content", "content": [ +                {"tag": "div", "lang": "?????", "style": {"fontSize": "xxx-large"}, "content": "直次茶冷 (invalid lang)"}, +                {"tag": "div", "lang": "ja-JP", "style": {"fontSize": "xxx-large"}, "content": "直次茶冷 (lang=ja-JP)"}, +                {"tag": "div", "lang": "zh-CN", "style": {"fontSize": "xxx-large"}, "content": "直次茶冷 (lang=zh-CN)"}, +                {"tag": "div", "lang": "zh-TW", "style": {"fontSize": "xxx-large"}, "content": "直次茶冷 (lang=zh-TW)"} +            ]}, +            {"type": "structured-content", "content": [ +                {"tag": "ul", "style": {"listStyleType": "japanese-formal"}, "content": [ +                    {"tag": "li", "content": ["【", {"tag": "a", "href": "?query=直次茶冷&wildcards=off", "content": "直次茶冷"}, "】(default)"]}, +                    {"tag": "li", "content": ["【", {"tag": "a", "href": "?query=直次茶冷&wildcards=off", "content": "直次茶冷", "lang": "ja"}, "】(lang=ja)"]}, +                    {"tag": "li", "content": ["【", {"tag": "a", "href": "?query=直次茶冷&wildcards=off", "content": "直次茶冷", "lang": "zh-CN"}, "】(lang=zh-CN)"]}, +                    {"tag": "li", "content": ["【", {"tag": "a", "href": "?query=直次茶冷&wildcards=off", "content": "直次茶冷", "lang": "zh-TW"}, "】(lang=zh-TW)"]} +                ]} +            ]}, +            {"type": "structured-content", "content": [ +                {"tag": "table", "lang": "", "content": [ +                    {"tag": "thead", "content": [ +                        {"tag": "tr", "content": [ +                            {"tag": "th", "content": "JP"}, +                            {"tag": "th", "content": "SC"}, +                            {"tag": "th", "content": "TC"}, +                            {"tag": "th", "content": "??"} +                        ]} +                    ]}, +                    {"tag": "tbody", "content": [ +                        {"tag": "tr", "content": [ +                            {"tag": "td", "lang": "ja-JP", "content": "直次茶冷"}, +                            {"tag": "td", "lang": "zh-CN", "content": "直次茶冷"}, +                            {"tag": "td", "lang": "zh-TW", "content": "直次茶冷"}, +                            {"tag": "td", "content": "直次茶冷"} +                        ]} +                    ]} +                ]} +            ]}, +            {"type": "structured-content", "content": [ +                {"tag": "table", "lang": "ja", "content": [ +                    {"tag": "thead", "content": [ +                        {"tag": "tr", "content": [ +                            {"tag": "th", "content": "lang=ja applied to whole table"} +                        ]} +                    ]}, +                    {"tag": "tbody", "content": [ +                        {"tag": "tr", "content": [ +                            {"tag": "td", "content": "直次茶冷"} +                        ]} +                    ]} +                ]} +            ]}, +            {"type": "structured-content", "content": [ +                {"tag": "table", "lang": "zh-CN", "content": [ +                    {"tag": "thead", "content": [ +                        {"tag": "tr", "content": [ +                            {"tag": "th", "content": "lang=zh-CN applied to whole table"} +                        ]} +                    ]}, +                    {"tag": "tbody", "content": [ +                        {"tag": "tr", "content": [ +                            {"tag": "td", "content": "直次茶冷"} +                        ]} +                    ]} +                ]}              ]}          ],          100, "P E1" |