fix(ext/popup): fix reversed search, remove auto-focus, Enter opens items
- Search no longer auto-focuses; use "/" to focus it - Typing in search no longer re-renders the entire view, just the item list — fixes backwards text caused by cursor reset to pos 0 - Arrow keys also update list without full re-render - Enter opens the selected item even when search is focused Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -25,11 +25,10 @@ function typeIcon(t: ItemType): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderItemList(app: HTMLElement): void {
|
function buildRowsHtml(): string {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const filtered = getFilteredEntries();
|
const filtered = getFilteredEntries();
|
||||||
|
return filtered.length > 0
|
||||||
const rowsHtml = filtered.length > 0
|
|
||||||
? filtered.map(([id, e], i) => `
|
? filtered.map(([id, e], i) => `
|
||||||
<div class="entry-row ${i === state.selectedIndex ? 'selected' : ''}" data-id="${escapeHtml(id)}" data-index="${i}">
|
<div class="entry-row ${i === state.selectedIndex ? 'selected' : ''}" data-id="${escapeHtml(id)}" data-index="${i}">
|
||||||
<span class="entry-name"><span class="type-icon" aria-hidden="true">${typeIcon(e.type)}</span> ${escapeHtml(e.title)}${e.attachment_summaries.length > 0 ? ' <span class="entry-row__attach-indicator" title="has attachments">📎</span>' : ''}</span>
|
<span class="entry-name"><span class="type-icon" aria-hidden="true">${typeIcon(e.type)}</span> ${escapeHtml(e.title)}${e.attachment_summaries.length > 0 ? ' <span class="entry-row__attach-indicator" title="has attachments">📎</span>' : ''}</span>
|
||||||
@@ -37,7 +36,28 @@ export function renderItemList(app: HTMLElement): void {
|
|||||||
</div>
|
</div>
|
||||||
`).join('')
|
`).join('')
|
||||||
: '<div class="empty">no items</div>';
|
: '<div class="empty">no items</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateItemList(): void {
|
||||||
|
const list = document.getElementById('item-list');
|
||||||
|
if (list) {
|
||||||
|
list.innerHTML = buildRowsHtml();
|
||||||
|
wireRowClicks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function wireRowClicks(): void {
|
||||||
|
document.querySelectorAll('.entry-row').forEach(row => {
|
||||||
|
row.addEventListener('click', async () => {
|
||||||
|
const id = (row as HTMLElement).dataset.id!;
|
||||||
|
document.removeEventListener('keydown', handleListKeydown);
|
||||||
|
await openItem(id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function renderItemList(app: HTMLElement): void {
|
||||||
|
const state = getState();
|
||||||
app.innerHTML = `
|
app.innerHTML = `
|
||||||
<div class="search-bar">
|
<div class="search-bar">
|
||||||
<input type="text" id="search-input" placeholder="/ search..." value="${escapeHtml(state.searchQuery)}">
|
<input type="text" id="search-input" placeholder="/ search..." value="${escapeHtml(state.searchQuery)}">
|
||||||
@@ -50,7 +70,7 @@ export function renderItemList(app: HTMLElement): void {
|
|||||||
<button class="btn" id="lock-btn" style="font-size:11px;">lock</button>
|
<button class="btn" id="lock-btn" style="font-size:11px;">lock</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="entry-list" id="item-list">
|
<div class="entry-list" id="item-list">
|
||||||
${rowsHtml}
|
${buildRowsHtml()}
|
||||||
</div>
|
</div>
|
||||||
<div class="keyhints">
|
<div class="keyhints">
|
||||||
<span><kbd>/</kbd> search</span>
|
<span><kbd>/</kbd> search</span>
|
||||||
@@ -64,7 +84,12 @@ export function renderItemList(app: HTMLElement): void {
|
|||||||
|
|
||||||
const searchInput = document.getElementById('search-input') as HTMLInputElement | null;
|
const searchInput = document.getElementById('search-input') as HTMLInputElement | null;
|
||||||
searchInput?.addEventListener('input', () => {
|
searchInput?.addEventListener('input', () => {
|
||||||
setState({ searchQuery: searchInput.value, selectedIndex: 0 });
|
const state2 = getState();
|
||||||
|
state2.searchQuery = searchInput.value;
|
||||||
|
state2.selectedIndex = 0;
|
||||||
|
const list = document.getElementById('item-list');
|
||||||
|
if (list) list.innerHTML = buildRowsHtml();
|
||||||
|
wireRowClicks();
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('new-btn')?.addEventListener('click', () => {
|
document.getElementById('new-btn')?.addEventListener('click', () => {
|
||||||
@@ -98,21 +123,9 @@ export function renderItemList(app: HTMLElement): void {
|
|||||||
showSettingsPicker(e.currentTarget as HTMLElement);
|
showSettingsPicker(e.currentTarget as HTMLElement);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Item row clicks.
|
wireRowClicks();
|
||||||
const rows = app.querySelectorAll('.entry-row');
|
|
||||||
rows.forEach(row => {
|
|
||||||
row.addEventListener('click', async () => {
|
|
||||||
const id = (row as HTMLElement).dataset.id!;
|
|
||||||
document.removeEventListener('keydown', handleListKeydown);
|
|
||||||
await openItem(id);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Keyboard navigation.
|
|
||||||
document.addEventListener('keydown', handleListKeydown);
|
document.addEventListener('keydown', handleListKeydown);
|
||||||
|
|
||||||
// Focus search on open.
|
|
||||||
searchInput?.focus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openItem(id: ItemId): Promise<void> {
|
async function openItem(id: ItemId): Promise<void> {
|
||||||
@@ -195,17 +208,19 @@ function handleListKeydown(e: KeyboardEvent): void {
|
|||||||
if (e.key === 'ArrowDown') {
|
if (e.key === 'ArrowDown') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const max = Math.max(filtered.length - 1, 0);
|
const max = Math.max(filtered.length - 1, 0);
|
||||||
setState({ selectedIndex: Math.min(state.selectedIndex + 1, max) });
|
state.selectedIndex = Math.min(state.selectedIndex + 1, max);
|
||||||
|
updateItemList();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.key === 'ArrowUp') {
|
if (e.key === 'ArrowUp') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setState({ selectedIndex: Math.max(state.selectedIndex - 1, 0) });
|
state.selectedIndex = Math.max(state.selectedIndex - 1, 0);
|
||||||
|
updateItemList();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.key === 'Enter' && !isSearch) {
|
if (e.key === 'Enter') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const selected = filtered[state.selectedIndex];
|
const selected = filtered[state.selectedIndex];
|
||||||
if (selected) {
|
if (selected) {
|
||||||
|
|||||||
Reference in New Issue
Block a user