@proyecto-viviana/ui 0.1.7 → 0.2.0

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.
Files changed (130) hide show
  1. package/README.md +192 -0
  2. package/dist/autocomplete/index.d.ts +89 -0
  3. package/dist/autocomplete/index.d.ts.map +1 -0
  4. package/dist/breadcrumbs/index.d.ts +38 -0
  5. package/dist/breadcrumbs/index.d.ts.map +1 -0
  6. package/dist/button/Button.d.ts.map +1 -1
  7. package/dist/calendar/DateField.d.ts +47 -0
  8. package/dist/calendar/DateField.d.ts.map +1 -0
  9. package/dist/calendar/DatePicker.d.ts +48 -0
  10. package/dist/calendar/DatePicker.d.ts.map +1 -0
  11. package/dist/calendar/RangeCalendar.d.ts +42 -0
  12. package/dist/calendar/RangeCalendar.d.ts.map +1 -0
  13. package/dist/calendar/TimeField.d.ts +44 -0
  14. package/dist/calendar/TimeField.d.ts.map +1 -0
  15. package/dist/calendar/index.d.ts +50 -0
  16. package/dist/calendar/index.d.ts.map +1 -0
  17. package/dist/checkbox/index.d.ts.map +1 -1
  18. package/dist/color/index.d.ts +228 -0
  19. package/dist/color/index.d.ts.map +1 -0
  20. package/dist/combobox/index.d.ts +81 -0
  21. package/dist/combobox/index.d.ts.map +1 -0
  22. package/dist/components.css +116 -14
  23. package/dist/custom/chip/index.d.ts +7 -2
  24. package/dist/custom/chip/index.d.ts.map +1 -1
  25. package/dist/custom/event-card/index.d.ts +5 -1
  26. package/dist/custom/event-card/index.d.ts.map +1 -1
  27. package/dist/custom/header/index.d.ts +16 -0
  28. package/dist/custom/header/index.d.ts.map +1 -0
  29. package/dist/custom/logo/index.d.ts +2 -0
  30. package/dist/custom/logo/index.d.ts.map +1 -1
  31. package/dist/custom/page-layout/index.d.ts +2 -0
  32. package/dist/custom/page-layout/index.d.ts.map +1 -1
  33. package/dist/custom/profile-card/index.d.ts +5 -1
  34. package/dist/custom/profile-card/index.d.ts.map +1 -1
  35. package/dist/custom/timeline-item/index.d.ts +12 -2
  36. package/dist/custom/timeline-item/index.d.ts.map +1 -1
  37. package/dist/dialog/Dialog.d.ts +67 -0
  38. package/dist/dialog/Dialog.d.ts.map +1 -0
  39. package/dist/dialog/index.d.ts +2 -17
  40. package/dist/dialog/index.d.ts.map +1 -1
  41. package/dist/disclosure/index.d.ts +84 -0
  42. package/dist/disclosure/index.d.ts.map +1 -0
  43. package/dist/gridlist/index.d.ts +92 -0
  44. package/dist/gridlist/index.d.ts.map +1 -0
  45. package/dist/index.d.ts +58 -4
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +6984 -783
  48. package/dist/index.js.map +1 -1
  49. package/dist/index.ssr.js +5905 -571
  50. package/dist/index.ssr.js.map +1 -1
  51. package/dist/landmark/index.d.ts +83 -0
  52. package/dist/landmark/index.d.ts.map +1 -0
  53. package/dist/link/index.d.ts.map +1 -1
  54. package/dist/listbox/index.d.ts +47 -0
  55. package/dist/listbox/index.d.ts.map +1 -0
  56. package/dist/menu/index.d.ts +74 -0
  57. package/dist/menu/index.d.ts.map +1 -0
  58. package/dist/meter/index.d.ts +49 -0
  59. package/dist/meter/index.d.ts.map +1 -0
  60. package/dist/numberfield/index.d.ts +50 -0
  61. package/dist/numberfield/index.d.ts.map +1 -0
  62. package/dist/popover/index.d.ts +85 -0
  63. package/dist/popover/index.d.ts.map +1 -0
  64. package/dist/radio/index.d.ts +7 -4
  65. package/dist/radio/index.d.ts.map +1 -1
  66. package/dist/searchfield/index.d.ts +44 -0
  67. package/dist/searchfield/index.d.ts.map +1 -0
  68. package/dist/select/index.d.ts +72 -0
  69. package/dist/select/index.d.ts.map +1 -0
  70. package/dist/slider/index.d.ts +53 -0
  71. package/dist/slider/index.d.ts.map +1 -0
  72. package/dist/switch/ToggleSwitch.d.ts.map +1 -1
  73. package/dist/table/index.d.ts +140 -0
  74. package/dist/table/index.d.ts.map +1 -0
  75. package/dist/tabs/index.d.ts +56 -0
  76. package/dist/tabs/index.d.ts.map +1 -0
  77. package/dist/tag-group/index.d.ts +80 -0
  78. package/dist/tag-group/index.d.ts.map +1 -0
  79. package/dist/toast/index.d.ts +101 -0
  80. package/dist/toast/index.d.ts.map +1 -0
  81. package/dist/toolbar/index.d.ts +42 -0
  82. package/dist/toolbar/index.d.ts.map +1 -0
  83. package/dist/tooltip/index.d.ts +66 -5
  84. package/dist/tooltip/index.d.ts.map +1 -1
  85. package/dist/tree/index.d.ts +99 -0
  86. package/dist/tree/index.d.ts.map +1 -0
  87. package/package.json +66 -58
  88. package/src/autocomplete/index.tsx +313 -0
  89. package/src/breadcrumbs/index.tsx +207 -0
  90. package/src/button/Button.tsx +74 -75
  91. package/src/calendar/DateField.tsx +200 -0
  92. package/src/calendar/DatePicker.tsx +298 -0
  93. package/src/calendar/RangeCalendar.tsx +236 -0
  94. package/src/calendar/TimeField.tsx +196 -0
  95. package/src/calendar/index.tsx +223 -0
  96. package/src/checkbox/index.tsx +3 -4
  97. package/src/color/index.tsx +687 -0
  98. package/src/combobox/index.tsx +383 -0
  99. package/src/components.css +116 -14
  100. package/src/custom/chip/index.tsx +17 -3
  101. package/src/custom/event-card/index.tsx +8 -2
  102. package/src/custom/header/index.tsx +33 -0
  103. package/src/custom/logo/index.tsx +7 -3
  104. package/src/custom/page-layout/index.tsx +12 -3
  105. package/src/custom/profile-card/index.tsx +8 -2
  106. package/src/custom/timeline-item/index.tsx +28 -4
  107. package/src/dialog/Dialog.tsx +260 -0
  108. package/src/dialog/index.tsx +3 -69
  109. package/src/disclosure/index.tsx +307 -0
  110. package/src/gridlist/index.tsx +403 -0
  111. package/src/index.ts +219 -4
  112. package/src/landmark/index.tsx +231 -0
  113. package/src/link/index.tsx +1 -2
  114. package/src/listbox/index.tsx +231 -0
  115. package/src/menu/index.tsx +297 -0
  116. package/src/meter/index.tsx +163 -0
  117. package/src/numberfield/index.tsx +482 -0
  118. package/src/popover/index.tsx +260 -0
  119. package/src/radio/index.tsx +36 -82
  120. package/src/searchfield/index.tsx +453 -0
  121. package/src/select/index.tsx +349 -0
  122. package/src/slider/index.tsx +382 -0
  123. package/src/switch/ToggleSwitch.tsx +1 -2
  124. package/src/table/index.tsx +531 -0
  125. package/src/tabs/index.tsx +273 -0
  126. package/src/tag-group/index.tsx +240 -0
  127. package/src/toast/index.tsx +324 -0
  128. package/src/toolbar/index.tsx +108 -0
  129. package/src/tooltip/index.tsx +171 -5
  130. package/src/tree/index.tsx +494 -0
