Display audio menu refactor (#1404)
* Update display and generation of the audio menu * Wrap menu items in a div to allow for horizontal layout groupings
This commit is contained in:
parent
74a9c7499c
commit
7027d537a9
@ -1673,13 +1673,13 @@ button.footer-notification-close-button:active {
|
|||||||
.audio-button-popup-menu[data-show-icons=false] .popup-menu-item-icon {
|
.audio-button-popup-menu[data-show-icons=false] .popup-menu-item-icon {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.popup-menu-item-icon[data-icon=checkmark] {
|
.audio-button-popup-menu .popup-menu-item-icon[data-icon=checkmark] {
|
||||||
background-color: var(--success-color);
|
background-color: var(--success-color);
|
||||||
}
|
}
|
||||||
.popup-menu-item-icon[data-icon=cross] {
|
.audio-button-popup-menu .popup-menu-item-icon[data-icon=cross] {
|
||||||
background-color: var(--danger-color);
|
background-color: var(--danger-color);
|
||||||
}
|
}
|
||||||
.popup-menu-item[data-source-in-options=false][data-valid=null] {
|
.audio-button-popup-menu .popup-menu-item-group[data-source-in-options=false][data-valid=null] .popup-menu-item {
|
||||||
color: var(--text-color-light1);
|
color: var(--text-color-light1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -994,3 +994,8 @@ button.popup-menu-item:disabled {
|
|||||||
padding: 0.5em 0.75em;
|
padding: 0.5em 0.75em;
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
}
|
}
|
||||||
|
.popup-menu-item-group {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
@ -157,13 +157,7 @@
|
|||||||
</label></template>
|
</label></template>
|
||||||
|
|
||||||
<!-- Popup menu -->
|
<!-- Popup menu -->
|
||||||
<template id="audio-button-popup-menu-template"><div class="popup-menu-container scan-disable audio-button-popup-menu" tabindex="-1" role="dialog"><div class="popup-menu popup-menu-auto-size"><div class="popup-menu-body">
|
<template id="audio-button-popup-menu-template"><div class="popup-menu-container scan-disable audio-button-popup-menu" tabindex="-1" role="dialog"><div class="popup-menu popup-menu-auto-size"><div class="popup-menu-body"></div></div></div></template>
|
||||||
<button class="popup-menu-item" data-menu-action="playAudioFromSource" data-source="jpod101"><div class="popup-menu-item-icon icon" data-icon="none"></div><span class="popup-menu-item-label">JapanesePod101</span></button>
|
<template id="audio-button-popup-menu-item-template"><div class="popup-menu-item-group"><button class="popup-menu-item" data-menu-action="playAudioFromSource"><div class="popup-menu-item-icon icon" data-icon="none"></div><span class="popup-menu-item-label"></span></button></div></template>
|
||||||
<button class="popup-menu-item" data-menu-action="playAudioFromSource" data-source="jpod101-alternate"><div class="popup-menu-item-icon icon" data-icon="none"></div><span class="popup-menu-item-label">JapanesePod101 (Alternate)</span></button>
|
|
||||||
<button class="popup-menu-item" data-menu-action="playAudioFromSource" data-source="jisho"><div class="popup-menu-item-icon icon" data-icon="none"></div><span class="popup-menu-item-label">Jisho.org</span></button>
|
|
||||||
<button class="popup-menu-item" data-menu-action="playAudioFromSource" data-source="text-to-speech"><div class="popup-menu-item-icon icon" data-icon="none"></div><span class="popup-menu-item-label">Text-to-speech</span></button>
|
|
||||||
<button class="popup-menu-item" data-menu-action="playAudioFromSource" data-source="text-to-speech-reading"><div class="popup-menu-item-icon icon" data-icon="none"></div><span class="popup-menu-item-label">Text-to-speech (Kana reading)</span></button>
|
|
||||||
<button class="popup-menu-item" data-menu-action="playAudioFromSource" data-source="custom"><div class="popup-menu-item-icon icon" data-icon="none"></div><span class="popup-menu-item-label">Custom</span></button>
|
|
||||||
</div></div></div></template>
|
|
||||||
|
|
||||||
</body></html>
|
</body></html>
|
||||||
|
@ -189,7 +189,10 @@ class DisplayAudio {
|
|||||||
switch (action) {
|
switch (action) {
|
||||||
case 'playAudioFromSource':
|
case 'playAudioFromSource':
|
||||||
{
|
{
|
||||||
const {source, index} = item.dataset;
|
const group = item.closest('.popup-menu-item-group');
|
||||||
|
if (group === null) { break; }
|
||||||
|
|
||||||
|
const {source, index} = group.dataset;
|
||||||
let sourceDetailsMap = null;
|
let sourceDetailsMap = null;
|
||||||
if (typeof index !== 'undefined') {
|
if (typeof index !== 'undefined') {
|
||||||
const index2 = Number.parseInt(index, 10);
|
const index2 = Number.parseInt(index, 10);
|
||||||
@ -405,139 +408,120 @@ class DisplayAudio {
|
|||||||
popupMenu.prepare();
|
popupMenu.prepare();
|
||||||
}
|
}
|
||||||
|
|
||||||
_createMenu(button, expression, reading) {
|
_getAudioSources(audioOptions) {
|
||||||
// Options
|
const {sources, textToSpeechVoice, customSourceUrl} = audioOptions;
|
||||||
const {sources, textToSpeechVoice, customSourceUrl} = this._getAudioOptions();
|
const ttsSupported = (textToSpeechVoice.length > 0);
|
||||||
|
const customSupported = (customSourceUrl.length > 0);
|
||||||
|
|
||||||
const sourceIndexMap = new Map();
|
const sourceIndexMap = new Map();
|
||||||
for (let i = 0, ii = sources.length; i < ii; ++i) {
|
const optionsSourcesCount = sources.length;
|
||||||
|
for (let i = 0; i < optionsSourcesCount; ++i) {
|
||||||
sourceIndexMap.set(sources[i], i);
|
sourceIndexMap.set(sources[i], i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create menu
|
const rawSources = [
|
||||||
const menuNode = this._display.displayGenerator.createPopupMenu('audio-button');
|
['jpod101', 'JapanesePod101', true],
|
||||||
|
['jpod101-alternate', 'JapanesePod101 (Alternate)', true],
|
||||||
|
['jisho', 'Jisho.org', true],
|
||||||
|
['text-to-speech', 'Text-to-speech', ttsSupported],
|
||||||
|
['text-to-speech-reading', 'Text-to-speech (Kana reading)', ttsSupported],
|
||||||
|
['custom', 'Custom', customSupported]
|
||||||
|
];
|
||||||
|
|
||||||
// Create menu item metadata
|
const results = [];
|
||||||
const menuItems = [];
|
for (const [source, displayName, supported] of rawSources) {
|
||||||
const menuItemNodes = menuNode.querySelectorAll('.popup-menu-item');
|
if (!supported) { continue; }
|
||||||
for (let i = 0, ii = menuItemNodes.length; i < ii; ++i) {
|
|
||||||
const node = menuItemNodes[i];
|
|
||||||
const {source} = node.dataset;
|
|
||||||
let optionsIndex = sourceIndexMap.get(source);
|
let optionsIndex = sourceIndexMap.get(source);
|
||||||
if (typeof optionsIndex === 'undefined') { optionsIndex = null; }
|
const isInOptions = typeof optionsIndex !== 'undefined';
|
||||||
menuItems.push({node, source, index: i, optionsIndex});
|
if (!isInOptions) {
|
||||||
|
optionsIndex = optionsSourcesCount;
|
||||||
|
}
|
||||||
|
results.push({
|
||||||
|
source,
|
||||||
|
displayName,
|
||||||
|
index: results.length,
|
||||||
|
optionsIndex,
|
||||||
|
isInOptions
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort according to source order in options
|
// Sort according to source order in options
|
||||||
menuItems.sort((a, b) => {
|
results.sort((a, b) => {
|
||||||
const ai = a.optionsIndex;
|
const i = a.optionsIndex - b.optionsIndex;
|
||||||
const bi = b.optionsIndex;
|
return i !== 0 ? i : a.index - b.index;
|
||||||
if (ai !== null) {
|
|
||||||
if (bi !== null) {
|
|
||||||
const i = ai - bi;
|
|
||||||
if (i !== 0) { return i; }
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (bi !== null) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return a.index - b.index;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set up items based on cache data
|
return results;
|
||||||
const sourceMap = this._cache.get(this._getExpressionReadingKey(expression, reading));
|
}
|
||||||
const menuEntryMap = new Map();
|
|
||||||
|
_createMenu(sourceButton, expression, reading) {
|
||||||
|
// Options
|
||||||
|
const sources = this._getAudioSources(this._getAudioOptions());
|
||||||
|
|
||||||
|
// Create menu
|
||||||
|
const {displayGenerator} = this._display;
|
||||||
|
const menuNode = displayGenerator.instantiateTemplate('audio-button-popup-menu');
|
||||||
|
const menuBody = menuNode.querySelector('.popup-menu-body');
|
||||||
|
|
||||||
|
// Set up items based on options and cache data
|
||||||
let showIcons = false;
|
let showIcons = false;
|
||||||
for (let i = 0, ii = menuItems.length; i < ii; ++i) {
|
for (const {source, displayName, isInOptions} of sources) {
|
||||||
const {node, source, optionsIndex} = menuItems[i];
|
const entries = this._getMenuItemEntries(source, expression, reading);
|
||||||
const entries = this._getMenuItemEntries(node, sourceMap, source);
|
for (let i = 0, ii = entries.length; i < ii; ++i) {
|
||||||
menuEntryMap.set(source, entries);
|
const {valid, index, name} = entries[i];
|
||||||
for (const {node: node2, valid, index} of entries) {
|
const node = displayGenerator.instantiateTemplate('audio-button-popup-menu-item');
|
||||||
|
|
||||||
|
const labelNode = node.querySelector('.popup-menu-item-label');
|
||||||
|
let label = displayName;
|
||||||
|
if (ii > 1) { label = `${label} ${i + 1}`; }
|
||||||
|
if (typeof name === 'string' && name.length > 0) { label += `: ${name}`; }
|
||||||
|
labelNode.textContent = label;
|
||||||
|
|
||||||
if (valid !== null) {
|
if (valid !== null) {
|
||||||
const icon = node2.querySelector('.popup-menu-item-icon');
|
const icon = node.querySelector('.popup-menu-item-icon');
|
||||||
icon.dataset.icon = valid ? 'checkmark' : 'cross';
|
icon.dataset.icon = valid ? 'checkmark' : 'cross';
|
||||||
showIcons = true;
|
showIcons = true;
|
||||||
}
|
}
|
||||||
|
node.dataset.source = source;
|
||||||
if (index !== null) {
|
if (index !== null) {
|
||||||
node2.dataset.index = `${index}`;
|
node.dataset.index = `${index}`;
|
||||||
}
|
}
|
||||||
node2.dataset.valid = `${valid}`;
|
node.dataset.valid = `${valid}`;
|
||||||
node2.dataset.sourceInOptions = `${optionsIndex !== null}`;
|
node.dataset.sourceInOptions = `${isInOptions}`;
|
||||||
node2.style.order = `${i}`;
|
|
||||||
|
menuBody.appendChild(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
menuNode.dataset.showIcons = `${showIcons}`;
|
menuNode.dataset.showIcons = `${showIcons}`;
|
||||||
|
|
||||||
// Hide options
|
|
||||||
if (textToSpeechVoice.length === 0) {
|
|
||||||
this._setMenuItemEntriesHidden(menuEntryMap, 'text-to-speech', true);
|
|
||||||
this._setMenuItemEntriesHidden(menuEntryMap, 'text-to-speech-reading', true);
|
|
||||||
}
|
|
||||||
if (customSourceUrl.length === 0) {
|
|
||||||
this._setMenuItemEntriesHidden(menuEntryMap, 'custom', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create popup menu
|
// Create popup menu
|
||||||
this._menuContainer.appendChild(menuNode);
|
this._menuContainer.appendChild(menuNode);
|
||||||
return new PopupMenu(button, menuNode);
|
return new PopupMenu(sourceButton, menuNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
_getMenuItemEntries(node, sourceMap, source) {
|
_getMenuItemEntries(source, expression, reading) {
|
||||||
const entries = [{node, valid: null, index: null}];
|
const sourceMap = this._cache.get(this._getExpressionReadingKey(expression, reading));
|
||||||
|
if (typeof sourceMap !== 'undefined') {
|
||||||
const nextNode = node.nextSibling;
|
|
||||||
|
|
||||||
if (typeof sourceMap === 'undefined') { return entries; }
|
|
||||||
|
|
||||||
const sourceInfo = sourceMap.get(source);
|
const sourceInfo = sourceMap.get(source);
|
||||||
if (typeof sourceInfo === 'undefined') { return entries; }
|
if (typeof sourceInfo !== 'undefined') {
|
||||||
|
|
||||||
const {infoList} = sourceInfo;
|
const {infoList} = sourceInfo;
|
||||||
if (infoList === null) { return entries; }
|
if (infoList !== null) {
|
||||||
|
const ii = infoList.length;
|
||||||
if (infoList.length === 0) {
|
if (ii === 0) {
|
||||||
entries[0].valid = false;
|
return [{valid: false, index: null, name: null}];
|
||||||
return entries;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultLabel = node.querySelector('.popup-menu-item-label').textContent;
|
const results = [];
|
||||||
|
for (let i = 0; i < ii; ++i) {
|
||||||
for (let i = 0, ii = infoList.length; i < ii; ++i) {
|
|
||||||
// Get/create entry
|
|
||||||
let entry;
|
|
||||||
if (i < entries.length) {
|
|
||||||
entry = entries[i];
|
|
||||||
} else {
|
|
||||||
const node2 = node.cloneNode(true);
|
|
||||||
nextNode.parentNode.insertBefore(node2, nextNode);
|
|
||||||
entry = {node: node2, valid: null, index: null};
|
|
||||||
entries.push(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entry info
|
|
||||||
entry.index = i;
|
|
||||||
|
|
||||||
const {audio, audioResolved, info: {name}} = infoList[i];
|
const {audio, audioResolved, info: {name}} = infoList[i];
|
||||||
if (audioResolved) { entry.valid = (audio !== null); }
|
const valid = audioResolved ? (audio !== null) : null;
|
||||||
|
const entry = {valid, index: i, name};
|
||||||
const labelNode = entry.node.querySelector('.popup-menu-item-label');
|
results.push(entry);
|
||||||
let label = defaultLabel;
|
|
||||||
if (ii > 1) { label = `${label} ${i + 1}`; }
|
|
||||||
if (typeof name === 'string' && name.length > 0) { label += `: ${name}`; }
|
|
||||||
labelNode.textContent = label;
|
|
||||||
}
|
}
|
||||||
|
return results;
|
||||||
return entries;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_setMenuItemEntriesHidden(menuEntryMap, source, hidden) {
|
|
||||||
const entries = menuEntryMap.get(source);
|
|
||||||
if (typeof entries === 'undefined') { return; }
|
|
||||||
|
|
||||||
for (const {node} of entries) {
|
|
||||||
node.hidden = hidden;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return [{valid: null, index: null, name: null}];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -213,8 +213,8 @@ class DisplayGenerator {
|
|||||||
return this._templates.instantiate('profile-list-item');
|
return this._templates.instantiate('profile-list-item');
|
||||||
}
|
}
|
||||||
|
|
||||||
createPopupMenu(name) {
|
instantiateTemplate(name) {
|
||||||
return this._templates.instantiate(`${name}-popup-menu`);
|
return this._templates.instantiate(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private
|
// Private
|
||||||
|
Loading…
Reference in New Issue
Block a user