@shadow-js/ui 0.0.0-stage → 1.0.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 (190) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/LICENSE +21 -0
  3. package/README.md +598 -2
  4. package/dist/cli.mjs +2293 -0
  5. package/dist/dev/cli/schema.d.ts +73 -0
  6. package/dist/dev/index.d.ts +6 -0
  7. package/dist/dev/index.js +5 -0
  8. package/dist/dev/locales/ar.d.ts +141 -0
  9. package/dist/dev/locales/ar.js +128 -0
  10. package/dist/dev/locales/ar.js.map +1 -0
  11. package/dist/dev/locales/en.d.ts +141 -0
  12. package/dist/dev/locales/en.js +128 -0
  13. package/dist/dev/locales/en.js.map +1 -0
  14. package/dist/dev/registry/utils/utils.d.ts +29 -0
  15. package/dist/dev/registry/utils/utils.js +27 -0
  16. package/dist/dev/registry/utils/utils.js.map +1 -0
  17. package/dist/dev/theme/theme-logic.d.ts +187 -0
  18. package/dist/dev/theme/theme-logic.js +184 -0
  19. package/dist/dev/theme/theme-logic.js.map +1 -0
  20. package/dist/dev/theme/theme-provider.d.ts +90 -0
  21. package/dist/dev/theme/theme-provider.js +150 -0
  22. package/dist/dev/theme/theme-provider.js.map +1 -0
  23. package/dist/prod/index.js +1 -0
  24. package/dist/prod/locales/ar.js +2 -0
  25. package/dist/prod/locales/ar.js.map +1 -0
  26. package/dist/prod/locales/en.js +2 -0
  27. package/dist/prod/locales/en.js.map +1 -0
  28. package/dist/prod/registry/utils/utils.js +2 -0
  29. package/dist/prod/registry/utils/utils.js.map +1 -0
  30. package/dist/prod/theme/theme-logic.js +2 -0
  31. package/dist/prod/theme/theme-logic.js.map +1 -0
  32. package/dist/prod/theme/theme-provider.js +2 -0
  33. package/dist/prod/theme/theme-provider.js.map +1 -0
  34. package/package.json +102 -4
  35. package/registry/accordion/accordion.tsx +210 -0
  36. package/registry/accordion/item.json +4 -0
  37. package/registry/alert/alert.tsx +200 -0
  38. package/registry/alert/item.json +4 -0
  39. package/registry/alert-dialog/alert-dialog.tsx +530 -0
  40. package/registry/alert-dialog/item.json +4 -0
  41. package/registry/aspect-ratio/aspect-ratio.tsx +71 -0
  42. package/registry/aspect-ratio/item.json +4 -0
  43. package/registry/attachment/attachment.tsx +459 -0
  44. package/registry/attachment/item.json +4 -0
  45. package/registry/avatar/avatar.tsx +385 -0
  46. package/registry/avatar/item.json +4 -0
  47. package/registry/badge/badge.tsx +108 -0
  48. package/registry/badge/item.json +4 -0
  49. package/registry/breadcrumb/breadcrumb.tsx +354 -0
  50. package/registry/breadcrumb/item.json +4 -0
  51. package/registry/bubble/bubble.tsx +260 -0
  52. package/registry/bubble/item.json +4 -0
  53. package/registry/button/button.tsx +125 -0
  54. package/registry/button/item.json +4 -0
  55. package/registry/button-group/button-group.tsx +189 -0
  56. package/registry/button-group/item.json +4 -0
  57. package/registry/calendar/calendar.tsx +348 -0
  58. package/registry/calendar/item.json +4 -0
  59. package/registry/card/card.tsx +311 -0
  60. package/registry/card/item.json +4 -0
  61. package/registry/carousel/carousel.tsx +851 -0
  62. package/registry/carousel/item.json +4 -0
  63. package/registry/chart/chart.tsx +1956 -0
  64. package/registry/chart/item.json +4 -0
  65. package/registry/chart-scale/chart-scale.ts +670 -0
  66. package/registry/chart-scale/item.json +5 -0
  67. package/registry/checkbox/checkbox.tsx +79 -0
  68. package/registry/checkbox/item.json +4 -0
  69. package/registry/code-block/code-block.tsx +470 -0
  70. package/registry/code-block/item.json +4 -0
  71. package/registry/collapsible/collapsible.tsx +117 -0
  72. package/registry/collapsible/item.json +4 -0
  73. package/registry/combobox/combobox.tsx +977 -0
  74. package/registry/combobox/item.json +4 -0
  75. package/registry/command/command.tsx +757 -0
  76. package/registry/command/item.json +4 -0
  77. package/registry/context-menu/context-menu.tsx +697 -0
  78. package/registry/context-menu/item.json +4 -0
  79. package/registry/data-table/data-table.tsx +437 -0
  80. package/registry/data-table/item.json +4 -0
  81. package/registry/date-picker/date-picker.tsx +349 -0
  82. package/registry/date-picker/item.json +4 -0
  83. package/registry/dialog/dialog.tsx +412 -0
  84. package/registry/dialog/item.json +4 -0
  85. package/registry/direction/direction.tsx +88 -0
  86. package/registry/direction/item.json +4 -0
  87. package/registry/drawer/drawer.tsx +787 -0
  88. package/registry/drawer/item.json +4 -0
  89. package/registry/dropdown-menu/dropdown-menu.tsx +692 -0
  90. package/registry/dropdown-menu/item.json +4 -0
  91. package/registry/empty/empty.tsx +240 -0
  92. package/registry/empty/item.json +4 -0
  93. package/registry/field/field.tsx +624 -0
  94. package/registry/field/item.json +4 -0
  95. package/registry/form/form.tsx +1112 -0
  96. package/registry/form/item.json +4 -0
  97. package/registry/hover-card/hover-card.tsx +144 -0
  98. package/registry/hover-card/item.json +4 -0
  99. package/registry/icons/icons.tsx +1691 -0
  100. package/registry/icons/item.json +4 -0
  101. package/registry/input/input.tsx +43 -0
  102. package/registry/input/item.json +4 -0
  103. package/registry/input-group/input-group.tsx +315 -0
  104. package/registry/input-group/item.json +4 -0
  105. package/registry/input-otp/input-otp.tsx +329 -0
  106. package/registry/input-otp/item.json +4 -0
  107. package/registry/item/item.json +4 -0
  108. package/registry/item/item.tsx +439 -0
  109. package/registry/kbd/item.json +4 -0
  110. package/registry/kbd/kbd.tsx +65 -0
  111. package/registry/label/item.json +4 -0
  112. package/registry/label/label.tsx +60 -0
  113. package/registry/marker/item.json +4 -0
  114. package/registry/marker/marker.tsx +175 -0
  115. package/registry/menubar/item.json +4 -0
  116. package/registry/menubar/menubar.tsx +694 -0
  117. package/registry/message/item.json +4 -0
  118. package/registry/message/message.tsx +280 -0
  119. package/registry/message-scroller/item.json +4 -0
  120. package/registry/message-scroller/message-scroller.tsx +262 -0
  121. package/registry/native-select/item.json +4 -0
  122. package/registry/native-select/native-select.tsx +113 -0
  123. package/registry/navigation-menu/item.json +4 -0
  124. package/registry/navigation-menu/navigation-menu.tsx +362 -0
  125. package/registry/pagination/item.json +4 -0
  126. package/registry/pagination/pagination.tsx +460 -0
  127. package/registry/password-input/item.json +5 -0
  128. package/registry/password-input/password-input.tsx +494 -0
  129. package/registry/popover/item.json +4 -0
  130. package/registry/popover/popover.tsx +284 -0
  131. package/registry/progress/item.json +4 -0
  132. package/registry/progress/progress.tsx +190 -0
  133. package/registry/questionnaire/item.json +4 -0
  134. package/registry/questionnaire/questionnaire.tsx +569 -0
  135. package/registry/radio-group/item.json +4 -0
  136. package/registry/radio-group/radio-group.tsx +101 -0
  137. package/registry/resizable/item.json +4 -0
  138. package/registry/resizable/resizable.tsx +455 -0
  139. package/registry/scroll-area/item.json +4 -0
  140. package/registry/scroll-area/scroll-area.tsx +149 -0
  141. package/registry/select/item.json +4 -0
  142. package/registry/select/select.tsx +571 -0
  143. package/registry/separator/item.json +4 -0
  144. package/registry/separator/separator.tsx +52 -0
  145. package/registry/sheet/item.json +4 -0
  146. package/registry/sheet/sheet.tsx +476 -0
  147. package/registry/sidebar/item.json +4 -0
  148. package/registry/sidebar/sidebar.tsx +1870 -0
  149. package/registry/skeleton/item.json +4 -0
  150. package/registry/skeleton/skeleton.tsx +47 -0
  151. package/registry/slider/item.json +4 -0
  152. package/registry/slider/slider.tsx +149 -0
  153. package/registry/spinner/item.json +4 -0
  154. package/registry/spinner/spinner.tsx +51 -0
  155. package/registry/switch/item.json +4 -0
  156. package/registry/switch/switch.tsx +93 -0
  157. package/registry/table/item.json +4 -0
  158. package/registry/table/table.tsx +373 -0
  159. package/registry/tabs/item.json +4 -0
  160. package/registry/tabs/tabs.tsx +209 -0
  161. package/registry/textarea/item.json +4 -0
  162. package/registry/textarea/textarea.tsx +51 -0
  163. package/registry/toast/item.json +4 -0
  164. package/registry/toast/toast.tsx +949 -0
  165. package/registry/toggle/item.json +4 -0
  166. package/registry/toggle/toggle.tsx +110 -0
  167. package/registry/toggle-group/item.json +4 -0
  168. package/registry/toggle-group/toggle-group.tsx +189 -0
  169. package/registry/tooltip/item.json +4 -0
  170. package/registry/tooltip/tooltip.tsx +169 -0
  171. package/registry/trace/item.json +4 -0
  172. package/registry/trace/trace.tsx +356 -0
  173. package/registry/ui-strings/item.json +5 -0
  174. package/registry/ui-strings/ui-strings.tsx +454 -0
  175. package/registry/use-highlighted-code/item.json +5 -0
  176. package/registry/use-highlighted-code/use-highlighted-code.tsx +145 -0
  177. package/registry/use-mobile/item.json +5 -0
  178. package/registry/use-mobile/use-mobile.ts +46 -0
  179. package/registry/utils/item.json +5 -0
  180. package/registry/utils/utils.ts +38 -0
  181. package/registry.json +3274 -0
  182. package/styles/animations.css +321 -0
  183. package/styles/theme-core.css +55 -0
  184. package/styles/theme.css +12 -0
  185. package/styles/themes/gray.css +69 -0
  186. package/styles/themes/neutral.css +69 -0
  187. package/styles/themes/slate.css +69 -0
  188. package/styles/themes/stone.css +69 -0
  189. package/styles/themes/zinc.css +69 -0
  190. package/styles/typeset.css +251 -0