@@ -0,0 +1,236 @@
1
+ /**
2
+ * RangeCalendar component for proyecto-viviana-ui
3
+ *
4
+ * Styled range calendar component built on top of solidaria-components.
5
+ * A range calendar displays a grid of days and allows users to select a date range.
6
+ */
7
+
8
+ import { type JSX, splitProps } from 'solid-js';
9
+ import {
10
+ RangeCalendar as HeadlessRangeCalendar,
11
+ RangeCalendarHeading,
12
+ RangeCalendarButton,
13
+ RangeCalendarGrid,
14
+ RangeCalendarCell,
15
+ type CalendarDate,
16
+ type DateValue,
17
+ type RangeValue,
18
+ } from '@proyecto-viviana/solidaria-components';
19
+ import type { RangeCalendarStateProps } from '@proyecto-viviana/solid-stately';
20
+
21
+ // ============================================
22
+ // TYPES
23
+ // ============================================
24
+
25
+ export type RangeCalendarSize = 'sm' | 'md' | 'lg';
26
+
27
+ export interface RangeCalendarProps<T extends DateValue = DateValue>
28
+ extends Omit<RangeCalendarStateProps<T>, 'locale'> {
29
+ /** The size of the calendar. @default 'md' */
30
+ size?: RangeCalendarSize;
31
+ /** Additional CSS class name. */
32
+ class?: string;
33
+ /** The locale to use for formatting. */
34
+ locale?: string;
35
+ /** Custom aria label. */
36
+ 'aria-label'?: string;
37
+ }
38
+
39
+ // ============================================
40
+ // STYLES
41
+ // ============================================
42
+
43
+ const sizeStyles = {
44
+ sm: {
45
+ container: 'w-64',
46
+ header: 'text-sm',
47
+ cell: 'w-8 h-8 text-xs',
48
+ button: 'w-6 h-6',
49
+ },
50
+ md: {
51
+ container: 'w-80',
52
+ header: 'text-base',
53
+ cell: 'w-10 h-10 text-sm',
54
+ button: 'w-8 h-8',
55
+ },
56
+ lg: {
57
+ container: 'w-96',
58
+ header: 'text-lg',
59
+ cell: 'w-12 h-12 text-base',
60
+ button: 'w-10 h-10',
61
+ },
62
+ };
63
+
64
+ // ============================================
65
+ // RANGE CALENDAR COMPONENT
66
+ // ============================================
67
+
68
+ /**
69
+ * A range calendar displays a grid of days and allows users to select a date range.
70
+ *
71
+ * @example
72
+ * ```tsx
73
+ * // Basic usage
74
+ * <RangeCalendar
75
+ * aria-label="Trip dates"
76
+ * onChange={(range) => console.log(range)}
77
+ * />
78
+ *
79
+ * // Controlled
80
+ * const [range, setRange] = createSignal<RangeValue<CalendarDate> | null>(null);
81
+ * <RangeCalendar
82
+ * value={range()}
83
+ * onChange={setRange}
84
+ * />
85
+ * ```
86
+ */
87
+ export function RangeCalendar<T extends DateValue = CalendarDate>(
88
+ props: RangeCalendarProps<T>
89
+ ): JSX.Element {
90
+ const [local, rest] = splitProps(props, [
91
+ 'size',
92
+ 'class',
93
+ 'aria-label',
94
+ ]);
95
+
96
+ const size = () => local.size ?? 'md';
97
+ const sizeConfig = () => sizeStyles[size()];
98
+
99
+ return (
100
+ <HeadlessRangeCalendar
101
+ {...rest}
102
+ aria-label={local['aria-label']}
103
+ class={`
104
+ ${sizeConfig().container}
105
+ bg-bg-500 rounded-lg border border-primary-700 p-4
106
+ ${local.class ?? ''}
107
+ `}
108
+ >
109
+ {/* Header with navigation */}
110
+ <header class="flex items-center justify-between mb-4">
111
+ <RangeCalendarButton
112
+ slot="previous"
113
+ class={`
114
+ ${sizeConfig().button}
115
+ flex items-center justify-center
116
+ rounded-md text-primary-200
117
+ hover:bg-bg-400 transition-colors
118
+ disabled:opacity-50 disabled:cursor-not-allowed
119
+ focus:outline-none focus:ring-2 focus:ring-accent/50
120
+ `}
121
+ >
122
+ <svg
123
+ viewBox="0 0 24 24"
124
+ fill="none"
125
+ stroke="currentColor"
126
+ stroke-width="2"
127
+ stroke-linecap="round"
128
+ stroke-linejoin="round"
129
+ class="w-4 h-4"
130
+ >
131
+ <polyline points="15 18 9 12 15 6" />
132
+ </svg>
133
+ </RangeCalendarButton>
134
+
135
+ <RangeCalendarHeading
136
+ class={`
137
+ font-semibold text-primary-100
138
+ ${sizeConfig().header}
139
+ `}
140
+ />
141
+
142
+ <RangeCalendarButton
143
+ slot="next"
144
+ class={`
145
+ ${sizeConfig().button}
146
+ flex items-center justify-center
147
+ rounded-md text-primary-200
148
+ hover:bg-bg-400 transition-colors
149
+ disabled:opacity-50 disabled:cursor-not-allowed
150
+ focus:outline-none focus:ring-2 focus:ring-accent/50
151
+ `}
152
+ >
153
+ <svg
154
+ viewBox="0 0 24 24"
155
+ fill="none"
156
+ stroke="currentColor"
157
+ stroke-width="2"
158
+ stroke-linecap="round"
159
+ stroke-linejoin="round"
160
+ class="w-4 h-4"
161
+ >
162
+ <polyline points="9 18 15 12 9 6" />
163
+ </svg>
164
+ </RangeCalendarButton>
165
+ </header>
166
+
167
+ {/* Calendar grid */}
168
+ <RangeCalendarGrid
169
+ class="w-full border-collapse"
170
+ >
171
+ {(date) => (
172
+ <RangeCalendarCell
173
+ date={date}
174
+ class={({
175
+ isSelected,
176
+ isSelectionStart,
177
+ isSelectionEnd,
178
+ isFocused,
179
+ isDisabled,
180
+ isOutsideMonth,
181
+ isToday,
182
+ isPressed,
183
+ }) => {
184
+ const base = `
185
+ ${sizeConfig().cell}
186
+ flex items-center justify-center
187
+ cursor-pointer
188
+ transition-colors duration-150
189
+ focus:outline-none
190
+ `;
191
+
192
+ let stateClass = '';
193
+ let roundedClass = 'rounded-md';
194
+
195
+ if (isDisabled) {
196
+ stateClass = 'text-primary-600 cursor-not-allowed';
197
+ } else if (isSelectionStart && isSelectionEnd) {
198
+ // Single day selection
199
+ stateClass = 'bg-accent text-white font-medium';
200
+ roundedClass = 'rounded-md';
201
+ } else if (isSelectionStart) {
202
+ stateClass = 'bg-accent text-white font-medium';
203
+ roundedClass = 'rounded-l-md rounded-r-none';
204
+ } else if (isSelectionEnd) {
205
+ stateClass = 'bg-accent text-white font-medium';
206
+ roundedClass = 'rounded-r-md rounded-l-none';
207
+ } else if (isSelected) {
208
+ stateClass = 'bg-accent/20 text-primary-100';
209
+ roundedClass = 'rounded-none';
210
+ } else if (isOutsideMonth) {
211
+ stateClass = 'text-primary-600';
212
+ } else if (isToday) {
213
+ stateClass = 'ring-1 ring-accent text-primary-100';
214
+ } else {
215
+ stateClass = 'text-primary-200 hover:bg-bg-400';
216
+ }
217
+
218
+ const focusClass = isFocused && !isSelected
219
+ ? 'ring-2 ring-accent/50'
220
+ : '';
221
+
222
+ const pressedClass = isPressed && !isDisabled
223
+ ? 'scale-95'
224
+ : '';
225
+
226
+ return `${base} ${stateClass} ${roundedClass} ${focusClass} ${pressedClass}`.trim();
227
+ }}
228
+ />
229
+ )}
230
+ </RangeCalendarGrid>
231
+ </HeadlessRangeCalendar>
232
+ );
233
+ }
234
+
235
+ // Re-export types
236
+ export type { RangeValue };
@@ -0,0 +1,196 @@
1
+ /**
2
+ * TimeField component for proyecto-viviana-ui
3
+ *
4
+ * Styled time field component with segment-based editing.
5
+ */
6
+
7
+ import { type JSX, splitProps } from 'solid-js';
8
+ import {
9
+ TimeField as HeadlessTimeField,
10
+ TimeInput,
11
+ TimeSegment,
12
+ type TimeFieldProps as HeadlessTimeFieldProps,
13
+ type TimeValue,
14
+ } from '@proyecto-viviana/solidaria-components';
15
+
16
+ // ============================================
17
+ // TYPES
18
+ // ============================================
19
+
20
+ export type TimeFieldSize = 'sm' | 'md' | 'lg';
21
+
22
+ export interface TimeFieldProps<T extends TimeValue = TimeValue>
23
+ extends Omit<HeadlessTimeFieldProps<T>, 'class' | 'style' | 'children'> {
24
+ /** The size of the field. @default 'md' */
25
+ size?: TimeFieldSize;
26
+ /** Additional CSS class name. */
27
+ class?: string;
28
+ /** Label for the field. */
29
+ label?: string;
30
+ /** Description text. */
31
+ description?: string;
32
+ /** Error message. */
33
+ errorMessage?: string;
34
+ }
35
+
36
+ // ============================================
37
+ // STYLES
38
+ // ============================================
39
+
40
+ const sizeStyles = {
41
+ sm: {
42
+ container: 'text-sm',
43
+ input: 'px-2 py-1 gap-0.5',
44
+ segment: 'px-0.5',
45
+ label: 'text-xs',
46
+ },
47
+ md: {
48
+ container: 'text-base',
49
+ input: 'px-3 py-2 gap-1',
50
+ segment: 'px-1',
51
+ label: 'text-sm',
52
+ },
53
+ lg: {
54
+ container: 'text-lg',
55
+ input: 'px-4 py-3 gap-1.5',
56
+ segment: 'px-1.5',
57
+ label: 'text-base',
58
+ },
59
+ };
60
+
61
+ // ============================================
62
+ // TIME FIELD COMPONENT
63
+ // ============================================
64
+
65
+ /**
66
+ * A time field allows users to enter and edit time values using a keyboard.
67
+ *
68
+ * @example
69
+ * ```tsx
70
+ * // Basic usage
71
+ * <TimeField label="Start time" />
72
+ *
73
+ * // With 24-hour format
74
+ * <TimeField
75
+ * label="Meeting time"
76
+ * hourCycle={24}
77
+ * />
78
+ *
79
+ * // With seconds
80
+ * <TimeField
81
+ * label="Precise time"
82
+ * granularity="second"
83
+ * />
84
+ * ```
85
+ */
86
+ export function TimeField<T extends TimeValue = TimeValue>(
87
+ props: TimeFieldProps<T>
88
+ ): JSX.Element {
89
+ const [local, rest] = splitProps(props, [
90
+ 'size',
91
+ 'class',
92
+ 'label',
93
+ 'description',
94
+ 'errorMessage',
95
+ 'isInvalid',
96
+ ]);
97
+
98
+ const size = () => local.size ?? 'md';
99
+ const sizeConfig = () => sizeStyles[size()];
100
+ const isInvalid = () => local.isInvalid || !!local.errorMessage;
101
+
102
+ return (
103
+ <HeadlessTimeField
104
+ {...rest}
105
+ isInvalid={isInvalid()}
106
+ class={`
107
+ flex flex-col gap-1
108
+ ${sizeConfig().container}
109
+ ${local.class ?? ''}
110
+ `}
111
+ >
112
+ {/* Label */}
113
+ {local.label && (
114
+ <label class={`font-medium text-primary-200 ${sizeConfig().label}`}>
115
+ {local.label}
116
+ {rest.isRequired && <span class="text-red-500 ml-0.5">*</span>}
117
+ </label>
118
+ )}
119
+
120
+ {/* Input container */}
121
+ <TimeInput
122
+ class={({ isFocused, isDisabled }) => {
123
+ const base = `
124
+ inline-flex items-center
125
+ ${sizeConfig().input}
126
+ bg-bg-400 rounded-md border
127
+ transition-colors duration-150
128
+ `;
129
+
130
+ let borderClass = 'border-primary-600';
131
+ if (isInvalid()) {
132
+ borderClass = 'border-red-500';
133
+ } else if (isFocused) {
134
+ borderClass = 'border-accent';
135
+ }
136
+
137
+ const disabledClass = isDisabled
138
+ ? 'opacity-50 cursor-not-allowed'
139
+ : '';
140
+
141
+ const focusClass = isFocused
142
+ ? 'ring-2 ring-accent/30'
143
+ : '';
144
+
145
+ return `${base} ${borderClass} ${disabledClass} ${focusClass}`.trim();
146
+ }}
147
+ >
148
+ {(segment) => (
149
+ <TimeSegment
150
+ segment={segment}
151
+ class={({ isFocused, isPlaceholder, isEditable }) => {
152
+ const base = `
153
+ ${sizeConfig().segment}
154
+ rounded
155
+ outline-none
156
+ tabular-nums
157
+ `;
158
+
159
+ let stateClass = '';
160
+ if (segment.type === 'literal') {
161
+ stateClass = 'text-primary-400';
162
+ } else if (isPlaceholder) {
163
+ stateClass = 'text-primary-500 italic';
164
+ } else {
165
+ stateClass = 'text-primary-100';
166
+ }
167
+
168
+ const focusClass = isFocused && isEditable
169
+ ? 'bg-accent text-white'
170
+ : '';
171
+
172
+ return `${base} ${stateClass} ${focusClass}`.trim();
173
+ }}
174
+ />
175
+ )}
176
+ </TimeInput>
177
+
178
+ {/* Description */}
179
+ {local.description && !isInvalid() && (
180
+ <p class={`text-primary-400 ${sizeConfig().label}`}>
181
+ {local.description}
182
+ </p>
183
+ )}
184
+
185
+ {/* Error message */}
186
+ {isInvalid() && local.errorMessage && (
187
+ <p class={`text-red-500 ${sizeConfig().label}`}>
188
+ {local.errorMessage}
189
+ </p>
190
+ )}
191
+ </HeadlessTimeField>
192
+ );
193
+ }
194
+
195
+ // Re-export types
196
+ export type { TimeValue };
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Calendar component for proyecto-viviana-ui
3
+ *
4
+ * Styled calendar component built on top of solidaria-components.
5
+ * A calendar displays a grid of days and allows users to select dates.
6
+ */
7
+
8
+ import { type JSX, splitProps } from 'solid-js';
9
+ import {
10
+ Calendar as HeadlessCalendar,
11
+ CalendarHeading,
12
+ CalendarButton,
13
+ CalendarGrid,
14
+ CalendarCell,
15
+ type CalendarDate,
16
+ type DateValue,
17
+ } from '@proyecto-viviana/solidaria-components';
18
+ import type { CalendarStateProps } from '@proyecto-viviana/solid-stately';
19
+
20
+ // ============================================
21
+ // TYPES
22
+ // ============================================
23
+
24
+ export type CalendarSize = 'sm' | 'md' | 'lg';
25
+
26
+ export interface CalendarProps<T extends DateValue = DateValue>
27
+ extends Omit<CalendarStateProps<T>, 'locale'> {
28
+ /** The size of the calendar. @default 'md' */
29
+ size?: CalendarSize;
30
+ /** Additional CSS class name. */
31
+ class?: string;
32
+ /** Whether to show week numbers. */
33
+ showWeekNumbers?: boolean;
34
+ /** The locale to use for formatting. */
35
+ locale?: string;
36
+ /** Custom aria label. */
37
+ 'aria-label'?: string;
38
+ }
39
+
40
+ // ============================================
41
+ // STYLES
42
+ // ============================================
43
+
44
+ const sizeStyles = {
45
+ sm: {
46
+ container: 'w-64',
47
+ header: 'text-sm',
48
+ cell: 'w-8 h-8 text-xs',
49
+ button: 'w-6 h-6',
50
+ },
51
+ md: {
52
+ container: 'w-80',
53
+ header: 'text-base',
54
+ cell: 'w-10 h-10 text-sm',
55
+ button: 'w-8 h-8',
56
+ },
57
+ lg: {
58
+ container: 'w-96',
59
+ header: 'text-lg',
60
+ cell: 'w-12 h-12 text-base',
61
+ button: 'w-10 h-10',
62
+ },
63
+ };
64
+
65
+ // ============================================
66
+ // CALENDAR COMPONENT
67
+ // ============================================
68
+
69
+ /**
70
+ * A calendar displays a grid of days and allows users to select a date.
71
+ *
72
+ * @example
73
+ * ```tsx
74
+ * // Basic usage
75
+ * <Calendar
76
+ * aria-label="Event date"
77
+ * onChange={(date) => console.log(date)}
78
+ * />
79
+ *
80
+ * // Controlled
81
+ * const [date, setDate] = createSignal<CalendarDate | null>(null);
82
+ * <Calendar
83
+ * value={date()}
84
+ * onChange={setDate}
85
+ * />
86
+ *
87
+ * // With min/max dates
88
+ * <Calendar
89
+ * minValue={today(getLocalTimeZone())}
90
+ * maxValue={today(getLocalTimeZone()).add({ months: 3 })}
91
+ * />
92
+ * ```
93
+ */
94
+ export function Calendar<T extends DateValue = CalendarDate>(
95
+ props: CalendarProps<T>
96
+ ): JSX.Element {
97
+ const [local, rest] = splitProps(props, [
98
+ 'size',
99
+ 'class',
100
+ 'showWeekNumbers',
101
+ 'aria-label',
102
+ ]);
103
+
104
+ const size = () => local.size ?? 'md';
105
+ const sizeConfig = () => sizeStyles[size()];
106
+
107
+ return (
108
+ <HeadlessCalendar
109
+ {...rest}
110
+ aria-label={local['aria-label']}
111
+ class={`
112
+ ${sizeConfig().container}
113
+ bg-bg-500 rounded-lg border border-primary-700 p-4
114
+ ${local.class ?? ''}
115
+ `}
116
+ >
117
+ {/* Header with navigation */}
118
+ <header class="flex items-center justify-between mb-4">
119
+ <CalendarButton
120
+ slot="previous"
121
+ class={`
122
+ ${sizeConfig().button}
123
+ flex items-center justify-center
124
+ rounded-md text-primary-200
125
+ hover:bg-bg-400 transition-colors
126
+ disabled:opacity-50 disabled:cursor-not-allowed
127
+ focus:outline-none focus:ring-2 focus:ring-accent/50
128
+ `}
129
+ >
130
+ <svg
131
+ viewBox="0 0 24 24"
132
+ fill="none"
133
+ stroke="currentColor"
134
+ stroke-width="2"
135
+ stroke-linecap="round"
136
+ stroke-linejoin="round"
137
+ class="w-4 h-4"
138
+ >
139
+ <polyline points="15 18 9 12 15 6" />
140
+ </svg>
141
+ </CalendarButton>
142
+
143
+ <CalendarHeading
144
+ class={`
145
+ font-semibold text-primary-100
146
+ ${sizeConfig().header}
147
+ `}
148
+ />
149
+
150
+ <CalendarButton
151
+ slot="next"
152
+ class={`
153
+ ${sizeConfig().button}
154
+ flex items-center justify-center
155
+ rounded-md text-primary-200
156
+ hover:bg-bg-400 transition-colors
157
+ disabled:opacity-50 disabled:cursor-not-allowed
158
+ focus:outline-none focus:ring-2 focus:ring-accent/50
159
+ `}
160
+ >
161
+ <svg
162
+ viewBox="0 0 24 24"
163
+ fill="none"
164
+ stroke="currentColor"
165
+ stroke-width="2"
166
+ stroke-linecap="round"
167
+ stroke-linejoin="round"
168
+ class="w-4 h-4"
169
+ >
170
+ <polyline points="9 18 15 12 9 6" />
171
+ </svg>
172
+ </CalendarButton>
173
+ </header>
174
+
175
+ {/* Calendar grid */}
176
+ <CalendarGrid
177
+ class="w-full border-collapse"
178
+ >
179
+ {(date) => (
180
+ <CalendarCell
181
+ date={date}
182
+ class={({ isSelected, isFocused, isDisabled, isOutsideMonth, isToday, isPressed }) => {
183
+ const base = `
184
+ ${sizeConfig().cell}
185
+ flex items-center justify-center
186
+ rounded-md cursor-pointer
187
+ transition-colors duration-150
188
+ focus:outline-none
189
+ `;
190
+
191
+ let stateClass = '';
192
+
193
+ if (isDisabled) {
194
+ stateClass = 'text-primary-600 cursor-not-allowed';
195
+ } else if (isSelected) {
196
+ stateClass = 'bg-accent text-white font-medium';
197
+ } else if (isOutsideMonth) {
198
+ stateClass = 'text-primary-600';
199
+ } else if (isToday) {
200
+ stateClass = 'ring-1 ring-accent text-primary-100';
201
+ } else {
202
+ stateClass = 'text-primary-200 hover:bg-bg-400';
203
+ }
204
+
205
+ const focusClass = isFocused && !isSelected
206
+ ? 'ring-2 ring-accent/50'
207
+ : '';
208
+
209
+ const pressedClass = isPressed && !isDisabled
210
+ ? 'scale-95'
211
+ : '';
212
+
213
+ return `${base} ${stateClass} ${focusClass} ${pressedClass}`.trim();
214
+ }}
215
+ />
216
+ )}
217
+ </CalendarGrid>
218
+ </HeadlessCalendar>
219
+ );
220
+ }
221
+
222
+ // Re-export types
223
+ export type { CalendarDate, DateValue };
@@ -194,8 +194,8 @@ export function Checkbox(props: CheckboxProps): JSX.Element {
194
194
  <CheckIcon class={iconClasses()} />
195
195
  </Show>
196
196
  </span>
197
- <Show when={local.children}>
198
- <span class={labelClasses()}>{local.children}</span>
197
+ <Show when={props.children}>
198
+ <span class={labelClasses()}>{props.children}</span>
199
199
  </Show>
200
200
  </>
201
201
  )
@@ -216,7 +216,6 @@ export function Checkbox(props: CheckboxProps): JSX.Element {
216
216
  export function CheckboxGroup(props: CheckboxGroupProps): JSX.Element {
217
217
  const [local, headlessProps] = splitProps(props, [
218
218
  'class',
219
- 'children',
220
219
  'label',
221
220
  'description',
222
221
  'errorMessage',
@@ -237,7 +236,7 @@ export function CheckboxGroup(props: CheckboxGroupProps): JSX.Element {
237
236
  <span class="text-sm font-medium text-primary-200">{local.label}</span>
238
237
  </Show>
239
238
  <div class="flex flex-col gap-2">
240
- {local.children}
239
+ {props.children}
241
240
  </div>
242
241
  <Show when={local.description && !renderProps.isInvalid}>
243
242
  <span class="text-sm text-primary-400">{local.description}</span>