feat: add IconGrid, EntityPicker controls and enhance search panel
Port icon grid and entity picker patterns from wled-screen-controller. IconGrid replaces plain <select> elements with visual icon grids for known item sets (widget type, icon type, healthcheck method, permission level). EntityPicker replaces search dropdowns with a command-palette style overlay with keyboard navigation and filtering. Enhance SearchDialog with keyboard navigation (arrow keys, Enter, Escape), grouped results with section headers, active highlight, and a footer with shortcut hints.
This commit is contained in:
@@ -4,6 +4,8 @@
|
||||
import type { z } from 'zod';
|
||||
import type { createAppSchema } from '$lib/utils/validators.js';
|
||||
import AppIconPicker from './AppIconPicker.svelte';
|
||||
import IconGrid from '$lib/components/ui/IconGrid.svelte';
|
||||
import type { IconGridItem } from '$lib/components/ui/IconGrid.svelte';
|
||||
|
||||
type AppSchema = z.infer<typeof createAppSchema>;
|
||||
|
||||
@@ -19,6 +21,11 @@
|
||||
});
|
||||
|
||||
let showAdvanced = $state(false);
|
||||
|
||||
const healthcheckMethodItems: IconGridItem[] = [
|
||||
{ value: 'GET', icon: '🔍', label: 'GET', desc: 'Full response' },
|
||||
{ value: 'HEAD', icon: '📋', label: 'HEAD', desc: 'Headers only' }
|
||||
];
|
||||
</script>
|
||||
|
||||
<form method="POST" {action} use:enhance class="space-y-4">
|
||||
@@ -140,20 +147,17 @@
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-3">
|
||||
<div>
|
||||
<label
|
||||
for="healthcheckMethod"
|
||||
class="mb-1 block text-sm font-medium text-card-foreground"
|
||||
>
|
||||
{$t('app.healthcheck_method')}
|
||||
</label>
|
||||
<select
|
||||
id="healthcheckMethod"
|
||||
<IconGrid
|
||||
items={healthcheckMethodItems}
|
||||
value={$form.healthcheckMethod ?? 'GET'}
|
||||
onchange={(v) => ($form.healthcheckMethod = v as 'GET' | 'HEAD')}
|
||||
name="healthcheckMethod"
|
||||
bind:value={$form.healthcheckMethod}
|
||||
class="w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-ring"
|
||||
>
|
||||
<option value="GET">GET</option>
|
||||
<option value="HEAD">HEAD</option>
|
||||
</select>
|
||||
columns={2}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { t } from 'svelte-i18n';
|
||||
import IconGrid from '$lib/components/ui/IconGrid.svelte';
|
||||
import type { IconGridItem } from '$lib/components/ui/IconGrid.svelte';
|
||||
|
||||
interface Props {
|
||||
iconType: string;
|
||||
@@ -9,9 +11,15 @@
|
||||
|
||||
let { iconType = $bindable('lucide'), iconValue = $bindable(''), onchange }: Props = $props();
|
||||
|
||||
function handleTypeChange(e: Event) {
|
||||
const target = e.target as HTMLSelectElement;
|
||||
iconType = target.value;
|
||||
const iconTypeItems: IconGridItem[] = [
|
||||
{ value: 'lucide', icon: '◇', label: $t('app.icon_lucide') ?? 'Lucide' },
|
||||
{ value: 'simple', icon: '◆', label: $t('app.icon_simple') ?? 'Simple Icons' },
|
||||
{ value: 'url', icon: '🔗', label: $t('app.icon_url') ?? 'Image URL' },
|
||||
{ value: 'emoji', icon: '😀', label: $t('app.icon_emoji') ?? 'Emoji' }
|
||||
];
|
||||
|
||||
function handleTypeChange(value: string) {
|
||||
iconType = value;
|
||||
iconValue = '';
|
||||
onchange?.(iconType, iconValue);
|
||||
}
|
||||
@@ -27,16 +35,14 @@
|
||||
<label class="block text-sm font-medium text-card-foreground">{$t('app.icon')}</label>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<select
|
||||
value={iconType}
|
||||
onchange={handleTypeChange}
|
||||
class="rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-ring"
|
||||
>
|
||||
<option value="lucide">{$t('app.icon_lucide')}</option>
|
||||
<option value="simple">{$t('app.icon_simple')}</option>
|
||||
<option value="url">{$t('app.icon_url')}</option>
|
||||
<option value="emoji">{$t('app.icon_emoji')}</option>
|
||||
</select>
|
||||
<div class="w-44 shrink-0">
|
||||
<IconGrid
|
||||
items={iconTypeItems}
|
||||
value={iconType}
|
||||
onchange={handleTypeChange}
|
||||
columns={2}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
|
||||
Reference in New Issue
Block a user