@@ -0,0 +1,349 @@
1
+ import { splitProps, useEffect, useSignal } from "@shadow-js/core";
2
+ import {
3
+ type CalendarDate,
4
+ type CalendarValue,
5
+ compareDates,
6
+ formatDate,
7
+ parseDateInput,
8
+ useControllableState,
9
+ } from "@shadow-js/primitives";
10
+ import type { JSX } from "@shadow-js/web";
11
+ import { Button } from "@/components/ui/button";
12
+ import { Calendar, isCalendarDay } from "@/components/ui/calendar";
13
+ import { Input } from "@/components/ui/input";
14
+ import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
15
+ import { useUiStrings } from "@/lib/ui-strings";
16
+ import { cn } from "@/lib/utils";
17
+
18
+ /** A calendar glyph for the trigger, inline so the kit needs no icon package. @internal */
19
+ function CalendarGlyph(): JSX.Element {
20
+ return (
21
+ <svg
22
+ xmlns="http://www.w3.org/2000/svg"
23
+ viewBox="0 0 24 24"
24
+ fill="none"
25
+ stroke="currentColor"
26
+ stroke-width="2"
27
+ stroke-linecap="round"
28
+ stroke-linejoin="round"
29
+ class="size-4"
30
+ aria-hidden="true"
31
+ >
32
+ <rect width="18" height="18" x="3" y="4" rx="2" />
33
+ <path d="M16 2v4M8 2v4M3 10h18" />
34
+ </svg>
35
+ );
36
+ }
37
+
38
+ /**
39
+ * Props of {@link DatePicker}: the day (controlled or uncontrolled), the locale and the limits, and the
40
+ * field's own attributes such as `id` and `aria-describedby`.
41
+ * @see {@link DatePicker}
42
+ * @since 1.0.0
43
+ */
44
+ export interface DatePickerProps {
45
+ /**
46
+ * The chosen day. Setting it controls the picker; `undefined` means the picker is uncontrolled.
47
+ * @default none
48
+ */
49
+ readonly value?: CalendarDate | undefined;
50
+ /**
51
+ * The day when the picker is uncontrolled.
52
+ * @default none
53
+ */
54
+ readonly defaultValue?: CalendarDate | undefined;
55
+ /**
56
+ * Called with the day when the user picks a day or types a valid one, and with `undefined` when the
57
+ * field is cleared.
58
+ * @default none
59
+ */
60
+ readonly onValueChange?: ((value: CalendarDate | undefined) => void) | undefined;
61
+ /**
62
+ * A BCP 47 tag, such as `"de-DE"`. It sets the format of the typed date and the language and week
63
+ * start of the calendar.
64
+ * @default "en-US"
65
+ */
66
+ readonly locale?: string | undefined;
67
+ /**
68
+ * The earliest day that can be typed or picked.
69
+ * @default none
70
+ */
71
+ readonly min?: CalendarDate | undefined;
72
+ /**
73
+ * The latest day that can be typed or picked.
74
+ * @default none
75
+ */
76
+ readonly max?: CalendarDate | undefined;
77
+ /**
78
+ * The text shown while the field is empty.
79
+ * @default "MM/DD/YYYY"
80
+ */
81
+ readonly placeholder?: string | undefined;
82
+ /**
83
+ * Reading direction of the calendar. The popover opens in a portal, so a `dir` attribute on an ancestor of the field
84
+ * does not reach it: by default the calendar takes the direction of the nearest ancestor `dir` attribute of the field.
85
+ * @default the nearest ancestor `dir` of the field, else inherited from `DirectionProvider` or the document
86
+ */
87
+ readonly dir?: "ltr" | "rtl" | undefined;
88
+ /**
89
+ * The accessible name of the calendar button. Translate it for another language.
90
+ * @default "Open calendar"
91
+ */
92
+ readonly triggerLabel?: string | undefined;
93
+ /**
94
+ * Marks the field invalid from outside, for example a required field left empty. A typed date that
95
+ * does not exist marks it invalid too.
96
+ * @default false
97
+ */
98
+ readonly invalid?: boolean | undefined;
99
+ /**
100
+ * The id of the element that describes the field, such as an error message. It is set on the field,
101
+ * so a screen reader reads the message with it.
102
+ * @default none
103
+ */
104
+ readonly "aria-describedby"?: string | undefined;
105
+ /**
106
+ * Disables the field and the calendar button.
107
+ * @default false
108
+ */
109
+ readonly disabled?: boolean | undefined;
110
+ /**
111
+ * The id of the field. Point a `Label` at it with `htmlFor`.
112
+ * @default none
113
+ */
114
+ readonly id?: string | undefined;
115
+ /**
116
+ * The name under which the typed date is submitted with a form.
117
+ * @default none
118
+ */
119
+ readonly name?: string | undefined;
120
+ /**
121
+ * Classes for the wrapper around the field and the button.
122
+ * @default none
123
+ */
124
+ readonly class?: string | undefined;
125
+ }
126
+
127
+ /**
128
+ * The code point of zero in each script whose ten digits are consecutive: ASCII, Arabic-Indic (used by
129
+ * `ar-EG`), Persian, Devanagari, Bengali, Thai and the others a date can be written in. @internal
130
+ */
131
+ const DIGIT_ZEROS: readonly number[] = [
132
+ 0x30, 0x660, 0x6f0, 0x7c0, 0x966, 0x9e6, 0xa66, 0xae6, 0xb66, 0xbe6, 0xc66, 0xce6, 0xd66, 0xde6,
133
+ 0xe50, 0xed0, 0xf20, 0x1040, 0x1090, 0x17e0, 0x1810, 0xff10,
134
+ ];
135
+
136
+ /**
137
+ * The text with every digit written as ASCII. Locales such as `ar-EG` format with Arabic-Indic digits,
138
+ * and the parser reads ASCII digits only; any other character is kept as it is. @internal
139
+ */
140
+ function asciiDigits(text: string): string {
141
+ return [...text]
142
+ .map((char) => {
143
+ const code = char.codePointAt(0) ?? 0;
144
+ const zero = DIGIT_ZEROS.find((start) => code >= start && code <= start + 9);
145
+ return zero === undefined ? char : String(code - zero);
146
+ })
147
+ .join("");
148
+ }
149
+
150
+ /** Whether a typed day is inside the allowed range. @internal */
151
+ function inBounds(
152
+ day: CalendarDate,
153
+ min: CalendarDate | undefined,
154
+ max: CalendarDate | undefined,
155
+ ): boolean {
156
+ if (min !== undefined && compareDates(day, min) < 0) return false;
157
+ if (max !== undefined && compareDates(day, max) > 0) return false;
158
+ return true;
159
+ }
160
+
161
+ /**
162
+ * A text field for a day, with a calendar button that opens a calendar in a popover. Typing reads the
163
+ * day in the locale's format, or in ISO form (`2026-03-05`), when the field loses focus or Enter is
164
+ * pressed. A day that does not exist, or that is outside `min` or `max`, marks the field invalid and is
165
+ * not taken. Picking a day in the calendar fills the field and closes the popover. Use it for a date
166
+ * that people type; the calendar is there for picking.
167
+ *
168
+ * Keyboard and ARIA: the field is a text input with `inputmode="numeric"`, and Enter reads the typed
169
+ * date without submitting a surrounding form. Name it with a `Label` (`htmlFor` pointing at the `id`).
170
+ * An invalid field has `aria-invalid="true"`, and `aria-describedby` points at your error message. The
171
+ * calendar button is a native button named by `triggerLabel` (default "Open calendar"): Enter or Space
172
+ * opens the calendar and moves focus into it, Escape or a press outside closes it. Inside the calendar,
173
+ * the arrow keys move between days, PageUp and PageDown move a month, and Enter or Space picks the
174
+ * focused day. Shift with PageUp or PageDown moves a year.
175
+ *
176
+ * shadcn/ui differences: shadcn's date picker is usually a button that shows the chosen date. This one
177
+ * is a typed text field with a calendar button next to it, so a user can type a date or pick one. `class`
178
+ * replaces `className`, and the parts have `data-slot` values (`date-picker`, `date-picker-input` and
179
+ * `date-picker-trigger`).
180
+ *
181
+ * @param props The picker props: see {@link DatePickerProps}.
182
+ * @returns The field and its calendar button.
183
+ * @example
184
+ * import { useSignal } from "@shadow-js/core";
185
+ * import { DatePicker } from "@/components/ui/date-picker";
186
+ * import { Label } from "@/components/ui/label";
187
+ *
188
+ * export function DueDate() {
189
+ * const [due, setDue] = useSignal<{ year: number; month: number; day: number } | undefined>(
190
+ * undefined,
191
+ * );
192
+ * return (
193
+ * <>
194
+ * <Label htmlFor="due">Due date</Label>
195
+ * <DatePicker id="due" value={due()} onValueChange={setDue} />
196
+ * </>
197
+ * );
198
+ * }
199
+ * @see {@link DatePickerProps}
200
+ * @since 1.0.0
201
+ */
202
+ function DatePicker(props: DatePickerProps): JSX.Element {
203
+ const [local, rest] = splitProps(props as DatePickerProps & Record<string, unknown>, [
204
+ "value",
205
+ "defaultValue",
206
+ "onValueChange",
207
+ "locale",
208
+ "min",
209
+ "max",
210
+ "placeholder",
211
+ "triggerLabel",
212
+ "invalid",
213
+ "disabled",
214
+ "id",
215
+ "name",
216
+ "class",
217
+ "dir",
218
+ ]);
219
+ const t = useUiStrings();
220
+ let root: HTMLElement | undefined;
221
+ // The calendar is built when the popover opens, so this reads the field's surroundings at that moment.
222
+ const direction = (): "ltr" | "rtl" | undefined => {
223
+ if (local.dir === "ltr" || local.dir === "rtl") return local.dir;
224
+ const own = root?.closest("[dir]")?.getAttribute("dir");
225
+ return own === "ltr" || own === "rtl" ? own : undefined;
226
+ };
227
+ const locale = (local.locale as string | undefined) ?? "en-US";
228
+ const [day, setDay] = useControllableState<CalendarDate | undefined>({
229
+ value: () => local.value as CalendarDate | undefined,
230
+ defaultValue: () => local.defaultValue as CalendarDate | undefined,
231
+ onChange: () => {
232
+ const callback = local.onValueChange as
233
+ | ((value: CalendarDate | undefined) => void)
234
+ | undefined;
235
+ return callback === undefined ? undefined : (next) => callback(next);
236
+ },
237
+ name: "DatePicker value",
238
+ });
239
+ const [text, setText] = useSignal<string>(
240
+ day() === undefined ? "" : formatDate(locale, day() as CalendarDate),
241
+ );
242
+ const [invalid, setInvalid] = useSignal(false);
243
+ const [open, setOpen] = useSignal(false);
244
+
245
+ // The field shows the day the app holds, so a day set from outside (a controlled value that changes,
246
+ // or a locale switch) replaces the text, and an invalid typed text is cleared with it.
247
+ useEffect(() => {
248
+ const current = day();
249
+ const tag = (local.locale as string | undefined) ?? "en-US";
250
+ setText(current === undefined ? "" : formatDate(tag, current));
251
+ setInvalid(false);
252
+ });
253
+
254
+ const commitText = (): void => {
255
+ const trimmed = text().trim();
256
+ if (trimmed === "") {
257
+ setInvalid(false);
258
+ if (day() !== undefined) setDay(undefined);
259
+ return;
260
+ }
261
+ const parsed = parseDateInput(asciiDigits(trimmed), locale);
262
+ if (
263
+ parsed === undefined ||
264
+ !inBounds(
265
+ parsed,
266
+ local.min as CalendarDate | undefined,
267
+ local.max as CalendarDate | undefined,
268
+ )
269
+ ) {
270
+ setInvalid(true);
271
+ return;
272
+ }
273
+ setInvalid(false);
274
+ setDay(parsed);
275
+ setText(formatDate(locale, parsed));
276
+ };
277
+
278
+ const pick = (next: CalendarValue): void => {
279
+ if (!isCalendarDay(next)) return;
280
+ setDay(next);
281
+ setText(formatDate(locale, next));
282
+ setInvalid(false);
283
+ setOpen(false);
284
+ };
285
+
286
+ return (
287
+ <div
288
+ ref={(element: HTMLElement) => {
289
+ root = element;
290
+ }}
291
+ data-slot="date-picker"
292
+ class={cn("relative flex w-full max-w-xs gap-2", local.class as string | undefined)}
293
+ >
294
+ <Input
295
+ id={local.id as string | undefined}
296
+ name={local.name as string | undefined}
297
+ data-slot="date-picker-input"
298
+ inputmode="numeric"
299
+ autocomplete="off"
300
+ placeholder={(local.placeholder as string | undefined) ?? "MM/DD/YYYY"}
301
+ value={text()}
302
+ disabled={local.disabled === true}
303
+ aria-invalid={invalid() || local.invalid === true ? "true" : undefined}
304
+ onInput={(event: Event) => {
305
+ setText((event.currentTarget as HTMLInputElement).value);
306
+ setInvalid(false);
307
+ }}
308
+ onBlur={commitText}
309
+ onKeyDown={(event: KeyboardEvent) => {
310
+ if (event.key === "Enter") {
311
+ event.preventDefault();
312
+ commitText();
313
+ }
314
+ }}
315
+ {...rest}
316
+ />
317
+ <Popover open={open()} onOpenChange={setOpen}>
318
+ <PopoverTrigger
319
+ as={Button}
320
+ variant="outline"
321
+ size="icon"
322
+ aria-label={
323
+ (local.triggerLabel as string | undefined) ?? t("datePicker.open", "Open calendar")
324
+ }
325
+ data-slot="date-picker-trigger"
326
+ disabled={local.disabled === true}
327
+ >
328
+ <CalendarGlyph />
329
+ </PopoverTrigger>
330
+ <PopoverContent class="w-auto p-0" align="end">
331
+ {/* The calendar is built when the popover opens, so its selection starts from the field's day and
332
+ stays its own until it closes: a controlled value that is sometimes undefined would warn. */}
333
+ <Calendar
334
+ mode="single"
335
+ locale={locale}
336
+ dir={direction()}
337
+ defaultValue={day()}
338
+ min={local.min as CalendarDate | undefined}
339
+ max={local.max as CalendarDate | undefined}
340
+ defaultMonth={day() ?? (local.min as CalendarDate | undefined)}
341
+ onValueChange={pick}
342
+ />
343
+ </PopoverContent>
344
+ </Popover>
345
+ </div>
346
+ );
347
+ }
348
+
349
+ export { DatePicker };
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Date Picker",
3
+ "description": "A date text field with a calendar in a popover; typed dates follow the locale and are checked against the limits."
4
+ }