@proyecto-viviana/solidaria-components 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ActionBar.d.ts.map +1 -1
- package/dist/ActionBar.js +2 -11
- package/dist/ActionBar.js.map +1 -1
- package/dist/ActionBar.jsx +2 -11
- package/dist/ActionBar.jsx.map +1 -1
- package/dist/ActionGroup.d.ts.map +1 -1
- package/dist/ActionGroup.js +1 -1
- package/dist/ActionGroup.js.map +1 -1
- package/dist/ActionGroup.jsx +1 -1
- package/dist/ActionGroup.jsx.map +1 -1
- package/dist/Autocomplete.d.ts +2 -2
- package/dist/Autocomplete.d.ts.map +1 -1
- package/dist/Autocomplete.js +7 -7
- package/dist/Autocomplete.js.map +1 -1
- package/dist/Autocomplete.jsx +7 -7
- package/dist/Autocomplete.jsx.map +1 -1
- package/dist/Breadcrumbs.d.ts.map +1 -1
- package/dist/Breadcrumbs.js +15 -16
- package/dist/Breadcrumbs.js.map +1 -1
- package/dist/Breadcrumbs.jsx +19 -24
- package/dist/Breadcrumbs.jsx.map +1 -1
- package/dist/Calendar.d.ts.map +1 -1
- package/dist/Calendar.js +100 -65
- package/dist/Calendar.js.map +1 -1
- package/dist/Calendar.jsx +10 -1
- package/dist/Calendar.jsx.map +1 -1
- package/dist/Color.d.ts.map +1 -1
- package/dist/Color.js +15 -18
- package/dist/Color.js.map +1 -1
- package/dist/Color.jsx +15 -19
- package/dist/Color.jsx.map +1 -1
- package/dist/ComboBox.d.ts +4 -0
- package/dist/ComboBox.d.ts.map +1 -1
- package/dist/ComboBox.js +21 -13
- package/dist/ComboBox.js.map +1 -1
- package/dist/ComboBox.jsx +23 -13
- package/dist/ComboBox.jsx.map +1 -1
- package/dist/DateField.d.ts +11 -2
- package/dist/DateField.d.ts.map +1 -1
- package/dist/DateField2.js +237 -234
- package/dist/DateField2.js.map +1 -1
- package/dist/DateField2.jsx +134 -97
- package/dist/DateField2.jsx.map +1 -1
- package/dist/DatePicker.d.ts +42 -3
- package/dist/DatePicker.d.ts.map +1 -1
- package/dist/DatePicker.js +6 -990
- package/dist/DatePicker.jsx +6 -681
- package/dist/DatePicker2.js +1171 -0
- package/dist/DatePicker2.js.map +1 -0
- package/dist/DatePicker2.jsx +820 -0
- package/dist/DatePicker2.jsx.map +1 -0
- package/dist/DateRangePickerContext.d.ts +2 -0
- package/dist/DateRangePickerContext.d.ts.map +1 -1
- package/dist/Disclosure.d.ts.map +1 -1
- package/dist/Disclosure.js +3 -0
- package/dist/Disclosure.js.map +1 -1
- package/dist/Disclosure.jsx +1 -1
- package/dist/Disclosure.jsx.map +1 -1
- package/dist/GridList.d.ts.map +1 -1
- package/dist/GridList.js +2 -2
- package/dist/GridList.js.map +1 -1
- package/dist/GridList.jsx +2 -2
- package/dist/GridList.jsx.map +1 -1
- package/dist/ListBox.d.ts +2 -2
- package/dist/ListBox.d.ts.map +1 -1
- package/dist/ListBox.js +327 -166
- package/dist/ListBox.js.map +1 -1
- package/dist/ListBox.jsx +161 -25
- package/dist/ListBox.jsx.map +1 -1
- package/dist/Modal.d.ts.map +1 -1
- package/dist/Modal.js +7 -5
- package/dist/Modal.js.map +1 -1
- package/dist/Modal.jsx +5 -5
- package/dist/Modal.jsx.map +1 -1
- package/dist/Popover.d.ts.map +1 -1
- package/dist/Popover.js +16 -9
- package/dist/Popover.js.map +1 -1
- package/dist/Popover.jsx +25 -4
- package/dist/Popover.jsx.map +1 -1
- package/dist/RangeCalendar.d.ts.map +1 -1
- package/dist/RangeCalendar.js +182 -46
- package/dist/RangeCalendar.js.map +1 -1
- package/dist/RangeCalendar.jsx +72 -2
- package/dist/RangeCalendar.jsx.map +1 -1
- package/dist/SearchField.d.ts.map +1 -1
- package/dist/SearchField.js +26 -2
- package/dist/SearchField.js.map +1 -1
- package/dist/SearchField.jsx +26 -2
- package/dist/SearchField.jsx.map +1 -1
- package/dist/Select.d.ts +7 -0
- package/dist/Select.d.ts.map +1 -1
- package/dist/Select.js +62 -49
- package/dist/Select.js.map +1 -1
- package/dist/Select.jsx +33 -18
- package/dist/Select.jsx.map +1 -1
- package/dist/SharedElementTransition.d.ts.map +1 -1
- package/dist/SharedElementTransition.js +35 -33
- package/dist/SharedElementTransition.js.map +1 -1
- package/dist/SharedElementTransition.jsx +35 -33
- package/dist/SharedElementTransition.jsx.map +1 -1
- package/dist/StepList.d.ts.map +1 -1
- package/dist/StepList.js +2 -9
- package/dist/StepList.js.map +1 -1
- package/dist/StepList.jsx +1 -6
- package/dist/StepList.jsx.map +1 -1
- package/dist/Table.d.ts +2 -0
- package/dist/Table.d.ts.map +1 -1
- package/dist/Table.js +10 -3
- package/dist/Table.js.map +1 -1
- package/dist/Table.jsx +10 -3
- package/dist/Table.jsx.map +1 -1
- package/dist/Tabs.d.ts.map +1 -1
- package/dist/Tabs.js +3 -0
- package/dist/Tabs.js.map +1 -1
- package/dist/Tabs.jsx +1 -1
- package/dist/Tabs.jsx.map +1 -1
- package/dist/TagGroup.d.ts +3 -1
- package/dist/TagGroup.d.ts.map +1 -1
- package/dist/TagGroup.js +12 -8
- package/dist/TagGroup.js.map +1 -1
- package/dist/TagGroup.jsx +11 -8
- package/dist/TagGroup.jsx.map +1 -1
- package/dist/TextField.js +4 -1
- package/dist/TextField.js.map +1 -1
- package/dist/TextField.jsx +4 -1
- package/dist/TextField.jsx.map +1 -1
- package/dist/TimeField.d.ts +14 -50
- package/dist/TimeField.d.ts.map +1 -1
- package/dist/TimeField.js +111 -205
- package/dist/TimeField.js.map +1 -1
- package/dist/TimeField.jsx +76 -82
- package/dist/TimeField.jsx.map +1 -1
- package/dist/Tree.d.ts.map +1 -1
- package/dist/Tree.js +3 -3
- package/dist/Tree.js.map +1 -1
- package/dist/Tree.jsx +3 -3
- package/dist/Tree.jsx.map +1 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -6
- package/dist/index.jsx +5 -6
- package/dist/useDragAndDrop.d.ts +0 -8
- package/dist/useDragAndDrop.d.ts.map +1 -1
- package/dist/useDragAndDrop2.js +6 -2
- package/dist/useDragAndDrop2.js.map +1 -1
- package/dist/useDragAndDrop2.jsx +6 -2
- package/dist/useDragAndDrop2.jsx.map +1 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +2 -2
- package/dist/utils.js.map +1 -1
- package/dist/utils.jsx +2 -2
- package/dist/utils.jsx.map +1 -1
- package/package.json +3 -9
- package/src/ActionBar.tsx +10 -12
- package/src/ActionGroup.tsx +5 -3
- package/src/Autocomplete.tsx +14 -9
- package/src/Breadcrumbs.tsx +55 -38
- package/src/Calendar.tsx +30 -1
- package/src/Color.tsx +42 -27
- package/src/ComboBox.tsx +49 -17
- package/src/DateField.tsx +285 -52
- package/src/DatePicker.tsx +345 -57
- package/src/DateRangePickerContext.tsx +2 -0
- package/src/Disclosure.tsx +4 -0
- package/src/GridList.tsx +8 -2
- package/src/ListBox.tsx +323 -21
- package/src/Modal.tsx +21 -5
- package/src/Popover.tsx +28 -7
- package/src/RangeCalendar.tsx +120 -1
- package/src/SearchField.tsx +53 -1
- package/src/Select.tsx +101 -20
- package/src/SharedElementTransition.tsx +92 -36
- package/src/StepList.tsx +0 -7
- package/src/Table.tsx +19 -4
- package/src/Tabs.tsx +1 -0
- package/src/TagGroup.tsx +26 -6
- package/src/TextField.tsx +1 -1
- package/src/TimeField.tsx +113 -196
- package/src/Tree.tsx +14 -4
- package/src/index.ts +1 -8
- package/src/useDragAndDrop.ts +8 -10
- package/src/utils.tsx +3 -3
- package/dist/DatePicker.js.map +0 -1
- package/dist/DatePicker.jsx.map +0 -1
- package/dist/HiddenTimeInput.d.ts +0 -25
- package/dist/HiddenTimeInput.d.ts.map +0 -1
- package/dist/HiddenTimeInput.js +0 -104
- package/dist/HiddenTimeInput.js.map +0 -1
- package/dist/HiddenTimeInput.jsx +0 -69
- package/dist/HiddenTimeInput.jsx.map +0 -1
- package/src/HiddenTimeInput.tsx +0 -133
package/src/DateField.tsx
CHANGED
|
@@ -15,14 +15,25 @@ import {
|
|
|
15
15
|
onCleanup,
|
|
16
16
|
splitProps,
|
|
17
17
|
useContext,
|
|
18
|
-
|
|
18
|
+
Index,
|
|
19
19
|
Show,
|
|
20
20
|
} from "solid-js";
|
|
21
21
|
import {
|
|
22
22
|
createDateField,
|
|
23
23
|
createDateSegment,
|
|
24
|
+
createFocusRing,
|
|
25
|
+
createHover,
|
|
26
|
+
createVisuallyHidden,
|
|
27
|
+
mergeProps,
|
|
24
28
|
type AriaDateFieldProps,
|
|
25
29
|
} from "@proyecto-viviana/solidaria";
|
|
30
|
+
import {
|
|
31
|
+
parseDate,
|
|
32
|
+
parseDateTime,
|
|
33
|
+
toCalendarDate,
|
|
34
|
+
toCalendarDateTime,
|
|
35
|
+
toLocalTimeZone,
|
|
36
|
+
} from "@internationalized/date";
|
|
26
37
|
import {
|
|
27
38
|
createDateFieldState,
|
|
28
39
|
access,
|
|
@@ -43,7 +54,6 @@ import {
|
|
|
43
54
|
Provider,
|
|
44
55
|
} from "./utils";
|
|
45
56
|
import { TextContext } from "./Text";
|
|
46
|
-
import { HiddenDateInput } from "./HiddenDateInput";
|
|
47
57
|
import { FormContext, type FormProps } from "./Form";
|
|
48
58
|
import {
|
|
49
59
|
DateRangePickerContext,
|
|
@@ -95,12 +105,20 @@ export interface DateInputProps extends SlotProps {
|
|
|
95
105
|
}
|
|
96
106
|
|
|
97
107
|
export interface DateSegmentRenderProps {
|
|
108
|
+
/** Whether the segment is hovered. */
|
|
109
|
+
isHovered: boolean;
|
|
98
110
|
/** Whether the segment is focused. */
|
|
99
111
|
isFocused: boolean;
|
|
100
|
-
/** Whether the segment is
|
|
101
|
-
|
|
112
|
+
/** Whether the segment is keyboard focused. */
|
|
113
|
+
isFocusVisible: boolean;
|
|
102
114
|
/** Whether the segment is a placeholder. */
|
|
103
115
|
isPlaceholder: boolean;
|
|
116
|
+
/** Whether the segment is read only. */
|
|
117
|
+
isReadOnly: boolean;
|
|
118
|
+
/** Whether the segment is disabled. */
|
|
119
|
+
isDisabled: boolean;
|
|
120
|
+
/** Whether the segment is invalid. */
|
|
121
|
+
isInvalid: boolean;
|
|
104
122
|
/** The segment type. */
|
|
105
123
|
type: DateSegmentType["type"];
|
|
106
124
|
/** The text to display. */
|
|
@@ -123,6 +141,10 @@ export interface DateFieldContextValue {
|
|
|
123
141
|
aria: {
|
|
124
142
|
labelProps: Record<string, unknown>;
|
|
125
143
|
inputProps: Record<string, unknown>;
|
|
144
|
+
// The hidden validation <input> props (native type/required/onChange). Only
|
|
145
|
+
// the standalone DateField provides these; DatePicker/DateRangePicker manage
|
|
146
|
+
// their own hidden inputs, so this stays optional and its <input> is gated.
|
|
147
|
+
hiddenInputProps?: Record<string, unknown>;
|
|
126
148
|
descriptionProps: Record<string, unknown>;
|
|
127
149
|
errorMessageProps: Record<string, unknown>;
|
|
128
150
|
};
|
|
@@ -278,14 +300,37 @@ function DateFieldInner<T extends DateValue = CalendarDate>(
|
|
|
278
300
|
);
|
|
279
301
|
|
|
280
302
|
const [fieldRef, setFieldRef] = createSignal<HTMLDivElement | null>(null);
|
|
303
|
+
// Ref to the hidden validation <input> (the DateInput's `<Input>` sibling).
|
|
304
|
+
// RAC's DateField creates this ref, passes it to useDateField, and threads it
|
|
305
|
+
// onto the <Input> via InputContext so useFormReset/useFormValidation target
|
|
306
|
+
// that element. We mirror that: create it here, hand it to createDateField
|
|
307
|
+
// below, and attach it to the same input through the context getter.
|
|
308
|
+
const [validationInputRef, setValidationInputRef] =
|
|
309
|
+
createSignal<HTMLInputElement | null>(null);
|
|
281
310
|
|
|
282
311
|
const state = createDateFieldState(stateProps);
|
|
283
312
|
|
|
284
313
|
const fieldAria = createDateField(
|
|
285
314
|
() => ({
|
|
286
315
|
...(rest as Record<string, unknown>),
|
|
316
|
+
// RAC threads the same props into both useDateFieldState and useDateField;
|
|
317
|
+
// these live in `stateProps` here, so forward them so the field hook can
|
|
318
|
+
// build aria-disabled and the native validation <input> (type/required).
|
|
319
|
+
// access() unwraps the MaybeAccessor state-prop declarations.
|
|
320
|
+
isDisabled: access(stateProps.isDisabled),
|
|
321
|
+
isReadOnly: access(stateProps.isReadOnly),
|
|
322
|
+
isRequired: access(stateProps.isRequired),
|
|
323
|
+
// Mirror RAC DateField: validationBehavior ?? formValidationBehavior ??
|
|
324
|
+
// 'native'. withFormValidationBehavior already folds the form default into
|
|
325
|
+
// stateProps.validationBehavior; the last `?? 'native'` is the standalone
|
|
326
|
+
// default that flips the hidden input to type="text".
|
|
327
|
+
validationBehavior: stateProps.validationBehavior ?? "native",
|
|
287
328
|
description: stateProps.description,
|
|
288
329
|
errorMessage: stateProps.errorMessage,
|
|
330
|
+
// Form-reset + native constraint validation are wired onto this input by
|
|
331
|
+
// createDateField (see its createFormReset/createFormValidation calls),
|
|
332
|
+
// exactly as RAC's useDateField wires them onto props.inputRef.
|
|
333
|
+
inputRef: () => validationInputRef() ?? undefined,
|
|
289
334
|
}),
|
|
290
335
|
state as unknown as DateFieldState<DateValue>,
|
|
291
336
|
fieldRef,
|
|
@@ -307,11 +352,6 @@ function DateFieldInner<T extends DateValue = CalendarDate>(
|
|
|
307
352
|
renderValues,
|
|
308
353
|
);
|
|
309
354
|
|
|
310
|
-
const validationBehavior = () =>
|
|
311
|
-
(stateProps as { validationBehavior?: "aria" | "native" }).validationBehavior ??
|
|
312
|
-
formContext?.validationBehavior ??
|
|
313
|
-
"native";
|
|
314
|
-
|
|
315
355
|
const textSlots = {
|
|
316
356
|
slots: {
|
|
317
357
|
get description() {
|
|
@@ -328,17 +368,41 @@ function DateFieldInner<T extends DateValue = CalendarDate>(
|
|
|
328
368
|
<DateFieldContext.Provider
|
|
329
369
|
value={{
|
|
330
370
|
state: state as unknown as DateFieldState<DateValue>,
|
|
371
|
+
// Read through getters so consumers see the LIVE memo values, not a
|
|
372
|
+
// snapshot frozen at first render. `createDescription` appends the
|
|
373
|
+
// value-description id via a deferred effect; the group's
|
|
374
|
+
// aria-describedby only picks it up if the props stay reactive here.
|
|
331
375
|
aria: {
|
|
332
|
-
labelProps
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
376
|
+
get labelProps() {
|
|
377
|
+
return fieldAria.labelProps;
|
|
378
|
+
},
|
|
379
|
+
// The segment group props (role="group", aria-labelledby, unicode-bidi
|
|
380
|
+
// isolate style, arrow-key nav, focus-within handlers) are routed to the
|
|
381
|
+
// DateInput element — mirroring RAC, where useDateField.fieldProps is
|
|
382
|
+
// published through GroupContext and consumed by the DateInput <Group>.
|
|
383
|
+
// The outer wrapper stays roleless.
|
|
384
|
+
get inputProps() {
|
|
385
|
+
return fieldAria.fieldProps;
|
|
386
|
+
},
|
|
387
|
+
// The REAL hidden validation input props (type/hidden/required/onChange
|
|
388
|
+
// under native validation). Rendered as the DateInput's <input> sibling.
|
|
389
|
+
// Thread the validation-input ref through here — mirroring RAC's
|
|
390
|
+
// `[InputContext, {...inputProps, ref: inputRef}]` — so createDateField's
|
|
391
|
+
// form-reset/validation wiring resolves to the mounted element.
|
|
392
|
+
get hiddenInputProps() {
|
|
393
|
+
return mergeProps(fieldAria.inputProps, { ref: setValidationInputRef });
|
|
394
|
+
},
|
|
395
|
+
get descriptionProps() {
|
|
396
|
+
return fieldAria.descriptionProps;
|
|
397
|
+
},
|
|
398
|
+
get errorMessageProps() {
|
|
399
|
+
return fieldAria.errorMessageProps;
|
|
400
|
+
},
|
|
336
401
|
},
|
|
337
402
|
}}
|
|
338
403
|
>
|
|
339
404
|
<div
|
|
340
405
|
ref={setFieldRef}
|
|
341
|
-
{...fieldAria.fieldProps}
|
|
342
406
|
class={renderProps.class()}
|
|
343
407
|
style={renderProps.style()}
|
|
344
408
|
data-disabled={dataAttr(state.isDisabled())}
|
|
@@ -350,29 +414,121 @@ function DateFieldInner<T extends DateValue = CalendarDate>(
|
|
|
350
414
|
{local.children as JSX.Element}
|
|
351
415
|
</Provider>
|
|
352
416
|
</div>
|
|
353
|
-
|
|
354
|
-
<HiddenDateInput
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
/>
|
|
370
|
-
</Show>
|
|
417
|
+
{/*
|
|
418
|
+
RAC renders <HiddenDateInput> UNCONDITIONALLY at the DateField root
|
|
419
|
+
(react-aria-components DateField.mjs). It is NOT a validation input — it
|
|
420
|
+
is a clipped, aria-hidden, tabIndex={-1} native date input that mirrors
|
|
421
|
+
the value for browser autofill. Its `form=""` detaches it from form
|
|
422
|
+
submission (the DateInput's <Input> above is what submits), so it never
|
|
423
|
+
double-counts in FormData. It renders regardless of `name` — hence no
|
|
424
|
+
<Show> gate — which is why it appears in the focus trail as the trailing
|
|
425
|
+
tabindex="-1" element.
|
|
426
|
+
*/}
|
|
427
|
+
<RootHiddenDateInput
|
|
428
|
+
autoComplete={(rest as Record<string, unknown>).autoComplete as string | undefined}
|
|
429
|
+
name={(rest as Record<string, unknown>).name as string | undefined}
|
|
430
|
+
isDisabled={state.isDisabled()}
|
|
431
|
+
state={state as unknown as DateFieldState<DateValue>}
|
|
432
|
+
/>
|
|
371
433
|
</DateFieldContext.Provider>
|
|
372
434
|
</DateFieldStateContext.Provider>
|
|
373
435
|
);
|
|
374
436
|
}
|
|
375
437
|
|
|
438
|
+
const HIDDEN_DATE_SEGMENTS: readonly string[] = ["day", "month", "year"];
|
|
439
|
+
const HIDDEN_TIME_SEGMENTS: readonly string[] = ["hour", "minute", "second"];
|
|
440
|
+
const HIDDEN_GRANULARITY_MAP: Record<string, number> = { hour: 1, minute: 2, second: 3 };
|
|
441
|
+
|
|
442
|
+
interface RootHiddenDateInputProps {
|
|
443
|
+
autoComplete?: string;
|
|
444
|
+
name?: string;
|
|
445
|
+
isDisabled?: boolean;
|
|
446
|
+
state: DateFieldState<DateValue>;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* The always-rendered hidden date input at the DateField root — a faithful port
|
|
451
|
+
* of react-aria-components' HiddenDateInput (private/HiddenDateInput.mjs). It is
|
|
452
|
+
* NOT a validation input: it is a clipped, aria-hidden, tabIndex={-1} native
|
|
453
|
+
* date input that mirrors the current value for browser autofill. `form=""`
|
|
454
|
+
* detaches it from form submission (the DateInput's <Input> submits), so it
|
|
455
|
+
* never double-counts in FormData. RAC renders it unconditionally, which is why
|
|
456
|
+
* it appears as the trailing tabindex="-1" element in the focus trail.
|
|
457
|
+
*/
|
|
458
|
+
function RootHiddenDateInput(props: RootHiddenDateInputProps): JSX.Element {
|
|
459
|
+
// RAC uses useVisuallyHidden with position:fixed/top:0/left:0 to keep the
|
|
460
|
+
// clipped input from scrolling the page.
|
|
461
|
+
const { visuallyHiddenProps } = createVisuallyHidden(() => ({
|
|
462
|
+
style: { position: "fixed", top: "0", left: "0" },
|
|
463
|
+
}));
|
|
464
|
+
|
|
465
|
+
const granularity = () => props.state.granularity ?? "day";
|
|
466
|
+
const inputStep = () => {
|
|
467
|
+
const g = granularity();
|
|
468
|
+
if (g === "second") return 1;
|
|
469
|
+
if (g === "hour") return 3600;
|
|
470
|
+
return 60;
|
|
471
|
+
};
|
|
472
|
+
const inputType = () => (granularity() === "day" ? "date" : "datetime-local");
|
|
473
|
+
const dateValue = () => {
|
|
474
|
+
const value = props.state.value();
|
|
475
|
+
if (!value) return "";
|
|
476
|
+
if (granularity() === "day") return toCalendarDate(value).toString();
|
|
477
|
+
return toCalendarDateTime(
|
|
478
|
+
"timeZone" in value ? toLocalTimeZone(value) : value,
|
|
479
|
+
).toString();
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
const onChange: JSX.EventHandler<HTMLInputElement, Event> = (event) => {
|
|
483
|
+
const targetString = (event.currentTarget.value ?? "").toString();
|
|
484
|
+
if (!targetString) return;
|
|
485
|
+
try {
|
|
486
|
+
const g = granularity();
|
|
487
|
+
const targetValue = (g === "day" ? parseDate(targetString) : parseDateTime(targetString)) as
|
|
488
|
+
unknown as Record<string, number>;
|
|
489
|
+
let timeSegments = HIDDEN_TIME_SEGMENTS;
|
|
490
|
+
if (HIDDEN_TIME_SEGMENTS.includes(g)) {
|
|
491
|
+
timeSegments = HIDDEN_TIME_SEGMENTS.slice(0, HIDDEN_GRANULARITY_MAP[g]);
|
|
492
|
+
}
|
|
493
|
+
const state = props.state as unknown as {
|
|
494
|
+
setSegment?: (type: string, value: number) => void;
|
|
495
|
+
setValue: (value: unknown) => void;
|
|
496
|
+
};
|
|
497
|
+
// Only DateFieldState (not DatePickerState) exposes setSegment; validate
|
|
498
|
+
// each parsed segment before committing the new value, mirroring RAC.
|
|
499
|
+
if (typeof state.setSegment === "function") {
|
|
500
|
+
for (const type in targetValue) {
|
|
501
|
+
if (HIDDEN_DATE_SEGMENTS.includes(type)) state.setSegment(type, targetValue[type]);
|
|
502
|
+
if (timeSegments.includes(type)) state.setSegment(type, targetValue[type]);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
state.setValue(targetValue);
|
|
506
|
+
} catch {
|
|
507
|
+
// ignore unparseable autofill values
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
return (
|
|
512
|
+
<div
|
|
513
|
+
{...(visuallyHiddenProps() as unknown as Record<string, unknown>)}
|
|
514
|
+
aria-hidden="true"
|
|
515
|
+
data-testid="hidden-dateinput-container"
|
|
516
|
+
>
|
|
517
|
+
<input
|
|
518
|
+
tabIndex={-1}
|
|
519
|
+
autocomplete={props.autoComplete}
|
|
520
|
+
disabled={props.isDisabled}
|
|
521
|
+
type={inputType()}
|
|
522
|
+
form=""
|
|
523
|
+
name={props.name}
|
|
524
|
+
step={inputStep()}
|
|
525
|
+
value={dateValue()}
|
|
526
|
+
onChange={onChange}
|
|
527
|
+
/>
|
|
528
|
+
</div>
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
|
|
376
532
|
/**
|
|
377
533
|
* The input area containing date segments.
|
|
378
534
|
*/
|
|
@@ -396,6 +552,20 @@ export function DateInput(props: DateInputProps): JSX.Element {
|
|
|
396
552
|
renderValues,
|
|
397
553
|
);
|
|
398
554
|
|
|
555
|
+
// The group props (role="group", aria-labelledby, unicode-bidi:isolate,
|
|
556
|
+
// arrow-key nav, focus-within handlers) carry the field's own `style` and
|
|
557
|
+
// focus handlers. Merge — not clobber — so the group keeps its unicode-bidi
|
|
558
|
+
// isolation and the local isFocused signal chains onto the field's
|
|
559
|
+
// focusWithin handlers. Solid onFocus/onBlur do not bubble, so track focus
|
|
560
|
+
// via onFocusIn/onFocusOut.
|
|
561
|
+
const inputDivProps = createMemo(() =>
|
|
562
|
+
mergeProps(aria.inputProps, {
|
|
563
|
+
onFocusIn: () => setIsFocused(true),
|
|
564
|
+
onFocusOut: () => setIsFocused(false),
|
|
565
|
+
style: renderProps.style(),
|
|
566
|
+
}),
|
|
567
|
+
);
|
|
568
|
+
|
|
399
569
|
createEffect(() => {
|
|
400
570
|
const element = inputRef();
|
|
401
571
|
const handler = props.onPointerDownCapture;
|
|
@@ -411,16 +581,41 @@ export function DateInput(props: DateInputProps): JSX.Element {
|
|
|
411
581
|
<DateFieldContext.Provider value={context as DateFieldContextValue}>
|
|
412
582
|
<div
|
|
413
583
|
ref={setInputRef}
|
|
414
|
-
{...
|
|
584
|
+
{...inputDivProps()}
|
|
415
585
|
class={renderProps.class()}
|
|
416
|
-
style={renderProps.style()}
|
|
417
586
|
data-disabled={dataAttr(state.isDisabled())}
|
|
418
587
|
data-focused={dataAttr(isFocused())}
|
|
419
|
-
onFocusIn={() => setIsFocused(true)}
|
|
420
|
-
onFocusOut={() => setIsFocused(false)}
|
|
421
588
|
>
|
|
422
|
-
|
|
589
|
+
{/*
|
|
590
|
+
<Index> keys by position, not identity, so each DateSegment instance
|
|
591
|
+
stays alive across keystrokes (state.segments() re-mints segment
|
|
592
|
+
objects every edit). A stable Proxy forwards property reads to the
|
|
593
|
+
live per-index accessor so the child stays reactive.
|
|
594
|
+
*/}
|
|
595
|
+
<Index each={state.segments()}>
|
|
596
|
+
{(segment) => {
|
|
597
|
+
const liveSegment = new Proxy({} as DateSegmentType, {
|
|
598
|
+
get: (_t, key) => (segment() as unknown as Record<PropertyKey, unknown>)[key],
|
|
599
|
+
has: (_t, key) => key in (segment() as object),
|
|
600
|
+
});
|
|
601
|
+
return props.children?.(liveSegment);
|
|
602
|
+
}}
|
|
603
|
+
</Index>
|
|
423
604
|
</div>
|
|
605
|
+
{/*
|
|
606
|
+
The hidden validation input, sibling of the group — mirrors RAC's
|
|
607
|
+
DateInputInner `<><Group/><Input/></>`. Under native validation
|
|
608
|
+
(default) `useDateField` publishes it as `type="text" hidden required`
|
|
609
|
+
so browser constraint validation fires; under aria validation it stays
|
|
610
|
+
`type="hidden"`. Both carry the ISO value string. Only the standalone
|
|
611
|
+
DateField supplies these props; inside a DatePicker/DateRangePicker the
|
|
612
|
+
field is presentation and its owner renders the hidden input instead.
|
|
613
|
+
*/}
|
|
614
|
+
<Show when={aria.hiddenInputProps}>
|
|
615
|
+
{(hiddenInputProps) => (
|
|
616
|
+
<input {...(hiddenInputProps() as JSX.InputHTMLAttributes<HTMLInputElement>)} />
|
|
617
|
+
)}
|
|
618
|
+
</Show>
|
|
424
619
|
</DateFieldContext.Provider>
|
|
425
620
|
);
|
|
426
621
|
}
|
|
@@ -432,14 +627,34 @@ export function DateSegment(props: DateSegmentProps): JSX.Element {
|
|
|
432
627
|
const { state } = useDateFieldContext();
|
|
433
628
|
const [segmentRef, setSegmentRef] = createSignal<HTMLElement | null>(null);
|
|
434
629
|
|
|
435
|
-
const segmentAria = createDateSegment(
|
|
630
|
+
const segmentAria = createDateSegment(
|
|
631
|
+
{
|
|
632
|
+
get segment() {
|
|
633
|
+
return props.segment;
|
|
634
|
+
},
|
|
635
|
+
},
|
|
636
|
+
state,
|
|
637
|
+
segmentRef,
|
|
638
|
+
);
|
|
639
|
+
|
|
640
|
+
// Mirror upstream DateSegment: focus/focus-visible come from useFocusRing()
|
|
641
|
+
// and hover from useHover({isDisabled: field disabled || literal segment}).
|
|
642
|
+
// Literals never hover; the field being disabled suppresses hover entirely.
|
|
643
|
+
const { isFocused, isFocusVisible, focusProps } = createFocusRing();
|
|
644
|
+
const { isHovered, hoverProps } = createHover(() => ({
|
|
645
|
+
isDisabled: state.isDisabled() || props.segment.type === "literal",
|
|
646
|
+
}));
|
|
436
647
|
|
|
437
648
|
const renderValues = createMemo<DateSegmentRenderProps>(() => ({
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
649
|
+
isHovered: isHovered(),
|
|
650
|
+
isFocused: isFocused(),
|
|
651
|
+
isFocusVisible: isFocusVisible(),
|
|
652
|
+
isPlaceholder: props.segment.isPlaceholder,
|
|
653
|
+
isReadOnly: state.isReadOnly(),
|
|
654
|
+
isDisabled: state.isDisabled(),
|
|
655
|
+
isInvalid: state.isInvalid(),
|
|
441
656
|
type: props.segment.type,
|
|
442
|
-
text:
|
|
657
|
+
text: props.segment.text,
|
|
443
658
|
}));
|
|
444
659
|
|
|
445
660
|
const renderProps = useRenderProps(
|
|
@@ -459,19 +674,37 @@ export function DateSegment(props: DateSegmentProps): JSX.Element {
|
|
|
459
674
|
if (typeof props.children === "function") {
|
|
460
675
|
return renderProps.renderChildren();
|
|
461
676
|
}
|
|
462
|
-
return
|
|
677
|
+
return props.segment.text;
|
|
463
678
|
};
|
|
464
679
|
|
|
680
|
+
// Mirror upstream's `mergeProps(filterDOMProps(otherProps), segmentProps,
|
|
681
|
+
// focusProps, hoverProps)`: the segment's behavior/style props (caretColor +
|
|
682
|
+
// RTL direction) chain with the focus-ring and hover handlers so neither
|
|
683
|
+
// clobbers the other.
|
|
684
|
+
const segmentElementProps = createMemo(() =>
|
|
685
|
+
mergeProps(
|
|
686
|
+
segmentAria.segmentProps,
|
|
687
|
+
focusProps as Record<string, unknown>,
|
|
688
|
+
hoverProps as Record<string, unknown>,
|
|
689
|
+
{
|
|
690
|
+
style: renderProps.style(),
|
|
691
|
+
},
|
|
692
|
+
),
|
|
693
|
+
);
|
|
694
|
+
|
|
465
695
|
return (
|
|
466
696
|
<span
|
|
467
697
|
ref={setSegmentRef}
|
|
468
|
-
{...
|
|
698
|
+
{...segmentElementProps()}
|
|
469
699
|
class={renderProps.class()}
|
|
470
|
-
|
|
471
|
-
data-
|
|
472
|
-
data-
|
|
473
|
-
data-
|
|
700
|
+
data-placeholder={props.segment.isPlaceholder || undefined}
|
|
701
|
+
data-invalid={state.isInvalid() || undefined}
|
|
702
|
+
data-readonly={state.isReadOnly() || undefined}
|
|
703
|
+
data-disabled={state.isDisabled() || undefined}
|
|
474
704
|
data-type={props.segment.type}
|
|
705
|
+
data-hovered={isHovered() || undefined}
|
|
706
|
+
data-focused={isFocused() || undefined}
|
|
707
|
+
data-focus-visible={isFocusVisible() || undefined}
|
|
475
708
|
>
|
|
476
709
|
{getChildren()}
|
|
477
710
|
</span>
|
|
@@ -500,9 +733,9 @@ export interface DateFieldDescriptionProps {
|
|
|
500
733
|
export function DateFieldDescription(props: DateFieldDescriptionProps): JSX.Element {
|
|
501
734
|
const { aria } = useDateFieldContext();
|
|
502
735
|
return (
|
|
503
|
-
<
|
|
736
|
+
<span {...aria.descriptionProps} class={props.class}>
|
|
504
737
|
{props.children}
|
|
505
|
-
</
|
|
738
|
+
</span>
|
|
506
739
|
);
|
|
507
740
|
}
|
|
508
741
|
|
|
@@ -514,9 +747,9 @@ export interface DateFieldErrorMessageProps {
|
|
|
514
747
|
export function DateFieldErrorMessage(props: DateFieldErrorMessageProps): JSX.Element {
|
|
515
748
|
const { aria } = useDateFieldContext();
|
|
516
749
|
return (
|
|
517
|
-
<
|
|
750
|
+
<span {...aria.errorMessageProps} class={props.class}>
|
|
518
751
|
{props.children}
|
|
519
|
-
</
|
|
752
|
+
</span>
|
|
520
753
|
);
|
|
521
754
|
}
|
|
522
755
|
|