feat: add filter search to IconGridSelect when item count > 4

This commit is contained in:
2026-03-22 01:19:30 +03:00
parent a9bb912c30
commit a7829c48a4
@@ -25,17 +25,25 @@
} = $props();
let open = $state(false);
let search = $state('');
let triggerEl: HTMLButtonElement;
let searchEl: HTMLInputElement;
let popupStyle = $state('');
const showSearch = $derived(items.length > 4);
const selected = $derived(items.find(i => String(i.value) === String(value)));
const filtered = $derived(
search.trim()
? items.filter(i => i.label.toLowerCase().includes(search.toLowerCase()))
: items
);
function toggle() {
if (disabled) return;
if (!open && triggerEl) {
const rect = triggerEl.getBoundingClientRect();
const spaceBelow = window.innerHeight - rect.bottom;
const popupHeight = Math.min(items.length * 60 + 16, 320);
const popupHeight = Math.min(items.length * 60 + 16 + (showSearch ? 40 : 0), 320);
const top = spaceBelow > popupHeight + 8
? rect.bottom + 4
: rect.top - popupHeight - 4;
@@ -43,16 +51,22 @@
popupStyle = `position:fixed; z-index:9999; top:${top}px; left:${Math.max(4, left)}px;`;
}
open = !open;
search = '';
if (open && showSearch) {
requestAnimationFrame(() => searchEl?.focus());
}
}
function select(item: GridItem) {
value = item.value;
open = false;
search = '';
}
function handleKeydown(e: KeyboardEvent) {
if (e.key === 'Escape' && open) {
open = false;
search = '';
}
}
</script>
@@ -80,8 +94,13 @@
<!-- Popup grid -->
<div style="{popupStyle} width: {columns * 160 + 16}px;"
class="icon-grid-popup">
{#if showSearch}
<input bind:this={searchEl} bind:value={search} placeholder="Filter..."
class="icon-grid-search" type="text" autocomplete="off"
onkeydown={handleKeydown} />
{/if}
<div class="icon-grid" style="grid-template-columns: repeat({columns}, 1fr);">
{#each items as item}
{#each filtered as item}
<button type="button"
class="icon-grid-cell"
class:active={String(item.value) === String(value)}
@@ -93,6 +112,9 @@
{/if}
</button>
{/each}
{#if filtered.length === 0}
<div class="icon-grid-empty" style="grid-column: 1 / -1; text-align: center; padding: 0.75rem; color: var(--color-muted-foreground); font-size: 0.75rem;">No matches</div>
{/if}
</div>
</div>
{/if}
@@ -149,6 +171,18 @@
overflow-y: auto;
scrollbar-width: thin;
}
.icon-grid-search {
width: 100%;
padding: 0.375rem 0.5rem;
margin-bottom: 0.375rem;
border: none;
border-bottom: 1px solid var(--color-border);
border-radius: 0;
background: transparent;
color: var(--color-foreground);
font-size: 0.8rem;
outline: none;
}
.icon-grid {
display: grid;
gap: 0.375rem;