From bc395a078805e091e14a06598699b10e9445509b Mon Sep 17 00:00:00 2001 From: Branden J Brown Date: Thu, 9 Jul 2026 14:54:03 -0400 Subject: [PATCH] zenno: refactor CharaPick to generic component --- zenno/src/lib/CharaPick.svelte | 136 +---------------------------- zenno/src/lib/Pick.svelte | 155 +++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+), 133 deletions(-) create mode 100644 zenno/src/lib/Pick.svelte diff --git a/zenno/src/lib/CharaPick.svelte b/zenno/src/lib/CharaPick.svelte index bcc330d..7956be0 100644 --- a/zenno/src/lib/CharaPick.svelte +++ b/zenno/src/lib/CharaPick.svelte @@ -2,7 +2,7 @@ import type { Character } from '$lib/data/character'; import type { Snippet } from 'svelte'; import type { ClassValue } from 'svelte/elements'; - import { stringsearch } from './stringsearch'; + import Pick from './Pick.svelte'; interface Props { id: string; @@ -15,139 +15,9 @@ let { id, characters, value = $bindable(), class: className, option, required = false }: Props = $props(); - let popover: HTMLElement | undefined = $state(); - let optionsContainer: HTMLElement | undefined = $state(); - const expanded = $derived(!popover?.hidden); - - let search = $state(''); - const charas = $derived(characters ?? []); - const searchedCharas = $derived(stringsearch(search, charas, ({ name }) => name) || charas); - $effect(() => { - if (required && value == null && charas.length > 0) { - value = charas[0]; - } - }); - - function setByElem(o: HTMLElement) { - const j = parseInt(o.dataset.charaId ?? ''); - value = charas.find((c) => c.chara_id === j) ?? value; - o.focus(); - } - - function onkeydown(evt: KeyboardEvent) { - switch (evt.key) { - case 'ArrowDown': { - const opts = [...optionsContainer!.children] as HTMLElement[]; - const i = opts.findIndex((n) => parseInt(n.dataset.charaId ?? '') === value?.chara_id); - const o = i != null && i >= 0 ? opts[Math.min(i + 1, opts.length - 1)] : opts[0]; - setByElem(o); - break; - } - case 'ArrowUp': { - const opts = [...optionsContainer!.children] as HTMLElement[]; - const i = opts.findIndex((n) => parseInt(n.dataset.charaId ?? '') === value?.chara_id); - const o = i != null && i > 0 ? opts[i - 1] : opts[0]; - setByElem(o); - break; - } - case 'Home': { - search = ''; - setByElem(optionsContainer!.children[0] as HTMLElement); - break; - } - case 'End': { - search = ''; - setByElem(optionsContainer!.children[optionsContainer!.childElementCount - 1] as HTMLElement); - break; - } - case ' ': - popover!.showPopover(); - break; - case 'Escape': - // TODO(zeph): restore chara that was selected when the popup opened? - // for now just hide - popover!.hidePopover(); - break; - case 'Enter': - popover!.togglePopover(); - break; - default: - return; - } - evt.preventDefault(); - } + const key = (v: Character) => ({id: v.chara_id, name: v.name}); -
- popover?.togglePopover()} - {onkeydown} - tabindex="0">{value?.name ?? ''} -
- -
- {#if !required} -
{ - value = undefined; - search = ''; - popover!.hidePopover(); - }} - onfocus={() => (value = undefined)} - {onkeydown} - > - Reset -
- {/if} - {#each searchedCharas as c (c.chara_id)} -
{ - value = c; - popover!.hidePopover(); - }} - onfocus={() => (value = c)} - {onkeydown} - > - {#if option != null} - {@render option(c)} - {:else} - {c.name} - {/if} -
- {:else} -
No matches.
- {/each} -
-
-
+ diff --git a/zenno/src/lib/Pick.svelte b/zenno/src/lib/Pick.svelte new file mode 100644 index 0000000..ee023be --- /dev/null +++ b/zenno/src/lib/Pick.svelte @@ -0,0 +1,155 @@ + + +
+ popover?.togglePopover()} + {onkeydown} + tabindex="0">{value != null ? key(value).name : ''} +
+ +
+ {#if !required} +
{ + value = undefined; + search = ''; + popover!.hidePopover(); + }} + onfocus={() => (value = undefined)} + {onkeydown} + > + Reset +
+ {/if} + {#each searched as c (key(c).id)} + {@const v = value != null ? key(value).id : 0} + {@const k = key(c)} +
{ + value = c; + popover!.hidePopover(); + }} + onfocus={() => (value = c)} + {onkeydown} + > + {#if option != null} + {@render option(c)} + {:else} + {k.name} + {/if} +
+ {:else} +
No matches.
+ {/each} +
+
+