mirror of
https://github.com/obsqrbtz/goose-highlighter.git
synced 2026-04-08 20:19:06 +03:00
fixed sizing
This commit is contained in:
@@ -5,14 +5,18 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background: radial-gradient(120% 120% at 10% 0%, rgba(204, 106, 42, 0.08) 0%, transparent 45%),
|
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%);
|
linear-gradient(180deg, var(--bg-color) 0%, #f5efe9 100%);
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
min-width: 800px;
|
height: 100%;
|
||||||
min-height: 600px;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
html.dark body,
|
html.dark body,
|
||||||
@@ -25,6 +29,7 @@ body.dark {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topbar {
|
.topbar {
|
||||||
@@ -70,10 +75,12 @@ body.dark .topbar {
|
|||||||
|
|
||||||
.layout {
|
.layout {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 320px 1fr;
|
grid-template-columns: minmax(280px, 25%) 1fr;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
height: calc(100vh - 72px);
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel {
|
.panel {
|
||||||
@@ -85,6 +92,7 @@ body.dark .topbar {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-header {
|
.panel-header {
|
||||||
@@ -149,6 +157,8 @@ body.dark .topbar {
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding-right: 4px;
|
padding-right: 4px;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Custom scrollbar styling - List Manager Specific */
|
/* Custom scrollbar styling - List Manager Specific */
|
||||||
@@ -291,37 +301,87 @@ body.dark .list-item.selected.active {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-settings {
|
.list-settings {
|
||||||
display: grid;
|
display: none;
|
||||||
gap: 12px;
|
gap: 8px;
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
padding: 8px;
|
||||||
align-items: end;
|
background: rgba(204, 106, 42, 0.05);
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid var(--input-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-settings label {
|
html.dark .list-settings,
|
||||||
|
body.dark .list-settings {
|
||||||
|
background: rgba(242, 168, 101, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-settings.expanded {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 6px;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-settings input[type="text"] {
|
.list-settings input[type="text"] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.colors {
|
.color-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
gap: 8px;
|
||||||
grid-column: span 2;
|
align-items: center;
|
||||||
align-items: end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.colors label {
|
.compact-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 0.85rem;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.compact-label span {
|
||||||
|
min-width: 24px;
|
||||||
|
font-weight: 500;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-label input[type="color"] {
|
||||||
|
flex: 1;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-btn {
|
||||||
|
padding: 6px 12px;
|
||||||
|
min-width: auto;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-header {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-title-section {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-title-section h2 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1rem;
|
||||||
|
max-width: 300px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.word-controls {
|
.word-controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -371,6 +431,8 @@ body.dark .list-item.selected.active {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
flex: 1 1 0;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
html.dark .word-list,
|
html.dark .word-list,
|
||||||
@@ -502,13 +564,16 @@ input[type="color"] {
|
|||||||
|
|
||||||
.pagination-container {
|
.pagination-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
padding: 12px;
|
padding: 8px 12px;
|
||||||
background: #f9f4f0;
|
background: #f9f4f0;
|
||||||
border: 1px solid var(--input-border);
|
border: 1px solid var(--input-border);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
margin-top: 8px;
|
flex-shrink: 0;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
html.dark .pagination-container,
|
html.dark .pagination-container,
|
||||||
@@ -517,32 +582,31 @@ body.dark .pagination-container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.pagination-info {
|
.pagination-info {
|
||||||
font-size: 0.85rem;
|
font-size: 0.8rem;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
text-align: center;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-controls {
|
.pagination-controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
gap: 6px;
|
||||||
gap: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-btn {
|
.pagination-btn {
|
||||||
background: var(--button-bg);
|
background: var(--button-bg);
|
||||||
border: 1px solid var(--input-border);
|
border: 1px solid var(--input-border);
|
||||||
color: var(--button-text);
|
color: var(--button-text);
|
||||||
border-radius: 8px;
|
border-radius: 6px;
|
||||||
padding: 6px 8px;
|
padding: 4px 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
min-width: 32px;
|
min-width: 28px;
|
||||||
min-height: 32px;
|
min-height: 28px;
|
||||||
font-size: 0.8rem;
|
font-size: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-btn:hover:not(:disabled) {
|
.pagination-btn:hover:not(:disabled) {
|
||||||
@@ -558,11 +622,11 @@ body.dark .pagination-container {
|
|||||||
.pagination-pages {
|
.pagination-pages {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 12px;
|
padding: 0 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-info {
|
.page-info {
|
||||||
font-size: 0.85rem;
|
font-size: 0.8rem;
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
@@ -570,23 +634,23 @@ body.dark .pagination-container {
|
|||||||
.page-size-controls {
|
.page-size-controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
gap: 6px;
|
||||||
gap: 8px;
|
font-size: 0.8rem;
|
||||||
font-size: 0.85rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-size-controls label {
|
.page-size-controls label {
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-size-select {
|
.page-size-select {
|
||||||
background: var(--input-bg);
|
background: var(--input-bg);
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
border: 1px solid var(--input-border);
|
border: 1px solid var(--input-border);
|
||||||
border-radius: 8px;
|
border-radius: 6px;
|
||||||
padding: 4px 8px;
|
padding: 3px 6px;
|
||||||
font-size: 0.85rem;
|
font-size: 0.8rem;
|
||||||
min-width: 60px;
|
min-width: 55px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-size-select:focus {
|
.page-size-select:focus {
|
||||||
@@ -596,31 +660,4 @@ body.dark .pagination-container {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
@media (max-width: 920px) {
|
/* Removed single column layout - minimum width enforced for two-column layout */
|
||||||
body {
|
|
||||||
min-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.layout {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=1280" />
|
||||||
<title>Goose Highlighter - List Manager</title>
|
<title>Goose Highlighter - List Manager</title>
|
||||||
<link rel="stylesheet" href="list-manager.css" />
|
<link rel="stylesheet" href="list-manager.css" />
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" />
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" />
|
||||||
@@ -46,31 +46,33 @@
|
|||||||
<button id="activateListsBtn"><i class="fa-solid fa-circle-check"></i> Activate</button>
|
<button id="activateListsBtn"><i class="fa-solid fa-circle-check"></i> Activate</button>
|
||||||
<button id="deactivateListsBtn"><i class="fa-solid fa-circle-xmark"></i> Deactivate</button>
|
<button id="deactivateListsBtn"><i class="fa-solid fa-circle-xmark"></i> Deactivate</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-hint">Drag lists to reorder</div>
|
<div class="list-hint">Drag lists to reorder • Ctrl+Click for multi-select</div>
|
||||||
<div id="listsContainer" class="lists"></div>
|
<div id="listsContainer" class="lists"></div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="panel details-panel">
|
<section class="panel details-panel">
|
||||||
<div class="panel-header">
|
<div class="panel-header compact-header">
|
||||||
<h2>Selected List</h2>
|
<div class="list-title-section">
|
||||||
|
<h2 id="selectedListName">Selected List</h2>
|
||||||
|
<button id="editListNameBtn" class="icon-btn" title="Edit list name and colors">
|
||||||
|
<i class="fa-solid fa-pen"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div class="stats" id="listStats">0 words</div>
|
<div class="stats" id="listStats">0 words</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-settings">
|
<div class="list-settings collapsed" id="listSettingsPanel">
|
||||||
<label>
|
<input type="text" id="listName" placeholder="List name" />
|
||||||
Name
|
<div class="color-row">
|
||||||
<input type="text" id="listName" />
|
<label class="compact-label">
|
||||||
</label>
|
<span>BG</span>
|
||||||
<div class="colors">
|
|
||||||
<label>
|
|
||||||
Background
|
|
||||||
<input type="color" id="listBg" />
|
<input type="color" id="listBg" />
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label class="compact-label">
|
||||||
Foreground
|
<span>FG</span>
|
||||||
<input type="color" id="listFg" />
|
<input type="color" id="listFg" />
|
||||||
</label>
|
</label>
|
||||||
|
<button id="applyListSettingsBtn" class="compact-btn primary"><i class="fa-solid fa-check"></i></button>
|
||||||
</div>
|
</div>
|
||||||
<button id="applyListSettingsBtn" class="primary"><i class="fa-solid fa-check"></i> Apply</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="word-controls">
|
<div class="word-controls">
|
||||||
@@ -91,7 +93,7 @@
|
|||||||
<button id="moveWordsBtn"><i class="fa-solid fa-arrow-right"></i> Move</button>
|
<button id="moveWordsBtn"><i class="fa-solid fa-arrow-right"></i> Move</button>
|
||||||
<button id="copyWordsBtn"><i class="fa-solid fa-copy"></i> Copy</button>
|
<button id="copyWordsBtn"><i class="fa-solid fa-copy"></i> Copy</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="selection-hint">Click to select • Ctrl/Cmd+Click for multi-select • Click edit icon to rename</div>
|
<div class="selection-hint">Click to select • Ctrl/Cmd+Click for multi-select • Drag words to lists to copy</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="wordList" class="word-list"></div>
|
<div id="wordList" class="word-list"></div>
|
||||||
|
|||||||
@@ -52,8 +52,8 @@ class BackgroundService {
|
|||||||
chrome.windows.create({
|
chrome.windows.create({
|
||||||
url: chrome.runtime.getURL('list-manager/list-manager.html'),
|
url: chrome.runtime.getURL('list-manager/list-manager.html'),
|
||||||
type: 'popup',
|
type: 'popup',
|
||||||
width: 800,
|
width: 1280,
|
||||||
height: 600,
|
height: 700,
|
||||||
focused: true
|
focused: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ async initialize(): Promise<void> {
|
|||||||
document.getElementById('deleteListsBtn')?.addEventListener('click', () => this.deleteSelectedLists());
|
document.getElementById('deleteListsBtn')?.addEventListener('click', () => this.deleteSelectedLists());
|
||||||
document.getElementById('activateListsBtn')?.addEventListener('click', () => this.setSelectedListsActive(true));
|
document.getElementById('activateListsBtn')?.addEventListener('click', () => this.setSelectedListsActive(true));
|
||||||
document.getElementById('deactivateListsBtn')?.addEventListener('click', () => this.setSelectedListsActive(false));
|
document.getElementById('deactivateListsBtn')?.addEventListener('click', () => this.setSelectedListsActive(false));
|
||||||
|
document.getElementById('editListNameBtn')?.addEventListener('click', () => this.toggleListSettings());
|
||||||
document.getElementById('applyListSettingsBtn')?.addEventListener('click', () => this.applyListSettings());
|
document.getElementById('applyListSettingsBtn')?.addEventListener('click', () => this.applyListSettings());
|
||||||
document.getElementById('importListBtn')?.addEventListener('click', () => this.triggerImport());
|
document.getElementById('importListBtn')?.addEventListener('click', () => this.triggerImport());
|
||||||
document.getElementById('exportListBtn')?.addEventListener('click', () => this.exportCurrentList());
|
document.getElementById('exportListBtn')?.addEventListener('click', () => this.exportCurrentList());
|
||||||
@@ -83,6 +84,8 @@ const wordSearch = document.getElementById('wordSearch') as HTMLInputElement;
|
|||||||
wordList?.addEventListener('change', (e) => this.handleWordListChange(e));
|
wordList?.addEventListener('change', (e) => this.handleWordListChange(e));
|
||||||
wordList?.addEventListener('keydown', (e) => this.handleWordListKeydown(e));
|
wordList?.addEventListener('keydown', (e) => this.handleWordListKeydown(e));
|
||||||
wordList?.addEventListener('blur', (e) => this.handleWordListBlur(e), true);
|
wordList?.addEventListener('blur', (e) => this.handleWordListBlur(e), true);
|
||||||
|
wordList?.addEventListener('dragstart', (e) => this.handleWordDragStart(e));
|
||||||
|
wordList?.addEventListener('dragend', () => this.clearDragState());
|
||||||
}
|
}
|
||||||
|
|
||||||
private setupStorageSync(): void {
|
private setupStorageSync(): void {
|
||||||
@@ -208,9 +211,24 @@ const wordSearch = document.getElementById('wordSearch') as HTMLInputElement;
|
|||||||
list.background = listBg.value;
|
list.background = listBg.value;
|
||||||
list.foreground = listFg.value;
|
list.foreground = listFg.value;
|
||||||
|
|
||||||
|
this.toggleListSettings();
|
||||||
this.save();
|
this.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private toggleListSettings(): void {
|
||||||
|
const panel = document.getElementById('listSettingsPanel');
|
||||||
|
if (!panel) return;
|
||||||
|
|
||||||
|
if (panel.classList.contains('expanded')) {
|
||||||
|
panel.classList.remove('expanded');
|
||||||
|
} else {
|
||||||
|
panel.classList.add('expanded');
|
||||||
|
const listName = document.getElementById('listName') as HTMLInputElement;
|
||||||
|
listName?.focus();
|
||||||
|
listName?.select();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private exportCurrentList(): void {
|
private exportCurrentList(): void {
|
||||||
const list = this.lists[this.currentListIndex];
|
const list = this.lists[this.currentListIndex];
|
||||||
if (!list) return;
|
if (!list) return;
|
||||||
@@ -427,6 +445,7 @@ const wordSearch = document.getElementById('wordSearch') as HTMLInputElement;
|
|||||||
this.currentListIndex = index;
|
this.currentListIndex = index;
|
||||||
this.selectedLists.clear();
|
this.selectedLists.clear();
|
||||||
this.selectedWords.clear();
|
this.selectedWords.clear();
|
||||||
|
this.currentPage = 1; // Reset to first page when selecting a list
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,25 +455,59 @@ const wordSearch = document.getElementById('wordSearch') as HTMLInputElement;
|
|||||||
const index = Number(target.dataset.index);
|
const index = Number(target.dataset.index);
|
||||||
if (Number.isNaN(index)) return;
|
if (Number.isNaN(index)) return;
|
||||||
|
|
||||||
event.dataTransfer?.setData('text/plain', index.toString());
|
event.dataTransfer?.setData('text/plain', JSON.stringify({ type: 'list', index }));
|
||||||
event.dataTransfer?.setDragImage(target, 10, 10);
|
event.dataTransfer?.setDragImage(target, 10, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleDragOver(event: DragEvent): void {
|
private handleDragOver(event: DragEvent): void {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const target = (event.target as HTMLElement).closest('.list-item') as HTMLElement | null;
|
const target = (event.target as HTMLElement).closest('.list-item') as HTMLElement | null;
|
||||||
if (!target) return;
|
if (!target) {
|
||||||
|
this.clearDragState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear drag state from all items first
|
||||||
|
this.clearDragState();
|
||||||
|
|
||||||
|
// Check if we're dragging words or lists
|
||||||
|
const data = event.dataTransfer?.types.includes('text/plain');
|
||||||
|
if (data) {
|
||||||
target.classList.add('drag-over');
|
target.classList.add('drag-over');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private handleDrop(event: DragEvent): void {
|
private handleDrop(event: DragEvent): void {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const target = (event.target as HTMLElement).closest('.list-item') as HTMLElement | null;
|
const target = (event.target as HTMLElement).closest('.list-item') as HTMLElement | null;
|
||||||
if (!target) return;
|
if (!target) return;
|
||||||
|
|
||||||
const sourceIndex = Number(event.dataTransfer?.getData('text/plain'));
|
|
||||||
const targetIndex = Number(target.dataset.index);
|
const targetIndex = Number(target.dataset.index);
|
||||||
if (Number.isNaN(sourceIndex) || Number.isNaN(targetIndex) || sourceIndex === targetIndex) {
|
if (Number.isNaN(targetIndex)) {
|
||||||
|
this.clearDragState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const dataStr = event.dataTransfer?.getData('text/plain');
|
||||||
|
if (!dataStr) {
|
||||||
|
this.clearDragState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = JSON.parse(dataStr);
|
||||||
|
|
||||||
|
// Handle word drag
|
||||||
|
if (data.type === 'words') {
|
||||||
|
this.dropWordsOnList(data.wordIndices, targetIndex);
|
||||||
|
this.clearDragState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle list drag (reordering)
|
||||||
|
if (data.type === 'list') {
|
||||||
|
const sourceIndex = data.index;
|
||||||
|
if (sourceIndex === targetIndex) {
|
||||||
this.clearDragState();
|
this.clearDragState();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -473,11 +526,61 @@ const wordSearch = document.getElementById('wordSearch') as HTMLInputElement;
|
|||||||
this.selectedLists.clear();
|
this.selectedLists.clear();
|
||||||
this.save();
|
this.save();
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Drop error:', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clearDragState();
|
||||||
|
}
|
||||||
|
|
||||||
private clearDragState(): void {
|
private clearDragState(): void {
|
||||||
document.querySelectorAll('.list-item.drag-over').forEach(item => item.classList.remove('drag-over'));
|
document.querySelectorAll('.list-item.drag-over').forEach(item => item.classList.remove('drag-over'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handleWordDragStart(event: DragEvent): void {
|
||||||
|
const target = (event.target as HTMLElement).closest('.word-item') as HTMLElement | null;
|
||||||
|
if (!target) return;
|
||||||
|
|
||||||
|
const index = Number(target.dataset.index);
|
||||||
|
if (Number.isNaN(index)) return;
|
||||||
|
|
||||||
|
// If dragging a selected word, drag all selected words
|
||||||
|
let wordIndices: number[];
|
||||||
|
if (this.selectedWords.has(index)) {
|
||||||
|
wordIndices = Array.from(this.selectedWords);
|
||||||
|
} else {
|
||||||
|
wordIndices = [index];
|
||||||
|
}
|
||||||
|
|
||||||
|
event.dataTransfer?.setData('text/plain', JSON.stringify({ type: 'words', wordIndices }));
|
||||||
|
if (event.dataTransfer) {
|
||||||
|
event.dataTransfer.effectAllowed = 'copy';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private dropWordsOnList(wordIndices: number[], targetListIndex: number): void {
|
||||||
|
const sourceList = this.lists[this.currentListIndex];
|
||||||
|
const targetList = this.lists[targetListIndex];
|
||||||
|
|
||||||
|
if (!sourceList || !targetList) return;
|
||||||
|
if (targetListIndex === this.currentListIndex) return; // Can't drop on same list
|
||||||
|
|
||||||
|
const wordsToCopy = wordIndices
|
||||||
|
.map(index => sourceList.words[index])
|
||||||
|
.filter(Boolean)
|
||||||
|
.map(word => ({ ...word })); // Create copies
|
||||||
|
|
||||||
|
if (wordsToCopy.length === 0) return;
|
||||||
|
|
||||||
|
targetList.words.push(...wordsToCopy);
|
||||||
|
this.save();
|
||||||
|
|
||||||
|
// Show feedback
|
||||||
|
const count = wordsToCopy.length;
|
||||||
|
const message = `Copied ${count} word${count > 1 ? 's' : ''} to "${targetList.name}"`;
|
||||||
|
console.log(message);
|
||||||
|
}
|
||||||
|
|
||||||
private handleWordListClick(event: Event): void {
|
private handleWordListClick(event: Event): void {
|
||||||
const target = event.target as HTMLElement;
|
const target = event.target as HTMLElement;
|
||||||
const list = this.lists[this.currentListIndex];
|
const list = this.lists[this.currentListIndex];
|
||||||
@@ -541,12 +644,9 @@ const wordSearch = document.getElementById('wordSearch') as HTMLInputElement;
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regular click - toggle selection
|
// Regular click - clear all and select only this one
|
||||||
if (this.selectedWords.has(index)) {
|
this.selectedWords.clear();
|
||||||
this.selectedWords.delete(index);
|
|
||||||
} else {
|
|
||||||
this.selectedWords.add(index);
|
this.selectedWords.add(index);
|
||||||
}
|
|
||||||
this.renderWords();
|
this.renderWords();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -664,6 +764,11 @@ const wordSearch = document.getElementById('wordSearch') as HTMLInputElement;
|
|||||||
const list = this.lists[this.currentListIndex];
|
const list = this.lists[this.currentListIndex];
|
||||||
if (!list) return;
|
if (!list) return;
|
||||||
|
|
||||||
|
const selectedListName = document.getElementById('selectedListName');
|
||||||
|
if (selectedListName) {
|
||||||
|
selectedListName.textContent = list.name;
|
||||||
|
}
|
||||||
|
|
||||||
(document.getElementById('listName') as HTMLInputElement).value = list.name;
|
(document.getElementById('listName') as HTMLInputElement).value = list.name;
|
||||||
(document.getElementById('listBg') as HTMLInputElement).value = list.background;
|
(document.getElementById('listBg') as HTMLInputElement).value = list.background;
|
||||||
(document.getElementById('listFg') as HTMLInputElement).value = list.foreground;
|
(document.getElementById('listFg') as HTMLInputElement).value = list.foreground;
|
||||||
@@ -675,6 +780,12 @@ const wordSearch = document.getElementById('wordSearch') as HTMLInputElement;
|
|||||||
stats.textContent = `${list.words.length} words • ${activeCount} active • ${inactiveCount} inactive`;
|
stats.textContent = `${list.words.length} words • ${activeCount} active • ${inactiveCount} inactive`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Collapse settings panel when switching lists
|
||||||
|
const panel = document.getElementById('listSettingsPanel');
|
||||||
|
if (panel) {
|
||||||
|
panel.classList.remove('expanded');
|
||||||
|
}
|
||||||
|
|
||||||
this.renderTargetListOptions();
|
this.renderTargetListOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -714,7 +825,7 @@ private renderWords(): void {
|
|||||||
const index = entry.index;
|
const index = entry.index;
|
||||||
const isSelected = this.selectedWords.has(index);
|
const isSelected = this.selectedWords.has(index);
|
||||||
return `
|
return `
|
||||||
<div class="word-item ${word.active ? '' : 'disabled'} ${isSelected ? 'selected' : ''}" data-index="${index}">
|
<div class="word-item ${word.active ? '' : 'disabled'} ${isSelected ? 'selected' : ''}" data-index="${index}" draggable="true">
|
||||||
<span class="word-text">${DOMUtils.escapeHtml(word.wordStr)}</span>
|
<span class="word-text">${DOMUtils.escapeHtml(word.wordStr)}</span>
|
||||||
<input type="text" class="word-edit-input" value="${DOMUtils.escapeHtml(word.wordStr)}" data-word-edit="${index}">
|
<input type="text" class="word-edit-input" value="${DOMUtils.escapeHtml(word.wordStr)}" data-word-edit="${index}">
|
||||||
<div class="word-actions">
|
<div class="word-actions">
|
||||||
|
|||||||
@@ -693,8 +693,8 @@ export class PopupController {
|
|||||||
chrome.windows.create({
|
chrome.windows.create({
|
||||||
url: chrome.runtime.getURL('list-manager/list-manager.html'),
|
url: chrome.runtime.getURL('list-manager/list-manager.html'),
|
||||||
type: 'popup',
|
type: 'popup',
|
||||||
width: 800,
|
width: 1280,
|
||||||
height: 600,
|
height: 700,
|
||||||
focused: true
|
focused: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user