Anki templates refactor (#970)

* Support menus with multiple sources

* Update anki templates controller
This commit is contained in:
toasted-nutbread 2020-10-30 17:41:52 -04:00 committed by GitHub
parent cb76848df9
commit 327d7b1f26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 76 additions and 43 deletions

View File

@ -272,7 +272,7 @@ html:root:not([data-options-general-result-output-mode=merge]) #dictionary-main-
#custom-popup-css, #custom-popup-css,
#custom-popup-outer-css, #custom-popup-outer-css,
#field-templates { #anki-card-templates-textarea {
width: 100%; width: 100%;
min-height: 34px; min-height: 34px;
line-height: 18px; line-height: 18px;
@ -280,12 +280,10 @@ html:root:not([data-options-general-result-output-mode=merge]) #dictionary-main-
resize: vertical; resize: vertical;
font-family: 'Courier New', Courier, monospace; font-family: 'Courier New', Courier, monospace;
white-space: pre; white-space: pre;
}
#field-templates {
height: 240px; height: 240px;
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
} }
#field-templates-reset { #anki-card-templates-reset-button {
border-top-left-radius: 0; border-top-left-radius: 0;
border-top-right-radius: 0; border-top-right-radius: 0;
} }

View File

@ -214,14 +214,20 @@ class AnkiController {
_setupFieldMenus() { _setupFieldMenus() {
const fieldMenuTargets = [ const fieldMenuTargets = [
['terms', '#anki-card-terms-field-menu-template'], [['terms'], '#anki-card-terms-field-menu-template'],
['kanji', '#anki-card-kanji-field-menu-template'] [['kanji'], '#anki-card-kanji-field-menu-template'],
[['terms', 'kanji'], '#anki-card-all-field-menu-template']
]; ];
for (const [type, selector] of fieldMenuTargets) { for (const [types, selector] of fieldMenuTargets) {
const element = document.querySelector(selector); const element = document.querySelector(selector);
if (element === null) { continue; } if (element === null) { continue; }
const markers = this.getFieldMarkers(type); let markers = [];
for (const type of types) {
markers.push(...this.getFieldMarkers(type));
}
markers = [...new Set(markers)];
const container = element.content.querySelector('.popup-menu'); const container = element.content.querySelector('.popup-menu');
if (container === null) { return; } if (container === null) { return; }

View File

@ -30,6 +30,10 @@ class AnkiTemplatesController {
this._cachedDefinitionValue = null; this._cachedDefinitionValue = null;
this._cachedDefinitionText = null; this._cachedDefinitionText = null;
this._defaultFieldTemplates = null; this._defaultFieldTemplates = null;
this._fieldTemplatesTextarea = null;
this._compileResultInfo = null;
this._renderFieldInput = null;
this._renderResult = null;
this._fieldTemplateResetModal = null; this._fieldTemplateResetModal = null;
this._templateRenderer = new TemplateRendererProxy(); this._templateRenderer = new TemplateRendererProxy();
} }
@ -37,24 +41,38 @@ class AnkiTemplatesController {
async prepare() { async prepare() {
this._defaultFieldTemplates = await api.getDefaultAnkiFieldTemplates(); this._defaultFieldTemplates = await api.getDefaultAnkiFieldTemplates();
this._fieldTemplateResetModal = this._modalController.getModal('field-template-reset-modal'); this._fieldTemplatesTextarea = document.querySelector('#anki-card-templates-textarea');
this._compileResultInfo = document.querySelector('#anki-card-templates-compile-result');
this._renderFieldInput = document.querySelector('#anki-card-templates-test-field-input');
this._renderTextInput = document.querySelector('#anki-card-templates-test-text-input');
this._renderResult = document.querySelector('#anki-card-templates-render-result');
const menuButton = document.querySelector('#anki-card-templates-test-field-menu-button');
const testRenderButton = document.querySelector('#anki-card-templates-test-render-button');
const resetButton = document.querySelector('#anki-card-templates-reset-button');
const resetConfirmButton = document.querySelector('#anki-card-templates-reset-button-confirm');
const fieldList = document.querySelector('#anki-card-templates-field-list');
this._fieldTemplateResetModal = this._modalController.getModal('anki-card-templates-reset');
const markers = new Set([ const markers = new Set([
...this._ankiController.getFieldMarkers('terms'), ...this._ankiController.getFieldMarkers('terms'),
...this._ankiController.getFieldMarkers('kanji') ...this._ankiController.getFieldMarkers('kanji')
]); ]);
const fragment = this._ankiController.getFieldMarkersHtml(markers);
const list = document.querySelector('#field-templates-list'); if (fieldList !== null) {
list.appendChild(fragment); const fragment = this._ankiController.getFieldMarkersHtml(markers);
for (const node of list.querySelectorAll('.marker-link')) { fieldList.appendChild(fragment);
node.addEventListener('click', this._onMarkerClicked.bind(this), false); for (const node of fieldList.querySelectorAll('.marker-link')) {
node.addEventListener('click', this._onMarkerClicked.bind(this), false);
}
} }
document.querySelector('#field-templates').addEventListener('change', this._onChanged.bind(this), false); this._fieldTemplatesTextarea.addEventListener('change', this._onChanged.bind(this), false);
document.querySelector('#field-template-render').addEventListener('click', this._onRender.bind(this), false); testRenderButton.addEventListener('click', this._onRender.bind(this), false);
document.querySelector('#field-templates-reset').addEventListener('click', this._onReset.bind(this), false); resetButton.addEventListener('click', this._onReset.bind(this), false);
document.querySelector('#field-templates-reset-confirm').addEventListener('click', this._onResetConfirm.bind(this), false); resetConfirmButton.addEventListener('click', this._onResetConfirm.bind(this), false);
if (menuButton !== null) {
menuButton.addEventListener('menuClosed', this._onFieldMenuClosed.bind(this), false);
}
this._settingsController.on('optionsChanged', this._onOptionsChanged.bind(this)); this._settingsController.on('optionsChanged', this._onOptionsChanged.bind(this));
@ -67,7 +85,7 @@ class AnkiTemplatesController {
_onOptionsChanged({options}) { _onOptionsChanged({options}) {
let templates = options.anki.fieldTemplates; let templates = options.anki.fieldTemplates;
if (typeof templates !== 'string') { templates = this._defaultFieldTemplates; } if (typeof templates !== 'string') { templates = this._defaultFieldTemplates; }
document.querySelector('#field-templates').value = templates; this._fieldTemplatesTextarea.value = templates;
this._onValidateCompile(); this._onValidateCompile();
} }
@ -84,9 +102,8 @@ class AnkiTemplatesController {
const value = this._defaultFieldTemplates; const value = this._defaultFieldTemplates;
const element = document.querySelector('#field-templates'); this._fieldTemplatesTextarea.value = value;
element.value = value; this._fieldTemplatesTextarea.dispatchEvent(new Event('change'));
element.dispatchEvent(new Event('change'));
} }
async _onChanged(e) { async _onChanged(e) {
@ -105,24 +122,37 @@ class AnkiTemplatesController {
} }
_onValidateCompile() { _onValidateCompile() {
const infoNode = document.querySelector('#field-template-compile-result'); this._validate(this._compileResultInfo, '{expression}', 'term-kanji', false, true);
this._validate(infoNode, '{expression}', 'term-kanji', false, true);
} }
_onMarkerClicked(e) { _onMarkerClicked(e) {
e.preventDefault(); e.preventDefault();
document.querySelector('#field-template-render-text').value = `{${e.target.textContent}}`; this._renderFieldInput.value = `{${e.target.textContent}}`;
} }
_onRender(e) { _onRender(e) {
e.preventDefault(); e.preventDefault();
const field = document.querySelector('#field-template-render-text').value; const field = this._renderFieldInput.value;
const infoNode = document.querySelector('#field-template-render-result'); const infoNode = this._renderResult;
infoNode.hidden = true; infoNode.hidden = true;
this._validate(infoNode, field, 'term-kanji', true, false); this._validate(infoNode, field, 'term-kanji', true, false);
} }
_onFieldMenuClosed({currentTarget: node, detail: {action, item}}) {
switch (action) {
case 'setFieldMarker':
this._setFieldMarker(node, item.dataset.marker);
break;
}
}
_setFieldMarker(element, marker) {
const input = this._renderFieldInput;
input.value = `{${marker}}`;
input.dispatchEvent(new Event('change'));
}
async _getDefinition(text, optionsContext) { async _getDefinition(text, optionsContext) {
if (this._cachedDefinitionText !== text) { if (this._cachedDefinitionText !== text) {
const {definitions} = await api.termsFind(text, {}, optionsContext); const {definitions} = await api.termsFind(text, {}, optionsContext);
@ -135,7 +165,7 @@ class AnkiTemplatesController {
} }
async _validate(infoNode, field, mode, showSuccessResult, invalidateInput) { async _validate(infoNode, field, mode, showSuccessResult, invalidateInput) {
const text = document.querySelector('#field-templates-preview-text').value || ''; const text = this._renderTextInput.value || '';
const exceptions = []; const exceptions = [];
let result = `No definition found for ${text}`; let result = `No definition found for ${text}`;
try { try {
@ -179,8 +209,7 @@ class AnkiTemplatesController {
infoNode.textContent = hasException ? exceptions.map((e) => `${e}`).join('\n') : (showSuccessResult ? result : ''); infoNode.textContent = hasException ? exceptions.map((e) => `${e}`).join('\n') : (showSuccessResult ? result : '');
infoNode.classList.toggle('text-danger', hasException); infoNode.classList.toggle('text-danger', hasException);
if (invalidateInput) { if (invalidateInput) {
const input = document.querySelector('#field-templates'); this._fieldTemplatesTextarea.classList.toggle('is-invalid', hasException);
input.classList.toggle('is-invalid', hasException);
} }
} }

View File

@ -1004,32 +1004,32 @@
their Anki cards. If you encounter problems with your changes, you can always reset to the default template settings. their Anki cards. If you encounter problems with your changes, you can always reset to the default template settings.
</p> </p>
<div class="ignore-form-changes"> <div class="ignore-form-changes">
<textarea autocomplete="off" spellcheck="false" wrap="soft" class="form-control" rows="10" id="field-templates"></textarea> <textarea autocomplete="off" spellcheck="false" wrap="soft" class="form-control" rows="10" id="anki-card-templates-textarea"></textarea>
</div> </div>
<div> <div>
<button class="btn btn-danger" id="field-templates-reset">Reset Templates</button> <button class="btn btn-danger" id="anki-card-templates-reset-button">Reset Templates</button>
</div> </div>
<p></p> <p></p>
<pre id="field-template-compile-result" hidden></pre> <pre id="anki-card-templates-compile-result" hidden></pre>
<p>Templates can be tested using the inputs below.</p> <p>Templates can be tested using the inputs below.</p>
<div class="form-group"> <div class="form-group">
<div class="row"> <div class="row">
<div class="col-xs-6"> <div class="col-xs-6">
<label for="field-templates-preview-text">Preview text</label> <label for="anki-card-templates-test-text-input">Preview text</label>
<input type="text" id="field-templates-preview-text" class="form-control" value="読め" placeholder="Preview text"> <input type="text" id="anki-card-templates-test-text-input" class="form-control" value="読め" placeholder="Preview text">
</div> </div>
<div class="col-xs-6"> <div class="col-xs-6">
<label for="field-template-render-text">Test field</label> <label for="anki-card-templates-test-field-input">Test field</label>
<div class="input-group"> <div class="input-group">
<div class="input-group-btn"> <div class="input-group-btn">
<button class="btn btn-default" id="field-template-render" title="Test"><span class="glyphicon glyphicon-play"></span></button> <button class="btn btn-default" id="anki-card-templates-test-render-button" title="Test"><span class="glyphicon glyphicon-play"></span></button>
</div> </div>
<input type="text" class="form-control" id="field-template-render-text" value="{expression}" placeholder="{marker}"> <input type="text" class="form-control" id="anki-card-templates-test-field-input" value="{expression}" placeholder="{marker}">
<div class="input-group-btn"> <div class="input-group-btn">
<button class="btn btn-default dropdown-toggle" id="field-templates-dropdown" data-toggle="dropdown"><span class="caret"></span></button> <button class="btn btn-default dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></button>
<ul class="dropdown-menu dropdown-menu-right" id="field-templates-list"></ul> <ul class="dropdown-menu dropdown-menu-right" id="anki-card-templates-field-list"></ul>
</div> </div>
</div> </div>
</div> </div>
@ -1037,10 +1037,10 @@
</div> </div>
<p></p> <p></p>
<pre id="field-template-render-result" hidden></pre> <pre id="anki-card-templates-render-result" hidden></pre>
</div> </div>
<div class="modal fade" tabindex="-1" role="dialog" id="field-template-reset-modal"> <div class="modal fade" tabindex="-1" role="dialog" id="anki-card-templates-reset">
<div class="modal-dialog modal-dialog-centered"> <div class="modal-dialog modal-dialog-centered">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
@ -1053,7 +1053,7 @@
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button> <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger" id="field-templates-reset-confirm">Reset Templates</button> <button type="button" class="btn btn-danger" id="anki-card-templates-reset-button-confirm">Reset Templates</button>
</div> </div>
</div> </div>
</div> </div>