feat: add word search

This commit is contained in:
2025-06-25 16:25:52 +03:00
parent dbb6806a78
commit 80d4bff0b4
17 changed files with 78 additions and 15 deletions

View File

@@ -88,5 +88,8 @@
}, },
"global_highlight_toggle": { "global_highlight_toggle": {
"message": "Aktivieren" "message": "Aktivieren"
} },
"search_placeholder": {
"message": "Suchen..."
}
} }

View File

@@ -88,5 +88,8 @@
}, },
"global_highlight_toggle": { "global_highlight_toggle": {
"message": "Enable" "message": "Enable"
},
"search_placeholder": {
"message": "Search..."
} }
} }

View File

@@ -88,5 +88,8 @@
}, },
"global_highlight_toggle": { "global_highlight_toggle": {
"message": "Activar" "message": "Activar"
},
"search_placeholder": {
"message": "Buscar..."
} }
} }

View File

@@ -88,5 +88,8 @@
}, },
"global_highlight_toggle": { "global_highlight_toggle": {
"message": "Activer" "message": "Activer"
},
"search_placeholder": {
"message": "Rechercher..."
} }
} }

View File

@@ -88,5 +88,8 @@
}, },
"global_highlight_toggle": { "global_highlight_toggle": {
"message": "सक्षम करें" "message": "सक्षम करें"
},
"search_placeholder": {
"message": "खोजें..."
} }
} }

View File

@@ -88,5 +88,8 @@
}, },
"global_highlight_toggle": { "global_highlight_toggle": {
"message": "Attiva" "message": "Attiva"
},
"search_placeholder": {
"message": "Cerca..."
} }
} }

View File

@@ -88,5 +88,8 @@
}, },
"global_highlight_toggle": { "global_highlight_toggle": {
"message": "有効にする" "message": "有効にする"
},
"search_placeholder": {
"message": "検索..."
} }
} }

View File

@@ -88,5 +88,8 @@
}, },
"global_highlight_toggle": { "global_highlight_toggle": {
"message": "활성화" "message": "활성화"
},
"search_placeholder": {
"message": "검색..."
} }
} }

View File

@@ -88,5 +88,8 @@
}, },
"global_highlight_toggle": { "global_highlight_toggle": {
"message": "Inschakelen" "message": "Inschakelen"
},
"search_placeholder": {
"message": "Zoeken..."
} }
} }

View File

@@ -88,5 +88,8 @@
}, },
"global_highlight_toggle": { "global_highlight_toggle": {
"message": "Włącz" "message": "Włącz"
},
"search_placeholder": {
"message": "Szukaj..."
} }
} }

View File

@@ -88,5 +88,8 @@
}, },
"global_highlight_toggle": { "global_highlight_toggle": {
"message": "Ativar" "message": "Ativar"
} },
"search_placeholder": {
"message": "Pesquisar..."
}
} }

View File

@@ -88,5 +88,8 @@
}, },
"global_highlight_toggle": { "global_highlight_toggle": {
"message": "Вкл" "message": "Вкл"
},
"search_placeholder": {
"message": "Поиск..."
} }
} }

View File

@@ -88,5 +88,8 @@
}, },
"global_highlight_toggle": { "global_highlight_toggle": {
"message": "Etkinleştir" "message": "Etkinleştir"
},
"search_placeholder": {
"message": "Ara..."
} }
} }

View File

@@ -88,5 +88,8 @@
}, },
"global_highlight_toggle": { "global_highlight_toggle": {
"message": "启用" "message": "启用"
} },
"search_placeholder": {
"message": "搜索..."
}
} }

View File

@@ -236,6 +236,12 @@ button.danger:hover {
gap: 6px; gap: 6px;
} }
#wordSearch{
width:100%;
margin-bottom:8px;
margin-top: 8px;
}
#wordList { #wordList {
margin-top: 8px; margin-top: 8px;
position: relative; position: relative;

View File

@@ -77,6 +77,7 @@
<button id="disableSelectedBtn"><span data-i18n="disable_selected">Disable</span></button> <button id="disableSelectedBtn"><span data-i18n="disable_selected">Disable</span></button>
<button id="deleteSelectedBtn" class="danger"><span data-i18n="delete_selected">Delete</span></button> <button id="deleteSelectedBtn" class="danger"><span data-i18n="delete_selected">Delete</span></button>
</div> </div>
<input type="text" id="wordSearch" data-i18n="search_placeholder" placeholder="Search..."/>
<div id="wordList"></div> <div id="wordList"></div>
</div> </div>

View File

