add CheckboxGroup component for multiselect fields
This commit is contained in:
parent
f26d3e3883
commit
96f040a91b
2 changed files with 105 additions and 2 deletions
|
|
@ -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}
|
||||
|
|
|
|||
96
src/lib/components/ui/checkbox/CheckboxGroup.svelte
Normal file
96
src/lib/components/ui/checkbox/CheckboxGroup.svelte
Normal 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>
|
||||
Loading…
Reference in a new issue