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 {
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;
}

View File

@ -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
}
}
}

View File

@ -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 -->

View File

@ -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';
}
}

View File

@ -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;
}