add CheckboxGroup component for multiselect fields

This commit is contained in:
Justin Edmund 2025-12-02 05:25:35 -08:00
parent f26d3e3883
commit 96f040a91b
2 changed files with 105 additions and 2 deletions

View file

@ -5,6 +5,7 @@
import Input from './Input.svelte'
import Select from './Select.svelte'
import Checkbox from './checkbox/Checkbox.svelte'
import CheckboxGroup from './checkbox/CheckboxGroup.svelte'
import DatePicker from './DatePicker.svelte'
interface SelectOption {
@ -29,10 +30,10 @@
label: string
/** Secondary label displayed below the main label */
sublabel?: string
value?: string | number | boolean | null | undefined
value?: string | number | boolean | number[] | null | undefined
children?: Snippet
editable?: boolean
type?: 'text' | 'number' | 'select' | 'checkbox' | 'date'
type?: 'text' | 'number' | 'select' | 'checkbox' | 'date' | 'multiselect'
options?: SelectOption[]
placeholder?: string
element?: 'wind' | 'fire' | 'water' | 'earth' | 'dark' | 'light'
@ -70,6 +71,12 @@
size="medium"
contained
/>
{:else if type === 'multiselect' && options}
<CheckboxGroup
bind:value={value as number[]}
options={options as { value: number; label: string }[]}
{element}
/>
{:else if type === 'checkbox'}
<Checkbox
checked={checkboxValue}

View file

@ -0,0 +1,96 @@
<svelte:options runes={true} />
<script lang="ts">
import Checkbox from './Checkbox.svelte'
interface Option {
value: number
label: string
}
interface Props {
/** Array of selected values */
value?: number[]
/** Available options */
options: Option[]
/** Disable all checkboxes */
disabled?: boolean
/** Layout direction */
direction?: 'row' | 'column'
/** Element color theme for checked state */
element?: 'wind' | 'fire' | 'water' | 'earth' | 'dark' | 'light' | undefined
}
let {
value = $bindable([]),
options,
disabled = false,
direction = 'row',
element
}: Props = $props()
function isChecked(optionValue: number): boolean {
return value.includes(optionValue)
}
function handleChange(optionValue: number, checked: boolean) {
if (checked) {
value = [...value, optionValue]
} else {
value = value.filter((v) => v !== optionValue)
}
}
</script>
<div class="checkbox-group {direction}">
{#each options as option (option.value)}
<label class="checkbox-option">
<Checkbox
checked={isChecked(option.value)}
onCheckedChange={(checked) => handleChange(option.value, checked)}
{disabled}
{element}
size="small"
/>
<span class="option-label">{option.label}</span>
</label>
{/each}
</div>
<style lang="scss">
@use '$src/themes/spacing' as *;
@use '$src/themes/colors' as *;
@use '$src/themes/typography' as *;
.checkbox-group {
display: flex;
gap: $unit;
flex-wrap: wrap;
&.column {
flex-direction: column;
}
&.row {
flex-direction: row;
}
}
.checkbox-option {
display: flex;
align-items: center;
gap: $unit-half;
cursor: pointer;
&:hover .option-label {
color: $grey-30;
}
}
.option-label {
font-size: $font-small;
color: $grey-50;
user-select: none;
transition: color 0.15s ease;
}
</style>