Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 33 additions & 8 deletions ext/css/settings.css
Original file line number Diff line number Diff line change
Expand Up @@ -2364,26 +2364,43 @@ button.hotkey-list-item-enabled-button[data-scope-count='0'] {
/* Dictionary settings */
.dictionary-list {
width: 100%;
display: grid;
grid-template-columns: auto auto 1fr auto auto auto;
grid-template-rows: auto;
place-items: center start;
margin-top: 0.5em;
display: flex;
flex-direction: column;
}

.dictionary-list-index {
margin-right: 0.5em;
}
.dictionary-list[data-count='0']>.dictionary-item-top {
display: none;
margin-right: var(--dictionary-item-index-margin);
}

.dictionary-item-button-height {
height: var(--icon-button-size);
}

.dictionary-item.top {
color: var(--text-color-light2);
padding-left: calc(var(--dictionary-item-index-width) + var(--dictionary-item-index-margin));
}

.dictionary-item .generic-list-index-prefix::after {
display: block;
flex-flow: row nowrap;
width: var(--dictionary-item-index-width);
}

.dictionary-list-header {
display: flex;
margin-left: 25px;
}

.dictionary-item {
display: flex;
flex-flow: row nowrap;
align-items: center;
border-top: var(--thin-border-size) solid var(--separator-color2);
--dictionary-item-index-margin: 0.5em;
--dictionary-item-index-width: 1.2em;
}
.dictionary-item-enabled-toggle-container {
margin-right: 0.5em;
Expand Down Expand Up @@ -2452,7 +2469,12 @@ button.hotkey-list-item-enabled-button[data-scope-count='0'] {
display: table-cell;
white-space: pre-line;
}

.dictionary-highlight {
border: 2px dashed #8ab4f8;
}
.dragged-dictionary {
opacity: 50%;
}
#dictionary-alias-input {
width: 100%;
}
Expand All @@ -2470,6 +2492,9 @@ button.hotkey-list-item-enabled-button[data-scope-count='0'] {
padding: 0.375em 0;
}

.sort-icon {
font-size: 1.3em;
}

/* Dictionary Import */
#dictionary-import-url-text {
Expand Down
138 changes: 138 additions & 0 deletions ext/js/pages/settings/dictionary-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
this._dictionaryController = dictionaryController;
/** @type {number} */
this._index = index;
/** @type {number} */
this._clientY = index;
/** @type {import('dictionary-importer').Summary} */
this._dictionaryInfo = dictionaryInfo;
/** @type {string | null} */
Expand All @@ -51,6 +53,8 @@
this._databaseCounts = dictionaryDatabaseCounts;
/** @type {ChildNode[]} */
this._nodes = [...fragment.childNodes];
/** @type {HTMLElement} */
this.dictionaryItem = querySelectorNotNull(fragment, '.dictionary-item');
/** @type {HTMLInputElement} */
this._enabledCheckbox = querySelectorNotNull(fragment, '.dictionary-enabled');
/** @type {HTMLButtonElement} */
Expand Down Expand Up @@ -100,6 +104,8 @@
this._eventListeners.addEventListener(this._integrityButtonCheck, 'click', this._onIntegrityButtonClick.bind(this), false);
this._eventListeners.addEventListener(this._integrityButtonWarning, 'click', this._onIntegrityButtonClick.bind(this), false);
this._eventListeners.addEventListener(this._updatesAvailable, 'click', this._onUpdateButtonClick.bind(this), false);
this._eventListeners.addEventListener(this.dictionaryItem, 'dragstart', this._onDragStart.bind(this), false);
this._eventListeners.addEventListener(this.dictionaryItem, 'dragend', this._onDragEnd.bind(this), false);

this.setCounts(this._databaseCounts);
}
Expand Down Expand Up @@ -282,6 +288,27 @@
this._dictionaryController.updateDictionary(this.dictionaryTitle, downloadUrl);
}

/**
* @typedef {object} EntryDragStartDetail
* @property {number} index The index of the entry being dragged.
*/
/**
* @typedef {CustomEvent<EntryDragStartDetail>} EntryDragStartEvent
*/

