aboutsummaryrefslogtreecommitdiff
path: root/ext/mixed
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2021-01-09 16:02:03 -0500
committerGitHub <noreply@github.com>2021-01-09 16:02:03 -0500
commit5b58a9aeef66194bc51fe25f66aebf95f673089a (patch)
treeadf2381475c6f9ad1b502e49e403a7e361184321 /ext/mixed
parent06d23f59d83ef89ebda89db547195ecf2a1c6ebf (diff)
Update term tags display and fix a layout issue (#1208)
* Fix layout issue with term expression display * Update display of term tags * Update tag notification to include disambiguation information
Diffstat (limited to 'ext/mixed')
-rw-r--r--ext/mixed/css/display.css33
-rw-r--r--ext/mixed/display-templates.html9
-rw-r--r--ext/mixed/js/dictionary-data-util.js37
-rw-r--r--ext/mixed/js/display-generator.js52
-rw-r--r--ext/mixed/js/display-notification.js9
-rw-r--r--ext/mixed/js/display.js7
6 files changed, 137 insertions, 10 deletions
diff --git a/ext/mixed/css/display.css b/ext/mixed/css/display.css
index fd320904..e33ef802 100644
--- a/ext/mixed/css/display.css
+++ b/ext/mixed/css/display.css
@@ -699,6 +699,26 @@ button.action-button[data-icon=source-term]::before {
.tag-inner {
display: block;
}
+.tag-details-disambiguation-list::before {
+ content: 'Only: ';
+}
+.tag-details-disambiguation-list ruby>rt {
+ display: inline;
+ font-size: 1em;
+}
+.tag-details-disambiguation-list ruby>rt::before {
+ content: '(';
+}
+.tag-details-disambiguation-list ruby>rt::after {
+ content: ')';
+}
+.tag-details-disambiguation-list[data-unmatched-expression-count='0'],
+.tag-details-disambiguation-list:not([data-unmatched-expression-count]) {
+ display: none;
+}
+.tag-details-disambiguation:not(:last-child)::after {
+ content: ', ';
+}
/* Entries */
@@ -755,13 +775,13 @@ button.action-button[data-icon=source-term]::before {
display: inline;
}
.term-expression {
- display: inline;
+ display: inline-block;
}
.term-expression-details {
display: inline;
}
.term-expression-details>.tags {
- display: inline;
+ display: none;
}
.term-expression-details>.frequencies {
display: none;
@@ -769,6 +789,15 @@ button.action-button[data-icon=source-term]::before {
.term-expression-list>.term-expression:not(:last-of-type)>.term-expression-text-container>.term-expression-text::after {
content: '\3001';
}
+.term-details {
+ display: inline;
+}
+.term-tags {
+ display: inline;
+}
+.entry[data-expression-multi=true] .term-details {
+ display: block;
+}
/* Entry indicator */
diff --git a/ext/mixed/display-templates.html b/ext/mixed/display-templates.html
index ac42e138..83232b4c 100644
--- a/ext/mixed/display-templates.html
+++ b/ext/mixed/display-templates.html
@@ -15,7 +15,10 @@
</div>
<div class="term-expression-list"></div>
</div>
- <div class="term-reasons"></div>
+ <div class="term-details">
+ <div class="term-tags tag-list"></div>
+ <div class="term-reasons"></div>
+ </div>
</div>
</div>
<div class="entry-body">
@@ -137,6 +140,10 @@
<div class="footer-notification-body"></div>
<button class="footer-notification-close-button"><span class="footer-notification-close-button-icon icon" data-icon="cross"></span></button>
</div></template>
+<template id="footer-notification-tag-details-template" data-remove-whitespace-text="true">
+ <div class="tag-details"></div>
+ <div class="tag-details-disambiguation-list"></div>
+</div></template>
<template id="profile-list-item-template"><label class="profile-list-item">
<div class="profile-list-item-selection"><label class="radio"><input type="radio" class="profile-entry-is-default-radio" name="profile-entry-default-radio"><span class="radio-body"><span class="radio-border"></span><span class="radio-dot"></span></span></label></div>
<div class="profile-list-item-name"></div>
diff --git a/ext/mixed/js/dictionary-data-util.js b/ext/mixed/js/dictionary-data-util.js
index 1e82ef63..70a51e89 100644
--- a/ext/mixed/js/dictionary-data-util.js
+++ b/ext/mixed/js/dictionary-data-util.js
@@ -16,6 +16,37 @@
*/
class DictionaryDataUtil {
+ static groupTermTags(definition) {
+ const {expressions} = definition;
+ const expressionsLength = expressions.length;
+ const uniqueCheck = (expressionsLength > 1);
+ const resultsMap = new Map();
+ const results = [];
+ for (let i = 0; i < expressionsLength; ++i) {
+ const {termTags, expression, reading} = expressions[i];
+ for (const tag of termTags) {
+ if (uniqueCheck) {
+ const {name, category, notes, dictionary} = tag;
+ const key = this._createMapKey([name, category, notes, dictionary]);
+ const index = resultsMap.get(key);
+ if (typeof index !== 'undefined') {
+ const existingItem = results[index];
+ existingItem.expressions.push({index: i, expression, reading});
+ continue;
+ }
+ resultsMap.set(key, results.length);
+ }
+
+ const item = {
+ tag,
+ expressions: [{index: i, expression, reading}]
+ };
+ results.push(item);
+ }
+ }
+ return results;
+ }
+
static groupTermFrequencies(frequencies) {
const map1 = new Map();
for (const {dictionary, expression, reading, hasReading, frequency} of frequencies) {
@@ -26,7 +57,7 @@ class DictionaryDataUtil {
}
const readingKey = hasReading ? reading : null;
- const key = JSON.stringify([expression, readingKey], null, 0);
+ const key = this._createMapKey([expression, readingKey]);
let frequencyData = map2.get(key);
if (typeof frequencyData === 'undefined') {
frequencyData = {expression, reading: readingKey, frequencies: new Set()};
@@ -179,4 +210,8 @@ class DictionaryDataUtil {
}
return result;
}
+
+ static _createMapKey(array) {
+ return JSON.stringify(array);
+ }
}
diff --git a/ext/mixed/js/display-generator.js b/ext/mixed/js/display-generator.js
index a47a65ee..b48ddadc 100644
--- a/ext/mixed/js/display-generator.js
+++ b/ext/mixed/js/display-generator.js
@@ -49,6 +49,7 @@ class DisplayGenerator {
const pitchesContainer = node.querySelector('.term-pitch-accent-group-list');
const frequencyGroupListContainer = node.querySelector('.frequency-group-list');
const definitionsContainer = node.querySelector('.term-definition-list');
+ const termTagsContainer = node.querySelector('.term-tags');
const {expressions, type, reasons, frequencies} = details;
const definitions = (type === 'term' ? [details] : details.definitions);
@@ -56,6 +57,7 @@ class DisplayGenerator {
const pitches = DictionaryDataUtil.getPitchAccentInfos(details);
const pitchCount = pitches.reduce((i, v) => i + v.pitches.length, 0);
const groupedFrequencies = DictionaryDataUtil.groupTermFrequencies(frequencies);
+ const termTags = DictionaryDataUtil.groupTermTags(details);
const uniqueExpressions = new Set();
const uniqueReadings = new Set();
@@ -80,6 +82,7 @@ class DisplayGenerator {
this._appendMultiple(frequencyGroupListContainer, this._createFrequencyGroup.bind(this), groupedFrequencies, false);
this._appendMultiple(pitchesContainer, this._createPitches.bind(this), pitches);
this._appendMultiple(definitionsContainer, this._createTermDefinitionItem.bind(this), definitions);
+ this._appendMultiple(termTagsContainer, this._createTermTag.bind(this), termTags, expressions.length);
return node;
}
@@ -119,6 +122,45 @@ class DisplayGenerator {
return this._templates.instantiate('footer-notification');
}
+ createTagFooterNotificationDetails(tagNode) {
+ const node = this._templates.instantiateFragment('footer-notification-tag-details');
+
+ const details = tagNode.dataset.details;
+ node.querySelector('.tag-details').textContent = details;
+
+ let disambiguation = null;
+ try {
+ let a = tagNode.dataset.disambiguation;
+ if (typeof a !== 'undefined') {
+ a = JSON.parse(a);
+ if (Array.isArray(a)) { disambiguation = a; }
+ }
+ } catch (e) {
+ // NOP
+ }
+
+ if (disambiguation !== null) {
+ const disambiguationContainer = node.querySelector('.tag-details-disambiguation-list');
+ const copyAttributes = ['totalExpressionCount', 'matchedExpressionCount', 'unmatchedExpressionCount'];
+ for (const attribute of copyAttributes) {
+ const value = tagNode.dataset[attribute];
+ if (typeof value === 'undefined') { continue; }
+ disambiguationContainer.dataset[attribute] = value;
+ }
+ for (const {expression, reading} of disambiguation) {
+ const segments = this._japaneseUtil.distributeFurigana(expression, reading);
+ const disambiguationItem = document.createElement('span');
+ disambiguationItem.className = 'tag-details-disambiguation';
+ this._appendFurigana(disambiguationItem, segments, (container, text) => {
+ container.appendChild(document.createTextNode(text));
+ });
+ disambiguationContainer.appendChild(disambiguationItem);
+ }
+ }
+
+ return node;
+ }
+
createProfileListItem() {
return this._templates.instantiate('profile-list-item');
}
@@ -321,6 +363,16 @@ class DisplayGenerator {
return node;
}
+ _createTermTag(details, totalExpressionCount) {
+ const {tag, expressions} = details;
+ const node = this._createTag(tag);
+ node.dataset.disambiguation = `${JSON.stringify(expressions)}`;
+ node.dataset.totalExpressionCount = `${totalExpressionCount}`;
+ node.dataset.matchedExpressionCount = `${expressions.length}`;
+ node.dataset.unmatchedExpressionCount = `${Math.max(0, totalExpressionCount - expressions.length)}`;
+ return node;
+ }
+
_createSearchTag(text) {
return this._createTag({
notes: '',
diff --git a/ext/mixed/js/display-notification.js b/ext/mixed/js/display-notification.js
index 0e79f1c6..8b6325d0 100644
--- a/ext/mixed/js/display-notification.js
+++ b/ext/mixed/js/display-notification.js
@@ -58,8 +58,13 @@ class DisplayNotification {
}
}
- setContent(text) {
- this._body.textContent = text;
+ setContent(value) {
+ if (typeof value === 'string') {
+ this._body.textContent = value;
+ } else {
+ this._body.textContent = '';
+ this._body.appendChild(value);
+ }
}
isClosing() {
diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js
index c36a0c1c..e4433925 100644
--- a/ext/mixed/js/display.js
+++ b/ext/mixed/js/display.js
@@ -837,18 +837,17 @@ class Display extends EventDispatcher {
}
_onTagClick(e) {
- const node = e.currentTarget;
- const {dataset: {details}} = node;
- this._showTagNotification(details);
+ this._showTagNotification(e.currentTarget);
}
- _showTagNotification(content) {
+ _showTagNotification(tagNode) {
if (this._tagNotification === null) {
const node = this._displayGenerator.createEmptyFooterNotification();
node.classList.add('click-scannable');
this._tagNotification = new DisplayNotification(this._tagNotificationContainer, node);
}
+ const content = this._displayGenerator.createTagFooterNotificationDetails(tagNode);
this._tagNotification.setContent(content);
this._tagNotification.open();
}