Dictionary image display refactoring (#1687)

* Generalize image definition generation

* Enable optional aspect ratio

* Move styles

* Update styles

* Add more options for collapsing images

* Add image options for collapsing

* Improve layout for images that are collapsed
This commit is contained in:
toasted-nutbread 2021-05-18 17:41:27 -04:00 committed by GitHub
parent 76276e78da
commit f3cf4d10c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 87 additions and 52 deletions

View File

@ -1571,6 +1571,8 @@ button.definition-item-expansion-button:focus:focus-visible+.definition-item-con
.gloss-image-link { .gloss-image-link {
cursor: inherit; cursor: inherit;
color: inherit; color: inherit;
display: inline-block;
position: relative;
} }
.gloss-image-link[href]:hover { .gloss-image-link[href]:hover {
cursor: pointer; cursor: pointer;
@ -1588,7 +1590,7 @@ button.definition-item-expansion-button:focus:focus-visible+.definition-item-con
white-space: normal; white-space: normal;
color: var(--text-color-light3); 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'; content: 'Image failed to load';
display: table-cell; display: table-cell;
width: 100%; width: 100%;
@ -1599,15 +1601,17 @@ button.definition-item-expansion-button:focus:focus-visible+.definition-item-con
} }
.gloss-image { .gloss-image {
display: inline-block; 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; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
vertical-align: top;
object-fit: contain;
border: none;
outline: none;
} }
.gloss-image:not([src]) { .gloss-image:not([src]) {
display: none; display: none;
@ -1619,13 +1623,15 @@ button.definition-item-expansion-button:focus:focus-visible+.definition-item-con
image-rendering: pixelated; image-rendering: pixelated;
image-rendering: crisp-edges; image-rendering: crisp-edges;
} }
.gloss-image-aspect-ratio-sizer { .gloss-image-link[data-has-aspect-ratio=true] .gloss-image-aspect-ratio-sizer {
content: '';
display: inline-block; display: inline-block;
width: 0; width: 0;
vertical-align: top; vertical-align: top;
font-size: 0; font-size: 0;
} }
.gloss-image-link-text {
display: none;
}
.gloss-image-link-text::before { .gloss-image-link-text::before {
content: '['; content: '[';
} }
@ -1633,9 +1639,38 @@ button.definition-item-expansion-button:focus:focus-visible+.definition-item-con
content: ']'; content: ']';
} }
.gloss-image-description { .gloss-image-description {
display: block;
white-space: pre-line; 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 */
.kanji-glyph-container { .kanji-glyph-container {
@ -2110,32 +2145,6 @@ button.footer-notification-close-button {
color: var(--text-color-light3); 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 { :root[data-popup-display-mode=full-width] .frame-resizer-container {
display: none; display: none;
} }

View File

@ -104,6 +104,16 @@
"type": "boolean", "type": "boolean",
"description": "Whether or not the image should appear pixelated at sizes larger than the image's native resolution.", "description": "Whether or not the image should appear pixelated at sizes larger than the image's native resolution.",
"default": false "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
} }
} }
} }

View File

@ -57,7 +57,8 @@
</li></template> </li></template>
<template id="definition-disambiguation-template"><span class="definition-disambiguation"></span></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-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> <template id="inflection-template"><span class="inflection"></span><span class="inflection-separator"> </span></template>
<!-- Frequency templates --> <!-- Frequency templates -->

View File

@ -309,7 +309,26 @@ class DisplayGenerator {
} }
_createTermDefinitionEntryImage(data, dictionary) { _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 = ( const usedWidth = (
typeof preferredWidth === 'number' ? typeof preferredWidth === 'number' ?
@ -327,6 +346,9 @@ class DisplayGenerator {
node.dataset.path = path; node.dataset.path = path;
node.dataset.dictionary = dictionary; node.dataset.dictionary = dictionary;
node.dataset.imageLoadState = 'not-loaded'; 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'); const imageContainer = node.querySelector('.gloss-image-container');
imageContainer.style.width = `${usedWidth}em`; imageContainer.style.width = `${usedWidth}em`;
@ -338,35 +360,29 @@ class DisplayGenerator {
aspectRatioSizer.style.paddingTop = `${aspectRatio * 100.0}%`; aspectRatioSizer.style.paddingTop = `${aspectRatio * 100.0}%`;
const image = node.querySelector('img.gloss-image'); const image = node.querySelector('img.gloss-image');
const imageLink = node.querySelector('.gloss-image-link');
image.dataset.pixelated = `${pixelated === true}`; image.dataset.pixelated = `${pixelated === true}`;
if (this._mediaLoader !== null) { if (this._mediaLoader !== null) {
this._mediaLoader.loadMedia( this._mediaLoader.loadMedia(
path, path,
dictionary, dictionary,
(url) => this._setImageData(node, image, imageLink, url, false), (url) => this._setImageData(node, image, url, false),
() => this._setImageData(node, image, imageLink, null, true) () => this._setImageData(node, image, null, true)
); );
} }
if (typeof description === 'string') {
const container = node.querySelector('.gloss-image-description');
this._setMultilineTextContent(container, description);
}
return node; return node;
} }
_setImageData(container, image, imageLink, url, unloaded) { _setImageData(node, image, url, unloaded) {
if (url !== null) { if (url !== null) {
image.src = url; image.src = url;
imageLink.href = url; node.href = url;
container.dataset.imageLoadState = 'loaded'; node.dataset.imageLoadState = 'loaded';
} else { } else {
image.removeAttribute('src'); image.removeAttribute('src');
imageLink.removeAttribute('href'); node.removeAttribute('href');
container.dataset.imageLoadState = unloaded ? 'unloaded' : 'load-error'; node.dataset.imageLoadState = unloaded ? 'unloaded' : 'load-error';
} }
} }

View File

@ -306,10 +306,8 @@ class DictionaryImporter {
} }
async _formatDictionaryTermGlossaryImage(data, context, entry) { 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); const {width, height} = await this._getImageMedia(path, context, entry);
// Create new data
const newData = { const newData = {
type: 'image', type: 'image',
path, path,
@ -321,7 +319,8 @@ class DictionaryImporter {
if (typeof title === 'string') { newData.title = title; } if (typeof title === 'string') { newData.title = title; }
if (typeof description === 'string') { newData.description = description; } if (typeof description === 'string') { newData.description = description; }
if (typeof pixelated === 'boolean') { newData.pixelated = pixelated; } if (typeof pixelated === 'boolean') { newData.pixelated = pixelated; }
if (typeof collapsed === 'boolean') { newData.collapsed = collapsed; }
if (typeof collapsible === 'boolean') { newData.collapsible = collapsible; }
return newData; return newData;
} }