diff --git a/packages/bits-ui/src/lib/bits/date-field/components/date-field-input.svelte b/packages/bits-ui/src/lib/bits/date-field/components/date-field-input.svelte index ab02567d2..aa4342ca3 100644 --- a/packages/bits-ui/src/lib/bits/date-field/components/date-field-input.svelte +++ b/packages/bits-ui/src/lib/bits/date-field/components/date-field-input.svelte @@ -9,6 +9,7 @@ let { id = useId(), ref = $bindable(null), + name = "", children, child, ...restProps @@ -20,6 +21,7 @@ () => ref, (v) => (ref = v) ), + name: box.with(() => name), }); const mergedProps = $derived(mergeProps(restProps, inputState.props)); diff --git a/packages/bits-ui/src/lib/bits/date-field/components/date-field.svelte b/packages/bits-ui/src/lib/bits/date-field/components/date-field.svelte index 284668e5f..c53b77236 100644 --- a/packages/bits-ui/src/lib/bits/date-field/components/date-field.svelte +++ b/packages/bits-ui/src/lib/bits/date-field/components/date-field.svelte @@ -15,7 +15,6 @@ locale = "en", maxValue, minValue, - name = "", onPlaceholderChange = noop, onValueChange = noop, placeholder = $bindable(), @@ -64,7 +63,6 @@ readonly: box.with(() => readonly), readonlySegments: box.with(() => readonlySegments), required: box.with(() => required), - name: box.with(() => name), }); diff --git a/packages/bits-ui/src/lib/bits/date-field/date-field.svelte.ts b/packages/bits-ui/src/lib/bits/date-field/date-field.svelte.ts index f12ec89b0..220f9e491 100644 --- a/packages/bits-ui/src/lib/bits/date-field/date-field.svelte.ts +++ b/packages/bits-ui/src/lib/bits/date-field/date-field.svelte.ts @@ -74,7 +74,6 @@ export type DateFieldRootStateProps = WritableBoxedValues<{ hourCycle: HourCycle | undefined; locale: string; hideTimeZone: boolean; - name: string; required: boolean; }>; @@ -91,7 +90,6 @@ export class DateFieldRootState { hourCycle: DateFieldRootStateProps["hourCycle"]; locale: DateFieldRootStateProps["locale"]; hideTimeZone: DateFieldRootStateProps["hideTimeZone"]; - name: DateFieldRootStateProps["name"]; required: DateFieldRootStateProps["required"]; descriptionId = useId(); formatter: Formatter; @@ -107,6 +105,7 @@ export class DateFieldRootState { states = initSegmentStates(); dayPeriodNode = $state(null); rangeRoot: DateRangeFieldRootState | undefined = undefined; + name = $state(""); constructor(props: DateFieldRootStateProps, rangeRoot?: DateRangeFieldRootState) { this.rangeRoot = rangeRoot; @@ -128,7 +127,6 @@ export class DateFieldRootState { this.locale = rangeRoot ? rangeRoot.locale : props.locale; this.hideTimeZone = rangeRoot ? rangeRoot.hideTimeZone : props.hideTimeZone; this.required = rangeRoot ? rangeRoot.required : props.required; - this.name = props.name; this.formatter = createFormatter(this.locale.current); this.initialSegments = initializeSegmentValues(this.inferredGranularity); this.segmentValues = this.initialSegments; @@ -185,6 +183,10 @@ export class DateFieldRootState { }); } + setName = (name: string) => { + this.name = name; + }; + /** * Sets the field node for the `DateFieldRootState` instance. We use this method so we can * keep `#fieldNode` private to prevent accidental usage of the incorrect field node. @@ -569,17 +571,26 @@ export class DateFieldRootState { } } -type DateFieldInputStateProps = WithRefProps; +type DateFieldInputStateProps = WithRefProps & + ReadableBoxedValues<{ + name: string; + }>; class DateFieldInputState { #id: DateFieldInputStateProps["id"]; #ref: DateFieldInputStateProps["ref"]; + #name: DateFieldInputStateProps["name"]; root: DateFieldRootState; constructor(props: DateFieldInputStateProps, root: DateFieldRootState) { this.#id = props.id; this.#ref = props.ref; this.root = root; + this.#name = props.name; + + $effect(() => { + this.root.setName(this.#name.current); + }); useRefById({ id: this.#id, @@ -614,7 +625,7 @@ class DateFieldInputState { class DateFieldHiddenInputState { #root: DateFieldRootState; - shouldRender = $derived.by(() => this.#root.name.current !== ""); + shouldRender = $derived.by(() => this.#root.name !== ""); isoValue = $derived.by(() => this.#root.value.current ? this.#root.value.current.toString() : "" ); @@ -625,7 +636,7 @@ class DateFieldHiddenInputState { props = $derived.by(() => { return { - name: this.#root.name.current, + name: this.#root.name, value: this.isoValue, required: this.#root.required.current, "aria-hidden": getAriaHidden(true), diff --git a/packages/bits-ui/src/lib/bits/date-field/types.ts b/packages/bits-ui/src/lib/bits/date-field/types.ts index a2ade1fba..b69aec33f 100644 --- a/packages/bits-ui/src/lib/bits/date-field/types.ts +++ b/packages/bits-ui/src/lib/bits/date-field/types.ts @@ -115,14 +115,6 @@ export type DateFieldRootPropsWithoutHTML = { */ hideTimeZone?: boolean; - /** - * The name to use for the hidden input element of the date field, - * which is used to submit the ISO string value of the date field - * to a server. If not provided, the hidden input element will not - * be rendered. - */ - name?: string; - /** * Whether or not the hidden input of the date field requires a value * to be submitted. @@ -138,7 +130,18 @@ export type DateFieldRootProps = DateFieldRootPropsWithoutHTML; export type DateFieldInputSnippetProps = { segments: Array<{ part: SegmentPart; value: string }> }; -export type DateFieldInputPropsWithoutHTML = WithChild<{}, DateFieldInputSnippetProps>; +export type DateFieldInputPropsWithoutHTML = WithChild< + { + /** + * The name to use for the hidden input element of the date field, + * which is used to submit the ISO string value of the date field + * to a server. If not provided, the hidden input element will not + * be rendered. + */ + name?: string; + }, + DateFieldInputSnippetProps +>; export type DateFieldInputProps = DateFieldInputPropsWithoutHTML & Without; diff --git a/packages/bits-ui/src/tests/date-field/DateFieldTest.svelte b/packages/bits-ui/src/tests/date-field/DateFieldTest.svelte index 3f7c9004d..bcab1f614 100644 --- a/packages/bits-ui/src/tests/date-field/DateFieldTest.svelte +++ b/packages/bits-ui/src/tests/date-field/DateFieldTest.svelte @@ -1,10 +1,12 @@
@@ -12,7 +14,7 @@
Label - + {#snippet children({ segments })} {#each segments as { part, value }} ` attributes. - [ ] Expose the various `open` states via snippet props for all compoents with the `forceMount` prop for controlled transitions and animations. Thinking more about this, we don't need to pass it via `children`, only via the `child` snippet.