From 5abc5eb7b44cc914cdb420043ebe21ac11eae7a1 Mon Sep 17 00:00:00 2001 From: ph1r3754r73r <6174900+ph1r3574r73r@users.noreply.github.com> Date: Wed, 24 Jun 2026 10:48:03 -0700 Subject: [PATCH] search/filter update for transforms, node version bump --- .github/workflows/README.md | 2 +- .github/workflows/deploy.yml | 10 +- css/style.css | 211 +++++++++++++++++++++++++++++++++-- js/tools/TransformTool.js | 151 ++++++++++++++++--------- templates/transforms.html | 80 +++++++++---- 5 files changed, 372 insertions(+), 82 deletions(-) diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 836de0e..2c3e499 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -8,7 +8,7 @@ This workflow automatically builds and deploys the project to GitHub Pages whene 1. **Build Stage:** - Checks out the repository - - Sets up Node.js (v20) with npm cache enabled + - Sets up Node.js (v24) with npm cache enabled - Installs dependencies with `npm ci` - Runs test suite with `npm run test:all` - Runs `npm run build` which: diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 892aab0..ea99a33 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -24,12 +24,12 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v5 with: - node-version: '20' + node-version: '24' cache: 'npm' - name: Install dependencies @@ -52,7 +52,7 @@ jobs: echo "✅ All critical build files present" - name: Upload artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v4 with: path: 'dist/' retention-days: 7 @@ -67,4 +67,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v5 diff --git a/css/style.css b/css/style.css index dfff7cb..384c906 100644 --- a/css/style.css +++ b/css/style.css @@ -2448,7 +2448,184 @@ body.theme-light .mobile-tool-dropdown { border: 1px solid var(--input-border); } -/* Transform category legend styling */ +/* Transform filter bar (search + category filters) */ +.transform-filter-bar { + display: flex; + flex-direction: column; + gap: 14px; + margin-bottom: 16px; + padding: 14px 16px; + border-radius: 10px; + border: 1px solid var(--input-border); + background: color-mix(in srgb, var(--button-bg) 65%, var(--main-bg-color)); +} + +.transform-search-wrap { + display: flex; + flex-direction: row; + align-items: center; + gap: 10px; + padding: 0 14px; + min-height: 42px; + border-radius: 8px; + border: 1px solid var(--input-border); + background: var(--input-bg); + box-sizing: border-box; +} + +.transform-search-icon { + color: var(--text-color); + opacity: 0.45; + flex-shrink: 0; + font-size: 0.9rem; +} + +.transform-search-wrap input[type="search"] { + flex: 1; + min-width: 0; + height: 40px; + padding: 0; + border: none; + background: transparent; + color: var(--text-color); + font: inherit; + font-size: 0.92rem; + outline: none; + line-height: 1.4; +} + +.transform-search-wrap input[type="search"]::placeholder { + color: var(--text-color); + opacity: 0.4; +} + +.transform-search-wrap input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; +} + +.transform-filter-clear { + display: inline-flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + border: none; + background: var(--button-bg); + color: var(--text-color); + opacity: 0.7; + cursor: pointer; + border-radius: 6px; + flex-shrink: 0; +} + +.transform-filter-clear:hover { + opacity: 1; + background: var(--button-hover-bg); +} + +.transform-category-filters { + display: flex; + flex-direction: column; + gap: 8px; +} + +.transform-filter-label { + font-size: 0.72rem; + font-weight: 600; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--text-color); + opacity: 0.5; +} + +.transform-category-chips { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.category-chip { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 12px; + font-size: 0.8rem; + font-weight: 500; + line-height: 1.2; + border-radius: 999px; + border: 1px solid var(--input-border); + background: var(--main-bg-color); + color: var(--text-color); + cursor: pointer; + transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease; + text-transform: capitalize; + box-shadow: none; +} + +.category-chip::before { + content: ''; + width: 7px; + height: 7px; + border-radius: 50%; + background: var(--chip-accent, var(--accent-color)); + flex-shrink: 0; + opacity: 0.9; +} + +.category-chip.category-chip-all::before { + background: var(--accent-color); +} + +.category-chip:hover { + border-color: color-mix(in srgb, var(--chip-accent, var(--accent-color)) 45%, var(--input-border)); + background: var(--button-hover-bg); +} + +.category-chip.active-filter { + background: var(--chip-accent, var(--accent-color)); + border-color: var(--chip-accent, var(--accent-color)); + color: var(--text-on-accent); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15); +} + +.category-chip.active-filter::before { + background: var(--text-on-accent); + opacity: 0.9; +} + +.category-chip.transform-category-case { --chip-accent: var(--case-color); } +.category-chip.transform-category-cipher { --chip-accent: var(--cipher-color); } +.category-chip.transform-category-concealment { --chip-accent: var(--concealment-color); } +.category-chip.transform-category-encoding { --chip-accent: var(--encoding-color); } +.category-chip.transform-category-format { --chip-accent: var(--format-color); } +.category-chip.transform-category-signwriting { --chip-accent: var(--signwriting-color); } +.category-chip.transform-category-special { --chip-accent: var(--special-color); } +.category-chip.transform-category-symbol { --chip-accent: var(--symbol-color); } +.category-chip.transform-category-technical { --chip-accent: var(--technical-color); } +.category-chip.transform-category-unicode { --chip-accent: var(--unicode-color); } +.category-chip.transform-category-visual { --chip-accent: var(--visual-color); } +.category-chip.transform-category-randomizer { --chip-accent: var(--randomizer-color); } +.category-chip.transform-category-custom_spelling { --chip-accent: var(--custom-spelling-color); } + +.transform-filter-empty { + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; + padding: 24px 16px; + text-align: center; + color: var(--text-color); + opacity: 0.8; + border: 1px dashed var(--input-border); + border-radius: 8px; +} + +.transform-filter-empty i { + font-size: 1.25rem; + opacity: 0.6; +} + +/* Legacy alias — category filter chips reuse legend-item styles */ .transform-category-legend { display: flex; flex-wrap: wrap; @@ -2491,6 +2668,7 @@ body.theme-light .mobile-tool-dropdown { background-color: var(--button-hover-bg); } +.legend-item.active-filter, .legend-item.active-category { background-color: var(--accent-color); color: var(--text-on-accent); @@ -6887,16 +7065,35 @@ html { font-size: 0.85rem; } - /* Legend items wrap better */ + /* Category filter bar */ + .transform-filter-bar { + padding: 12px; + gap: 12px; + } + + .transform-search-wrap { + min-height: 38px; + padding: 0 12px; + } + + .transform-search-wrap input[type="search"] { + height: 36px; + font-size: 0.88rem; + } + + .transform-category-chips { + gap: 5px; + } + + .category-chip { + font-size: 0.75rem; + padding: 5px 10px; + } + .transform-category-legend { gap: var(--spacing-xs); } - .legend-item { - font-size: 0.75rem; - padding: 3px var(--spacing-xs); - } - /* Transform chain items stack vertically */ .transform-chain-item .transform-select { min-width: 100%; diff --git a/js/tools/TransformTool.js b/js/tools/TransformTool.js index 939d1ca..4ae561c 100644 --- a/js/tools/TransformTool.js +++ b/js/tools/TransformTool.js @@ -54,7 +54,9 @@ class TransformTool extends Tool { transformOptionPrefs: this.loadTransformOptionPrefs(), transformOptionsModalOpen: false, transformOptionsModalTransform: null, - transformOptionsDraft: {} + transformOptionsDraft: {}, + transformSearchQuery: '', + transformCategoryFilter: '' }; } @@ -395,6 +397,104 @@ class TransformTool extends Tool { !this.favorites.some(f => typeof f === 'string' && f === t.name) ); }, + formatCategoryLabel: function(category) { + return String(category || '').replace(/_/g, ' '); + }, + toggleCategoryFilter: function(category) { + this.transformCategoryFilter = this.transformCategoryFilter === category ? '' : category; + }, + clearTransformFilters: function() { + this.transformSearchQuery = ''; + this.transformCategoryFilter = ''; + }, + transformSearchActive: function() { + return String(this.transformSearchQuery || '').trim().length > 0; + }, + transformMatchesSearchText: function(text) { + const query = String(this.transformSearchQuery || '').trim().toLowerCase(); + if (!query) { + return true; + } + return String(text || '').toLowerCase().indexOf(query) !== -1; + }, + transformMatchesSearch: function(transform) { + return this.transformMatchesSearchText(transform && transform.name); + }, + getFilteredTransformsByCategory: function(category) { + const list = this.getTransformsByCategory(category); + if (!this.transformSearchActive()) { + return list; + } + return list.filter(t => this.transformMatchesSearch(t)); + }, + categorySectionVisible: function(category) { + if (this.transformCategoryFilter && this.transformCategoryFilter !== category) { + return false; + } + return this.getFilteredTransformsByCategory(category).length > 0; + }, + displayItemMatchesFilters: function(item) { + if (!item) { + return false; + } + if (this.transformCategoryFilter) { + if (item.type === 'translate') { + return false; + } + if (item.type === 'transform' && item.transform) { + const category = item.transform.category || this.getDisplayCategory(item.transform.name); + if (category !== this.transformCategoryFilter) { + return false; + } + } + } + if (!this.transformSearchActive()) { + return true; + } + if (item.type === 'translate') { + return this.transformMatchesSearchText(item.langName); + } + if (item.type === 'transform' && item.transform) { + return this.transformMatchesSearch(item.transform); + } + return true; + }, + getFilteredFavoriteDisplayItems: function() { + return this.getFavoriteDisplayItems().filter(item => this.displayItemMatchesFilters(item)); + }, + getFilteredLastUsedDisplayItems: function() { + return this.getLastUsedDisplayItems().filter(item => this.displayItemMatchesFilters(item)); + }, + favoritesSectionVisible: function() { + return this.showFavorites && this.getFilteredFavoriteDisplayItems().length > 0; + }, + lastUsedSectionVisible: function() { + return this.showLastUsed && this.getFilteredLastUsedDisplayItems().length > 0; + }, + translateSectionVisible: function() { + if (this.transformCategoryFilter) { + return false; + } + if (!this.transformSearchActive()) { + return true; + } + const langs = (this.translateMainLangs || []) + .concat(this.translateExoticLangs || []) + .concat(this.translateCustomLangs || []); + return langs.some(lang => this.transformMatchesSearchText(lang.name)); + }, + translateLangVisible: function(langName) { + return this.transformMatchesSearchText(langName); + }, + transformListHasNoMatches: function() { + if (!this.transformSearchActive() && !this.transformCategoryFilter) { + return false; + } + if (this.favoritesSectionVisible() || this.lastUsedSectionVisible() || this.translateSectionVisible()) { + return false; + } + return !this.categories.some(category => this.categorySectionVisible(category)); + }, isSpecialCategory: function(category) { return category === 'randomizer'; }, @@ -667,52 +767,7 @@ class TransformTool extends Tool { if (nextCustomCount !== previousCustomCount) { this.saveCategoryOrder(this.categories); } - - this.$nextTick(() => { - if (typeof this.initializeCategoryNavigation === 'function') { - this.initializeCategoryNavigation(); - } - }); }, - initializeCategoryNavigation: function() { - this.$nextTick(() => { - const legendItems = document.querySelectorAll('.transform-category-legend .legend-item'); - legendItems.forEach(item => { - const newItem = item.cloneNode(true); - item.parentNode.replaceChild(newItem, item); - }); - - document.querySelectorAll('.transform-category-legend .legend-item').forEach(item => { - item.addEventListener('click', () => { - const targetId = item.getAttribute('data-target'); - if (targetId) { - const targetElement = document.getElementById(targetId); - if (targetElement) { - document.querySelectorAll('.transform-category-legend .legend-item').forEach(li => { - li.classList.remove('active-category'); - }); - item.classList.add('active-category'); - - const inputSection = document.querySelector('.input-section'); - const inputSectionHeight = inputSection.offsetHeight; - const elementPosition = targetElement.getBoundingClientRect().top + window.pageYOffset; - const offsetPosition = elementPosition - inputSectionHeight - 10; - - window.scrollTo({ - top: offsetPosition, - behavior: 'smooth' - }); - - targetElement.classList.add('highlight-section'); - setTimeout(() => { - targetElement.classList.remove('highlight-section'); - }, 1000); - } - } - }); - }); - }); - } }; } @@ -741,7 +796,6 @@ class TransformTool extends Tool { if (typeof this.refreshCustomSpellingTransforms === 'function') { this.refreshCustomSpellingTransforms(); } - this.initializeCategoryNavigation(); // Save initial category order to localStorage if it doesn't exist // This ensures consistent state for category reordering operations @@ -761,9 +815,6 @@ class TransformTool extends Tool { if (typeof vueInstance.refreshCustomSpellingTransforms === 'function') { vueInstance.refreshCustomSpellingTransforms(); } - vueInstance.$nextTick(() => { - vueInstance.initializeCategoryNavigation(); - }); } } diff --git a/templates/transforms.html b/templates/transforms.html index b735719..d44fec0 100644 --- a/templates/transforms.html +++ b/templates/transforms.html @@ -57,23 +57,56 @@
-
-
Categories:
-
-
+
+ + + +
+
+ Category +
+ +
@@ -81,7 +114,7 @@ Favorites
-