/**
* Initiates the drag process by dispatching the 'entryDragStart' custom event.
* @fires EntryDragStartEvent - When the drag starts.
*/
_onDragStart() {
this.dictionaryItem.dispatchEvent(new CustomEvent('entryDragStart', {detail: {index: this._index}}));
}

/** */
_onDragEnd() {
this.dictionaryItem.dispatchEvent(new CustomEvent('entryDragEnd'));
}

/** */
_onIntegrityButtonClick() {
this._showDetails();
Expand Down Expand Up @@ -595,6 +622,8 @@
this._checkingUpdates = false;
/** @type {boolean} */
this._checkingIntegrity = false;
/** @type {number} */
this._clientY = 0;
/** @type {?HTMLButtonElement} */
this._checkUpdatesButton = document.querySelector('#dictionary-check-updates');
/** @type {?HTMLButtonElement} */
Expand All @@ -617,6 +646,8 @@
this._allCheckbox = querySelectorNotNull(document, '#all-dictionaries-enabled');
/** @type {?DictionaryExtraInfo} */
this._extraInfo = null;
/** @type {number} */
this._draggingIndex = 0;
/** @type {import('dictionary-controller.js').DictionaryTask[]} */
this._dictionaryTaskQueue = [];
/** @type {boolean} */
Expand Down Expand Up @@ -665,6 +696,8 @@
dictionarySetAliasButton.addEventListener('click', this._onDictionarySetAliasButtonClick.bind(this), false);
dictionaryResetAliasButton.addEventListener('click', this._onDictionaryResetAliasButtonClick.bind(this), false);

this._dictionaryEntryContainer.addEventListener('dragover', this._onDragOver.bind(this), false);

if (this._checkUpdatesButton !== null) {
this._checkUpdatesButton.addEventListener('click', this._onCheckUpdatesButtonClick.bind(this), false);
}
Expand Down Expand Up @@ -971,6 +1004,7 @@
entry.cleanup();
}

this._dictionaryEntries = [];
const dictionaryOptionsArray = options.dictionaries;
for (let i = 0; i < dictionaryOptionsArray.length; i++) {
const {name} = dictionaryOptionsArray[i];
Expand Down Expand Up @@ -1135,6 +1169,24 @@
}
}

/**
* @param {DragEvent} e
*/
_onDragOver(e) {
e.preventDefault();
this._clientY = e.clientY;
const cursorY = e.clientY;
const containerRect = this._dictionaryEntryContainer.getBoundingClientRect();
const topZone = [containerRect.top - 60, containerRect.top + 60];
const bottomZone = [containerRect.bottom - 60, containerRect.bottom + 60];
this.highlightDragOverDictionaryItem(e.clientY)
if (cursorY > topZone[0] && cursorY < topZone[1]) {
this._dictionaryEntryContainer.scrollBy(0, -5); // Scroll up
} else if (cursorY > bottomZone[0] && cursorY < bottomZone[1]) {
this._dictionaryEntryContainer.scrollBy(0, 5); // Scroll down
}
}

/**
* @param {import('dictionary-importer').Summary[]} dictionaries
*/
Expand Down Expand Up @@ -1244,6 +1296,7 @@
* @param {import('dictionary-importer').Summary} dictionaryInfo
* @param {string|null} updateDownloadUrl
* @param {import('dictionary-database').DictionaryCountGroup|null} dictionaryDatabaseCounts
* @throws Error
*/
_createDictionaryEntry(index, dictionaryInfo, updateDownloadUrl, dictionaryDatabaseCounts) {
const fragment = this.instantiateTemplateFragment('dictionary');
Expand All @@ -1252,13 +1305,98 @@
this._dictionaryEntries.push(entry);
entry.prepare();

entry.dictionaryItem.addEventListener('entryDragStart', /** @type {EventListener} */ (this._onEntryDragStart.bind(this)), false);
entry.dictionaryItem.addEventListener('entryDragEnd', this._onEntryDragEnd.bind(this), false);

const container = /** @type {HTMLElement} */ (this._dictionaryEntryContainer);
const relative = container.querySelector('.dictionary-item-bottom');
container.insertBefore(fragment, relative);

this._updateDictionaryEntryCount();
}