@@ -11,6 +11,7 @@ let currentListIndex = 0;
let saveTimeout; let saveTimeout;
let selectedCheckboxes = new Set(); let selectedCheckboxes = new Set();
let globalHighlightEnabled = true; let globalHighlightEnabled = true;
let wordSearchQuery = "";
function escapeHtml(str) { function escapeHtml(str) {
return str.replace(/[&<>"']/g, function (m) { return str.replace(/[&<>"']/g, function (m) {
@@ -108,7 +109,12 @@ function updateListForm() {
function renderWords() { function renderWords() {
const list = lists[currentListIndex]; const list = lists[currentListIndex];
const fragment = document.createDocumentFragment();
let filteredWords = list.words;
if (wordSearchQuery.trim()) {
const q = wordSearchQuery.trim().toLowerCase();
filteredWords = list.words.filter(w => w.wordStr.toLowerCase().includes(q));
}
const itemHeight = 32; const itemHeight = 32;
const containerHeight = wordList.clientHeight; const containerHeight = wordList.clientHeight;
@@ -116,18 +122,19 @@ function renderWords() {
const startIndex = Math.floor(scrollTop / itemHeight); const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.min( const endIndex = Math.min(
startIndex + Math.ceil(containerHeight / itemHeight) + 2, startIndex + Math.ceil(containerHeight / itemHeight) + 2,
list.words.length filteredWords.length
); );
wordList.innerHTML = ''; wordList.innerHTML = '';
const spacer = document.createElement('div'); const spacer = document.createElement('div');
spacer.style.position = 'relative'; spacer.style.position = 'relative';
spacer.style.height = `${list.words.length * itemHeight}px`; spacer.style.height = `${filteredWords.length * itemHeight}px`;
spacer.style.width = '100%'; spacer.style.width = '100%';
for (let i = startIndex; i < endIndex; i++) { for (let i = startIndex; i < endIndex; i++) {
const w = list.words[i]; const w = filteredWords[i];
if (!w) continue;
const container = document.createElement("div"); const container = document.createElement("div");
container.style.height = `${itemHeight}px`; container.style.height = `${itemHeight}px`;
container.style.position = 'absolute'; container.style.position = 'absolute';
@@ -143,18 +150,20 @@ function renderWords() {
container.style.background = 'var(--highlight-tag)'; container.style.background = 'var(--highlight-tag)';
container.style.border = '1px solid var(--highlight-tag-border)'; container.style.border = '1px solid var(--highlight-tag-border)';
const realIndex = list.words.indexOf(w);
const cbSelect = document.createElement("input"); const cbSelect = document.createElement("input");
cbSelect.type = "checkbox"; cbSelect.type = "checkbox";
cbSelect.className = "word-checkbox"; cbSelect.className = "word-checkbox";
cbSelect.dataset.index = i; cbSelect.dataset.index = realIndex;
if (selectedCheckboxes.has(i)) { if (selectedCheckboxes.has(realIndex)) {
cbSelect.checked = true; cbSelect.checked = true;
} }
const inputWord = document.createElement("input"); const inputWord = document.createElement("input");
inputWord.type = "text"; inputWord.type = "text";
inputWord.value = w.wordStr; inputWord.value = w.wordStr;
inputWord.dataset.wordEdit = i; inputWord.dataset.wordEdit = realIndex;
inputWord.style.flexGrow = '1'; inputWord.style.flexGrow = '1';
inputWord.style.minWidth = '0'; inputWord.style.minWidth = '0';
inputWord.style.padding = '4px 8px'; inputWord.style.padding = '4px 8px';
@@ -166,7 +175,7 @@ function renderWords() {
const inputBg = document.createElement("input"); const inputBg = document.createElement("input");
inputBg.type = "color"; inputBg.type = "color";
inputBg.value = w.background || list.background; inputBg.value = w.background || list.background;
inputBg.dataset.bgEdit = i; inputBg.dataset.bgEdit = realIndex;
inputBg.style.width = '24px'; inputBg.style.width = '24px';
inputBg.style.height = '24px'; inputBg.style.height = '24px';
inputBg.style.flexShrink = '0'; inputBg.style.flexShrink = '0';
@@ -174,7 +183,7 @@ function renderWords() {
const inputFg = document.createElement("input"); const inputFg = document.createElement("input");
inputFg.type = "color"; inputFg.type = "color";
inputFg.value = w.foreground || list.foreground; inputFg.value = w.foreground || list.foreground;
inputFg.dataset.fgEdit = i; inputFg.dataset.fgEdit = realIndex;
inputFg.style.width = '24px'; inputFg.style.width = '24px';
inputFg.style.height = '24px'; inputFg.style.height = '24px';
inputFg.style.flexShrink = '0'; inputFg.style.flexShrink = '0';
@@ -189,7 +198,7 @@ function renderWords() {
const cbActive = document.createElement("input"); const cbActive = document.createElement("input");
cbActive.type = "checkbox"; cbActive.type = "checkbox";
cbActive.checked = w.active !== false; cbActive.checked = w.active !== false;
cbActive.dataset.activeEdit = i; cbActive.dataset.activeEdit = realIndex;
cbActive.className = "switch"; cbActive.className = "switch";
activeContainer.appendChild(cbActive); activeContainer.appendChild(cbActive);
@@ -207,7 +216,7 @@ function renderWords() {
const wordCount = document.getElementById('wordCount'); const wordCount = document.getElementById('wordCount');
if (wordCount) { if (wordCount) {
wordCount.textContent = list.words.length; wordCount.textContent = filteredWords.length;
} }
} }
@@ -221,7 +230,6 @@ document.addEventListener('DOMContentLoaded', () => {
renderWords(); renderWords();
}; };
// Add event listener for the global toggle
document.getElementById("globalHighlightToggle").addEventListener('change', function () { document.getElementById("globalHighlightToggle").addEventListener('change', function () {
globalHighlightEnabled = this.checked; globalHighlightEnabled = this.checked;
updateGlobalToggleState(); updateGlobalToggleState();
@@ -415,5 +423,11 @@ document.addEventListener('DOMContentLoaded', () => {
renderWords(); renderWords();
}; };
const wordSearch = document.getElementById("wordSearch");
wordSearch.addEventListener("input", (e) => {
wordSearchQuery = e.target.value;
renderWords();
});
load(); load();
}); });