wol-dash/src/lib/components/forms/InputSelect.svelte
2025-04-12 00:19:40 +02:00

74 lines
3.1 KiB
Svelte

<script lang="ts">
import { browser } from "$app/environment";
import { slide } from "svelte/transition";
import IconListDetails from "~icons/tabler/list-details";
import IconMinus from "~icons/tabler/minus";
import IconPlus from "~icons/tabler/plus";
import IconSearch from "~icons/tabler/search";
import IconX from "~icons/tabler/x";
import Button from "../ui/Button.svelte";
let {
label,
sublabel = "",
name,
data
}: {
label: string,
sublabel?: string,
name: string,
data: {
value: string,
name: string,
selected: boolean
}[]
} = $props();
let selectData = $state(data);
let expanded = $state(false);
let search = $state("");
let focused = $state(false);
if (browser) {
document.addEventListener("click", () => {
if (!focused) expanded = false;
});
}
</script>
<div class="flex gap-x-10 gap-y-3 flex-wrap mb-5">
<label for={name} class="block w-1/3 min-w-[300px]">
<p>{label}</p>
{#if sublabel}
<p class="text-sm text-neutral-400">{sublabel}</p>
{/if}
</label>
<select multiple name={name} class="hidden">
{#each selectData.filter(d => d.selected) as el}
<option value={el.value} selected></option>
{/each}
</select>
<div class="relative flex gap-10 items-center h-fit rounded border border-gray-300 shadow-sm pl-4 pr-2 py-2 text-sm focus:ring-indigo-500 focus:border-indigo-500">
{#if selectData.length == 0}
<p>None available</p>
{:else}
<p>{selectData.filter(d => d.selected).length} selected</p>
<Button Icon={expanded ? IconX : IconListDetails} type="button" extra="!px-2 !py-1 !text-xs !rounded-sm" onclick={() => {expanded = !expanded; focused = true}} onmouseleave={() => focused = false}>{expanded ? "Close" : "Select"}</Button>
{/if}
{#if expanded}
<ul transition:slide onmouseleave={() => focused = false} onmouseenter={() => focused = true} class="absolute flex flex-col gap-2 top-[calc(100%+10px)] z-1 left-0 w-full max-h-40 overflow-y-scroll bg-white rounded border border-gray-300 shadow-sm p-2 text-xs">
<div class="flex gap-2 items-center">
<IconSearch class="text-neutral-400"/>
<input type="text" placeholder="Search..." bind:value={search} class="block w-full text-sm !outline-none transition-all duration-300 ease-in-out border-transparent focus:border-b-2 focus:border-indigo-500">
</div>
<div class="w-full h-px bg-neutral-200"></div>
{#each search ? selectData.filter(d => d.name.toLowerCase().includes(search.toLowerCase())) : selectData as el}
<Button Icon={el.selected ? IconMinus : IconPlus} type="button" extra="!p-1 !text-xs !rounded-sm w-full !border-none" onclick={() => {el.selected = !el.selected}} inverted={!el.selected}>{el.name}</Button>
{/each}
</ul>
{/if}
</div>
</div>