@proyecto-viviana/solidaria-components 0.1.3 → 0.2.2
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/Color.d.ts +6 -2
- package/dist/Color.d.ts.map +1 -1
- package/dist/ComboBox.d.ts +3 -3
- package/dist/ComboBox.d.ts.map +1 -1
- package/dist/GridList.d.ts +2 -2
- package/dist/GridList.d.ts.map +1 -1
- package/dist/ListBox.d.ts +5 -5
- package/dist/ListBox.d.ts.map +1 -1
- package/dist/Menu.d.ts +3 -3
- package/dist/Menu.d.ts.map +1 -1
- package/dist/Select.d.ts +3 -3
- package/dist/Select.d.ts.map +1 -1
- package/dist/Table.d.ts +2 -2
- package/dist/Table.d.ts.map +1 -1
- package/dist/Tabs.d.ts +1 -1
- package/dist/Tabs.d.ts.map +1 -1
- package/dist/index.js +15 -15
- package/dist/index.js.map +2 -2
- package/dist/index.jsx +9056 -0
- package/dist/index.jsx.map +7 -0
- package/dist/index.ssr.js +15 -15
- package/dist/index.ssr.js.map +2 -2
- package/package.json +8 -10
- package/src/Autocomplete.tsx +0 -174
- package/src/Breadcrumbs.tsx +0 -264
- package/src/Button.tsx +0 -238
- package/src/Calendar.tsx +0 -471
- package/src/Checkbox.tsx +0 -387
- package/src/Color.tsx +0 -1370
- package/src/ComboBox.tsx +0 -824
- package/src/DateField.tsx +0 -337
- package/src/DatePicker.tsx +0 -367
- package/src/Dialog.tsx +0 -262
- package/src/Disclosure.tsx +0 -439
- package/src/GridList.tsx +0 -511
- package/src/Landmark.tsx +0 -203
- package/src/Link.tsx +0 -201
- package/src/ListBox.tsx +0 -346
- package/src/Menu.tsx +0 -544
- package/src/Meter.tsx +0 -157
- package/src/Modal.tsx +0 -433
- package/src/NumberField.tsx +0 -542
- package/src/Popover.tsx +0 -540
- package/src/ProgressBar.tsx +0 -162
- package/src/RadioGroup.tsx +0 -356
- package/src/RangeCalendar.tsx +0 -462
- package/src/SearchField.tsx +0 -479
- package/src/Select.tsx +0 -734
- package/src/Separator.tsx +0 -130
- package/src/Slider.tsx +0 -500
- package/src/Switch.tsx +0 -213
- package/src/Table.tsx +0 -857
- package/src/Tabs.tsx +0 -552
- package/src/TagGroup.tsx +0 -421
- package/src/TextField.tsx +0 -271
- package/src/TimeField.tsx +0 -455
- package/src/Toast.tsx +0 -503
- package/src/Toolbar.tsx +0 -160
- package/src/Tooltip.tsx +0 -423
- package/src/Tree.tsx +0 -551
- package/src/VisuallyHidden.tsx +0 -60
- package/src/contexts.ts +0 -74
- package/src/index.ts +0 -620
- package/src/utils.tsx +0 -329
package/src/DateField.tsx
DELETED
|
@@ -1,337 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DateField component for solidaria-components
|
|
3
|
-
*
|
|
4
|
-
* Pre-wired headless date field component with segment-based editing.
|
|
5
|
-
* Port of react-aria-components/src/DateField.tsx
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
type JSX,
|
|
10
|
-
createContext,
|
|
11
|
-
createMemo,
|
|
12
|
-
createSignal,
|
|
13
|
-
splitProps,
|
|
14
|
-
useContext,
|
|
15
|
-
For,
|
|
16
|
-
Show,
|
|
17
|
-
} from 'solid-js';
|
|
18
|
-
import {
|
|
19
|
-
createDateField,
|
|
20
|
-
createDateSegment,
|
|
21
|
-
type AriaDateFieldProps,
|
|
22
|
-
} from '@proyecto-viviana/solidaria';
|
|
23
|
-
import {
|
|
24
|
-
createDateFieldState,
|
|
25
|
-
type DateFieldState,
|
|
26
|
-
type DateFieldStateProps,
|
|
27
|
-
type DateSegment as DateSegmentType,
|
|
28
|
-
type CalendarDate,
|
|
29
|
-
type DateValue,
|
|
30
|
-
} from '@proyecto-viviana/solid-stately';
|
|
31
|
-
import {
|
|
32
|
-
type RenderChildren,
|
|
33
|
-
type ClassNameOrFunction,
|
|
34
|
-
type StyleOrFunction,
|
|
35
|
-
type SlotProps,
|
|
36
|
-
useRenderProps,
|
|
37
|
-
dataAttr,
|
|
38
|
-
useIsHydrated,
|
|
39
|
-
} from './utils';
|
|
40
|
-
|
|
41
|
-
// ============================================
|
|
42
|
-
// TYPES
|
|
43
|
-
// ============================================
|
|
44
|
-
|
|
45
|
-
export interface DateFieldRenderProps {
|
|
46
|
-
/** Whether the field is disabled. */
|
|
47
|
-
isDisabled: boolean;
|
|
48
|
-
/** Whether the field is read-only. */
|
|
49
|
-
isReadOnly: boolean;
|
|
50
|
-
/** Whether the field is required. */
|
|
51
|
-
isRequired: boolean;
|
|
52
|
-
/** Whether the field is invalid. */
|
|
53
|
-
isInvalid: boolean;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface DateFieldProps<T extends DateValue = DateValue>
|
|
57
|
-
extends Omit<AriaDateFieldProps, 'id' | 'isDisabled' | 'isReadOnly' | 'isRequired'>,
|
|
58
|
-
Omit<DateFieldStateProps<T>, 'locale'>,
|
|
59
|
-
SlotProps {
|
|
60
|
-
/** The children of the component. */
|
|
61
|
-
children?: JSX.Element | ((segment: DateSegmentType) => JSX.Element);
|
|
62
|
-
/** The CSS className for the element. */
|
|
63
|
-
class?: ClassNameOrFunction<DateFieldRenderProps>;
|
|
64
|
-
/** The inline style for the element. */
|
|
65
|
-
style?: StyleOrFunction<DateFieldRenderProps>;
|
|
66
|
-
/** The locale to use for formatting. */
|
|
67
|
-
locale?: string;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export interface DateInputRenderProps {
|
|
71
|
-
/** Whether the input is disabled. */
|
|
72
|
-
isDisabled: boolean;
|
|
73
|
-
/** Whether the input is focused. */
|
|
74
|
-
isFocused: boolean;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export interface DateInputProps extends SlotProps {
|
|
78
|
-
/** The children of the component (render function receiving segments). */
|
|
79
|
-
children?: (segment: DateSegmentType) => JSX.Element;
|
|
80
|
-
/** The CSS className for the element. */
|
|
81
|
-
class?: ClassNameOrFunction<DateInputRenderProps>;
|
|
82
|
-
/** The inline style for the element. */
|
|
83
|
-
style?: StyleOrFunction<DateInputRenderProps>;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export interface DateSegmentRenderProps {
|
|
87
|
-
/** Whether the segment is focused. */
|
|
88
|
-
isFocused: boolean;
|
|
89
|
-
/** Whether the segment is editable. */
|
|
90
|
-
isEditable: boolean;
|
|
91
|
-
/** Whether the segment is a placeholder. */
|
|
92
|
-
isPlaceholder: boolean;
|
|
93
|
-
/** The segment type. */
|
|
94
|
-
type: DateSegmentType['type'];
|
|
95
|
-
/** The text to display. */
|
|
96
|
-
text: string;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export interface DateSegmentProps extends SlotProps {
|
|
100
|
-
/** The segment data. */
|
|
101
|
-
segment: DateSegmentType;
|
|
102
|
-
/** The children of the component. A function may be provided to receive render props. */
|
|
103
|
-
children?: RenderChildren<DateSegmentRenderProps>;
|
|
104
|
-
/** The CSS className for the element. */
|
|
105
|
-
class?: ClassNameOrFunction<DateSegmentRenderProps>;
|
|
106
|
-
/** The inline style for the element. */
|
|
107
|
-
style?: StyleOrFunction<DateSegmentRenderProps>;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// ============================================
|
|
111
|
-
// CONTEXT
|
|
112
|
-
// ============================================
|
|
113
|
-
|
|
114
|
-
export const DateFieldContext = createContext<DateFieldState<DateValue> | null>(null);
|
|
115
|
-
|
|
116
|
-
export function useDateFieldContext(): DateFieldState<DateValue> {
|
|
117
|
-
const context = useContext(DateFieldContext);
|
|
118
|
-
if (!context) {
|
|
119
|
-
throw new Error('DateField components must be used within a DateField');
|
|
120
|
-
}
|
|
121
|
-
return context;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// ============================================
|
|
125
|
-
// DATE FIELD COMPONENT
|
|
126
|
-
// ============================================
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* A date field allows users to enter and edit date values using a keyboard.
|
|
130
|
-
*
|
|
131
|
-
* @example
|
|
132
|
-
* ```tsx
|
|
133
|
-
* <DateField label="Date">
|
|
134
|
-
* <Label>Date</Label>
|
|
135
|
-
* <DateInput>
|
|
136
|
-
* {(segment) => <DateSegment segment={segment} />}
|
|
137
|
-
* </DateInput>
|
|
138
|
-
* </DateField>
|
|
139
|
-
* ```
|
|
140
|
-
*/
|
|
141
|
-
export function DateField<T extends DateValue = CalendarDate>(
|
|
142
|
-
props: DateFieldProps<T>
|
|
143
|
-
): JSX.Element {
|
|
144
|
-
// Use hydration-safe pattern for client-only rendering
|
|
145
|
-
const isHydrated = useIsHydrated();
|
|
146
|
-
|
|
147
|
-
return (
|
|
148
|
-
<Show
|
|
149
|
-
when={isHydrated()}
|
|
150
|
-
fallback={<div class="solidaria-DateField solidaria-DateField--placeholder" aria-hidden="true" />}
|
|
151
|
-
>
|
|
152
|
-
<DateFieldInner {...props} />
|
|
153
|
-
</Show>
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Internal DateField component that renders after client mount.
|
|
159
|
-
*/
|
|
160
|
-
function DateFieldInner<T extends DateValue = CalendarDate>(
|
|
161
|
-
props: DateFieldProps<T>
|
|
162
|
-
): JSX.Element {
|
|
163
|
-
const [local, stateProps, rest] = splitProps(
|
|
164
|
-
props,
|
|
165
|
-
['children', 'class', 'style', 'slot'],
|
|
166
|
-
[
|
|
167
|
-
'value',
|
|
168
|
-
'defaultValue',
|
|
169
|
-
'onChange',
|
|
170
|
-
'minValue',
|
|
171
|
-
'maxValue',
|
|
172
|
-
'isDisabled',
|
|
173
|
-
'isReadOnly',
|
|
174
|
-
'isRequired',
|
|
175
|
-
'locale',
|
|
176
|
-
'granularity',
|
|
177
|
-
'hourCycle',
|
|
178
|
-
'hideTimeZone',
|
|
179
|
-
'placeholderValue',
|
|
180
|
-
'validationState',
|
|
181
|
-
'description',
|
|
182
|
-
'errorMessage',
|
|
183
|
-
]
|
|
184
|
-
);
|
|
185
|
-
|
|
186
|
-
const [fieldRef, setFieldRef] = createSignal<HTMLDivElement | null>(null);
|
|
187
|
-
|
|
188
|
-
// Create date field state
|
|
189
|
-
const state = createDateFieldState(stateProps);
|
|
190
|
-
|
|
191
|
-
// Create date field ARIA props
|
|
192
|
-
const fieldAria = createDateField(rest, state as unknown as DateFieldState<DateValue>, fieldRef);
|
|
193
|
-
|
|
194
|
-
// Render props values
|
|
195
|
-
const renderValues = createMemo<DateFieldRenderProps>(() => ({
|
|
196
|
-
isDisabled: state.isDisabled(),
|
|
197
|
-
isReadOnly: state.isReadOnly(),
|
|
198
|
-
isRequired: state.isRequired(),
|
|
199
|
-
isInvalid: state.isInvalid(),
|
|
200
|
-
}));
|
|
201
|
-
|
|
202
|
-
// Resolve render props
|
|
203
|
-
const renderProps = useRenderProps(
|
|
204
|
-
{
|
|
205
|
-
class: local.class,
|
|
206
|
-
style: local.style,
|
|
207
|
-
defaultClassName: 'solidaria-DateField',
|
|
208
|
-
},
|
|
209
|
-
renderValues
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
return (
|
|
213
|
-
<DateFieldContext.Provider value={state as unknown as DateFieldState<DateValue>}>
|
|
214
|
-
<div
|
|
215
|
-
ref={setFieldRef}
|
|
216
|
-
{...fieldAria.fieldProps}
|
|
217
|
-
class={renderProps.class()}
|
|
218
|
-
style={renderProps.style()}
|
|
219
|
-
data-disabled={dataAttr(state.isDisabled())}
|
|
220
|
-
data-readonly={dataAttr(state.isReadOnly())}
|
|
221
|
-
data-required={dataAttr(state.isRequired())}
|
|
222
|
-
data-invalid={dataAttr(state.isInvalid())}
|
|
223
|
-
>
|
|
224
|
-
{props.children as JSX.Element}
|
|
225
|
-
</div>
|
|
226
|
-
</DateFieldContext.Provider>
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// ============================================
|
|
231
|
-
// DATE INPUT COMPONENT
|
|
232
|
-
// ============================================
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* The input area containing date segments.
|
|
236
|
-
*/
|
|
237
|
-
export function DateInput(props: DateInputProps): JSX.Element {
|
|
238
|
-
const state = useDateFieldContext();
|
|
239
|
-
const [isFocused, setIsFocused] = createSignal(false);
|
|
240
|
-
|
|
241
|
-
// Render props values
|
|
242
|
-
const renderValues = createMemo<DateInputRenderProps>(() => ({
|
|
243
|
-
isDisabled: state.isDisabled(),
|
|
244
|
-
isFocused: isFocused(),
|
|
245
|
-
}));
|
|
246
|
-
|
|
247
|
-
// Resolve render props
|
|
248
|
-
const renderProps = useRenderProps(
|
|
249
|
-
{
|
|
250
|
-
class: props.class,
|
|
251
|
-
style: props.style,
|
|
252
|
-
defaultClassName: 'solidaria-DateInput',
|
|
253
|
-
},
|
|
254
|
-
renderValues
|
|
255
|
-
);
|
|
256
|
-
|
|
257
|
-
return (
|
|
258
|
-
<div
|
|
259
|
-
role="presentation"
|
|
260
|
-
class={renderProps.class()}
|
|
261
|
-
style={renderProps.style()}
|
|
262
|
-
data-disabled={dataAttr(state.isDisabled())}
|
|
263
|
-
data-focused={dataAttr(isFocused())}
|
|
264
|
-
onFocusIn={() => setIsFocused(true)}
|
|
265
|
-
onFocusOut={() => setIsFocused(false)}
|
|
266
|
-
>
|
|
267
|
-
<For each={state.segments()}>
|
|
268
|
-
{(segment) => props.children?.(segment)}
|
|
269
|
-
</For>
|
|
270
|
-
</div>
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// ============================================
|
|
275
|
-
// DATE SEGMENT COMPONENT
|
|
276
|
-
// ============================================
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* A segment of a date field (year, month, day, etc.).
|
|
280
|
-
*/
|
|
281
|
-
export function DateSegment(props: DateSegmentProps): JSX.Element {
|
|
282
|
-
const state = useDateFieldContext();
|
|
283
|
-
const [segmentRef, setSegmentRef] = createSignal<HTMLDivElement | null>(null);
|
|
284
|
-
|
|
285
|
-
// Create segment ARIA props
|
|
286
|
-
const segmentAria = createDateSegment(
|
|
287
|
-
{ segment: props.segment },
|
|
288
|
-
state,
|
|
289
|
-
segmentRef
|
|
290
|
-
);
|
|
291
|
-
|
|
292
|
-
// Render props values
|
|
293
|
-
const renderValues = createMemo<DateSegmentRenderProps>(() => ({
|
|
294
|
-
isFocused: segmentAria.isFocused,
|
|
295
|
-
isEditable: segmentAria.isEditable,
|
|
296
|
-
isPlaceholder: segmentAria.isPlaceholder,
|
|
297
|
-
type: props.segment.type,
|
|
298
|
-
text: segmentAria.text,
|
|
299
|
-
}));
|
|
300
|
-
|
|
301
|
-
// Resolve render props
|
|
302
|
-
const renderProps = useRenderProps(
|
|
303
|
-
{
|
|
304
|
-
children: props.children,
|
|
305
|
-
class: props.class,
|
|
306
|
-
style: props.style,
|
|
307
|
-
defaultClassName: 'solidaria-DateSegment',
|
|
308
|
-
},
|
|
309
|
-
renderValues
|
|
310
|
-
);
|
|
311
|
-
|
|
312
|
-
// Determine children content - avoid Show for SSR hydration compatibility
|
|
313
|
-
const getChildren = () => {
|
|
314
|
-
if (typeof props.children === 'function') {
|
|
315
|
-
return renderProps.renderChildren();
|
|
316
|
-
}
|
|
317
|
-
return segmentAria.text;
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
return (
|
|
321
|
-
<div
|
|
322
|
-
ref={setSegmentRef}
|
|
323
|
-
{...segmentAria.segmentProps}
|
|
324
|
-
class={renderProps.class()}
|
|
325
|
-
style={renderProps.style()}
|
|
326
|
-
data-focused={dataAttr(segmentAria.isFocused)}
|
|
327
|
-
data-editable={dataAttr(segmentAria.isEditable)}
|
|
328
|
-
data-placeholder={dataAttr(segmentAria.isPlaceholder)}
|
|
329
|
-
data-type={props.segment.type}
|
|
330
|
-
>
|
|
331
|
-
{getChildren()}
|
|
332
|
-
</div>
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Re-export types
|
|
337
|
-
export type { DateFieldState, DateSegmentType };
|
package/src/DatePicker.tsx
DELETED
|
@@ -1,367 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DatePicker component for solidaria-components
|
|
3
|
-
*
|
|
4
|
-
* Pre-wired headless date picker component that combines a date field with a calendar popup.
|
|
5
|
-
* Port of react-aria-components/src/DatePicker.tsx
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
type JSX,
|
|
10
|
-
createContext,
|
|
11
|
-
createMemo,
|
|
12
|
-
createSignal,
|
|
13
|
-
splitProps,
|
|
14
|
-
useContext,
|
|
15
|
-
Show,
|
|
16
|
-
} from 'solid-js';
|
|
17
|
-
import {
|
|
18
|
-
createDatePicker,
|
|
19
|
-
type AriaDatePickerProps,
|
|
20
|
-
type DatePickerState as AriaDatePickerState,
|
|
21
|
-
} from '@proyecto-viviana/solidaria';
|
|
22
|
-
import {
|
|
23
|
-
createDateFieldState,
|
|
24
|
-
createCalendarState,
|
|
25
|
-
type DateFieldState,
|
|
26
|
-
type CalendarState,
|
|
27
|
-
type DateFieldStateProps,
|
|
28
|
-
type CalendarDate,
|
|
29
|
-
type DateValue,
|
|
30
|
-
} from '@proyecto-viviana/solid-stately';
|
|
31
|
-
import {
|
|
32
|
-
type RenderChildren,
|
|
33
|
-
type ClassNameOrFunction,
|
|
34
|
-
type StyleOrFunction,
|
|
35
|
-
type SlotProps,
|
|
36
|
-
useRenderProps,
|
|
37
|
-
dataAttr,
|
|
38
|
-
useIsHydrated,
|
|
39
|
-
} from './utils';
|
|
40
|
-
import { DateFieldContext } from './DateField';
|
|
41
|
-
|
|
42
|
-
// ============================================
|
|
43
|
-
// TYPES
|
|
44
|
-
// ============================================
|
|
45
|
-
|
|
46
|
-
export interface DatePickerRenderProps {
|
|
47
|
-
/** Whether the picker is disabled. */
|
|
48
|
-
isDisabled: boolean;
|
|
49
|
-
/** Whether the picker is read-only. */
|
|
50
|
-
isReadOnly: boolean;
|
|
51
|
-
/** Whether the picker is required. */
|
|
52
|
-
isRequired: boolean;
|
|
53
|
-
/** Whether the picker is invalid. */
|
|
54
|
-
isInvalid: boolean;
|
|
55
|
-
/** Whether the calendar is open. */
|
|
56
|
-
isOpen: boolean;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface DatePickerContextValue {
|
|
60
|
-
fieldState: DateFieldState<DateValue>;
|
|
61
|
-
calendarState: CalendarState<DateValue>;
|
|
62
|
-
overlayState: {
|
|
63
|
-
isOpen: boolean;
|
|
64
|
-
open: () => void;
|
|
65
|
-
close: () => void;
|
|
66
|
-
toggle: () => void;
|
|
67
|
-
};
|
|
68
|
-
pickerAria: ReturnType<typeof createDatePicker>;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export interface DatePickerProps<T extends DateValue = DateValue>
|
|
72
|
-
extends Omit<AriaDatePickerProps, 'id' | 'isDisabled' | 'isReadOnly' | 'isRequired'>,
|
|
73
|
-
Omit<DateFieldStateProps<T>, 'locale'>,
|
|
74
|
-
SlotProps {
|
|
75
|
-
/** The children of the component. */
|
|
76
|
-
children?: JSX.Element;
|
|
77
|
-
/** The CSS className for the element. */
|
|
78
|
-
class?: ClassNameOrFunction<DatePickerRenderProps>;
|
|
79
|
-
/** The inline style for the element. */
|
|
80
|
-
style?: StyleOrFunction<DatePickerRenderProps>;
|
|
81
|
-
/** The locale to use for formatting. */
|
|
82
|
-
locale?: string;
|
|
83
|
-
/** Whether the calendar should close when a date is selected. */
|
|
84
|
-
shouldCloseOnSelect?: boolean;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export interface DatePickerButtonRenderProps {
|
|
88
|
-
/** Whether the button is disabled. */
|
|
89
|
-
isDisabled: boolean;
|
|
90
|
-
/** Whether the calendar is open. */
|
|
91
|
-
isOpen: boolean;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export interface DatePickerButtonProps extends SlotProps {
|
|
95
|
-
/** The children of the component. */
|
|
96
|
-
children?: RenderChildren<DatePickerButtonRenderProps>;
|
|
97
|
-
/** The CSS className for the element. */
|
|
98
|
-
class?: ClassNameOrFunction<DatePickerButtonRenderProps>;
|
|
99
|
-
/** The inline style for the element. */
|
|
100
|
-
style?: StyleOrFunction<DatePickerButtonRenderProps>;
|
|
101
|
-
/** Whether the button is disabled. */
|
|
102
|
-
isDisabled?: boolean;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// ============================================
|
|
106
|
-
// CONTEXT
|
|
107
|
-
// ============================================
|
|
108
|
-
|
|
109
|
-
export const DatePickerContext = createContext<DatePickerContextValue | null>(null);
|
|
110
|
-
|
|
111
|
-
export function useDatePickerContext(): DatePickerContextValue {
|
|
112
|
-
const context = useContext(DatePickerContext);
|
|
113
|
-
if (!context) {
|
|
114
|
-
throw new Error('DatePicker components must be used within a DatePicker');
|
|
115
|
-
}
|
|
116
|
-
return context;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// ============================================
|
|
120
|
-
// DATE PICKER COMPONENT
|
|
121
|
-
// ============================================
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* A date picker combines a DateField and a Calendar popover.
|
|
125
|
-
*
|
|
126
|
-
* @example
|
|
127
|
-
* ```tsx
|
|
128
|
-
* <DatePicker label="Event date">
|
|
129
|
-
* <Label>Event date</Label>
|
|
130
|
-
* <Group>
|
|
131
|
-
* <DateInput>
|
|
132
|
-
* {(segment) => <DateSegment segment={segment} />}
|
|
133
|
-
* </DateInput>
|
|
134
|
-
* <DatePickerButton>📅</DatePickerButton>
|
|
135
|
-
* </Group>
|
|
136
|
-
* <Popover>
|
|
137
|
-
* <Dialog>
|
|
138
|
-
* <Calendar>
|
|
139
|
-
* <CalendarGrid>
|
|
140
|
-
* {(date) => <CalendarCell date={date} />}
|
|
141
|
-
* </CalendarGrid>
|
|
142
|
-
* </Calendar>
|
|
143
|
-
* </Dialog>
|
|
144
|
-
* </Popover>
|
|
145
|
-
* </DatePicker>
|
|
146
|
-
* ```
|
|
147
|
-
*/
|
|
148
|
-
export function DatePicker<T extends DateValue = CalendarDate>(
|
|
149
|
-
props: DatePickerProps<T>
|
|
150
|
-
): JSX.Element {
|
|
151
|
-
// Use hydration-safe pattern for client-only rendering
|
|
152
|
-
const isHydrated = useIsHydrated();
|
|
153
|
-
|
|
154
|
-
return (
|
|
155
|
-
<Show
|
|
156
|
-
when={isHydrated()}
|
|
157
|
-
fallback={<div class="solidaria-DatePicker solidaria-DatePicker--placeholder" aria-hidden="true" />}
|
|
158
|
-
>
|
|
159
|
-
<DatePickerInner {...props} />
|
|
160
|
-
</Show>
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Internal DatePicker component that renders after client mount.
|
|
166
|
-
*/
|
|
167
|
-
function DatePickerInner<T extends DateValue = CalendarDate>(
|
|
168
|
-
props: DatePickerProps<T>
|
|
169
|
-
): JSX.Element {
|
|
170
|
-
const [local, stateProps, rest] = splitProps(
|
|
171
|
-
props,
|
|
172
|
-
['children', 'class', 'style', 'slot', 'shouldCloseOnSelect'],
|
|
173
|
-
[
|
|
174
|
-
'value',
|
|
175
|
-
'defaultValue',
|
|
176
|
-
'onChange',
|
|
177
|
-
'minValue',
|
|
178
|
-
'maxValue',
|
|
179
|
-
'isDisabled',
|
|
180
|
-
'isReadOnly',
|
|
181
|
-
'isRequired',
|
|
182
|
-
'locale',
|
|
183
|
-
'granularity',
|
|
184
|
-
'hourCycle',
|
|
185
|
-
'hideTimeZone',
|
|
186
|
-
'placeholderValue',
|
|
187
|
-
'validationState',
|
|
188
|
-
'description',
|
|
189
|
-
'errorMessage',
|
|
190
|
-
]
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
// Create overlay trigger state
|
|
194
|
-
const [isOpen, setIsOpen] = createSignal(false);
|
|
195
|
-
|
|
196
|
-
const overlayState = {
|
|
197
|
-
get isOpen() { return isOpen(); },
|
|
198
|
-
open: () => setIsOpen(true),
|
|
199
|
-
close: () => setIsOpen(false),
|
|
200
|
-
toggle: () => setIsOpen((prev) => !prev),
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
// Create date field state
|
|
204
|
-
const fieldState = createDateFieldState({
|
|
205
|
-
...stateProps,
|
|
206
|
-
onChange: (value) => {
|
|
207
|
-
stateProps.onChange?.(value);
|
|
208
|
-
if (local.shouldCloseOnSelect !== false && value) {
|
|
209
|
-
overlayState.close();
|
|
210
|
-
}
|
|
211
|
-
},
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
// Create calendar state that syncs with field
|
|
215
|
-
const calendarState = createCalendarState({
|
|
216
|
-
value: () => fieldState.value(),
|
|
217
|
-
onChange: (value) => {
|
|
218
|
-
fieldState.setValue(value as T | null);
|
|
219
|
-
if (local.shouldCloseOnSelect !== false) {
|
|
220
|
-
overlayState.close();
|
|
221
|
-
}
|
|
222
|
-
},
|
|
223
|
-
minValue: stateProps.minValue,
|
|
224
|
-
maxValue: stateProps.maxValue,
|
|
225
|
-
isDisabled: stateProps.isDisabled,
|
|
226
|
-
isReadOnly: stateProps.isReadOnly,
|
|
227
|
-
locale: stateProps.locale,
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
// Create date picker ARIA props
|
|
231
|
-
const pickerAria = createDatePicker(
|
|
232
|
-
rest,
|
|
233
|
-
fieldState as unknown as DateFieldState<DateValue>,
|
|
234
|
-
overlayState as AriaDatePickerState,
|
|
235
|
-
calendarState as unknown as CalendarState<DateValue>
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
// Context value
|
|
239
|
-
const contextValue: DatePickerContextValue = {
|
|
240
|
-
fieldState: fieldState as unknown as DateFieldState<DateValue>,
|
|
241
|
-
calendarState: calendarState as unknown as CalendarState<DateValue>,
|
|
242
|
-
overlayState,
|
|
243
|
-
pickerAria,
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
// Render props values
|
|
247
|
-
const renderValues = createMemo<DatePickerRenderProps>(() => ({
|
|
248
|
-
isDisabled: fieldState.isDisabled(),
|
|
249
|
-
isReadOnly: fieldState.isReadOnly(),
|
|
250
|
-
isRequired: fieldState.isRequired(),
|
|
251
|
-
isInvalid: fieldState.isInvalid(),
|
|
252
|
-
isOpen: overlayState.isOpen,
|
|
253
|
-
}));
|
|
254
|
-
|
|
255
|
-
// Resolve render props
|
|
256
|
-
const renderProps = useRenderProps(
|
|
257
|
-
{
|
|
258
|
-
class: local.class,
|
|
259
|
-
style: local.style,
|
|
260
|
-
defaultClassName: 'solidaria-DatePicker',
|
|
261
|
-
},
|
|
262
|
-
renderValues
|
|
263
|
-
);
|
|
264
|
-
|
|
265
|
-
return (
|
|
266
|
-
<DatePickerContext.Provider value={contextValue}>
|
|
267
|
-
{/* Also provide DateFieldContext so DateInput/DateSegment work inside DatePicker */}
|
|
268
|
-
<DateFieldContext.Provider value={fieldState as unknown as DateFieldState<DateValue>}>
|
|
269
|
-
<div
|
|
270
|
-
{...pickerAria.groupProps}
|
|
271
|
-
class={renderProps.class()}
|
|
272
|
-
style={renderProps.style()}
|
|
273
|
-
data-disabled={dataAttr(fieldState.isDisabled())}
|
|
274
|
-
data-readonly={dataAttr(fieldState.isReadOnly())}
|
|
275
|
-
data-required={dataAttr(fieldState.isRequired())}
|
|
276
|
-
data-invalid={dataAttr(fieldState.isInvalid())}
|
|
277
|
-
data-open={dataAttr(overlayState.isOpen)}
|
|
278
|
-
>
|
|
279
|
-
{props.children}
|
|
280
|
-
</div>
|
|
281
|
-
</DateFieldContext.Provider>
|
|
282
|
-
</DatePickerContext.Provider>
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// ============================================
|
|
287
|
-
// DATE PICKER BUTTON COMPONENT
|
|
288
|
-
// ============================================
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* A button that opens the date picker calendar.
|
|
292
|
-
*/
|
|
293
|
-
export function DatePickerButton(props: DatePickerButtonProps): JSX.Element {
|
|
294
|
-
const context = useDatePickerContext();
|
|
295
|
-
|
|
296
|
-
// Render props values
|
|
297
|
-
const renderValues = createMemo<DatePickerButtonRenderProps>(() => ({
|
|
298
|
-
isDisabled: context.fieldState.isDisabled() || (props.isDisabled ?? false),
|
|
299
|
-
isOpen: context.overlayState.isOpen,
|
|
300
|
-
}));
|
|
301
|
-
|
|
302
|
-
// Resolve render props
|
|
303
|
-
const renderProps = useRenderProps(
|
|
304
|
-
{
|
|
305
|
-
children: props.children,
|
|
306
|
-
class: props.class,
|
|
307
|
-
style: props.style,
|
|
308
|
-
defaultClassName: 'solidaria-DatePickerButton',
|
|
309
|
-
},
|
|
310
|
-
renderValues
|
|
311
|
-
);
|
|
312
|
-
|
|
313
|
-
// Determine children content - avoid Show for SSR hydration compatibility
|
|
314
|
-
const getChildren = () => {
|
|
315
|
-
if (typeof props.children === 'function') {
|
|
316
|
-
return renderProps.renderChildren();
|
|
317
|
-
}
|
|
318
|
-
return props.children ?? '📅';
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
return (
|
|
322
|
-
<button
|
|
323
|
-
{...context.pickerAria.buttonProps}
|
|
324
|
-
class={renderProps.class()}
|
|
325
|
-
style={renderProps.style()}
|
|
326
|
-
disabled={context.fieldState.isDisabled() || props.isDisabled}
|
|
327
|
-
data-disabled={dataAttr(context.fieldState.isDisabled() || props.isDisabled)}
|
|
328
|
-
data-open={dataAttr(context.overlayState.isOpen)}
|
|
329
|
-
>
|
|
330
|
-
{getChildren()}
|
|
331
|
-
</button>
|
|
332
|
-
);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// ============================================
|
|
336
|
-
// DATE PICKER CONTENT COMPONENT
|
|
337
|
-
// ============================================
|
|
338
|
-
|
|
339
|
-
export interface DatePickerContentProps extends SlotProps {
|
|
340
|
-
/** The children of the component. */
|
|
341
|
-
children?: JSX.Element;
|
|
342
|
-
/** The CSS className for the element. */
|
|
343
|
-
class?: string;
|
|
344
|
-
/** The inline style for the element. */
|
|
345
|
-
style?: JSX.CSSProperties;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
/**
|
|
349
|
-
* The content area of the date picker (typically contains a Calendar).
|
|
350
|
-
*/
|
|
351
|
-
export function DatePickerContent(props: DatePickerContentProps): JSX.Element {
|
|
352
|
-
const context = useDatePickerContext();
|
|
353
|
-
|
|
354
|
-
return (
|
|
355
|
-
<Show when={context.overlayState.isOpen}>
|
|
356
|
-
<div
|
|
357
|
-
{...context.pickerAria.dialogProps}
|
|
358
|
-
class={props.class ?? 'solidaria-DatePickerContent'}
|
|
359
|
-
style={props.style}
|
|
360
|
-
>
|
|
361
|
-
{props.children}
|
|
362
|
-
</div>
|
|
363
|
-
</Show>
|
|
364
|
-
);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// DatePickerContextValue is already exported at declaration
|