@proyecto-viviana/solidaria-components 0.1.2 → 0.2.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/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.ssr.js +15 -15
- package/dist/index.ssr.js.map +2 -2
- package/package.json +8 -9
- 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/Button.tsx
DELETED
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Button component for solidaria-components
|
|
3
|
-
*
|
|
4
|
-
* A pre-wired headless button that combines state + aria hooks.
|
|
5
|
-
* Port of react-aria-components/src/Button.tsx
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
type JSX,
|
|
10
|
-
createContext,
|
|
11
|
-
createMemo,
|
|
12
|
-
splitProps,
|
|
13
|
-
useContext,
|
|
14
|
-
} from 'solid-js';
|
|
15
|
-
import {
|
|
16
|
-
createButton,
|
|
17
|
-
createFocusRing,
|
|
18
|
-
createHover,
|
|
19
|
-
type AriaButtonProps,
|
|
20
|
-
} from '@proyecto-viviana/solidaria';
|
|
21
|
-
import {
|
|
22
|
-
type RenderChildren,
|
|
23
|
-
type ClassNameOrFunction,
|
|
24
|
-
type StyleOrFunction,
|
|
25
|
-
type SlotProps,
|
|
26
|
-
useRenderProps,
|
|
27
|
-
filterDOMProps,
|
|
28
|
-
} from './utils';
|
|
29
|
-
import { DialogTriggerContext, PopoverTriggerContext } from './contexts';
|
|
30
|
-
|
|
31
|
-
// ============================================
|
|
32
|
-
// TYPES
|
|
33
|
-
// ============================================
|
|
34
|
-
|
|
35
|
-
export interface ButtonRenderProps {
|
|
36
|
-
/** Whether the button is currently hovered with a mouse. */
|
|
37
|
-
isHovered: boolean;
|
|
38
|
-
/** Whether the button is currently in a pressed state. */
|
|
39
|
-
isPressed: boolean;
|
|
40
|
-
/** Whether the button is focused, either via a mouse or keyboard. */
|
|
41
|
-
isFocused: boolean;
|
|
42
|
-
/** Whether the button is keyboard focused. */
|
|
43
|
-
isFocusVisible: boolean;
|
|
44
|
-
/** Whether the button is disabled. */
|
|
45
|
-
isDisabled: boolean;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface ButtonProps
|
|
49
|
-
extends Omit<AriaButtonProps, 'children'>,
|
|
50
|
-
SlotProps {
|
|
51
|
-
/** The children of the component. A function may be provided to receive render props. */
|
|
52
|
-
children?: RenderChildren<ButtonRenderProps>;
|
|
53
|
-
/** The CSS className for the element. */
|
|
54
|
-
class?: ClassNameOrFunction<ButtonRenderProps>;
|
|
55
|
-
/** The inline style for the element. */
|
|
56
|
-
style?: StyleOrFunction<ButtonRenderProps>;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// ============================================
|
|
60
|
-
// CONTEXT
|
|
61
|
-
// ============================================
|
|
62
|
-
|
|
63
|
-
export const ButtonContext = createContext<ButtonProps | null>(null);
|
|
64
|
-
|
|
65
|
-
// ============================================
|
|
66
|
-
// COMPONENT
|
|
67
|
-
// ============================================
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* A button allows a user to perform an action.
|
|
71
|
-
*
|
|
72
|
-
* This is a headless component that provides accessibility and state management.
|
|
73
|
-
* Style it using the render props pattern or data attributes.
|
|
74
|
-
*
|
|
75
|
-
* @example
|
|
76
|
-
* ```tsx
|
|
77
|
-
* <Button onPress={() => alert('Pressed!')}>
|
|
78
|
-
* {({ isPressed, isHovered }) => (
|
|
79
|
-
* <span class={isPressed ? 'bg-blue-700' : isHovered ? 'bg-blue-600' : 'bg-blue-500'}>
|
|
80
|
-
* Click me
|
|
81
|
-
* </span>
|
|
82
|
-
* )}
|
|
83
|
-
* </Button>
|
|
84
|
-
* ```
|
|
85
|
-
*/
|
|
86
|
-
export function Button(props: ButtonProps): JSX.Element {
|
|
87
|
-
// Split props
|
|
88
|
-
const [local, ariaProps] = splitProps(props, [
|
|
89
|
-
'children',
|
|
90
|
-
'class',
|
|
91
|
-
'style',
|
|
92
|
-
'slot',
|
|
93
|
-
]);
|
|
94
|
-
|
|
95
|
-
// Check if inside a DialogTrigger or PopoverTrigger - if so, toggle on press
|
|
96
|
-
// NOTE: Context is captured at component creation time. For Buttons inside a Modal,
|
|
97
|
-
// the Modal provides OverlayTriggerStateContext, but due to SolidJS's eager JSX evaluation,
|
|
98
|
-
// components inside Modal children are created before the Modal's Show renders.
|
|
99
|
-
// So we can't reliably use context here to determine if we're inside a Modal.
|
|
100
|
-
const dialogTriggerContext = useContext(DialogTriggerContext);
|
|
101
|
-
const popoverTriggerContext = useContext(PopoverTriggerContext);
|
|
102
|
-
|
|
103
|
-
// Helper to resolve isDisabled (handles both boolean and Accessor<boolean>)
|
|
104
|
-
const resolveDisabled = (): boolean => {
|
|
105
|
-
const disabled = ariaProps.isDisabled;
|
|
106
|
-
if (typeof disabled === 'function') {
|
|
107
|
-
return disabled();
|
|
108
|
-
}
|
|
109
|
-
return !!disabled;
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
// Determine if this button should act as a dialog/popover trigger
|
|
113
|
-
// We only toggle if:
|
|
114
|
-
// 1. We have DialogTriggerContext or PopoverTriggerContext (we're inside a trigger)
|
|
115
|
-
// 2. AND there is NO onPress handler (the trigger button typically has no onPress,
|
|
116
|
-
// while close buttons inside dialogs have onPress={close})
|
|
117
|
-
// This heuristic works because:
|
|
118
|
-
// - Trigger buttons: don't have onPress, should toggle
|
|
119
|
-
// - Close buttons: have onPress={close}, should NOT toggle (just call onPress)
|
|
120
|
-
const isDialogTrigger = () => dialogTriggerContext && !ariaProps.onPress;
|
|
121
|
-
const isPopoverTrigger = () => popoverTriggerContext && !ariaProps.onPress;
|
|
122
|
-
|
|
123
|
-
// Wrap onPress to also toggle dialog/popover if this is a trigger button
|
|
124
|
-
const handlePress = (e: any) => {
|
|
125
|
-
// Call original onPress if provided
|
|
126
|
-
if (typeof ariaProps.onPress === 'function') {
|
|
127
|
-
ariaProps.onPress(e);
|
|
128
|
-
}
|
|
129
|
-
// Toggle dialog only if this is a trigger button (has no onPress handler)
|
|
130
|
-
if (isDialogTrigger()) {
|
|
131
|
-
dialogTriggerContext!.state.toggle();
|
|
132
|
-
}
|
|
133
|
-
// Toggle popover only if this is a trigger button (has no onPress handler)
|
|
134
|
-
if (isPopoverTrigger()) {
|
|
135
|
-
popoverTriggerContext!.state.toggle();
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
// Create button aria props
|
|
140
|
-
const buttonAria = createButton({
|
|
141
|
-
...ariaProps,
|
|
142
|
-
onPress: handlePress,
|
|
143
|
-
get isDisabled() {
|
|
144
|
-
return resolveDisabled();
|
|
145
|
-
},
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
// Create focus ring
|
|
149
|
-
const { isFocused, isFocusVisible, focusProps } = createFocusRing();
|
|
150
|
-
|
|
151
|
-
// Create hover
|
|
152
|
-
const { isHovered, hoverProps } = createHover({
|
|
153
|
-
get isDisabled() {
|
|
154
|
-
return resolveDisabled();
|
|
155
|
-
},
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
// Render props values
|
|
159
|
-
const renderValues = createMemo<ButtonRenderProps>(() => ({
|
|
160
|
-
isHovered: isHovered(),
|
|
161
|
-
isPressed: buttonAria.isPressed(),
|
|
162
|
-
isFocused: isFocused(),
|
|
163
|
-
isFocusVisible: isFocusVisible(),
|
|
164
|
-
isDisabled: resolveDisabled(),
|
|
165
|
-
}));
|
|
166
|
-
|
|
167
|
-
// Resolve render props
|
|
168
|
-
const renderProps = useRenderProps(
|
|
169
|
-
{
|
|
170
|
-
children: props.children,
|
|
171
|
-
class: local.class,
|
|
172
|
-
style: local.style,
|
|
173
|
-
defaultClassName: 'solidaria-Button',
|
|
174
|
-
},
|
|
175
|
-
renderValues
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
// Filter DOM props
|
|
179
|
-
// Remove onClick from DOM props - it's already handled by createPress
|
|
180
|
-
// This matches React Aria Components behavior (Button.tsx line 144: delete DOMProps.onClick)
|
|
181
|
-
const domProps = createMemo(() => {
|
|
182
|
-
const filtered = filterDOMProps(ariaProps, { global: true });
|
|
183
|
-
// onClick is handled by createPress, not passed directly to DOM
|
|
184
|
-
delete (filtered as Record<string, unknown>).onClick;
|
|
185
|
-
return filtered;
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
// Extract refs from props to combine them manually
|
|
189
|
-
const buttonPropsRef = (buttonAria.buttonProps as Record<string, unknown>).ref as ((el: HTMLElement) => void) | undefined;
|
|
190
|
-
const focusPropsRef = (focusProps as Record<string, unknown>).ref as ((el: HTMLElement) => void) | undefined;
|
|
191
|
-
const hoverPropsRef = (hoverProps as Record<string, unknown>).ref as ((el: HTMLElement) => void) | undefined;
|
|
192
|
-
|
|
193
|
-
// Remove ref from spread props to avoid type conflicts
|
|
194
|
-
const cleanButtonProps = () => {
|
|
195
|
-
const { ref: _ref1, ...rest } = buttonAria.buttonProps as Record<string, unknown>;
|
|
196
|
-
return rest;
|
|
197
|
-
};
|
|
198
|
-
const cleanFocusProps = () => {
|
|
199
|
-
const { ref: _ref2, ...rest } = focusProps as Record<string, unknown>;
|
|
200
|
-
return rest;
|
|
201
|
-
};
|
|
202
|
-
const cleanHoverProps = () => {
|
|
203
|
-
const { ref: _ref3, ...rest } = hoverProps as Record<string, unknown>;
|
|
204
|
-
return rest;
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
// Ref callback that combines all refs
|
|
208
|
-
const handleRef = (el: HTMLButtonElement) => {
|
|
209
|
-
// Call the focusable ref for autoFocus support
|
|
210
|
-
buttonPropsRef?.(el);
|
|
211
|
-
focusPropsRef?.(el);
|
|
212
|
-
hoverPropsRef?.(el);
|
|
213
|
-
|
|
214
|
-
// If this button is a popover trigger, register it
|
|
215
|
-
if (isPopoverTrigger() && popoverTriggerContext?.setTriggerRef) {
|
|
216
|
-
popoverTriggerContext.setTriggerRef(el);
|
|
217
|
-
}
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
return (
|
|
221
|
-
<button
|
|
222
|
-
ref={handleRef}
|
|
223
|
-
{...domProps()}
|
|
224
|
-
{...cleanButtonProps()}
|
|
225
|
-
{...cleanFocusProps()}
|
|
226
|
-
{...cleanHoverProps()}
|
|
227
|
-
class={renderProps.class()}
|
|
228
|
-
style={renderProps.style()}
|
|
229
|
-
data-pressed={buttonAria.isPressed() || undefined}
|
|
230
|
-
data-hovered={isHovered() || undefined}
|
|
231
|
-
data-focused={isFocused() || undefined}
|
|
232
|
-
data-focus-visible={isFocusVisible() || undefined}
|
|
233
|
-
data-disabled={resolveDisabled() || undefined}
|
|
234
|
-
>
|
|
235
|
-
{renderProps.renderChildren()}
|
|
236
|
-
</button>
|
|
237
|
-
);
|
|
238
|
-
}
|
package/src/Calendar.tsx
DELETED
|
@@ -1,471 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Calendar component for solidaria-components
|
|
3
|
-
*
|
|
4
|
-
* Pre-wired headless calendar component that combines aria hooks.
|
|
5
|
-
* Port of react-aria-components/src/Calendar.tsx
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
type JSX,
|
|
10
|
-
createContext,
|
|
11
|
-
createMemo,
|
|
12
|
-
createSignal,
|
|
13
|
-
splitProps,
|
|
14
|
-
useContext,
|
|
15
|
-
For,
|
|
16
|
-
Index,
|
|
17
|
-
Show,
|
|
18
|
-
} from 'solid-js';
|
|
19
|
-
|
|
20
|
-
import {
|
|
21
|
-
createCalendar,
|
|
22
|
-
createCalendarGrid,
|
|
23
|
-
createCalendarCell,
|
|
24
|
-
type AriaCalendarProps,
|
|
25
|
-
type AriaCalendarGridProps,
|
|
26
|
-
} from '@proyecto-viviana/solidaria';
|
|
27
|
-
import {
|
|
28
|
-
createCalendarState,
|
|
29
|
-
type CalendarState,
|
|
30
|
-
type CalendarStateProps,
|
|
31
|
-
type CalendarDate,
|
|
32
|
-
type DateValue,
|
|
33
|
-
} from '@proyecto-viviana/solid-stately';
|
|
34
|
-
import {
|
|
35
|
-
type RenderChildren,
|
|
36
|
-
type ClassNameOrFunction,
|
|
37
|
-
type StyleOrFunction,
|
|
38
|
-
type SlotProps,
|
|
39
|
-
useRenderProps,
|
|
40
|
-
dataAttr,
|
|
41
|
-
useIsHydrated,
|
|
42
|
-
} from './utils';
|
|
43
|
-
|
|
44
|
-
// ============================================
|
|
45
|
-
// TYPES
|
|
46
|
-
// ============================================
|
|
47
|
-
|
|
48
|
-
export interface CalendarRenderProps {
|
|
49
|
-
/** Whether the calendar is disabled. */
|
|
50
|
-
isDisabled: boolean;
|
|
51
|
-
/** Whether the calendar is read-only. */
|
|
52
|
-
isReadOnly: boolean;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface CalendarProps<T extends DateValue = DateValue>
|
|
56
|
-
extends Omit<AriaCalendarProps, 'id' | 'isDisabled' | 'isReadOnly'>,
|
|
57
|
-
Omit<CalendarStateProps<T>, 'locale'>,
|
|
58
|
-
SlotProps {
|
|
59
|
-
/** The children of the component. */
|
|
60
|
-
children?: JSX.Element;
|
|
61
|
-
/** The CSS className for the element. */
|
|
62
|
-
class?: ClassNameOrFunction<CalendarRenderProps>;
|
|
63
|
-
/** The inline style for the element. */
|
|
64
|
-
style?: StyleOrFunction<CalendarRenderProps>;
|
|
65
|
-
/** The locale to use for formatting. */
|
|
66
|
-
locale?: string;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export interface CalendarGridRenderProps {
|
|
70
|
-
/** Whether the grid is disabled. */
|
|
71
|
-
isDisabled: boolean;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export interface CalendarGridProps extends Omit<AriaCalendarGridProps, 'startDate' | 'endDate'>, SlotProps {
|
|
75
|
-
/** The children of the component (render function receiving weeks). */
|
|
76
|
-
children?: (date: CalendarDate) => JSX.Element;
|
|
77
|
-
/** The CSS className for the element. */
|
|
78
|
-
class?: ClassNameOrFunction<CalendarGridRenderProps>;
|
|
79
|
-
/** The inline style for the element. */
|
|
80
|
-
style?: StyleOrFunction<CalendarGridRenderProps>;
|
|
81
|
-
/** Number of weeks to offset from the start. */
|
|
82
|
-
offset?: { months?: number };
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export interface CalendarCellRenderProps {
|
|
86
|
-
/** Whether the cell is selected. */
|
|
87
|
-
isSelected: boolean;
|
|
88
|
-
/** Whether the cell is focused. */
|
|
89
|
-
isFocused: boolean;
|
|
90
|
-
/** Whether the cell is disabled. */
|
|
91
|
-
isDisabled: boolean;
|
|
92
|
-
/** Whether the cell is unavailable. */
|
|
93
|
-
isUnavailable: boolean;
|
|
94
|
-
/** Whether the cell is outside the visible month. */
|
|
95
|
-
isOutsideMonth: boolean;
|
|
96
|
-
/** Whether the cell represents today. */
|
|
97
|
-
isToday: boolean;
|
|
98
|
-
/** Whether the cell is pressed. */
|
|
99
|
-
isPressed: boolean;
|
|
100
|
-
/** The formatted date string. */
|
|
101
|
-
formattedDate: string;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export interface CalendarCellProps extends SlotProps {
|
|
105
|
-
/** The date for this cell. */
|
|
106
|
-
date: CalendarDate;
|
|
107
|
-
/** The children of the component. A function may be provided to receive render props. */
|
|
108
|
-
children?: RenderChildren<CalendarCellRenderProps>;
|
|
109
|
-
/** The CSS className for the element. */
|
|
110
|
-
class?: ClassNameOrFunction<CalendarCellRenderProps>;
|
|
111
|
-
/** The inline style for the element. */
|
|
112
|
-
style?: StyleOrFunction<CalendarCellRenderProps>;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export interface CalendarHeaderCellProps extends SlotProps {
|
|
116
|
-
/** The children of the component. */
|
|
117
|
-
children?: JSX.Element;
|
|
118
|
-
/** The CSS className for the element. */
|
|
119
|
-
class?: string;
|
|
120
|
-
/** The inline style for the element. */
|
|
121
|
-
style?: JSX.CSSProperties;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// ============================================
|
|
125
|
-
// CONTEXT
|
|
126
|
-
// ============================================
|
|
127
|
-
|
|
128
|
-
export const CalendarContext = createContext<CalendarState<DateValue> | null>(null);
|
|
129
|
-
|
|
130
|
-
export function useCalendarContext(): CalendarState<DateValue> {
|
|
131
|
-
const context = useContext(CalendarContext);
|
|
132
|
-
if (!context) {
|
|
133
|
-
throw new Error('Calendar components must be used within a Calendar');
|
|
134
|
-
}
|
|
135
|
-
return context;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// ============================================
|
|
139
|
-
// CALENDAR COMPONENT
|
|
140
|
-
// ============================================
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* A calendar displays a grid of days in a month and allows users to select a single date.
|
|
144
|
-
*
|
|
145
|
-
* @example
|
|
146
|
-
* ```tsx
|
|
147
|
-
* <Calendar aria-label="Event date">
|
|
148
|
-
* <header>
|
|
149
|
-
* <CalendarButton slot="previous">◀</CalendarButton>
|
|
150
|
-
* <CalendarHeading />
|
|
151
|
-
* <CalendarButton slot="next">▶</CalendarButton>
|
|
152
|
-
* </header>
|
|
153
|
-
* <CalendarGrid>
|
|
154
|
-
* {(date) => <CalendarCell date={date} />}
|
|
155
|
-
* </CalendarGrid>
|
|
156
|
-
* </Calendar>
|
|
157
|
-
* ```
|
|
158
|
-
*/
|
|
159
|
-
export function Calendar<T extends DateValue = CalendarDate>(
|
|
160
|
-
props: CalendarProps<T>
|
|
161
|
-
): JSX.Element {
|
|
162
|
-
// Use hydration-safe pattern for client-only rendering
|
|
163
|
-
const isHydrated = useIsHydrated();
|
|
164
|
-
|
|
165
|
-
return (
|
|
166
|
-
<Show
|
|
167
|
-
when={isHydrated()}
|
|
168
|
-
fallback={<div class="solidaria-Calendar solidaria-Calendar--placeholder" aria-hidden="true" />}
|
|
169
|
-
>
|
|
170
|
-
<CalendarInner {...props} />
|
|
171
|
-
</Show>
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Internal Calendar component that renders after client mount.
|
|
177
|
-
*/
|
|
178
|
-
function CalendarInner<T extends DateValue = CalendarDate>(
|
|
179
|
-
props: CalendarProps<T>
|
|
180
|
-
): JSX.Element {
|
|
181
|
-
const [local, stateProps, rest] = splitProps(
|
|
182
|
-
props,
|
|
183
|
-
['children', 'class', 'style', 'slot'],
|
|
184
|
-
[
|
|
185
|
-
'value',
|
|
186
|
-
'defaultValue',
|
|
187
|
-
'onChange',
|
|
188
|
-
'minValue',
|
|
189
|
-
'maxValue',
|
|
190
|
-
'isDisabled',
|
|
191
|
-
'isReadOnly',
|
|
192
|
-
'autoFocus',
|
|
193
|
-
'focusedValue',
|
|
194
|
-
'defaultFocusedValue',
|
|
195
|
-
'onFocusChange',
|
|
196
|
-
'locale',
|
|
197
|
-
'isDateUnavailable',
|
|
198
|
-
'visibleMonths',
|
|
199
|
-
'isDateDisabled',
|
|
200
|
-
'validationState',
|
|
201
|
-
'errorMessage',
|
|
202
|
-
'firstDayOfWeek',
|
|
203
|
-
]
|
|
204
|
-
);
|
|
205
|
-
|
|
206
|
-
// Create calendar state
|
|
207
|
-
const state = createCalendarState(stateProps);
|
|
208
|
-
|
|
209
|
-
// Create calendar ARIA props
|
|
210
|
-
const calendarAria = createCalendar(rest, state as unknown as CalendarState<DateValue>);
|
|
211
|
-
|
|
212
|
-
// Render props values
|
|
213
|
-
const renderValues = createMemo<CalendarRenderProps>(() => ({
|
|
214
|
-
isDisabled: state.isDisabled(),
|
|
215
|
-
isReadOnly: state.isReadOnly(),
|
|
216
|
-
}));
|
|
217
|
-
|
|
218
|
-
// Resolve render props
|
|
219
|
-
const renderProps = useRenderProps(
|
|
220
|
-
{
|
|
221
|
-
class: local.class,
|
|
222
|
-
style: local.style,
|
|
223
|
-
defaultClassName: 'solidaria-Calendar',
|
|
224
|
-
},
|
|
225
|
-
renderValues
|
|
226
|
-
);
|
|
227
|
-
|
|
228
|
-
return (
|
|
229
|
-
<CalendarContext.Provider value={state as unknown as CalendarState<DateValue>}>
|
|
230
|
-
<div
|
|
231
|
-
{...calendarAria.calendarProps}
|
|
232
|
-
class={renderProps.class()}
|
|
233
|
-
style={renderProps.style()}
|
|
234
|
-
data-disabled={dataAttr(state.isDisabled())}
|
|
235
|
-
data-readonly={dataAttr(state.isReadOnly())}
|
|
236
|
-
>
|
|
237
|
-
{props.children}
|
|
238
|
-
</div>
|
|
239
|
-
</CalendarContext.Provider>
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// ============================================
|
|
244
|
-
// CALENDAR HEADING COMPONENT
|
|
245
|
-
// ============================================
|
|
246
|
-
|
|
247
|
-
export interface CalendarHeadingProps extends SlotProps {
|
|
248
|
-
/** The CSS className for the element. */
|
|
249
|
-
class?: string;
|
|
250
|
-
/** The inline style for the element. */
|
|
251
|
-
style?: JSX.CSSProperties;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Displays the current month and year in the calendar.
|
|
256
|
-
*/
|
|
257
|
-
export function CalendarHeading(props: CalendarHeadingProps): JSX.Element {
|
|
258
|
-
const state = useCalendarContext();
|
|
259
|
-
|
|
260
|
-
return (
|
|
261
|
-
<h2
|
|
262
|
-
class={props.class ?? 'solidaria-CalendarHeading'}
|
|
263
|
-
style={props.style}
|
|
264
|
-
aria-live="polite"
|
|
265
|
-
>
|
|
266
|
-
{state.title()}
|
|
267
|
-
</h2>
|
|
268
|
-
);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// ============================================
|
|
272
|
-
// CALENDAR BUTTON COMPONENT
|
|
273
|
-
// ============================================
|
|
274
|
-
|
|
275
|
-
export interface CalendarButtonProps extends SlotProps {
|
|
276
|
-
/** The slot for this button (previous or next). */
|
|
277
|
-
slot?: 'previous' | 'next';
|
|
278
|
-
/** The children of the component. */
|
|
279
|
-
children?: JSX.Element;
|
|
280
|
-
/** The CSS className for the element. */
|
|
281
|
-
class?: string;
|
|
282
|
-
/** The inline style for the element. */
|
|
283
|
-
style?: JSX.CSSProperties;
|
|
284
|
-
/** Whether the button is disabled. */
|
|
285
|
-
isDisabled?: boolean;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* A button for navigating the calendar.
|
|
290
|
-
*/
|
|
291
|
-
export function CalendarButton(props: CalendarButtonProps): JSX.Element {
|
|
292
|
-
const state = useCalendarContext();
|
|
293
|
-
const calendarAria = createCalendar({}, state);
|
|
294
|
-
|
|
295
|
-
const buttonProps = createMemo(() => {
|
|
296
|
-
if (props.slot === 'previous') {
|
|
297
|
-
return calendarAria.prevButtonProps;
|
|
298
|
-
}
|
|
299
|
-
return calendarAria.nextButtonProps;
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
return (
|
|
303
|
-
<button
|
|
304
|
-
{...buttonProps()}
|
|
305
|
-
class={props.class ?? 'solidaria-CalendarButton'}
|
|
306
|
-
style={props.style}
|
|
307
|
-
disabled={props.isDisabled || state.isDisabled()}
|
|
308
|
-
>
|
|
309
|
-
{props.children}
|
|
310
|
-
</button>
|
|
311
|
-
);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// ============================================
|
|
315
|
-
// CALENDAR GRID COMPONENT
|
|
316
|
-
// ============================================
|
|
317
|
-
|
|
318
|
-
/**
|
|
319
|
-
* Displays a grid of calendar cells.
|
|
320
|
-
*/
|
|
321
|
-
export function CalendarGrid(props: CalendarGridProps): JSX.Element {
|
|
322
|
-
const state = useCalendarContext();
|
|
323
|
-
const [gridRef, setGridRef] = createSignal<HTMLTableElement | null>(null);
|
|
324
|
-
|
|
325
|
-
// Create grid ARIA props
|
|
326
|
-
const gridAria = createCalendarGrid(
|
|
327
|
-
{
|
|
328
|
-
weekdayStyle: props.weekdayStyle,
|
|
329
|
-
},
|
|
330
|
-
state,
|
|
331
|
-
gridRef
|
|
332
|
-
);
|
|
333
|
-
|
|
334
|
-
// Render props values
|
|
335
|
-
const renderValues = createMemo<CalendarGridRenderProps>(() => ({
|
|
336
|
-
isDisabled: state.isDisabled(),
|
|
337
|
-
}));
|
|
338
|
-
|
|
339
|
-
// Resolve render props
|
|
340
|
-
const renderProps = useRenderProps(
|
|
341
|
-
{
|
|
342
|
-
class: props.class,
|
|
343
|
-
style: props.style,
|
|
344
|
-
defaultClassName: 'solidaria-CalendarGrid',
|
|
345
|
-
},
|
|
346
|
-
renderValues
|
|
347
|
-
);
|
|
348
|
-
|
|
349
|
-
// Memoize ALL dates for the grid at once to avoid reactive loops.
|
|
350
|
-
// This breaks the cycle where accessing visibleRange() inside For loop
|
|
351
|
-
// would cause infinite re-renders.
|
|
352
|
-
const allDates = createMemo(() => {
|
|
353
|
-
const numWeeks = state.getWeeksInMonth();
|
|
354
|
-
const weekDates: (CalendarDate | null)[][] = [];
|
|
355
|
-
|
|
356
|
-
for (let weekIndex = 0; weekIndex < numWeeks; weekIndex++) {
|
|
357
|
-
weekDates.push(state.getDatesInWeek(weekIndex));
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
return weekDates;
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
return (
|
|
364
|
-
<table
|
|
365
|
-
ref={setGridRef}
|
|
366
|
-
{...gridAria.gridProps}
|
|
367
|
-
class={renderProps.class()}
|
|
368
|
-
style={renderProps.style()}
|
|
369
|
-
>
|
|
370
|
-
<thead {...gridAria.headerProps}>
|
|
371
|
-
<tr>
|
|
372
|
-
<For each={gridAria.weekDays}>
|
|
373
|
-
{(day) => (
|
|
374
|
-
<th scope="col" class="solidaria-CalendarHeaderCell">
|
|
375
|
-
{day}
|
|
376
|
-
</th>
|
|
377
|
-
)}
|
|
378
|
-
</For>
|
|
379
|
-
</tr>
|
|
380
|
-
</thead>
|
|
381
|
-
<tbody>
|
|
382
|
-
<Index each={allDates()}>
|
|
383
|
-
{(weekDates) => (
|
|
384
|
-
<tr>
|
|
385
|
-
<Index each={weekDates()}>
|
|
386
|
-
{(date) => (
|
|
387
|
-
<Show when={date()}>
|
|
388
|
-
<td role="gridcell">
|
|
389
|
-
{props.children?.(date()!)}
|
|
390
|
-
</td>
|
|
391
|
-
</Show>
|
|
392
|
-
)}
|
|
393
|
-
</Index>
|
|
394
|
-
</tr>
|
|
395
|
-
)}
|
|
396
|
-
</Index>
|
|
397
|
-
</tbody>
|
|
398
|
-
</table>
|
|
399
|
-
);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// ============================================
|
|
403
|
-
// CALENDAR CELL COMPONENT
|
|
404
|
-
// ============================================
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* A cell in the calendar grid representing a single day.
|
|
408
|
-
*/
|
|
409
|
-
export function CalendarCell(props: CalendarCellProps): JSX.Element {
|
|
410
|
-
const state = useCalendarContext();
|
|
411
|
-
const [cellRef, setCellRef] = createSignal<HTMLDivElement | null>(null);
|
|
412
|
-
|
|
413
|
-
// Create cell ARIA props
|
|
414
|
-
const cellAria = createCalendarCell(
|
|
415
|
-
{ date: props.date },
|
|
416
|
-
state,
|
|
417
|
-
cellRef
|
|
418
|
-
);
|
|
419
|
-
|
|
420
|
-
// Render props values
|
|
421
|
-
const renderValues = createMemo<CalendarCellRenderProps>(() => ({
|
|
422
|
-
isSelected: cellAria.isSelected,
|
|
423
|
-
isFocused: cellAria.isFocused,
|
|
424
|
-
isDisabled: cellAria.isDisabled,
|
|
425
|
-
isUnavailable: cellAria.isUnavailable,
|
|
426
|
-
isOutsideMonth: cellAria.isOutsideMonth,
|
|
427
|
-
isToday: cellAria.isToday,
|
|
428
|
-
isPressed: cellAria.isPressed,
|
|
429
|
-
formattedDate: cellAria.formattedDate,
|
|
430
|
-
}));
|
|
431
|
-
|
|
432
|
-
// Resolve render props
|
|
433
|
-
const renderProps = useRenderProps(
|
|
434
|
-
{
|
|
435
|
-
children: props.children,
|
|
436
|
-
class: props.class,
|
|
437
|
-
style: props.style,
|
|
438
|
-
defaultClassName: 'solidaria-CalendarCell',
|
|
439
|
-
},
|
|
440
|
-
renderValues
|
|
441
|
-
);
|
|
442
|
-
|
|
443
|
-
// Determine children content - avoid Show for SSR hydration compatibility
|
|
444
|
-
const getChildren = () => {
|
|
445
|
-
if (typeof props.children === 'function') {
|
|
446
|
-
return renderProps.renderChildren();
|
|
447
|
-
}
|
|
448
|
-
return cellAria.formattedDate;
|
|
449
|
-
};
|
|
450
|
-
|
|
451
|
-
return (
|
|
452
|
-
<div
|
|
453
|
-
ref={setCellRef}
|
|
454
|
-
{...cellAria.buttonProps}
|
|
455
|
-
class={renderProps.class()}
|
|
456
|
-
style={renderProps.style()}
|
|
457
|
-
data-selected={dataAttr(cellAria.isSelected)}
|
|
458
|
-
data-focused={dataAttr(cellAria.isFocused)}
|
|
459
|
-
data-disabled={dataAttr(cellAria.isDisabled)}
|
|
460
|
-
data-unavailable={dataAttr(cellAria.isUnavailable)}
|
|
461
|
-
data-outside-month={dataAttr(cellAria.isOutsideMonth)}
|
|
462
|
-
data-today={dataAttr(cellAria.isToday)}
|
|
463
|
-
data-pressed={dataAttr(cellAria.isPressed)}
|
|
464
|
-
>
|
|
465
|
-
{getChildren()}
|
|
466
|
-
</div>
|
|
467
|
-
);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// Re-export types
|
|
471
|
-
export type { CalendarState, CalendarDate, DateValue };
|