/**
* Handles the 'entryDragStart' custom event.
* @param {EntryDragStartEvent} e The custom event object with entry drag details.
* @returns {void}
*/
_onEntryDragStart(e) {
const index = e?.detail?.index;
if (typeof index !== 'number') { return; }
this._draggingIndex = index;
const item = this._dictionaryEntries[index].dictionaryItem;
item.classList.add('dragged-dictionary')

}

/**
* Handles the 'entryDragEnd' custom event.
* @throws Error
* @returns {void}
*/
_onEntryDragEnd() {

const draggingIndex = this._draggingIndex;

const cursorY = this._clientY;
if (draggingIndex === null) { throw new Error('No dragging index'); }
const item = this._dictionaryEntries[draggingIndex].dictionaryItem;

item.classList.remove('dragged-dictionary')
item.classList.remove('dictionary-highlight')

const nextDictionaryIndex = this._getDragOverDictionaryItem(draggingIndex, cursorY);
if (nextDictionaryIndex === draggingIndex) { return; }

void this.moveDictionaryOptions(draggingIndex, nextDictionaryIndex);

this._draggingIndex = nextDictionaryIndex;
this._draggingIndex = 0;
}

/**
* @param {number} draggingIndex
* @param {number} y
* @returns {number}
*/
_getDragOverDictionaryItem(draggingIndex, y) {
const neighbors = this._dictionaryEntries.map((entry, index) => index);

/** @type {{index: number|null, offset: number}} */
const currentBest = {index: null, offset: Number.POSITIVE_INFINITY};
for (const index of neighbors) {
const item = this._dictionaryEntries[index].dictionaryItem;
const {top, height} = item.getBoundingClientRect();
const offset = Math.abs(y - (top + height / 2));
if (offset < currentBest.offset) {
currentBest.index = index;
currentBest.offset = offset;
}
}

if (currentBest.index === null) {
return this._dictionaryEntries.length - 1;
}

return currentBest.index;
}

highlightDragOverDictionaryItem(y) {

Check failure on line 1384 in ext/js/pages/settings/dictionary-controller.js

View workflow job for this annotation

GitHub Actions / TypeScript (test)

Parameter 'y' implicitly has an 'any' type.

Check failure on line 1384 in ext/js/pages/settings/dictionary-controller.js

View workflow job for this annotation

GitHub Actions / TypeScript (main)

Parameter 'y' implicitly has an 'any' type.
const neighbors = this._dictionaryEntries.map((entry, index) => index);

const currentBest = {entry: null, offset: Number.POSITIVE_INFINITY};
for (const index of neighbors) {
const item = this._dictionaryEntries[index].dictionaryItem;
const {top, height} = item.getBoundingClientRect();
const offset = Math.abs(y - (top + height / 2));
item.classList.remove('dictionary-highlight')
if (offset < currentBest.offset) {
currentBest.entry = item;

Check failure on line 1394 in ext/js/pages/settings/dictionary-controller.js

View workflow job for this annotation

GitHub Actions / TypeScript (test)

Type 'HTMLElement' is not assignable to type 'null'.

Check failure on line 1394 in ext/js/pages/settings/dictionary-controller.js

View workflow job for this annotation

GitHub Actions / TypeScript (main)

Type 'HTMLElement' is not assignable to type 'null'.
currentBest.offset = offset;
}
}
currentBest.entry.classList.add('dictionary-highlight')

Check failure on line 1398 in ext/js/pages/settings/dictionary-controller.js

View workflow job for this annotation

GitHub Actions / TypeScript (test)

'currentBest.entry' is possibly 'null'.

Check failure on line 1398 in ext/js/pages/settings/dictionary-controller.js

View workflow job for this annotation

GitHub Actions / TypeScript (main)

'currentBest.entry' is possibly 'null'.
}

