radio group: custom class support, improved options prop
This commit is contained in:
@@ -1,26 +1,67 @@
|
|||||||
|
<script lang="ts" module>
|
||||||
|
export type RadioGroupOption =
|
||||||
|
| {
|
||||||
|
value: string;
|
||||||
|
label?: string;
|
||||||
|
}
|
||||||
|
| string;
|
||||||
|
|
||||||
|
/** getLabel returns the option label if it exists*/
|
||||||
|
const getLabel = (option: RadioGroupOption): string => {
|
||||||
|
if (typeof option === 'string') {
|
||||||
|
return option;
|
||||||
|
} else {
|
||||||
|
return option.label ?? option.value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** getValue returns the option value */
|
||||||
|
const getValue = (option: RadioGroupOption): string => {
|
||||||
|
if (typeof option === 'string') {
|
||||||
|
return option;
|
||||||
|
} else {
|
||||||
|
return option.value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { RadioGroup } from 'melt/builders';
|
import { RadioGroup, type RadioGroupProps } from 'melt/builders';
|
||||||
import type { RadioGroupProps } from 'melt/builders';
|
import type { ClassValue } from 'svelte/elements';
|
||||||
import { scale } from 'svelte/transition';
|
import { scale } from 'svelte/transition';
|
||||||
|
import Label from './Label.svelte';
|
||||||
|
import { validate } from '@svelte-toolkit/validate';
|
||||||
|
|
||||||
|
interface Props extends RadioGroupProps {
|
||||||
|
options: RadioGroupOption[];
|
||||||
|
label?: string;
|
||||||
|
required?: boolean;
|
||||||
|
invalidMessage?: string;
|
||||||
|
class?: ClassValue | null | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
items,
|
options,
|
||||||
label,
|
label,
|
||||||
|
class: classValue,
|
||||||
|
required = false,
|
||||||
|
invalidMessage = 'Field is required',
|
||||||
...props
|
...props
|
||||||
}: RadioGroupProps & {
|
}: Props = $props();
|
||||||
items: string[];
|
let valid = $state(true);
|
||||||
label: string;
|
|
||||||
} = $props();
|
|
||||||
|
|
||||||
const group = new RadioGroup(props);
|
const group = new RadioGroup(props);
|
||||||
|
console.log('group', group.root.id);
|
||||||
const isVert = $derived(group.orientation === 'vertical');
|
const isVert = $derived(group.orientation === 'vertical');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex w-fit flex-col gap-2" {...group.root}>
|
<div class={['flex w-fit flex-col', classValue]} {...group.root}>
|
||||||
<label {...group.label} class="font-medium">{label}</label>
|
{#if label}
|
||||||
|
<Label {...group.label}>{label}</Label>
|
||||||
|
{/if}
|
||||||
<div class="flex {isVert ? 'flex-col gap-1' : 'flex-row gap-3'}">
|
<div class="flex {isVert ? 'flex-col gap-1' : 'flex-row gap-3'}">
|
||||||
{#each items as i}
|
{#each options as opt}
|
||||||
{@const item = group.getItem(i)}
|
{@const item = group.getItem(getValue(opt))}
|
||||||
<div
|
<div
|
||||||
class="ring-accent-500 group -ml-1 flex items-center gap-3 rounded p-1 outline-hidden focus-visible:ring-3
|
class="ring-accent-500 group -ml-1 flex items-center gap-3 rounded p-1 outline-hidden focus-visible:ring-3
|
||||||
data-disabled:cursor-not-allowed data-disabled:opacity-50"
|
data-disabled:cursor-not-allowed data-disabled:opacity-50"
|
||||||
@@ -42,11 +83,24 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="cursor-default leading-none font-semibold capitalize select-none">
|
<span
|
||||||
{i}
|
class={[
|
||||||
|
'cursor-default leading-none font-medium select-none',
|
||||||
|
typeof opt === 'string' && 'capitalize'
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{getLabel(opt)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
<input {...group.hiddenInput} />
|
<input
|
||||||
|
{...group.hiddenInput}
|
||||||
|
use:validate={{ required }}
|
||||||
|
onvalidate={(e) => (valid = e.detail.valid)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class={['opacity-0 transition-opacity', !valid && 'opacity-100']}>
|
||||||
|
<Label for={group.label.for} error>{invalidMessage}</Label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -106,7 +106,12 @@
|
|||||||
<RadioGroup
|
<RadioGroup
|
||||||
name="example-radio-group"
|
name="example-radio-group"
|
||||||
label="Choose an option"
|
label="Choose an option"
|
||||||
items={['male', 'female', 'other', 'prefer not to say']}
|
options={[
|
||||||
|
'male',
|
||||||
|
'female',
|
||||||
|
'other',
|
||||||
|
{ value: 'prefer_not_to_say', label: 'Prefer not to say' }
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user