diff options
-rw-r--r-- | ext/css/display.css | 75 | ||||
-rw-r--r-- | ext/data/schemas/dictionary-term-bank-v3-schema.json | 10 | ||||
-rw-r--r-- | ext/display-templates.html | 3 | ||||
-rw-r--r-- | ext/js/display/display-generator.js | 44 | ||||
-rw-r--r-- | ext/js/language/dictionary-importer.js | 7 |
5 files changed, 87 insertions, 52 deletions
diff --git a/ext/css/display.css b/ext/css/display.css index d9a2acd2..529e4249 100644 --- a/ext/css/display.css +++ b/ext/css/display.css @@ -1571,6 +1571,8 @@ button.definition-item-expansion-button:focus:focus-visible+.definition-item-con .gloss-image-link { cursor: inherit; color: inherit; + display: inline-block; + position: relative; } .gloss-image-link[href]:hover { cursor: pointer; @@ -1588,7 +1590,7 @@ button.definition-item-expansion-button:focus:focus-visible+.definition-item-con white-space: normal; color: var(--text-color-light3); } -.gloss-item[data-has-image=true][data-image-load-state=load-error] .gloss-image-container-overlay::after { +.gloss-image-link[data-has-image=true][data-image-load-state=load-error] .gloss-image-container-overlay::after { content: 'Image failed to load'; display: table-cell; width: 100%; @@ -1599,15 +1601,17 @@ button.definition-item-expansion-button:focus:focus-visible+.definition-item-con } .gloss-image { display: inline-block; + vertical-align: top; + object-fit: contain; + border: none; + outline: none; +} +.gloss-image-link[data-has-aspect-ratio=true] .gloss-image { position: absolute; left: 0; top: 0; width: 100%; height: 100%; - vertical-align: top; - object-fit: contain; - border: none; - outline: none; } .gloss-image:not([src]) { display: none; @@ -1619,13 +1623,15 @@ button.definition-item-expansion-button:focus:focus-visible+.definition-item-con image-rendering: pixelated; image-rendering: crisp-edges; } -.gloss-image-aspect-ratio-sizer { - content: ''; +.gloss-image-link[data-has-aspect-ratio=true] .gloss-image-aspect-ratio-sizer { display: inline-block; width: 0; vertical-align: top; font-size: 0; } +.gloss-image-link-text { + display: none; +} .gloss-image-link-text::before { content: '['; } @@ -1633,9 +1639,38 @@ button.definition-item-expansion-button:focus:focus-visible+.definition-item-con content: ']'; } .gloss-image-description { + display: block; white-space: pre-line; } +.gloss-image-link[data-collapsed=true] .gloss-image-container, +:root[data-glossary-layout-mode=compact] .gloss-image-link[data-collapsible=true] .gloss-image-container { + display: none; + position: absolute; + left: 0; + top: 100%; + z-index: 1; +} +.entry:nth-last-of-type(1):not(:nth-of-type(1)) .gloss-image-link[data-collapsed=true] .gloss-image-container, +:root[data-glossary-layout-mode=compact] .entry:nth-last-of-type(1):not(:nth-of-type(1)) .gloss-image-link[data-collapsible=true] .gloss-image-container { + bottom: 100%; + top: auto; +} +.gloss-image-link[data-collapsed=true]:hover .gloss-image-container, +.gloss-image-link[data-collapsed=true]:focus .gloss-image-container, +:root[data-glossary-layout-mode=compact] .gloss-image-link[data-collapsible=true]:hover .gloss-image-container, +:root[data-glossary-layout-mode=compact] .gloss-image-link[data-collapsible=true]:focus .gloss-image-container { + display: block; +} +.gloss-image-link[data-collapsed=true] .gloss-image-link-text, +:root[data-glossary-layout-mode=compact] .gloss-image-link[data-collapsible=true] .gloss-image-link-text { + display: inline; +} +.gloss-image-link[data-collapsed=true]~.gloss-image-description, +:root[data-glossary-layout-mode=compact] .gloss-image-description { + display: inline; +} + /* Kanji */ .kanji-glyph-container { @@ -2110,32 +2145,6 @@ button.footer-notification-close-button { color: var(--text-color-light3); } -:root[data-glossary-layout-mode=compact] .gloss-image-container { - display: none; - position: absolute; - left: 0; - top: 100%; - z-index: 1; -} -:root[data-glossary-layout-mode=compact] .entry:nth-last-of-type(1):not(:nth-of-type(1)) .gloss-image-container { - bottom: 100%; - top: auto; -} -:root[data-glossary-layout-mode=compact] .gloss-image-link { - position: relative; - display: inline-block; -} -:root[data-glossary-layout-mode=compact] .gloss-image-link:hover .gloss-image-container, -:root[data-glossary-layout-mode=compact] .gloss-image-link:focus .gloss-image-container { - display: block; -} -:root:not([data-glossary-layout-mode=compact]) .gloss-image-link-text { - display: none; -} -:root:not([data-glossary-layout-mode=compact]) .gloss-image-description { - display: block; -} - :root[data-popup-display-mode=full-width] .frame-resizer-container { display: none; } diff --git a/ext/data/schemas/dictionary-term-bank-v3-schema.json b/ext/data/schemas/dictionary-term-bank-v3-schema.json index 4790e561..2289dfd6 100644 --- a/ext/data/schemas/dictionary-term-bank-v3-schema.json +++ b/ext/data/schemas/dictionary-term-bank-v3-schema.json @@ -104,6 +104,16 @@ "type": "boolean", "description": "Whether or not the image should appear pixelated at sizes larger than the image's native resolution.", "default": false + }, + "collapsed": { + "type": "boolean", + "description": "Whether or not the image is collapsed by default.", + "default": false + }, + "collapsible": { + "type": "boolean", + "description": "Whether or not the image can be collapsed.", + "default": true } } } diff --git a/ext/display-templates.html b/ext/display-templates.html index 3262c15a..61d55638 100644 --- a/ext/display-templates.html +++ b/ext/display-templates.html @@ -57,7 +57,8 @@ </li></template> <template id="definition-disambiguation-template"><span class="definition-disambiguation"></span></template> <template id="gloss-item-template"><li class="gloss-item click-scannable"><span class="gloss-separator"> </span><span class="gloss-content"></span></li></template> -<template id="gloss-item-image-template"><li class="gloss-item click-scannable" data-has-image="true"><span class="gloss-separator"> </span><span class="gloss-content"><a class="gloss-image-link" target="_blank" rel="noreferrer noopener"><span class="gloss-image-container"><span class="gloss-image-aspect-ratio-sizer"></span><img class="gloss-image" alt=""><span class="gloss-image-container-overlay"></span></span><span class="gloss-image-link-text">Image</span></a> <span class="gloss-image-description"></span></span></li></template> +<template id="gloss-item-image-template"><a class="gloss-image-link" target="_blank" rel="noreferrer noopener"><span class="gloss-image-container"><span class="gloss-image-aspect-ratio-sizer"></span><img class="gloss-image" alt=""><span class="gloss-image-container-overlay"></span></span><span class="gloss-image-link-text">Image</span></a></template> +<template id="gloss-item-image-description-template"> <span class="gloss-image-description"></span></template> <template id="inflection-template"><span class="inflection"></span><span class="inflection-separator"> </span></template> <!-- Frequency templates --> diff --git a/ext/js/display/display-generator.js b/ext/js/display/display-generator.js index eb9c122d..299d730b 100644 --- a/ext/js/display/display-generator.js +++ b/ext/js/display/display-generator.js @@ -309,7 +309,26 @@ class DisplayGenerator { } _createTermDefinitionEntryImage(data, dictionary) { - const {path, width, height, preferredWidth, preferredHeight, title, description, pixelated} = data; + const {description} = data; + + const node = this._templates.instantiate('gloss-item'); + + const contentContainer = node.querySelector('.gloss-content'); + const image = this._createDefinitionImage(data, dictionary); + contentContainer.appendChild(image); + + if (typeof description === 'string') { + const fragment = this._templates.instantiateFragment('gloss-item-image-description'); + const container = fragment.querySelector('.gloss-image-description'); + this._setMultilineTextContent(container, description); + contentContainer.appendChild(fragment); + } + + return node; + } + + _createDefinitionImage(data, dictionary) { + const {path, width, height, preferredWidth, preferredHeight, title, pixelated, collapsed, collapsible} = data; const usedWidth = ( typeof preferredWidth === 'number' ? @@ -327,6 +346,9 @@ class DisplayGenerator { node.dataset.path = path; node.dataset.dictionary = dictionary; node.dataset.imageLoadState = 'not-loaded'; + node.dataset.hasAspectRatio = 'true'; + node.dataset.collapsed = typeof collapsed === 'boolean' ? `${collapsed}` : 'false'; + node.dataset.collapsible = typeof collapsible === 'boolean' ? `${collapsible}` : 'true'; const imageContainer = node.querySelector('.gloss-image-container'); imageContainer.style.width = `${usedWidth}em`; @@ -338,35 +360,29 @@ class DisplayGenerator { aspectRatioSizer.style.paddingTop = `${aspectRatio * 100.0}%`; const image = node.querySelector('img.gloss-image'); - const imageLink = node.querySelector('.gloss-image-link'); image.dataset.pixelated = `${pixelated === true}`; if (this._mediaLoader !== null) { this._mediaLoader.loadMedia( path, dictionary, - (url) => this._setImageData(node, image, imageLink, url, false), - () => this._setImageData(node, image, imageLink, null, true) + (url) => this._setImageData(node, image, url, false), + () => this._setImageData(node, image, null, true) ); } - if (typeof description === 'string') { - const container = node.querySelector('.gloss-image-description'); - this._setMultilineTextContent(container, description); - } - return node; } - _setImageData(container, image, imageLink, url, unloaded) { + _setImageData(node, image, url, unloaded) { if (url !== null) { image.src = url; - imageLink.href = url; - container.dataset.imageLoadState = 'loaded'; + node.href = url; + node.dataset.imageLoadState = 'loaded'; } else { image.removeAttribute('src'); - imageLink.removeAttribute('href'); - container.dataset.imageLoadState = unloaded ? 'unloaded' : 'load-error'; + node.removeAttribute('href'); + node.dataset.imageLoadState = unloaded ? 'unloaded' : 'load-error'; } } diff --git a/ext/js/language/dictionary-importer.js b/ext/js/language/dictionary-importer.js index e060b3b1..051375a0 100644 --- a/ext/js/language/dictionary-importer.js +++ b/ext/js/language/dictionary-importer.js @@ -306,10 +306,8 @@ class DictionaryImporter { } async _formatDictionaryTermGlossaryImage(data, context, entry) { - const {path, width: preferredWidth, height: preferredHeight, title, description, pixelated} = data; + const {path, width: preferredWidth, height: preferredHeight, title, description, pixelated, collapsed, collapsible} = data; const {width, height} = await this._getImageMedia(path, context, entry); - - // Create new data const newData = { type: 'image', path, @@ -321,7 +319,8 @@ class DictionaryImporter { if (typeof title === 'string') { newData.title = title; } if (typeof description === 'string') { newData.description = description; } if (typeof pixelated === 'boolean') { newData.pixelated = pixelated; } - + if (typeof collapsed === 'boolean') { newData.collapsed = collapsed; } + if (typeof collapsible === 'boolean') { newData.collapsible = collapsible; } return newData; } |