fix: bits-ui component type mismatches with exactOptionalPropertyTypes

- Select.svelte: Use conditional spreading for optional `disabled` prop and `value` prop
- Switch.svelte: Conditionally spread `name` and `value` props
- Segment/RepSegment: Remove HTMLButtonAttributes extension and handle disabled prop properly
- Replace inline `import('svelte').Snippet` with proper import statements

Fixes type errors where bits-ui prop types don't include explicit `undefined` for optional properties, which conflicts with `exactOptionalPropertyTypes: true`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Justin Edmund 2025-11-28 18:05:27 -08:00
parent 67eb624bfc
commit a4b9e0e30a
5 changed files with 46 additions and 16 deletions

View file

@ -92,7 +92,13 @@
</Label.Root> </Label.Root>
{/if} {/if}
<SelectPrimitive.Root type="single" value={value !== undefined && value !== null ? String(value) : undefined} onValueChange={handleValueChange} {disabled} items={stringOptions}> <SelectPrimitive.Root
type="single"
{...(value !== undefined && value !== null ? { value: String(value) } : {})}
onValueChange={handleValueChange}
{disabled}
items={stringOptions}
>
<SelectPrimitive.Trigger class={selectClasses} data-placeholder={!selected}> <SelectPrimitive.Trigger class={selectClasses} data-placeholder={!selected}>
{#if selected?.image} {#if selected?.image}
<img src={selected.image} alt={selected.label} class="image" /> <img src={selected.image} alt={selected.label} class="image" />
@ -104,7 +110,11 @@
<SelectPrimitive.Content class="content"> <SelectPrimitive.Content class="content">
<SelectPrimitive.Viewport> <SelectPrimitive.Viewport>
{#each options as option} {#each options as option}
<SelectPrimitive.Item value={String(option.value)} disabled={option.disabled} class="item"> <SelectPrimitive.Item
value={String(option.value)}
{...(option.disabled !== undefined ? { disabled: option.disabled } : {})}
class="item"
>
{#snippet children({ selected })} {#snippet children({ selected })}
{#if option.image} {#if option.image}
<img src={option.image} alt={option.label} class="image" /> <img src={option.image} alt={option.label} class="image" />
@ -127,7 +137,14 @@
{/if} {/if}
</fieldset> </fieldset>
{:else} {:else}
<SelectPrimitive.Root type="single" value={value !== undefined && value !== null ? String(value) : undefined} onValueChange={handleValueChange} {disabled} items={stringOptions} class={className}> <SelectPrimitive.Root
type="single"
{...(value !== undefined && value !== null ? { value: String(value) } : {})}
onValueChange={handleValueChange}
{disabled}
items={stringOptions}
class={className}
>
<SelectPrimitive.Trigger class={selectClasses} data-placeholder={!selected}> <SelectPrimitive.Trigger class={selectClasses} data-placeholder={!selected}>
{#if selected?.image} {#if selected?.image}
<img src={selected.image} alt={selected.label} class="image" /> <img src={selected.image} alt={selected.label} class="image" />
@ -139,7 +156,11 @@
<SelectPrimitive.Content class="content"> <SelectPrimitive.Content class="content">
<SelectPrimitive.Viewport> <SelectPrimitive.Viewport>
{#each options as option} {#each options as option}
<SelectPrimitive.Item value={String(option.value)} disabled={option.disabled} class="item"> <SelectPrimitive.Item
value={String(option.value)}
{...(option.disabled !== undefined ? { disabled: option.disabled } : {})}
class="item"
>
{#snippet children({ selected })} {#snippet children({ selected })}
{#if option.image} {#if option.image}
<img src={option.image} alt={option.label} class="image" /> <img src={option.image} alt={option.label} class="image" />

View file

@ -3,14 +3,16 @@
<script lang="ts"> <script lang="ts">
import { RadioGroup as RadioGroupPrimitive } from 'bits-ui' import { RadioGroup as RadioGroupPrimitive } from 'bits-ui'
import type { Snippet } from 'svelte'
import styles from './rep-segment.module.scss' import styles from './rep-segment.module.scss'
import type { HTMLButtonAttributes } from 'svelte/elements'
interface Props extends Omit<HTMLButtonAttributes, 'value'> { interface Props {
value: string value: string
label: string label: string
class?: string class?: string
selected?: boolean selected?: boolean
disabled?: boolean
children?: Snippet
} }
let { let {
@ -18,15 +20,15 @@
label, label,
class: className, class: className,
selected = false, selected = false,
children: content, disabled,
...restProps children: content
}: Props = $props() }: Props = $props()
</script> </script>
<RadioGroupPrimitive.Item <RadioGroupPrimitive.Item
{value} {value}
{...(disabled !== undefined ? { disabled } : {})}
class={`${styles.repSegment} ${selected ? styles.selected : ''} ${className || ''}`} class={`${styles.repSegment} ${selected ? styles.selected : ''} ${className || ''}`}
{...restProps}
> >
{#snippet children({ checked })} {#snippet children({ checked })}
{#if checked} {#if checked}

View file

@ -4,16 +4,18 @@
<script lang="ts"> <script lang="ts">
import { RadioGroup as RadioGroupPrimitive } from 'bits-ui' import { RadioGroup as RadioGroupPrimitive } from 'bits-ui'
import { getContext } from 'svelte' import { getContext } from 'svelte'
import type { Snippet } from 'svelte'
import styles from './segment.module.scss' import styles from './segment.module.scss'
import type { HTMLButtonAttributes } from 'svelte/elements'
import type { SegmentedControlVariant } from './SegmentedControl.svelte' import type { SegmentedControlVariant } from './SegmentedControl.svelte'
interface Props extends Omit<HTMLButtonAttributes, 'value'> { interface Props {
value: string value: string
class?: string class?: string
disabled?: boolean
children?: Snippet
} }
let { value, class: className, children: content, ...restProps }: Props = $props() let { value, class: className, disabled, children: content }: Props = $props()
// Get variant from parent context // Get variant from parent context
const variant = getContext<SegmentedControlVariant>('segmented-control-variant') || 'default' const variant = getContext<SegmentedControlVariant>('segmented-control-variant') || 'default'
@ -36,7 +38,11 @@
) )
</script> </script>
<RadioGroupPrimitive.Item {value} class={segmentClass} {...restProps}> <RadioGroupPrimitive.Item
{value}
{...(disabled !== undefined ? { disabled } : {})}
class={segmentClass}
>
{#snippet children({ checked })} {#snippet children({ checked })}
{#if checked} {#if checked}
<div class={styles.indicator}></div> <div class={styles.indicator}></div>

View file

@ -4,6 +4,7 @@
<script lang="ts"> <script lang="ts">
import { RadioGroup as RadioGroupPrimitive } from 'bits-ui' import { RadioGroup as RadioGroupPrimitive } from 'bits-ui'
import { setContext } from 'svelte' import { setContext } from 'svelte'
import type { Snippet } from 'svelte'
import styles from './segmented-control.module.scss' import styles from './segmented-control.module.scss'
import type { HTMLAttributes } from 'svelte/elements' import type { HTMLAttributes } from 'svelte/elements'
@ -18,7 +19,7 @@
gap?: boolean gap?: boolean
class?: string class?: string
wrapperClass?: string wrapperClass?: string
children?: import('svelte').Snippet children?: Snippet
} }
let { let {

View file

@ -36,8 +36,8 @@
bind:checked bind:checked
{disabled} {disabled}
{required} {required}
{name} {...(name !== undefined ? { name } : {})}
{value} {...(value !== undefined ? { value } : {})}
class="switch {className || ''}" class="switch {className || ''}"
> >
<SwitchPrimitive.Thumb class="thumb {thumbClass || ''}" /> <SwitchPrimitive.Thumb class="thumb {thumbClass || ''}" />