/**
* @param {string} dictionaryTitle
Expand Down
11 changes: 5 additions & 6 deletions ext/templates-modals.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,11 @@
</div>
<div id="dictionary-error" class="danger-text margin-above" hidden></div>
<div id="dictionary-list" class="dictionary-list generic-list" data-count="0">
<div class="dictionary-item-top"></div>
<label class="dictionary-item-top toggle dictionary-item-enabled-toggle-container"><input type="checkbox" id="all-dictionaries-enabled"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
<div class="dictionary-item-top dictionary-item-title-container">All</div>
<div class="dictionary-item-top dictionary-item-button-height"></div>
<div class="dictionary-item-top dictionary-item-button-height"></div>
<div class="dictionary-item-top dictionary-item-button-height"></div>
<div class="dictionary-list-header">
<div class="dictionary-item-top"></div>
<label class="dictionary-item-top toggle dictionary-item-enabled-toggle-container"><input type="checkbox" id="all-dictionaries-enabled"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
<div class="dictionary-item-top dictionary-item-title-container">All</div>
</div>
</div>

<div hidden><input type="file" id="dictionary-import-file-input" accept=".zip,application/zip" multiple></div>
Expand Down
39 changes: 21 additions & 18 deletions ext/templates-settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,28 +48,31 @@

<!-- Dictionary -->
<template id="dictionary-template">
<div class="dictionary-list-index generic-list-index-prefix"></div>
<label class="toggle dictionary-item-enabled-toggle-container"><input type="checkbox" class="dictionary-enabled"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
<div class="dictionary-item-title-container">
<div class="dictionary-item" draggable="true">
<div class="dictionary-list-index generic-list-index-prefix"></div>
<span class="sort-icon" data-sort-direction="none">⇅</span>
<label class="toggle dictionary-item-enabled-toggle-container"><input type="checkbox" class="dictionary-enabled"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
<div class="dictionary-item-title-container">
<span>
<strong class="dictionary-title dictionary-alias"></strong> <span class="light dictionary-revision"></span>
</span>
<button type="button" class="dictionary-outdated-button" hidden>
<div class="badge warning-badge"><span class="icon" data-icon="exclamation-point-short"></span></div>
</button>
<button type="button" class="dictionary-integrity-button-check" hidden>
<div class="badge info-badge badge-small-icon"><span class="icon" data-icon="checkmark"></span></div>
</button>
<button type="button" class="dictionary-integrity-button-warning" hidden>
<div class="badge warning-badge"><span class="icon" data-icon="exclamation-point-short"></span></div>
</button>
<button type="button" class="dictionary-update-available" hidden>
<div class="badge info-badge badge-small-icon"><span class="icon" data-icon="exclamation-point-short" title="Update available"></span></div>
</button>
<button type="button" class="dictionary-outdated-button" hidden>
<div class="badge warning-badge"><span class="icon" data-icon="exclamation-point-short"></span></div>
</button>
<button type="button" class="dictionary-integrity-button-check" hidden>
<div class="badge info-badge badge-small-icon"><span class="icon" data-icon="checkmark"></span></div>
</button>
<button type="button" class="dictionary-integrity-button-warning" hidden>
<div class="badge warning-badge"><span class="icon" data-icon="exclamation-point-short"></span></div>
</button>
<button type="button" class="dictionary-update-available" hidden>
<div class="badge info-badge badge-small-icon"><span class="icon" data-icon="exclamation-point-short" title="Update available"></span></div>
</button>
</div>
<button type="button" class="icon-button" id="dictionary-move-up" data-menu-action="moveUp"><span class="icon-button-inner"><span class="icon" data-icon="up-chevron"></span></span></button>
<button type="button" class="icon-button" id="dictionary-move-down" data-menu-action="moveDown"><span class="icon-button-inner"><span class="icon" data-icon="down-chevron"></span></span></button>
<button type="button" class="icon-button dictionary-menu-button" data-menu="dictionary-menu" data-menu-position="below left"><span class="icon-button-inner"><span class="icon" data-icon="kebab-menu"></span></span></button>
</div>
<button type="button" class="icon-button" id="dictionary-move-up" data-menu-action="moveUp"><span class="icon-button-inner"><span class="icon" data-icon="up-chevron"></span></span></button>
<button type="button" class="icon-button" id="dictionary-move-down" data-menu-action="moveDown"><span class="icon-button-inner"><span class="icon" data-icon="down-chevron"></span></span></button>
<button type="button" class="icon-button dictionary-menu-button" data-menu="dictionary-menu" data-menu-position="below left"><span class="icon-button-inner"><span class="icon" data-icon="kebab-menu"></span></span></button>
</template>
<template id="dictionary-details-entry-template"><div class="dictionary-details-entry">
<span class="dictionary-details-entry-label"></span>
Expand Down
Loading