Visitenbuch/src/lib/components/filter/FilterChip.svelte

147 lines
4.3 KiB
Svelte

<script lang="ts">
import { mdiClose } from "@mdi/js";
import Icon from "$lib/components/ui/Icon.svelte";
import Autocomplete from "./Autocomplete.svelte";
import {
InputType,
type BaseItem, type FilterData, type FilterDef, type SelectionOrText,
} from "./types";
export let filter: FilterDef;
export let hiddenIds: () => Set<string | number> = () => new Set();
export let cache: Record<string, BaseItem[]> = {};
export let fdata: FilterData;
export let onRemove = (): void => {};
export let onSelection = (selection: SelectionOrText, kb: boolean): void => {};
let autocomplete: Autocomplete<BaseItem> | undefined;
function startEditing(): void {
fdata.editing = true;
}
function stopEditing(kb = false): void {
fdata.editing = false;
if (fdata.selection) onSelection(fdata.selection, kb);
}
function onClose(kb = false): void {
if (fdata.selection) stopEditing(kb);
else onRemove();
}
const onTextInput = filter.textToItem ? (text: string) => {
const res = filter.textToItem!(text);
if (res) {
fdata.selection = res;
return { close: true, newValue: "" };
}
} : undefined;
$: if (fdata.editing && autocomplete) {
autocomplete.open();
}
const TOFF = " aus"; // Toggle name suffix (in case toggleOff.name is unset)
$: toggleState = fdata.selection?.toggle !== false;
$: filterName = toggleState
? filter.name
: filter.toggleOff?.name ?? filter.name + TOFF;
$: filterIcon = toggleState ? filter.icon : filter.toggleOff?.icon ?? filter.icon;
$: hasInputField = filter.inputType !== InputType.None && filter.inputType !== InputType.Boolean;
</script>
<div
class="flex items-center overflow-hidden rounded-md bg-primary text-primary-content
gap-1 pl-1"
>
<button
class="flex items-center gap-1"
disabled={filter.inputType !== InputType.Boolean}
on:click={() => {
if (fdata.selection) {
fdata.selection.toggle = !fdata.selection.toggle;
} else {
fdata.selection = { toggle: false };
}
onSelection(fdata.selection, false);
}}
>
{#if filterIcon}
<Icon path={filterIcon} size={1.2} />
{/if}
<span class="flex items-center">
{filterName + (hasInputField ? ":" : "")}
</span>
</button>
{#if hasInputField}
{#if fdata.editing}
{#if filter.inputType === InputType.FilterList}
{@const hids = hiddenIds()}
<Autocomplete
bind:this={autocomplete}
{cache}
cacheKey={filter.id}
hiddenIds={hids}
items={filter.options ?? []}
onBackspace={onRemove}
{onClose}
onSelect={(item) => {
// Accept the selection if the user selected a variant
if (item.id) {
fdata.selection = item;
return { close: true, newValue: "" };
}
return { close: false, newValue: item.name ?? "" };
}}
{onTextInput}
padding={false}
partOfFilterbar
selection={fdata.selection?.id
? { id: fdata.selection?.id, name: fdata.selection?.name ?? "" }
: null}
/>
{:else}
<!-- svelte-ignore a11y-autofocus -->
<input
class="bg-transparent"
autofocus
type="text"
value={fdata.selection?.name ?? ""}
on:keydown={(e) => {
if (e.key === "Escape") onClose(true);
}}
on:keypress={(e) => {
if (e.key === "Enter") {
// @ts-expect-error Input value is checked
if (e.target?.value) {
// @ts-expect-error Input value is checked
fdata.selection = { id: null, name: e.target.value };
}
stopEditing(true);
}
}}
/>
{/if}
{:else}
<button
class="hover:underline focus:underline"
aria-label={`Filter "${filterName}" bearbeiten`}
on:click={startEditing}>{fdata.selection?.name}</button
>
{/if}
{/if}
<button
class=" border-l border-primary-content/10 px-1 flex h-full items-center
hover:bg-error/80 active:bg-error focus:bg-error/80"
aria-label={`Filter "${filterName}" entfernen"`}
on:click={onRemove}
>
<Icon path={mdiClose} size={1} />
</button>
</div>