diff --git a/package.json b/package.json index b169faf96..074da0b83 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "prettier": "^3.2.5", "prettier-plugin-svelte": "^3.2.2", "prettier-plugin-tailwindcss": "0.5.13", - "svelte": "5.0.0-next.104", + "svelte": "5.0.0-next.107", "svelte-eslint-parser": "^0.34.1", "wrangler": "^3.44.0" }, diff --git a/packages/bits-ui/package.json b/packages/bits-ui/package.json index a70eac69d..55443750d 100644 --- a/packages/bits-ui/package.json +++ b/packages/bits-ui/package.json @@ -43,7 +43,7 @@ "jsdom": "^24.0.0", "publint": "^0.2.7", "resize-observer-polyfill": "^1.5.1", - "svelte": "5.0.0-next.104", + "svelte": "5.0.0-next.107", "svelte-check": "^3.6.9", "tslib": "^2.6.2", "typescript": "^5.3.3", diff --git a/packages/bits-ui/src/lib/bits/accordion/accordion.svelte.ts b/packages/bits-ui/src/lib/bits/accordion/accordion.svelte.ts index fed2a9081..27b0abea5 100644 --- a/packages/bits-ui/src/lib/bits/accordion/accordion.svelte.ts +++ b/packages/bits-ui/src/lib/bits/accordion/accordion.svelte.ts @@ -12,7 +12,6 @@ import { getDataDisabled, getDataOpenClosed, kbd, - readonlyBox, styleToString, verifyContextDeps, } from "$lib/internal/index.js"; @@ -59,7 +58,7 @@ class AccordionBaseState { * SINGLE */ -type AccordionSingleStateProps = AccordionBaseStateProps & BoxedValues<{ value: string; }>; +type AccordionSingleStateProps = AccordionBaseStateProps & BoxedValues<{ value: string }>; export class AccordionSingleState extends AccordionBaseState { #value: Box; @@ -83,7 +82,7 @@ export class AccordionSingleState extends AccordionBaseState { * MULTIPLE */ -type AccordionMultiStateProps = AccordionBaseStateProps & BoxedValues<{ value: string[]; }>; +type AccordionMultiStateProps = AccordionBaseStateProps & BoxedValues<{ value: string[] }>; export class AccordionMultiState extends AccordionBaseState { #value: Box; @@ -261,7 +260,7 @@ class AccordionContentState { item: AccordionItemState; node: Box; #id: ReadonlyBox; - #originalStyles: { transitionDuration: string; animationName: string; } | undefined = undefined; + #originalStyles: { transitionDuration: string; animationName: string } | undefined = undefined; #isMountAnimationPrevented = false; #width = $state(0); #height = $state(0); diff --git a/packages/bits-ui/src/lib/bits/collapsible/collapsible.svelte.ts b/packages/bits-ui/src/lib/bits/collapsible/collapsible.svelte.ts index 0c357ef03..fcc5d62b0 100644 --- a/packages/bits-ui/src/lib/bits/collapsible/collapsible.svelte.ts +++ b/packages/bits-ui/src/lib/bits/collapsible/collapsible.svelte.ts @@ -63,9 +63,9 @@ type CollapsibleContentStateProps = ReadonlyBoxedValues<{ class CollapsibleContentState { root: CollapsibleRootState; - #originalStyles: { transitionDuration: string; animationName: string; } | undefined; + #originalStyles: { transitionDuration: string; animationName: string } | undefined; #styleProp: ReadonlyBox; - node: Box; + node = boxedState(null); #isMountAnimationPrevented = $state(false); #width = $state(0); #height = $state(0); diff --git a/packages/bits-ui/src/lib/bits/radio-group/components/radio-group-input.svelte b/packages/bits-ui/src/lib/bits/radio-group/components/radio-group-input.svelte index 0fbba9978..9f6911f69 100644 --- a/packages/bits-ui/src/lib/bits/radio-group/components/radio-group-input.svelte +++ b/packages/bits-ui/src/lib/bits/radio-group/components/radio-group-input.svelte @@ -1,2 +1,9 @@ + +{#if inputState.shouldRender} + +{/if} diff --git a/packages/bits-ui/src/lib/bits/radio-group/components/radio-group-item.svelte b/packages/bits-ui/src/lib/bits/radio-group/components/radio-group-item.svelte index 7f2280bf8..622cbbba2 100644 --- a/packages/bits-ui/src/lib/bits/radio-group/components/radio-group-item.svelte +++ b/packages/bits-ui/src/lib/bits/radio-group/components/radio-group-item.svelte @@ -9,6 +9,7 @@ id: idProp = generateId(), asChild, children, + indicator, child, value: valueProp, disabled: disabledProp = false, @@ -38,6 +39,10 @@ {@render child?.({ props: mergedProps })} {:else} {/if} diff --git a/packages/bits-ui/src/lib/bits/radio-group/radio-group.svelte.ts b/packages/bits-ui/src/lib/bits/radio-group/radio-group.svelte.ts index cf5540252..f6e2c6aae 100644 --- a/packages/bits-ui/src/lib/bits/radio-group/radio-group.svelte.ts +++ b/packages/bits-ui/src/lib/bits/radio-group/radio-group.svelte.ts @@ -12,6 +12,7 @@ import { getDirectionalKeys, kbd } from "$lib/internal/kbd.js"; import { getElemDirection } from "$lib/internal/locale.js"; import type { Orientation } from "$lib/shared/index.js"; import { verifyContextDeps } from "$lib/internal/context.js"; +import { srOnlyStyles, styleToString } from "$lib/internal/style.js"; type RadioGroupRootStateProps = ReadonlyBoxedValues<{ id: string; @@ -21,7 +22,7 @@ type RadioGroupRootStateProps = ReadonlyBoxedValues<{ orientation: Orientation; name: string | undefined; }> & - BoxedValues<{ value: string; }>; + BoxedValues<{ value: string }>; class RadioGroupRootState { id: RadioGroupRootStateProps["id"]; @@ -32,6 +33,7 @@ class RadioGroupRootState { orientation: RadioGroupRootStateProps["orientation"]; name: RadioGroupRootStateProps["name"]; value: RadioGroupRootStateProps["value"]; + activeTabIndexNode = boxedState(null); constructor(props: RadioGroupRootStateProps) { this.id = props.id; @@ -54,6 +56,7 @@ class RadioGroupRootState { getRadioItemNodes() { if (!this.node.value) return []; + return Array.from(this.node.value.querySelectorAll("[data-bits-radio-group-item]")).filter( (el): el is HTMLElement => el instanceof HTMLElement && !el.dataset.disabled ); @@ -63,8 +66,13 @@ class RadioGroupRootState { return new RadioGroupItemState(props, this); } + createInput() { + return new RadioGroupInputState(this); + } + get props() { return { + id: this.id.value, role: "radiogroup", "aria-required": getAriaRequired(this.required.value), "data-disabled": getDataDisabled(this.disabled.value), @@ -89,11 +97,12 @@ type RadioGroupItemStateProps = ReadonlyBoxedValues<{ class RadioGroupItemState { #id: RadioGroupItemStateProps["id"]; #node: Box; - #root: RadioGroupRootState; + #root = undefined as unknown as RadioGroupRootState; #disabled: RadioGroupItemStateProps["disabled"]; - #value: RadioGroupItemStateProps["value"]; + #value = undefined as unknown as RadioGroupItemStateProps["value"]; #composedClick: EventCallback; #composedKeydown: EventCallback; + checked = $derived(this.#root.value.value === this.#value.value); constructor(props: RadioGroupItemStateProps, root: RadioGroupRootState) { this.#disabled = props.disabled; @@ -104,6 +113,12 @@ class RadioGroupItemState { this.#composedKeydown = composeHandlers(props.onkeydown, this.#onkeydown); this.#node = useNodeById(this.#id); + + $effect(() => { + if (!this.#node.value) return; + if (!this.#root.isChecked(this.#value.value)) return; + this.#root.activeTabIndexNode.value = this.#node.value; + }); } #onclick = () => { @@ -152,6 +167,7 @@ class RadioGroupItemState { get props() { return { + id: this.#id.value, disabled: this.#isDisabled ? true : undefined, "data-value": this.#value.value, "data-orientation": this.#root.orientation.value, @@ -161,6 +177,7 @@ class RadioGroupItemState { "data-bits-radio-group-item": "", type: "button", role: "radio", + tabIndex: this.#root.activeTabIndexNode.value === this.#node.value ? 0 : -1, // onclick: this.#composedClick, onkeydown: this.#composedKeydown, @@ -168,6 +185,32 @@ class RadioGroupItemState { } } +// +// INPUT +// + +class RadioGroupInputState { + #root = undefined as unknown as RadioGroupRootState; + shouldRender = $derived(this.#root.name.value !== undefined); + + constructor(root: RadioGroupRootState) { + this.#root = root; + } + + get props() { + return { + name: this.#root.name.value, + value: this.#root.value.value, + required: this.#root.required.value, + disabled: this.#root.disabled.value, + "aria-hidden": "true", + hidden: true, + style: styleToString(srOnlyStyles), + tabIndex: -1, + } as const; + } +} + // // CONTEXT METHODS // @@ -186,3 +229,7 @@ export function getRadioGroupRootState() { export function setRadioGroupItemState(props: RadioGroupItemStateProps) { return getRadioGroupRootState().createItem(props); } + +export function getRadioGroupInputState() { + return getRadioGroupRootState().createInput(); +} diff --git a/packages/bits-ui/src/lib/bits/radio-group/types.ts b/packages/bits-ui/src/lib/bits/radio-group/types.ts index e92fa47b3..bb092dbb3 100644 --- a/packages/bits-ui/src/lib/bits/radio-group/types.ts +++ b/packages/bits-ui/src/lib/bits/radio-group/types.ts @@ -76,9 +76,9 @@ export type RadioGroupItemPropsWithoutHTML = WithAsChild<{ */ disabled?: boolean; - onclick: EventCallback; + onclick?: EventCallback; - onkeydown: EventCallback; + onkeydown?: EventCallback; /** * A snippet to render the radio item's indicator. diff --git a/packages/bits-ui/src/lib/bits/slider/components/slider-input.svelte b/packages/bits-ui/src/lib/bits/slider/components/slider-input.svelte index 4b259c57c..aa721588f 100644 --- a/packages/bits-ui/src/lib/bits/slider/components/slider-input.svelte +++ b/packages/bits-ui/src/lib/bits/slider/components/slider-input.svelte @@ -1,7 +1,7 @@