From 22bc39cf6a48286a63da1f06dfc7dc062962c5c6 Mon Sep 17 00:00:00 2001 From: Daniel Dada Date: Thu, 5 Feb 2026 14:29:43 +0300 Subject: [PATCH] fixed listmanager styling --- list-manager/list-manager.css | 367 +++++++++++++--------- list-manager/list-manager.html | 9 +- package-lock.json | 1 - popup/popup.css | 317 +------------------ popup/popup.html | 2 +- shared/colors.css | 52 +++ shared/ui-components.css | 354 +++++++++++++++++++++ src/list-manager/ListManagerController.ts | 142 ++++++++- src/list-manager/list-manager.ts | 7 + src/popup/PopupController.ts | 4 + src/popup/popup.ts | 9 +- 11 files changed, 793 insertions(+), 471 deletions(-) create mode 100644 shared/colors.css create mode 100644 shared/ui-components.css diff --git a/list-manager/list-manager.css b/list-manager/list-manager.css index 396b022..ac9a97e 100644 --- a/list-manager/list-manager.css +++ b/list-manager/list-manager.css @@ -1,20 +1,5 @@ -:root { - --bg-color: #12100e; - --text-color: #f4ede6; - --input-bg: #1a1714; - --input-border: #3a2e26; - --button-bg: #2b211b; - --button-hover: #3a2c24; - --button-text: #f7efe9; - --accent: #f2a865; - --accent-hover: #f7c38a; - --accent-text: #1b120b; - --danger: #f87171; - --success: #4ade80; - --shadow: 0 12px 24px rgba(0, 0, 0, 0.4); - --border-radius: 14px; - --panel-bg: #17130f; -} +@import url('../shared/colors.css'); +@import url('../shared/ui-components.css'); * { box-sizing: border-box; @@ -23,13 +8,19 @@ body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; margin: 0; - background: radial-gradient(120% 120% at 10% 0%, rgba(242, 168, 101, 0.08) 0%, transparent 45%), - linear-gradient(180deg, var(--bg-color) 0%, #0f0d0b 100%); + background: radial-gradient(120% 120% at 10% 0%, rgba(204, 106, 42, 0.08) 0%, transparent 45%), + linear-gradient(180deg, var(--bg-color) 0%, #f5efe9 100%); color: var(--text-color); min-width: 800px; min-height: 600px; } +html.dark body, +body.dark { + background: radial-gradient(120% 120% at 10% 0%, rgba(242, 168, 101, 0.08) 0%, transparent 45%), + linear-gradient(180deg, var(--bg-color) 0%, #0f0d0b 100%); +} + .app { display: flex; flex-direction: column; @@ -41,11 +32,16 @@ body { align-items: center; justify-content: space-between; padding: 16px 20px; - background: linear-gradient(145deg, #1a1511 0%, #0f0b08 100%); + background: linear-gradient(145deg, #f6eee7 0%, #f0e8df 100%); border-bottom: 1px solid var(--input-border); box-shadow: var(--shadow); } +html.dark .topbar, +body.dark .topbar { + background: linear-gradient(145deg, #2b211b 0%, #231a14 100%); +} + .title { display: flex; align-items: center; @@ -155,7 +151,7 @@ body { padding-right: 4px; } -/* Custom scrollbar styling */ +/* Custom scrollbar styling - List Manager Specific */ .lists::-webkit-scrollbar, .word-list::-webkit-scrollbar { width: 8px; @@ -163,7 +159,7 @@ body { .lists::-webkit-scrollbar-track, .word-list::-webkit-scrollbar-track { - background: #1a1511; + background: #f0e8df; border-radius: 10px; } @@ -176,11 +172,32 @@ body { .lists::-webkit-scrollbar-thumb:hover, .word-list::-webkit-scrollbar-thumb:hover { + background: #d4c4b8; +} + +html.dark .lists::-webkit-scrollbar-track, +html.dark .word-list::-webkit-scrollbar-track, +body.dark .lists::-webkit-scrollbar-track, +body.dark .word-list::-webkit-scrollbar-track { + background: #1a1511; +} + +html.dark .lists::-webkit-scrollbar-thumb, +html.dark .word-list::-webkit-scrollbar-thumb, +body.dark .lists::-webkit-scrollbar-thumb, +body.dark .word-list::-webkit-scrollbar-thumb { + background: var(--input-border); +} + +html.dark .lists::-webkit-scrollbar-thumb:hover, +html.dark .word-list::-webkit-scrollbar-thumb:hover, +body.dark .lists::-webkit-scrollbar-thumb:hover, +body.dark .word-list::-webkit-scrollbar-thumb:hover { background: #4a3e36; } .list-item { - background: #1f1813; + background: #ffffff; border: 2px solid transparent; border-radius: 12px; padding: 10px 12px; @@ -194,23 +211,51 @@ body { } .list-item:hover { - background: #251f19; - border-color: rgba(242, 168, 101, 0.3); + background: #f9f4f0; + border-color: rgba(204, 106, 42, 0.3); } .list-item.active { border-color: var(--accent); - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3); - background: #2a2218; + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1); + background: #fffaf6; } .list-item.selected { - background: rgba(242, 168, 101, 0.15); - border-color: rgba(242, 168, 101, 0.6); + background: rgba(204, 106, 42, 0.1); + border-color: rgba(204, 106, 42, 0.5); } .list-item.selected.active { border-color: var(--accent); + background: rgba(204, 106, 42, 0.15); +} + +html.dark .list-item, +body.dark .list-item { + background: #1f1813; +} + +html.dark .list-item:hover, +body.dark .list-item:hover { + background: #251f19; + border-color: rgba(242, 168, 101, 0.3); +} + +html.dark .list-item.active, +body.dark .list-item.active { + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3); + background: #2a2218; +} + +html.dark .list-item.selected, +body.dark .list-item.selected { + background: rgba(242, 168, 101, 0.15); + border-color: rgba(242, 168, 101, 0.6); +} + +html.dark .list-item.selected.active, +body.dark .list-item.selected.active { background: rgba(242, 168, 101, 0.2); } @@ -322,12 +367,17 @@ body { border: 1px solid var(--input-border); border-radius: 12px; padding: 8px; - background: #1a1511; + background: #f9f4f0; display: flex; flex-direction: column; gap: 8px; } +html.dark .word-list, +body.dark .word-list { + background: #1a1511; +} + .empty { padding: 12px; text-align: center; @@ -342,7 +392,7 @@ body { align-items: center; padding: 8px 10px; border-radius: 10px; - background: #201915; + background: #ffffff; border: 2px solid transparent; cursor: pointer; transition: all 0.2s ease; @@ -350,11 +400,28 @@ body { } .word-item:hover { + background: #f9f4f0; + border-color: rgba(204, 106, 42, 0.2); +} + +.word-item.selected { + background: rgba(204, 106, 42, 0.1); + border-color: rgba(204, 106, 42, 0.5); +} + +html.dark .word-item, +body.dark .word-item { + background: #201915; +} + +html.dark .word-item:hover, +body.dark .word-item:hover { background: #2a2218; border-color: rgba(242, 168, 101, 0.2); } -.word-item.selected { +html.dark .word-item.selected, +body.dark .word-item.selected { background: rgba(242, 168, 101, 0.15); border-color: rgba(242, 168, 101, 0.6); } @@ -401,111 +468,7 @@ body { pointer-events: auto; } -.icon-btn { - background: transparent; - border: none; - color: var(--text-color); - opacity: 0.6; - cursor: pointer; - padding: 4px 6px; - border-radius: 6px; - transition: all 0.2s ease; - font-size: 0.85rem; - display: flex; - align-items: center; - justify-content: center; - min-width: 28px; - min-height: 28px; -} -.icon-btn:hover { - opacity: 1; - background: rgba(242, 168, 101, 0.15); - color: var(--accent); -} - -.icon-btn i { - pointer-events: none; -} - -.toggle-btn { - width: 40px; - height: 22px; - background: rgba(255, 255, 255, 0.1); - border: 1px solid var(--input-border); - border-radius: 11px; - position: relative; - cursor: pointer; - transition: all 0.2s ease; - padding: 0; -} - -.toggle-btn::after { - content: ''; - position: absolute; - width: 16px; - height: 16px; - background: var(--text-color); - border-radius: 50%; - top: 2px; - left: 2px; - transition: all 0.2s ease; -} - -.toggle-btn.active { - background: var(--accent); - border-color: var(--accent); -} - -.toggle-btn.active::after { - left: 20px; - background: var(--accent-text); -} - -button { - border: none; - border-radius: 10px; - padding: 8px 10px; - background: var(--button-bg); - color: var(--button-text); - cursor: pointer; - transition: background 0.2s ease; - font-size: 0.85rem; -} - -button:hover { - background: var(--button-hover); -} - -button.primary { - background: var(--accent); - color: var(--accent-text); -} - -button.primary:hover { - background: var(--accent-hover); -} - -button.danger { - background: rgba(248, 113, 113, 0.15); - color: var(--danger); - border: 1px solid rgba(248, 113, 113, 0.4); -} - -button.ghost { - background: transparent; - border: 1px solid var(--input-border); -} - -input[type="text"], -select, -input[type="color"] { - background: var(--input-bg); - color: var(--text-color); - border: 1px solid var(--input-border); - border-radius: 10px; - padding: 6px 8px; -} input[type="color"] { padding: 0; @@ -516,16 +479,6 @@ input[type="color"] { cursor: pointer; } -input[type="color"]::-webkit-color-swatch-wrapper { - padding: 6px; -} - -input[type="color"]::-webkit-color-swatch { - border: none; - border-radius: 8px; - box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08); -} - .colors input[type="color"] { width: 100%; } @@ -537,14 +490,6 @@ input[type="color"]::-webkit-color-swatch { cursor: pointer; } -.word-item input[type="color"]::-webkit-color-swatch-wrapper { - padding: 4px; -} - -.word-item input[type="color"]::-webkit-color-swatch { - border-radius: 6px; -} - .word-item input[type="color"]:hover { transform: scale(1.05); transition: transform 0.2s ease; @@ -555,6 +500,102 @@ input[type="color"]::-webkit-color-swatch { opacity: 0.7; } +.pagination-container { + display: flex; + flex-direction: column; + gap: 12px; + padding: 12px; + background: #f9f4f0; + border: 1px solid var(--input-border); + border-radius: 12px; + margin-top: 8px; +} + +html.dark .pagination-container, +body.dark .pagination-container { + background: #1a1511; +} + +.pagination-info { + font-size: 0.85rem; + opacity: 0.8; + text-align: center; +} + +.pagination-controls { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; +} + +.pagination-btn { + background: var(--button-bg); + border: 1px solid var(--input-border); + color: var(--button-text); + border-radius: 8px; + padding: 6px 8px; + cursor: pointer; + transition: all 0.2s ease; + display: flex; + align-items: center; + justify-content: center; + min-width: 32px; + min-height: 32px; + font-size: 0.8rem; +} + +.pagination-btn:hover:not(:disabled) { + background: var(--button-hover); + border-color: var(--accent); +} + +.pagination-btn:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +.pagination-pages { + display: flex; + align-items: center; + padding: 0 12px; +} + +.page-info { + font-size: 0.85rem; + opacity: 0.9; + font-weight: 500; +} + +.page-size-controls { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + font-size: 0.85rem; +} + +.page-size-controls label { + opacity: 0.8; +} + +.page-size-select { + background: var(--input-bg); + color: var(--text-color); + border: 1px solid var(--input-border); + border-radius: 8px; + padding: 4px 8px; + font-size: 0.85rem; + min-width: 60px; +} + +.page-size-select:focus { + outline: none; + border-color: var(--accent); +} + + + @media (max-width: 920px) { body { min-width: 100%; @@ -564,4 +605,22 @@ input[type="color"]::-webkit-color-swatch { grid-template-columns: 1fr; height: auto; } + + .pagination-container { + flex-wrap: wrap; + gap: 8px; + } + + .pagination-controls { + order: 2; + } + + .pagination-info { + order: 1; + width: 100%; + } + + .page-size-controls { + order: 3; + } } diff --git a/list-manager/list-manager.html b/list-manager/list-manager.html index e96a2f0..d4afae1 100644 --- a/list-manager/list-manager.html +++ b/list-manager/list-manager.html @@ -20,7 +20,11 @@
List Manager
-
+
+
@@ -88,7 +92,8 @@
Click to select • Ctrl/Cmd+Click for multi-select • Click edit icon to rename
-
+
+
diff --git a/package-lock.json b/package-lock.json index 0979453..0faddd2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,7 +4,6 @@ "requires": true, "packages": { "": { - "name": "goose-highlighter", "devDependencies": { "@eslint/css": "^0.9.0", "@eslint/js": "^9.30.0", diff --git a/popup/popup.css b/popup/popup.css index 4cd8176..e25b9f6 100644 --- a/popup/popup.css +++ b/popup/popup.css @@ -1,32 +1,10 @@ -:root { - --bg-color: #12100e; - --text-color: #f4ede6; - --input-bg: #1a1714; - --input-border: #3a2e26; - --button-bg: #2b211b; - --button-hover: #3a2c24; - --button-text: #f7efe9; - --accent: #f2a865; - --accent-hover: #f7c38a; - --accent-text: #1b120b; - --highlight-tag: #231a14; - --highlight-tag-border: #46372c; - --danger: #f87171; - --success: #4ade80; - --shadow: 0 10px 22px rgba(0, 0, 0, 0.5); - --shadow-sm: 0 4px 10px rgba(0, 0, 0, 0.4); - --border-radius: 12px; - --section-bg: #17130f; - --switch-bg: #4b3a2f; - --checkbox-accent: #f2a865; - --checkbox-border: #5a483b; - --focus-ring: 0 0 0 3px rgba(242, 168, 101, 0.2); -} +@import url('../shared/colors.css'); +@import url('../shared/ui-components.css'); body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; - background: radial-gradient(120% 120% at 10% 0%, rgba(242, 168, 101, 0.08) 0%, transparent 45%), - linear-gradient(180deg, var(--bg-color) 0%, #0f0d0b 100%); + background: radial-gradient(120% 120% at 12% 0%, rgba(204, 106, 42, 0.08) 0%, transparent 45%), + linear-gradient(180deg, var(--bg-color) 0%, #f4e9df 100%); color: var(--text-color); margin: 0; padding: 0; @@ -39,6 +17,12 @@ body { flex-direction: column; } +html.dark body, +body.dark { + background: radial-gradient(120% 120% at 10% 0%, rgba(242, 168, 101, 0.08) 0%, transparent 45%), + linear-gradient(180deg, var(--bg-color) 0%, #0f0d0b 100%); +} + /* Loading Overlay */ .loading-overlay { position: fixed; @@ -74,35 +58,6 @@ body { } } -body.light { - --bg-color: #fbf6f1; - --text-color: #3b2a21; - --input-bg: #ffffff; - --input-border: #e6d7cc; - --button-bg: #f6eee7; - --button-hover: #efe3d9; - --button-text: #3b2a21; - --accent: #cc6a2a; - --accent-text: #ffffff; - --accent-hover: #e07b36; - --highlight-tag: #f2e7dd; - --highlight-tag-border: #e7d2c1; - --danger: #ef4444; - --success: #10b981; - --shadow: 0 10px 22px rgba(59, 42, 33, 0.12); - --shadow-sm: 0 4px 10px rgba(59, 42, 33, 0.08); - --section-bg: #fffaf6; - --switch-bg: #e1d5cb; - --checkbox-accent: #cc6a2a; - --checkbox-border: #d8c8bb; - --focus-ring: 0 0 0 3px rgba(204, 106, 42, 0.2); -} - -body.light { - background: radial-gradient(120% 120% at 12% 0%, rgba(204, 106, 42, 0.08) 0%, transparent 45%), - linear-gradient(180deg, var(--bg-color) 0%, #f4e9df 100%); -} - .container { padding: 14px; display: flex; @@ -208,27 +163,6 @@ body.light { gap: 4px; } -.icon-toggle { - cursor: pointer; - font-size: 16px; - color: var(--accent); - display: flex; - align-items: center; - padding: 4px; - border-radius: 8px; - border: 1px solid transparent; -} - -.icon-toggle:hover { - color: var(--accent-hover); - background: var(--highlight-tag); - border-color: var(--highlight-tag-border); -} - -.hidden-toggle { - display: none; -} - /* GLOBAL HIGHLIGHT ICON: toggle-on/off */ .global-icon::before { content: "\f204"; @@ -238,20 +172,6 @@ body.light { content: "\f205"; } -/* THEME ICON: sun/moon - NO CHECKMARKS */ -.theme-icon::before { - content: "\f185"; -} - -#themeToggle:checked+.theme-icon::before { - content: "\f186"; -} - -.toggle-icon { - font-family: "Font Awesome 6 Free"; - font-weight: 900; -} - /* Sections */ .section { background: var(--section-bg); @@ -300,79 +220,23 @@ body.light { opacity: 0.9; } -/* Form Elements */ input[type="text"], textarea, select { - width: 100%; - padding: 8px 10px; - border-radius: 8px; - border: 1px solid var(--input-border); - background-color: var(--input-bg); - color: var(--text-color); - font-size: 0.85em; - box-sizing: border-box; margin-top: 4px; margin-bottom: 6px; - font-family: inherit; -} - -input[type="text"]:focus, -textarea:focus, -select:focus { - outline: none; - border-color: var(--accent); - box-shadow: var(--focus-ring); } textarea { resize: none; height: 60px; - font-size: 0.85em; - line-height: 1.4; - font-family: inherit; } -/* Color Inputs */ input[type="color"] { - background: none; - border: 1.5px solid var(--input-border); - border-radius: 8px; box-shadow: var(--shadow-sm); width: 24px; height: 24px; margin-left: 6px; - cursor: pointer; - padding: 0; - appearance: none; - -webkit-appearance: none; - overflow: hidden; -} - -input[type="color"]:hover { - border-color: var(--accent); -} - -input[type="color"]::-webkit-color-swatch-wrapper { - padding: 0; - border-radius: 0; -} - -input[type="color"]::-webkit-color-swatch { - border-radius: 0; - border: none; - margin: 0; - padding: 0; - width: 100%; - height: 100%; -} - -input[type="color"]::-moz-color-swatch { - border-radius: 0; - border: none; - padding: 0; - width: 100%; - height: 100%; } .color-row { @@ -391,132 +255,11 @@ input[type="color"]::-moz-color-swatch { font-weight: 500; } -/* Checkboxes */ -input[type="checkbox"] { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-color: var(--input-bg); - border: 1.5px solid var(--checkbox-border); - border-radius: 4px; - width: 16px; - height: 16px; - cursor: pointer; - position: relative; - flex-shrink: 0; -} -input[type="checkbox"]:hover { - border-color: var(--accent); -} -input[type="checkbox"]:checked { - background-color: var(--accent); - border-color: var(--accent); -} - -input[type="checkbox"]:checked::before { - content: "✓"; - position: absolute; - top: -3px; - left: 2px; - color: white; - font-size: 12px; - font-weight: bold; -} - -/* Switch Toggle */ -input[type="checkbox"].switch { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - width: 36px; - height: 20px; - background: var(--switch-bg); - border: none; - border-radius: 20px; - position: relative; - outline: none; - cursor: pointer; - transition: background 0.2s; -} - -input[type="checkbox"].switch::before { - content: ""; - position: absolute; - top: 2px; - left: 2px; - width: 16px; - height: 16px; - background: white; - border-radius: 50%; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); - transition: left 0.2s; -} - -input[type="checkbox"].switch:checked { - background: var(--accent); -} - -input[type="checkbox"].switch:checked::before { - left: 18px; -} - -input[type="checkbox"].switch:hover { - opacity: 0.9; -} - -input[type="checkbox"].switch::after { - content: none !important; -} - -label:has(input.switch) { - display: flex; - align-items: center; - gap: 8px; - cursor: pointer; - padding: 4px 0; -} - -/* Buttons */ button { - background: var(--button-bg); - color: var(--button-text); - border: 1px solid var(--input-border); padding: 8px 12px; - border-radius: 10px; - font-weight: 500; - cursor: pointer; - display: inline-flex; - align-items: center; - justify-content: center; - gap: 5px; font-size: 0.8em; - font-family: inherit; - white-space: nowrap; - line-height: 1.2; -} - -button i { - display: flex; - align-items: center; - font-size: 0.95em; -} - -button:hover { - background: var(--button-hover); - border-color: var(--highlight-tag-border); -} - -button.danger { - background: var(--danger); - color: white !important; - border-color: var(--danger); -} - -button.danger:hover { - background: #dc2626; - border-color: #dc2626; } .button-row { @@ -875,47 +618,7 @@ input[type="file"] { border-color: var(--highlight-tag-border); } -/* Scrollbars */ -html, -body, -#wordList, -.exceptions-list { - scrollbar-width: thin; - scrollbar-color: var(--accent) var(--section-bg); -} -html::-webkit-scrollbar, -body::-webkit-scrollbar, -#wordList::-webkit-scrollbar, -.exceptions-list::-webkit-scrollbar { - width: 8px; - background: var(--section-bg); - border-radius: 8px; -} - -html::-webkit-scrollbar-thumb, -body::-webkit-scrollbar-thumb, -#wordList::-webkit-scrollbar-thumb, -.exceptions-list::-webkit-scrollbar-thumb { - background: var(--accent); - border-radius: 8px; - min-height: 24px; - border: 2px solid var(--section-bg); -} - -html::-webkit-scrollbar-thumb:hover, -body::-webkit-scrollbar-thumb:hover, -#wordList::-webkit-scrollbar-thumb:hover, -.exceptions-list::-webkit-scrollbar-thumb:hover { - background: var(--accent-hover); -} - -html::-webkit-scrollbar-corner, -body::-webkit-scrollbar-corner, -#wordList::-webkit-scrollbar-corner, -.exceptions-list::-webkit-scrollbar-corner { - background: var(--section-bg); -} /* Page Highlights Section */ .section[data-section="page-highlights"] { diff --git a/popup/popup.html b/popup/popup.html index 9d24836..d447b20 100644 --- a/popup/popup.html +++ b/popup/popup.html @@ -10,7 +10,7 @@ - +
diff --git a/shared/colors.css b/shared/colors.css new file mode 100644 index 0000000..73ef68d --- /dev/null +++ b/shared/colors.css @@ -0,0 +1,52 @@ +/* Shared Color Scheme */ +:root { + --bg-color: #fbf6f1; + --text-color: #3b2a21; + --input-bg: #ffffff; + --input-border: #e6d7cc; + --button-bg: #f6eee7; + --button-hover: #efe3d9; + --button-text: #3b2a21; + --accent: #cc6a2a; + --accent-text: #ffffff; + --accent-hover: #e07b36; + --highlight-tag: #f2e7dd; + --highlight-tag-border: #e7d2c1; + --danger: #ef4444; + --success: #10b981; + --shadow: 0 10px 22px rgba(59, 42, 33, 0.12); + --shadow-sm: 0 4px 10px rgba(59, 42, 33, 0.08); + --border-radius: 12px; + --section-bg: #fffaf6; + --panel-bg: #fffaf6; + --switch-bg: #e1d5cb; + --checkbox-accent: #cc6a2a; + --checkbox-border: #d8c8bb; + --focus-ring: 0 0 0 3px rgba(204, 106, 42, 0.2); +} + +html.dark, +body.dark { + --bg-color: #12100e; + --text-color: #f4ede6; + --input-bg: #1a1714; + --input-border: #3a2e26; + --button-bg: #2b211b; + --button-hover: #3a2c24; + --button-text: #f7efe9; + --accent: #f2a865; + --accent-hover: #f7c38a; + --accent-text: #1b120b; + --highlight-tag: #231a14; + --highlight-tag-border: #46372c; + --danger: #f87171; + --success: #4ade80; + --shadow: 0 10px 22px rgba(0, 0, 0, 0.5); + --shadow-sm: 0 4px 10px rgba(0, 0, 0, 0.4); + --section-bg: #17130f; + --panel-bg: #17130f; + --switch-bg: #4b3a2f; + --checkbox-accent: #f2a865; + --checkbox-border: #5a483b; + --focus-ring: 0 0 0 3px rgba(242, 168, 101, 0.2); +} diff --git a/shared/ui-components.css b/shared/ui-components.css new file mode 100644 index 0000000..6f5ba84 --- /dev/null +++ b/shared/ui-components.css @@ -0,0 +1,354 @@ +/* Buttons */ +button { + border: none; + border-radius: 10px; + padding: 8px 10px; + background: var(--button-bg); + color: var(--button-text); + cursor: pointer; + transition: all 0.2s ease; + font-size: 0.85rem; + font-family: inherit; + font-weight: 500; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 5px; + white-space: nowrap; + line-height: 1.2; +} + +button:hover { + background: var(--button-hover); +} + +button.primary { + background: var(--accent); + color: var(--accent-text); +} + +button.primary:hover { + background: var(--accent-hover); +} + +button.danger { + background: rgba(248, 113, 113, 0.15); + color: var(--danger); + border: 1px solid rgba(248, 113, 113, 0.4); +} + +button.danger:hover { + background: rgba(248, 113, 113, 0.25); +} + +button.ghost { + background: transparent; + border: 1px solid var(--input-border); +} + +button.ghost:hover { + background: var(--button-hover); +} + +button i { + display: flex; + align-items: center; + font-size: 0.95em; +} + +button:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +/* Icon Buttons */ +.icon-btn { + background: transparent; + border: none; + color: var(--text-color); + opacity: 0.6; + cursor: pointer; + padding: 4px 6px; + border-radius: 6px; + transition: all 0.2s ease; + font-size: 0.85rem; + display: flex; + align-items: center; + justify-content: center; + min-width: 28px; + min-height: 28px; +} + +.icon-btn:hover { + opacity: 1; + background: rgba(242, 168, 101, 0.15); + color: var(--accent); +} + +.icon-btn i { + pointer-events: none; +} + +/* Toggle Switches */ +.toggle-btn { + width: 40px; + height: 22px; + background: rgba(255, 255, 255, 0.1); + border: 1px solid var(--input-border); + border-radius: 11px; + position: relative; + cursor: pointer; + transition: all 0.2s ease; + padding: 0; + flex-shrink: 0; +} + +.toggle-btn::after { + content: ''; + position: absolute; + width: 16px; + height: 16px; + background: var(--text-color); + border-radius: 50%; + top: 2px; + left: 2px; + transition: all 0.2s ease; +} + +.toggle-btn.active { + background: var(--accent); + border-color: var(--accent); +} + +.toggle-btn.active::after { + left: 20px; + background: var(--accent-text); +} + +/* Switch Toggle (Checkbox Style) */ +input[type="checkbox"].switch { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + width: 36px; + height: 20px; + background: var(--switch-bg); + border: none; + border-radius: 20px; + position: relative; + outline: none; + cursor: pointer; + transition: background 0.2s; +} + +input[type="checkbox"].switch::before { + content: ""; + position: absolute; + top: 2px; + left: 2px; + width: 16px; + height: 16px; + background: white; + border-radius: 50%; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); + transition: left 0.2s; +} + +input[type="checkbox"].switch:checked { + background: var(--accent); +} + +input[type="checkbox"].switch:checked::before { + left: 18px; +} + +input[type="checkbox"].switch:hover { + opacity: 0.9; +} + +input[type="checkbox"].switch::after { + content: none !important; +} + +label:has(input.switch) { + display: flex; + align-items: center; + gap: 8px; + cursor: pointer; + padding: 4px 0; +} + +/* Checkboxes */ +input[type="checkbox"]:not(.switch) { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: var(--input-bg); + border: 1.5px solid var(--checkbox-border); + border-radius: 4px; + width: 16px; + height: 16px; + cursor: pointer; + position: relative; + flex-shrink: 0; +} + +input[type="checkbox"]:not(.switch):hover { + border-color: var(--accent); +} + +input[type="checkbox"]:not(.switch):checked { + background-color: var(--accent); + border-color: var(--accent); +} + +input[type="checkbox"]:not(.switch):checked::before { + content: "✓"; + position: absolute; + top: -3px; + left: 2px; + color: white; + font-size: 12px; + font-weight: bold; +} + +/* Scrollbars */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: var(--section-bg); + border-radius: 8px; +} + +::-webkit-scrollbar-thumb { + background: var(--accent); + border-radius: 8px; + min-height: 24px; + border: 2px solid var(--section-bg); +} + +::-webkit-scrollbar-thumb:hover { + background: var(--accent-hover); +} + +::-webkit-scrollbar-corner { + background: var(--section-bg); +} + +/* Firefox scrollbar */ +* { + scrollbar-width: thin; + scrollbar-color: var(--accent) var(--section-bg); +} + +/* Form Inputs */ +input[type="text"], +textarea, +select { + width: 100%; + padding: 8px 10px; + border-radius: 8px; + border: 1px solid var(--input-border); + background-color: var(--input-bg); + color: var(--text-color); + font-size: 0.85em; + box-sizing: border-box; + font-family: inherit; +} + +input[type="text"]:focus, +textarea:focus, +select:focus { + outline: none; + border-color: var(--accent); + box-shadow: var(--focus-ring); +} + +textarea { + resize: vertical; + line-height: 1.4; +} + +/* Color Inputs */ +input[type="color"] { + background: none; + border: 1px solid var(--input-border); + border-radius: 8px; + cursor: pointer; + padding: 0; + appearance: none; + -webkit-appearance: none; + overflow: hidden; +} + +input[type="color"]:hover { + border-color: var(--accent); +} + +input[type="color"]::-webkit-color-swatch-wrapper { + padding: 0; + border-radius: 0; +} + +input[type="color"]::-webkit-color-swatch { + border-radius: 0; + border: none; + margin: 0; + padding: 0; + width: 100%; + height: 100%; +} + +input[type="color"]::-moz-color-swatch { + border-radius: 0; + border: none; + padding: 0; + width: 100%; + height: 100%; +} + +/* Theme Toggle Icon */ +.icon-toggle { + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + cursor: pointer; + padding: 8px; + border-radius: 8px; + transition: background-color 0.2s ease; + color: var(--accent); + font-size: 16px; + border: 1px solid transparent; +} + +.icon-toggle:hover { + background: var(--highlight-tag); + border-color: var(--highlight-tag-border); + color: var(--accent-hover); +} + +.hidden-toggle { + position: absolute; + opacity: 0; + pointer-events: none; + display: none; +} + +.toggle-icon { + font-family: "Font Awesome 6 Free"; + font-weight: 900; + color: inherit; + transition: all 0.2s ease; +} + +.theme-icon::before { + content: "\f185"; /* sun icon */ +} + +#themeToggle:checked + .theme-icon::before { + content: "\f186"; /* moon icon */ +} diff --git a/src/list-manager/ListManagerController.ts b/src/list-manager/ListManagerController.ts index 939ea70..5167cbf 100644 --- a/src/list-manager/ListManagerController.ts +++ b/src/list-manager/ListManagerController.ts @@ -10,10 +10,14 @@ export class ListManagerController { private selectedWords = new Set(); private wordSearchQuery = ''; private isReloading = false; + private currentPage = 1; + private pageSize = 100; + private totalWords = 0; - async initialize(): Promise { +async initialize(): Promise { await this.loadData(); this.setupEventListeners(); + this.setupTheme(); this.render(); this.setupStorageSync(); } @@ -56,9 +60,10 @@ export class ListManagerController { document.getElementById('moveWordsBtn')?.addEventListener('click', () => this.moveOrCopySelectedWords(false)); document.getElementById('copyWordsBtn')?.addEventListener('click', () => this.moveOrCopySelectedWords(true)); - const wordSearch = document.getElementById('wordSearch') as HTMLInputElement; +const wordSearch = document.getElementById('wordSearch') as HTMLInputElement; wordSearch.addEventListener('input', (e) => { this.wordSearchQuery = (e.target as HTMLInputElement).value; + this.currentPage = 1; this.renderWords(); }); @@ -597,19 +602,25 @@ export class ListManagerController { select.disabled = options.length === 0; } - private renderWords(): void { +private renderWords(): void { const list = this.lists[this.currentListIndex]; const wordList = document.getElementById('wordList'); if (!list || !wordList) return; const entries = this.getFilteredWordEntries(list); + this.totalWords = entries.length; if (entries.length === 0) { wordList.innerHTML = '
No words in this list.
'; + this.renderPaginationControls(); return; } - wordList.innerHTML = entries.map(entry => { + const startIndex = (this.currentPage - 1) * this.pageSize; + const endIndex = Math.min(startIndex + this.pageSize, this.totalWords); + const paginatedEntries = entries.slice(startIndex, endIndex); + + wordList.innerHTML = paginatedEntries.map(entry => { const word = entry.word; const index = entry.index; const isSelected = this.selectedWords.has(index); @@ -628,13 +639,134 @@ export class ListManagerController { `; }).join(''); + + this.renderPaginationControls(); } - private async save(): Promise { + private renderPaginationControls(): void { + const paginationContainer = document.getElementById('paginationControls'); + if (!paginationContainer) return; + + const totalPages = Math.ceil(this.totalWords / this.pageSize); + +if (totalPages <= 1) { + paginationContainer.style.display = 'none'; + return; + } + +const startItem = (this.currentPage - 1) * this.pageSize + 1; + const endItem = Math.min(this.currentPage * this.pageSize, this.totalWords); + + paginationContainer.style.display = 'flex'; + paginationContainer.innerHTML = ` +
+ Showing ${startItem}-${endItem} of ${this.totalWords} words +
+
+ + +
+ Page ${this.currentPage} of ${totalPages} +
+ + +
+
+ + +
+ `; + + this.setupPaginationEventListeners(); + } + + private setupPaginationEventListeners(): void { + document.getElementById('firstPageBtn')?.addEventListener('click', () => { + this.goToPage(1); + }); + + document.getElementById('prevPageBtn')?.addEventListener('click', () => { + this.goToPage(this.currentPage - 1); + }); + + document.getElementById('nextPageBtn')?.addEventListener('click', () => { + this.goToPage(this.currentPage + 1); + }); + + document.getElementById('lastPageBtn')?.addEventListener('click', () => { + const totalPages = Math.ceil(this.totalWords / this.pageSize); + this.goToPage(totalPages); + }); + + const pageSizeSelect = document.getElementById('pageSizeSelect') as HTMLSelectElement; + pageSizeSelect?.addEventListener('change', (e) => { + const newSize = Number((e.target as HTMLSelectElement).value); + if (!Number.isNaN(newSize) && newSize > 0) { + this.pageSize = newSize; + this.currentPage = 1; + this.renderWords(); + } + }); + } + + private goToPage(page: number): void { + const totalPages = Math.ceil(this.totalWords / this.pageSize); + if (page < 1 || page > totalPages) return; + + this.currentPage = page; + this.renderWords(); + } + +private async save(): Promise { await StorageService.set({ lists: this.lists }); this.render(); MessageService.sendToAllTabs({ type: 'WORD_LIST_UPDATED' }); } + + private setupTheme(): void { + const toggle = document.getElementById('themeToggle') as HTMLInputElement; + const body = document.body; + + const savedTheme = localStorage.getItem('theme'); + if (savedTheme === 'light') { + body.classList.remove('dark'); + body.classList.add('light'); + toggle.checked = false; + } else { + body.classList.add('dark'); + body.classList.remove('light'); + toggle.checked = true; + } + + toggle.addEventListener('change', () => { + if (toggle.checked) { + body.classList.add('dark'); + body.classList.remove('light'); + document.documentElement.classList.add('dark'); + document.documentElement.classList.remove('light'); + localStorage.setItem('theme', 'dark'); + } else { + body.classList.remove('dark'); + body.classList.add('light'); + document.documentElement.classList.remove('dark'); + document.documentElement.classList.add('light'); + localStorage.setItem('theme', 'light'); + } + }); + } } diff --git a/src/list-manager/list-manager.ts b/src/list-manager/list-manager.ts index 461d312..2c53349 100644 --- a/src/list-manager/list-manager.ts +++ b/src/list-manager/list-manager.ts @@ -1,5 +1,12 @@ import { ListManagerController } from './ListManagerController.js'; +const savedTheme = localStorage.getItem('theme'); +if (savedTheme === 'light') { + document.documentElement.classList.add('light'); +} else { + document.documentElement.classList.add('dark'); +} + document.addEventListener('DOMContentLoaded', async () => { const controller = new ListManagerController(); await controller.initialize(); diff --git a/src/popup/PopupController.ts b/src/popup/PopupController.ts index 80ba384..0108a9f 100644 --- a/src/popup/PopupController.ts +++ b/src/popup/PopupController.ts @@ -649,10 +649,14 @@ export class PopupController { if (toggle.checked) { body.classList.add('dark'); body.classList.remove('light'); + document.documentElement.classList.add('dark'); + document.documentElement.classList.remove('light'); localStorage.setItem('theme', 'dark'); } else { body.classList.remove('dark'); body.classList.add('light'); + document.documentElement.classList.remove('dark'); + document.documentElement.classList.add('light'); localStorage.setItem('theme', 'light'); } }); diff --git a/src/popup/popup.ts b/src/popup/popup.ts index 7397045..064c78d 100644 --- a/src/popup/popup.ts +++ b/src/popup/popup.ts @@ -1,5 +1,12 @@ import { PopupController } from './PopupController.js'; +const savedTheme = localStorage.getItem('theme'); +if (savedTheme === 'light') { + document.documentElement.classList.add('light'); +} else { + document.documentElement.classList.add('dark'); +} + function localizePage(): void { const elements = document.querySelectorAll('[data-i18n]'); elements.forEach(element => { @@ -28,4 +35,4 @@ document.addEventListener('DOMContentLoaded', async () => { displayVersion(); const controller = new PopupController(); await controller.initialize(); -}); \ No newline at end of file +});