72 lines
1.7 KiB
Svelte
72 lines
1.7 KiB
Svelte
<script lang="ts">
|
|
import Label from './Label.svelte';
|
|
import ToggleSelect from './ToggleSelect.svelte';
|
|
import { validate, validateInput } from '@repo/validate';
|
|
|
|
let {
|
|
items,
|
|
selected = $bindable([]),
|
|
name,
|
|
label,
|
|
required,
|
|
missingMessage
|
|
}: {
|
|
items: string[];
|
|
selected?: string[];
|
|
name?: string;
|
|
label: string;
|
|
required?: boolean;
|
|
missingMessage?: string;
|
|
} = $props();
|
|
|
|
let inputElement: HTMLInputElement | undefined = $state<HTMLInputElement>();
|
|
let valid: boolean = $state(true);
|
|
|
|
const makeSelectedHandler = (name: string) => {
|
|
return (toggled: boolean) => {
|
|
if (!inputElement) return;
|
|
if (toggled) selected = [...selected, name];
|
|
else selected = selected.filter((item) => item !== name);
|
|
inputElement.value = JSON.stringify(selected);
|
|
validateInput(inputElement); // trigger validation on hidden input
|
|
};
|
|
};
|
|
</script>
|
|
|
|
<div>
|
|
{#if label && name}
|
|
<Label for={name}>{label}</Label>
|
|
{/if}
|
|
|
|
<div class="flex flex-wrap gap-3">
|
|
{#if name}
|
|
<input
|
|
type="hidden"
|
|
{name}
|
|
required={required ? true : false}
|
|
use:validate={{ required, baseval: '[]' }}
|
|
bind:this={inputElement}
|
|
onvalidate={(e) => {
|
|
valid = e.detail.valid;
|
|
}}
|
|
/>
|
|
{/if}
|
|
|
|
{#each items as item}
|
|
<ToggleSelect name={name ? undefined : `toggl_${item}`} ontoggle={makeSelectedHandler(item)}>
|
|
<span class="capitalize">{item}</span>
|
|
</ToggleSelect>
|
|
{/each}
|
|
</div>
|
|
|
|
{#if name}
|
|
<div class={['mt-2 opacity-0 transition-opacity', !valid && 'opacity-100']}>
|
|
<Label for={name} error>
|
|
{missingMessage !== undefined && missingMessage !== ''
|
|
? missingMessage
|
|
: 'Field is required'}
|
|
</Label>
|
|
</div>
|
|
{/if}
|
|
</div>
|