Multi view note (#1829)
* Add support for a menu to view multiple note IDs * Show a + badge when there are multiple notes available * Organize
This commit is contained in:
parent
260d10c3a3
commit
25d74140ce
@ -6,7 +6,7 @@
|
|||||||
<div class="entry-header">
|
<div class="entry-header">
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button class="action-button collapsible-action-button action-view-tags" hidden disabled><span class="icon" data-icon="tag"></span></button>
|
<button class="action-button collapsible-action-button action-view-tags" hidden disabled><span class="icon" data-icon="tag"></span></button>
|
||||||
<button class="action-button action-view-note" hidden disabled data-icon="view-note" title="View added note" data-hotkey='["viewNote","title","View added note ({0})"]'></button>
|
<button class="action-button action-view-note" hidden disabled data-icon="view-note" title="View added note" data-hotkey='["viewNote","title","View added note ({0})"]' data-menu-position="left below h-cover v-cover"><div class="action-button-badge icon" hidden></div></button>
|
||||||
<button class="action-button action-add-note" hidden disabled data-icon="add-term-kanji" data-mode="term-kanji" title="Add expression" data-hotkey='["addNoteTermKanji","title","Add expression ({0})"]'></button>
|
<button class="action-button action-add-note" hidden disabled data-icon="add-term-kanji" data-mode="term-kanji" title="Add expression" data-hotkey='["addNoteTermKanji","title","Add expression ({0})"]'></button>
|
||||||
<button class="action-button action-add-note" hidden disabled data-icon="add-term-kana" data-mode="term-kana" title="Add reading" data-hotkey='["addNoteTermKana","title","Add reading ({0})"]'></button>
|
<button class="action-button action-add-note" hidden disabled data-icon="add-term-kana" data-mode="term-kana" title="Add reading" data-hotkey='["addNoteTermKana","title","Add reading ({0})"]'></button>
|
||||||
<button class="action-button action-play-audio" data-icon="play-audio" title="Play audio" data-title-default="Play audio" data-hotkey='["playAudio",["title","data-title-default"],"Play audio ({0})"]' data-menu-position="left below h-cover v-cover"><div class="action-button-badge icon" hidden></div></button>
|
<button class="action-button action-play-audio" data-icon="play-audio" title="Play audio" data-title-default="Play audio" data-hotkey='["playAudio",["title","data-title-default"],"Play audio ({0})"]' data-menu-position="left below h-cover v-cover"><div class="action-button-badge icon" hidden></div></button>
|
||||||
@ -171,5 +171,7 @@
|
|||||||
<button class="popup-menu-item popup-menu-item-audio-button" data-menu-action="playAudioFromSource"><div class="popup-menu-item-icon icon" data-icon="none"></div><span class="popup-menu-item-label"></span></button>
|
<button class="popup-menu-item popup-menu-item-audio-button" data-menu-action="playAudioFromSource"><div class="popup-menu-item-icon icon" data-icon="none"></div><span class="popup-menu-item-label"></span></button>
|
||||||
<button class="popup-menu-item popup-menu-item-set-primary-audio-button" data-menu-action="setPrimaryAudio" title="Use as audio for Anki card"><div class="popup-menu-item-icon icon" data-icon="note-card"></div></button>
|
<button class="popup-menu-item popup-menu-item-set-primary-audio-button" data-menu-action="setPrimaryAudio" title="Use as audio for Anki card"><div class="popup-menu-item-icon icon" data-icon="note-card"></div></button>
|
||||||
</div></template>
|
</div></template>
|
||||||
|
<template id="view-note-button-popup-menu-template"><div class="popup-menu-container scan-disable view-note-button-popup-menu" tabindex="-1" role="dialog"><div class="popup-menu popup-menu-auto-size"><div class="popup-menu-body"></div></div></div></template>
|
||||||
|
<template id="view-note-button-popup-menu-item-template"><button class="popup-menu-item"><span class="popup-menu-item-label"></span></button></template>
|
||||||
|
|
||||||
</body></html>
|
</body></html>
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
* AnkiNoteBuilder
|
* AnkiNoteBuilder
|
||||||
* AnkiUtil
|
* AnkiUtil
|
||||||
* DisplayNotification
|
* DisplayNotification
|
||||||
|
* PopupMenu
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class DisplayAnki {
|
class DisplayAnki {
|
||||||
@ -52,9 +53,12 @@ class DisplayAnki {
|
|||||||
['kanji', ['kanji']],
|
['kanji', ['kanji']],
|
||||||
['term', ['term-kanji', 'term-kana']]
|
['term', ['term-kanji', 'term-kana']]
|
||||||
]);
|
]);
|
||||||
|
this._menuContainer = document.querySelector('#popup-menus');
|
||||||
this._onShowTagsBind = this._onShowTags.bind(this);
|
this._onShowTagsBind = this._onShowTags.bind(this);
|
||||||
this._onNoteAddBind = this._onNoteAdd.bind(this);
|
this._onNoteAddBind = this._onNoteAdd.bind(this);
|
||||||
this._onNoteViewBind = this._onNoteView.bind(this);
|
this._onViewNoteButtonClickBind = this._onViewNoteButtonClick.bind(this);
|
||||||
|
this._onViewNoteButtonContextMenuBind = this._onViewNoteButtonContextMenu.bind(this);
|
||||||
|
this._onViewNoteButtonMenuCloseBind = this._onViewNoteButtonMenuClose.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
prepare() {
|
prepare() {
|
||||||
@ -63,7 +67,7 @@ class DisplayAnki {
|
|||||||
['addNoteKanji', () => { this._tryAddAnkiNoteForSelectedEntry('kanji'); }],
|
['addNoteKanji', () => { this._tryAddAnkiNoteForSelectedEntry('kanji'); }],
|
||||||
['addNoteTermKanji', () => { this._tryAddAnkiNoteForSelectedEntry('term-kanji'); }],
|
['addNoteTermKanji', () => { this._tryAddAnkiNoteForSelectedEntry('term-kanji'); }],
|
||||||
['addNoteTermKana', () => { this._tryAddAnkiNoteForSelectedEntry('term-kana'); }],
|
['addNoteTermKana', () => { this._tryAddAnkiNoteForSelectedEntry('term-kana'); }],
|
||||||
['viewNote', () => { this._tryViewAnkiNoteForSelectedEntry(); }]
|
['viewNote', this._viewNoteForSelectedEntry.bind(this)]
|
||||||
]);
|
]);
|
||||||
this._display.on('optionsUpdated', this._onOptionsUpdated.bind(this));
|
this._display.on('optionsUpdated', this._onOptionsUpdated.bind(this));
|
||||||
}
|
}
|
||||||
@ -81,7 +85,9 @@ class DisplayAnki {
|
|||||||
setupEntry(entry) {
|
setupEntry(entry) {
|
||||||
this._addMultipleEventListeners(entry, '.action-view-tags', 'click', this._onShowTagsBind);
|
this._addMultipleEventListeners(entry, '.action-view-tags', 'click', this._onShowTagsBind);
|
||||||
this._addMultipleEventListeners(entry, '.action-add-note', 'click', this._onNoteAddBind);
|
this._addMultipleEventListeners(entry, '.action-add-note', 'click', this._onNoteAddBind);
|
||||||
this._addMultipleEventListeners(entry, '.action-view-note', 'click', this._onNoteViewBind);
|
this._addMultipleEventListeners(entry, '.action-view-note', 'click', this._onViewNoteButtonClickBind);
|
||||||
|
this._addMultipleEventListeners(entry, '.action-view-note', 'contextmenu', this._onViewNoteButtonContextMenuBind);
|
||||||
|
this._addMultipleEventListeners(entry, '.action-view-note', 'menuClose', this._onViewNoteButtonMenuCloseBind);
|
||||||
}
|
}
|
||||||
|
|
||||||
setupEntriesComplete() {
|
setupEntriesComplete() {
|
||||||
@ -180,12 +186,6 @@ class DisplayAnki {
|
|||||||
this._showAnkiTagsNotification(tags);
|
this._showAnkiTagsNotification(tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onNoteView(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const link = e.currentTarget;
|
|
||||||
yomichan.api.noteView(link.dataset.noteId);
|
|
||||||
}
|
|
||||||
|
|
||||||
_addMultipleEventListeners(container, selector, ...args) {
|
_addMultipleEventListeners(container, selector, ...args) {
|
||||||
for (const node of container.querySelectorAll(selector)) {
|
for (const node of container.querySelectorAll(selector)) {
|
||||||
this._eventListeners.addEventListener(node, ...args);
|
this._eventListeners.addEventListener(node, ...args);
|
||||||
@ -202,26 +202,11 @@ class DisplayAnki {
|
|||||||
return entry !== null ? entry.querySelector('.action-view-tags') : null;
|
return entry !== null ? entry.querySelector('.action-view-tags') : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_viewerButtonFind(index) {
|
|
||||||
const entry = this._getEntry(index);
|
|
||||||
return entry !== null ? entry.querySelector('.action-view-note') : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
_getEntry(index) {
|
_getEntry(index) {
|
||||||
const entries = this._display.dictionaryEntryNodes;
|
const entries = this._display.dictionaryEntryNodes;
|
||||||
return index >= 0 && index < entries.length ? entries[index] : null;
|
return index >= 0 && index < entries.length ? entries[index] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_viewerButtonShow(index, noteId) {
|
|
||||||
const viewerButton = this._viewerButtonFind(index);
|
|
||||||
if (viewerButton === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
viewerButton.disabled = false;
|
|
||||||
viewerButton.hidden = false;
|
|
||||||
viewerButton.dataset.noteId = noteId;
|
|
||||||
}
|
|
||||||
|
|
||||||
_getNoteContext() {
|
_getNoteContext() {
|
||||||
const {state} = this._display.history;
|
const {state} = this._display.history;
|
||||||
let {documentTitle, url, sentence} = (isObject(state) ? state : {});
|
let {documentTitle, url, sentence} = (isObject(state) ? state : {});
|
||||||
@ -269,7 +254,7 @@ class DisplayAnki {
|
|||||||
const displayTags = this._displayTags;
|
const displayTags = this._displayTags;
|
||||||
const dictionaryEntryDetails = this._dictionaryEntryDetails;
|
const dictionaryEntryDetails = this._dictionaryEntryDetails;
|
||||||
for (let i = 0, ii = dictionaryEntryDetails.length; i < ii; ++i) {
|
for (let i = 0, ii = dictionaryEntryDetails.length; i < ii; ++i) {
|
||||||
let noteId = null;
|
const allNoteIds = [];
|
||||||
for (const {mode, canAdd, noteIds, noteInfos, ankiError} of dictionaryEntryDetails[i].modeMap.values()) {
|
for (const {mode, canAdd, noteIds, noteInfos, ankiError} of dictionaryEntryDetails[i].modeMap.values()) {
|
||||||
const button = this._adderButtonFind(i, mode);
|
const button = this._adderButtonFind(i, mode);
|
||||||
if (button !== null) {
|
if (button !== null) {
|
||||||
@ -278,16 +263,14 @@ class DisplayAnki {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(noteIds) && noteIds.length > 0) {
|
if (Array.isArray(noteIds) && noteIds.length > 0) {
|
||||||
noteId = noteIds[0];
|
allNoteIds.push(...noteIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (displayTags !== 'never' && Array.isArray(noteInfos)) {
|
if (displayTags !== 'never' && Array.isArray(noteInfos)) {
|
||||||
this._setupTagsIndicator(i, noteInfos);
|
this._setupTagsIndicator(i, noteInfos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (noteId !== null) {
|
this._updateViewNoteButton(i, allNoteIds, false);
|
||||||
this._viewerButtonShow(i, noteId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,14 +316,6 @@ class DisplayAnki {
|
|||||||
this._addAnkiNote(index, mode);
|
this._addAnkiNote(index, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
_tryViewAnkiNoteForSelectedEntry() {
|
|
||||||
const index = this._display.selectedIndex;
|
|
||||||
const button = this._viewerButtonFind(index);
|
|
||||||
if (button !== null && !button.disabled) {
|
|
||||||
yomichan.api.noteView(button.dataset.noteId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async _addAnkiNote(dictionaryEntryIndex, mode) {
|
async _addAnkiNote(dictionaryEntryIndex, mode) {
|
||||||
const dictionaryEntries = this._display.dictionaryEntries;
|
const dictionaryEntries = this._display.dictionaryEntries;
|
||||||
const dictionaryEntryDetails = this._dictionaryEntryDetails;
|
const dictionaryEntryDetails = this._dictionaryEntryDetails;
|
||||||
@ -395,7 +370,7 @@ class DisplayAnki {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
button.disabled = true;
|
button.disabled = true;
|
||||||
this._viewerButtonShow(dictionaryEntryIndex, noteId);
|
this._updateViewNoteButton(dictionaryEntryIndex, [noteId], true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -594,4 +569,101 @@ class DisplayAnki {
|
|||||||
if (typeof offset !== 'number') { offset = 0; }
|
if (typeof offset !== 'number') { offset = 0; }
|
||||||
return {text, offset};
|
return {text, offset};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// View note functions
|
||||||
|
|
||||||
|
_onViewNoteButtonClick(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.shiftKey) {
|
||||||
|
this._showViewNoteMenu(e.currentTarget);
|
||||||
|
} else {
|
||||||
|
this._viewNote(e.currentTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_onViewNoteButtonContextMenu(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this._showViewNoteMenu(e.currentTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onViewNoteButtonMenuClose(e) {
|
||||||
|
const {detail: {action, item}} = e;
|
||||||
|
switch (action) {
|
||||||
|
case 'viewNote':
|
||||||
|
this._viewNote(item);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateViewNoteButton(index, noteIds, prepend) {
|
||||||
|
const button = this._getViewNoteButton(index);
|
||||||
|
if (button === null) { return; }
|
||||||
|
if (prepend) {
|
||||||
|
const currentNoteIds = button.dataset.noteIds;
|
||||||
|
if (typeof currentNoteIds === 'string' && currentNoteIds.length > 0) {
|
||||||
|
noteIds = [...noteIds, currentNoteIds.split(' ')];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const disabled = (noteIds.length === 0);
|
||||||
|
button.disabled = disabled;
|
||||||
|
button.hidden = disabled;
|
||||||
|
button.dataset.noteIds = noteIds.join(' ');
|
||||||
|
|
||||||
|
const badge = button.querySelector('.action-button-badge');
|
||||||
|
if (badge !== null) {
|
||||||
|
const badgeData = badge.dataset;
|
||||||
|
if (noteIds.length > 1) {
|
||||||
|
badgeData.icon = 'plus-thick';
|
||||||
|
badgeData.hidden = false;
|
||||||
|
} else {
|
||||||
|
delete badgeData.icon;
|
||||||
|
badgeData.hidden = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_viewNote(node) {
|
||||||
|
const noteIds = this._getNodeNoteIds(node);
|
||||||
|
if (noteIds.length === 0) { return; }
|
||||||
|
yomichan.api.noteView(noteIds[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
_showViewNoteMenu(node) {
|
||||||
|
const noteIds = this._getNodeNoteIds(node);
|
||||||
|
if (noteIds.length === 0) { return; }
|
||||||
|
|
||||||
|
const menuContainerNode = this._display.displayGenerator.instantiateTemplate('view-note-button-popup-menu');
|
||||||
|
const menuBodyNode = menuContainerNode.querySelector('.popup-menu-body');
|
||||||
|
|
||||||
|
for (let i = 0, ii = noteIds.length; i < ii; ++i) {
|
||||||
|
const noteId = noteIds[i];
|
||||||
|
const item = this._display.displayGenerator.instantiateTemplate('view-note-button-popup-menu-item');
|
||||||
|
item.querySelector('.popup-menu-item-label').textContent = `Note ${i + 1}: ${noteId}`;
|
||||||
|
item.dataset.menuAction = 'viewNote';
|
||||||
|
item.dataset.noteIds = `${noteId}`;
|
||||||
|
menuBodyNode.appendChild(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._menuContainer.appendChild(menuContainerNode);
|
||||||
|
const popupMenu = new PopupMenu(node, menuContainerNode);
|
||||||
|
popupMenu.prepare();
|
||||||
|
}
|
||||||
|
|
||||||
|
_getNodeNoteIds(node) {
|
||||||
|
const {noteIds} = node.dataset;
|
||||||
|
return typeof noteIds === 'string' && noteIds.length > 0 ? noteIds.split(' ') : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
_getViewNoteButton(index) {
|
||||||
|
const entry = this._getEntry(index);
|
||||||
|
return entry !== null ? entry.querySelector('.action-view-note') : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_viewNoteForSelectedEntry() {
|
||||||
|
const index = this._display.selectedIndex;
|
||||||
|
const button = this._getViewNoteButton(index);
|
||||||
|
if (button !== null) {
|
||||||
|
this._viewNote(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user