atlas-react-kit 0.11.2 → 0.12.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.
- package/COMPONENTS.md +86 -74
- package/README.md +2 -2
- package/dist/date-picker.d.ts +3 -7
- package/dist/date-picker.js +8 -38
- package/dist/date-time-picker.d.ts +1 -1
- package/dist/date-time-picker.js +3 -3
- package/dist/form/date-picker-field.d.ts +7 -1
- package/dist/form/date-picker-field.js +2 -2
- package/dist/form/date-time-picker-field.d.ts +8 -2
- package/dist/form/date-time-picker-field.js +2 -2
- package/dist/form/field-shell.d.ts +11 -3
- package/dist/form/field-shell.js +15 -6
- package/dist/form/markdown-field.d.ts +3 -1
- package/dist/form/markdown-field.js +2 -2
- package/dist/form/select-field.d.ts +3 -1
- package/dist/form/select-field.js +2 -2
- package/dist/form/text-field.d.ts +1 -0
- package/dist/form/text-field.js +1 -1
- package/dist/internal/date-like.d.ts +8 -0
- package/dist/internal/date-like.js +33 -0
- package/dist/label.d.ts +16 -1
- package/dist/label.js +7 -3
- package/dist/picker-shell.d.ts +18 -1
- package/dist/picker-shell.js +28 -0
- package/dist/toast.d.ts +4 -2
- package/dist/toast.js +18 -3
- package/package.json +1 -1
package/COMPONENTS.md
CHANGED
|
@@ -28,60 +28,60 @@ theme from `next-themes`, so toast consumers install that too.
|
|
|
28
28
|
| `toast` | `npm i sonner@^2.0.7 next-themes@^0.4.6` |
|
|
29
29
|
| `next/tabs-nav-link` | `npm i next@^16.0.0` |
|
|
30
30
|
|
|
31
|
-
| Subpath | Main exports and options
|
|
32
|
-
| ----------------------------- |
|
|
33
|
-
| `accordion` | `Accordion`, `AccordionItem`, `AccordionTrigger`, `AccordionContent`; Radix `type` `single` (with `collapsible`) or `multiple`
|
|
34
|
-
| `alert` | `Alert`, `AlertTitle`, `AlertDescription`, `alertVariants`; `variant` `default`, `success`, `warning`, `destructive`; tone on title and icon, body stays muted; destructive is `role="alert"`
|
|
35
|
-
| `avatar` | `Avatar`, `AvatarImage`, `AvatarFallback`; `size` `sm`, `default`, `lg`; round by design
|
|
36
|
-
| `breadcrumb` | `Breadcrumb`, `BreadcrumbList`, `BreadcrumbItem`, `BreadcrumbLink` (`asChild` for router links), `BreadcrumbPage` (`aria-current`), `BreadcrumbSeparator`, `BreadcrumbEllipsis`
|
|
37
|
-
| `button` | `Button`, `buttonVariants`, `quietButtonInteraction`; `variant`, `size`, `asChild`; outline/ghost hover is brand purple + selected wash, not muted grey
|
|
38
|
-
| `badge` | `Badge`, `badgeVariants`; `variant`, `tone`, `asChild`
|
|
39
|
-
| `calendar` | `Calendar`, `CalendarDayButton`; React Day Picker props and `buttonVariant`
|
|
40
|
-
| `card` | `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardAction`, `CardContent`, `CardFooter`; `Card` supports `flush`; `CardTitle as="h2"` (up to `h4`) makes it a section heading; root uses `min-h-min shrink-0` so flex columns do not collapse it, and `min-w-0` so nested tables scroll; `CardContent className="px-0"` overrides padding without `!`
|
|
41
|
-
| `checkbox` | `Checkbox`
|
|
42
|
-
| `color-legend` | `ColorLegend`; `title`, `items` as `{ color, range?, label }[]`, optional `caption`, `children`, `aria-label`, `className` (overlay chrome). No attribution.
|
|
43
|
-
| `combobox` | `Combobox`; `options` as `{ value, label }[]`, `value`, `onValueChange`; optional placeholder, search, empty text, `creatable`, `normalizeCreateValue` (query → value or `null`), `createLabel`; trigger matches Input/SelectTrigger chrome; keyboard highlight scrolls into view; create is a listbox option selected with Enter
|
|
44
|
-
| `date-picker` | `DatePickerButton`, `toDate`, `formatLocalDate`, `DateRange`; `mode` `"single"` (default) or `"range"`; single `value` is `Date` or `yyyy-MM-dd`; range `value` is `{ from, to }`; trigger matches Input chrome; optional `numberOfMonths` (range default 1 below `md`, 2 at `md+`), `side`, `aria-labelledby`, and `aria-describedby`
|
|
45
|
-
| `date-time-picker` | `DateTimePickerButton`, `resolveTimeStep`, `DEFAULT_TIME_STEP`; controlled `value` is `Date` or `null`; `onChange` emits `Date \
|
|
46
|
-
| `dialog` | `Dialog`, trigger, portal, overlay, content, close, header, footer, title, description; `DialogContent` supports `showCloseButton`
|
|
47
|
-
| `dropdown-menu` | `DropdownMenu` family (trigger, content, item, checkbox/radio items, label, separator, shortcut, sub menus); item `variant="destructive"` and `inset`; for commands, not values
|
|
48
|
-
| `empty-panel` | `EmptyPanel`; required `children`; optional `size`, `muted`, `className`
|
|
49
|
-
| `error-boundary` | `ErrorBoundary`, `ErrorFallback`; optional `message`; retry resets, reload refreshes the page
|
|
50
|
-
| `field` | Field, label, legend, description, group, set, title, content, separator, error; `Field` supports `orientation`
|
|
51
|
-
| `info-tip` | `InfoTip`, `InlineTip`; required `label` (aria-label); `title`, `description`, `children`, `layout` `box` \| `inline`, `side`, `align`, `badge`, `icon`, `className`. Popover, 20px glyph, 44px hit. Hover opens only on a fine pointer. Not a Tooltip.
|
|
52
|
-
| `input`, `textarea`, `label` | `Input`, `Textarea`, `Label`
|
|
53
|
-
| `markdown` | `Markdown`, `MarkdownContent`; `content`, optional `className`. GFM via `react-markdown` + `remark-gfm` in this subpath only. Sanitized (no raw HTML; http/https/mailto/#/`/` URLs). Token styles, no typography plugin.
|
|
54
|
-
| `meter` | `Meter`; `value`, `max`, optional `min` (default 0), `fillClassName`, `markers` as `{ position, className? }[]`, required `aria-label`, `className`. `role="meter"`. Not `Progress`.
|
|
55
|
-
| `pagination` | `Pagination`, `PaginationContent`, `PaginationItem`, `PaginationLink` (`isActive` sets `aria-current`, the selected wash, and a selected border; `disabled` drops the `href` for a page that does not exist, such as Previous on page 1), `PaginationPrevious`, `PaginationNext` (custom text becomes the link name), `PaginationEllipsis`
|
|
56
|
-
| `popover` | `Popover`, trigger, anchor, content, header, title, description
|
|
57
|
-
| `progress` | `Progress`; Radix props, `value` 0–100; label it with `aria-label` or `aria-labelledby`. For a value against a scale with ticks, use `Meter`.
|
|
58
|
-
| `radio-group` | `RadioGroup`, `RadioGroupItem`; short visible lists; arrow keys move the selection; round dots
|
|
59
|
-
| `search-picker` | `SearchPicker<T>`; always-visible combobox + listbox; `items`, `getValue`, `getLabel`, optional `filter` (default casefold + accent-fold includes), `value`, `onSelect`, `label`, `placeholder`, `emptyText`, `listLabel`, `disabled`, `className`. Arrows, Home/End, PageUp/PageDown, Enter, Escape.
|
|
60
|
-
| `select` | Select, trigger, value, content, group, label, item, separator, scroll buttons; trigger `size`; trigger text is `text-base md:text-sm` like Input
|
|
61
|
-
| `separator`, `skeleton` | `Separator`, `Skeleton`
|
|
62
|
-
| `slider` | `Slider`; `value` number, `onValueChange`, optional `onValueCommit`, `min`, `max`, `step`, `disabled`, `id`, `aria-labelledby`, `className`. Radix Slider; 44px thumb hit; square track; primary fill. Name it with `aria-labelledby`.
|
|
63
|
-
| `sortable-table-head` | `SortableTableHead`; `sorted`, `direction` (`asc` \
|
|
64
|
-
| `spinner` | `Spinner`; `role="status"` with `aria-label` (default `Loading`)
|
|
65
|
-
| `stat-tile` | `StatTile`, `statTileGrid`, `sectionLabel`; `label`, `value`, optional `sub`, `children`, `className`.
|
|
66
|
-
| `switch` | `Switch`; settings that apply immediately; square track and thumb
|
|
67
|
-
| `table` | Table, header, body, footer, row, head, cell, caption; root scroller is `min-w-0 overflow-x-auto` with a fade cue; cells are `px-2` below `sm`
|
|
68
|
-
| `table-body-skeleton` | `TableBodySkeleton`; `columns`, optional `rows` (default 5)
|
|
69
|
-
| `tabs` | `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`, `TabsNav`, `tabsListVariants`, `tabsTriggerVariants`; list `variant` is `default` or `line`; style `TabsNav` links with `tabsTriggerVariants()`; `TabsNav` / `TabsList` scroll horizontally only (`overflow-y` hidden, `p-1` counted in the scroller height, edge fade, active item into view) — do not wrap them in `overflow-x-auto`
|
|
70
|
-
| `toast` | `Toaster`, `toast`; mount one `Toaster` (`position`, `closeButton` (default on), `expand`, `visibleToasts`, `duration`), then `toast(message, options)
|
|
71
|
-
| `toggle` | `Toggle`, `toggleVariants`, `selectedToggleChrome`; `variant`, `size`. Pressed state is the selected wash, text, and border via `data-state=on`, `aria-pressed`, and `aria-checked`
|
|
72
|
-
| `toggle-group` | `ToggleGroup`, `ToggleGroupItem`; `variant`, `size`. Items share `toggleVariants`, so single (radio) and multiple selections match a pressed `Toggle`
|
|
73
|
-
| `theme-provider` | `ThemeProvider`, `useTheme`; optional `storageKey` (default `atlas-theme`), `enableShortcut` for the `d` toggle
|
|
74
|
-
| `tooltip` | `Tooltip`, `TooltipTrigger`, `TooltipContent`, `TooltipProvider`; opens on hover and focus; short labels only
|
|
75
|
-
| `form/markdown-field` | `FormMarkdownField`; `field` is `TextFieldApi`; `label`, optional `id`, `placeholder`, `rows`, `showError`. Write / preview; preview uses `Markdown`.
|
|
76
|
-
| `form/text-field` | `FormTextField`, type `TextFieldApi`; controlled field, label, multiline, description, input constraints
|
|
77
|
-
| `form/select-field` | `FormSelectField`; `value`, `onValueChange`, `label`, optional `meta
|
|
78
|
-
| `form/date-picker-field` | `FormDatePickerField`; `value`, `onValueChange`, `label`, optional `meta`, placeholder, and `className` (forwarded to the trigger)
|
|
79
|
-
| `form/date-time-picker-field` | `FormDateTimePickerField`; `value` is `Date` or `null`; `onValueChange` emits `Date
|
|
80
|
-
| `form/field-error` | `FieldError` (optional `id`), `getFieldErrorMessage`; types `FieldErrorMessage`, `FieldMetaState`
|
|
81
|
-
| `form/feedback-field` | `FormFeedbackField`; nullable `message`
|
|
82
|
-
| `next/tabs-nav-link` | `TabsNavLink`; Next Link props and required `active` boolean; requires Next.js 16
|
|
83
|
-
| `utils` | `cn`, `onInputChange`
|
|
84
|
-
| `theme.css` | Tailwind 4 theme; installation in [README](./README.md)
|
|
31
|
+
| Subpath | Main exports and options |
|
|
32
|
+
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
33
|
+
| `accordion` | `Accordion`, `AccordionItem`, `AccordionTrigger`, `AccordionContent`; Radix `type` `single` (with `collapsible`) or `multiple` |
|
|
34
|
+
| `alert` | `Alert`, `AlertTitle`, `AlertDescription`, `alertVariants`; `variant` `default`, `success`, `warning`, `destructive`; tone on title and icon, body stays muted; destructive is `role="alert"` |
|
|
35
|
+
| `avatar` | `Avatar`, `AvatarImage`, `AvatarFallback`; `size` `sm`, `default`, `lg`; round by design |
|
|
36
|
+
| `breadcrumb` | `Breadcrumb`, `BreadcrumbList`, `BreadcrumbItem`, `BreadcrumbLink` (`asChild` for router links), `BreadcrumbPage` (`aria-current`), `BreadcrumbSeparator`, `BreadcrumbEllipsis` |
|
|
37
|
+
| `button` | `Button`, `buttonVariants`, `quietButtonInteraction`; `variant`, `size`, `asChild`; outline/ghost hover is brand purple + selected wash, not muted grey |
|
|
38
|
+
| `badge` | `Badge`, `badgeVariants`; `variant`, `tone`, `asChild` |
|
|
39
|
+
| `calendar` | `Calendar`, `CalendarDayButton`; React Day Picker props and `buttonVariant` |
|
|
40
|
+
| `card` | `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardAction`, `CardContent`, `CardFooter`; `Card` supports `flush`; `CardTitle as="h2"` (up to `h4`) makes it a section heading; root uses `min-h-min shrink-0` so flex columns do not collapse it, and `min-w-0` so nested tables scroll; `CardContent className="px-0"` overrides padding without `!` |
|
|
41
|
+
| `checkbox` | `Checkbox` |
|
|
42
|
+
| `color-legend` | `ColorLegend`; `title`, `items` as `{ color, range?, label }[]`, optional `caption`, `children`, `aria-label`, `className` (overlay chrome). No attribution. |
|
|
43
|
+
| `combobox` | `Combobox`; `options` as `{ value, label }[]`, `value`, `onValueChange`; optional placeholder, search, empty text, `creatable`, `normalizeCreateValue` (query → value or `null`), `createLabel`; trigger matches Input/SelectTrigger chrome; keyboard highlight scrolls into view; create is a listbox option selected with Enter |
|
|
44
|
+
| `date-picker` | `DatePickerButton`, `toDate`, `formatLocalDate`, `DateRange`; `mode` `"single"` (default) or `"range"`; single `value` is `Date` or `yyyy-MM-dd`; range `value` is `{ from, to }`; trigger matches Input chrome; optional `numberOfMonths` (range default 1 below `md`, 2 at `md+`), `minDate` / `maxDate` (days outside are disabled), `side`, `aria-labelledby`, and `aria-describedby` |
|
|
45
|
+
| `date-time-picker` | `DateTimePickerButton`, `resolveTimeStep`, `DEFAULT_TIME_STEP`; controlled `value` is `Date` or `null`; `onChange` emits `Date \ |
|
|
46
|
+
| `dialog` | `Dialog`, trigger, portal, overlay, content, close, header, footer, title, description; `DialogContent` supports `showCloseButton` |
|
|
47
|
+
| `dropdown-menu` | `DropdownMenu` family (trigger, content, item, checkbox/radio items, label, separator, shortcut, sub menus); item `variant="destructive"` and `inset`; for commands, not values |
|
|
48
|
+
| `empty-panel` | `EmptyPanel`; required `children`; optional `size`, `muted`, `className` |
|
|
49
|
+
| `error-boundary` | `ErrorBoundary`, `ErrorFallback`; optional `message`; retry resets, reload refreshes the page |
|
|
50
|
+
| `field` | Field, label, legend, description, group, set, title, content, separator, error; `Field` supports `orientation` |
|
|
51
|
+
| `info-tip` | `InfoTip`, `InlineTip`; required `label` (aria-label); `title`, `description`, `children`, `layout` `box` \| `inline`, `side`, `align`, `badge`, `icon`, `className`. Popover, 20px glyph, 44px hit. Hover opens only on a fine pointer. Not a Tooltip. |
|
|
52
|
+
| `input`, `textarea`, `label` | `Input`, `Textarea`, `Label`; `Label` `required` shows a visible marker (set `required` on the control too) |
|
|
53
|
+
| `markdown` | `Markdown`, `MarkdownContent`; `content`, optional `className`. GFM via `react-markdown` + `remark-gfm` in this subpath only. Sanitized (no raw HTML; http/https/mailto/#/`/` URLs). Token styles, no typography plugin. |
|
|
54
|
+
| `meter` | `Meter`; `value`, `max`, optional `min` (default 0), `fillClassName`, `markers` as `{ position, className? }[]`, required `aria-label`, `className`. `role="meter"`. Not `Progress`. |
|
|
55
|
+
| `pagination` | `Pagination`, `PaginationContent`, `PaginationItem`, `PaginationLink` (`isActive` sets `aria-current`, the selected wash, and a selected border; `disabled` drops the `href` for a page that does not exist, such as Previous on page 1), `PaginationPrevious`, `PaginationNext` (custom text becomes the link name), `PaginationEllipsis` |
|
|
56
|
+
| `popover` | `Popover`, trigger, anchor, content, header, title, description |
|
|
57
|
+
| `progress` | `Progress`; Radix props, `value` 0–100; label it with `aria-label` or `aria-labelledby`. For a value against a scale with ticks, use `Meter`. |
|
|
58
|
+
| `radio-group` | `RadioGroup`, `RadioGroupItem`; short visible lists; arrow keys move the selection; round dots |
|
|
59
|
+
| `search-picker` | `SearchPicker<T>`; always-visible combobox + listbox; `items`, `getValue`, `getLabel`, optional `filter` (default casefold + accent-fold includes), `value`, `onSelect`, `label`, `placeholder`, `emptyText`, `listLabel`, `disabled`, `className`. Arrows, Home/End, PageUp/PageDown, Enter, Escape. |
|
|
60
|
+
| `select` | Select, trigger, value, content, group, label, item, separator, scroll buttons; trigger `size`; trigger text is `text-base md:text-sm` like Input |
|
|
61
|
+
| `separator`, `skeleton` | `Separator`, `Skeleton` |
|
|
62
|
+
| `slider` | `Slider`; `value` number, `onValueChange`, optional `onValueCommit`, `min`, `max`, `step`, `disabled`, `id`, `aria-labelledby`, `className`. Radix Slider; 44px thumb hit; square track; primary fill. Name it with `aria-labelledby`. |
|
|
63
|
+
| `sortable-table-head` | `SortableTableHead`; `sorted`, `direction` (`asc` \ |
|
|
64
|
+
| `spinner` | `Spinner`; `role="status"` with `aria-label` (default `Loading`) |
|
|
65
|
+
| `stat-tile` | `StatTile`, `statTileGrid`, `sectionLabel`; `label`, `value`, optional `sub`, `children`, `className`. |
|
|
66
|
+
| `switch` | `Switch`; settings that apply immediately; square track and thumb |
|
|
67
|
+
| `table` | Table, header, body, footer, row, head, cell, caption; root scroller is `min-w-0 overflow-x-auto` with a fade cue; cells are `px-2` below `sm` |
|
|
68
|
+
| `table-body-skeleton` | `TableBodySkeleton`; `columns`, optional `rows` (default 5) |
|
|
69
|
+
| `tabs` | `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`, `TabsNav`, `tabsListVariants`, `tabsTriggerVariants`; list `variant` is `default` or `line`; style `TabsNav` links with `tabsTriggerVariants()`; `TabsNav` / `TabsList` scroll horizontally only (`overflow-y` hidden, `p-1` counted in the scroller height, edge fade, active item into view) — do not wrap them in `overflow-x-auto` |
|
|
70
|
+
| `toast` | `Toaster`, `toast`; mount one `Toaster` (`position`, `closeButton` (default on), `expand`, `visibleToasts`, `duration`), then `toast(message, options)`. The stack expands on hover and while keyboard focus is inside it; collapsed toasts behind the front one are hidden, `.success`, `.info`, `.warning`, `.error`, `.loading`, `.dismiss(id?)`. Options: `id` (replaces that toast), `description`, `duration`, `action` / `cancel` as `{ label, onClick }`. Kit-owned API: no toast library is exposed |
|
|
71
|
+
| `toggle` | `Toggle`, `toggleVariants`, `selectedToggleChrome`; `variant`, `size`. Pressed state is the selected wash, text, and border via `data-state=on`, `aria-pressed`, and `aria-checked` |
|
|
72
|
+
| `toggle-group` | `ToggleGroup`, `ToggleGroupItem`; `variant`, `size`. Items share `toggleVariants`, so single (radio) and multiple selections match a pressed `Toggle` |
|
|
73
|
+
| `theme-provider` | `ThemeProvider`, `useTheme`; optional `storageKey` (default `atlas-theme`), `enableShortcut` for the `d` toggle |
|
|
74
|
+
| `tooltip` | `Tooltip`, `TooltipTrigger`, `TooltipContent`, `TooltipProvider`; opens on hover and focus; short labels only |
|
|
75
|
+
| `form/markdown-field` | `FormMarkdownField`; `field` is `TextFieldApi`; `label`, optional `id`, `placeholder`, `rows`, `showError`, `required`. Write / preview; preview uses `Markdown`. |
|
|
76
|
+
| `form/text-field` | `FormTextField`, type `TextFieldApi`; controlled field, label, multiline, description, input constraints; `required` also marks the label |
|
|
77
|
+
| `form/select-field` | `FormSelectField`; `value`, `onValueChange`, `label`, optional `meta`, placeholder, and `required`; select item children |
|
|
78
|
+
| `form/date-picker-field` | `FormDatePickerField`; `value`, `onValueChange`, `label`, optional `meta`, placeholder, `required`, `minDate` / `maxDate`, and `className` (forwarded to the trigger) |
|
|
79
|
+
| `form/date-time-picker-field` | `FormDateTimePickerField`; `value` is `Date` or `null`; `onValueChange` emits `Date` or `null`; optional `meta`, placeholder, `timeStep`, `required`, `minDate` / `maxDate`, and `className` (forwarded to the trigger) |
|
|
80
|
+
| `form/field-error` | `FieldError` (optional `id`), `getFieldErrorMessage`; types `FieldErrorMessage`, `FieldMetaState` |
|
|
81
|
+
| `form/feedback-field` | `FormFeedbackField`; nullable `message` |
|
|
82
|
+
| `next/tabs-nav-link` | `TabsNavLink`; Next Link props and required `active` boolean; requires Next.js 16 |
|
|
83
|
+
| `utils` | `cn`, `onInputChange` |
|
|
84
|
+
| `theme.css` | Tailwind 4 theme; installation in [README](./README.md) |
|
|
85
85
|
|
|
86
86
|
## Status
|
|
87
87
|
|
|
@@ -103,26 +103,27 @@ through to it. Where a part sits inside a component, a prop names it, and
|
|
|
103
103
|
each part carries a `data-slot` for parent selectors such as
|
|
104
104
|
`*:data-[slot=stat-tile-value]:text-2xl`.
|
|
105
105
|
|
|
106
|
-
| Component | Part props
|
|
107
|
-
| -------------------------------------------------- |
|
|
108
|
-
| `DialogContent` | `overlayClassName`, `closeButtonClassName`
|
|
109
|
-
| `SelectTrigger` | a `className` height replaces `size`
|
|
110
|
-
| `TooltipContent` | `arrowClassName`
|
|
111
|
-
| `Combobox` | `contentClassName` (panel, default trigger width), `listClassName`; button props go to trigger
|
|
112
|
-
| `SearchPicker` | `listClassName`; div props go to the root
|
|
113
|
-
| `InfoTip` | `contentClassName`; button props go to the trigger
|
|
114
|
-
| `
|
|
115
|
-
| `
|
|
116
|
-
| `
|
|
117
|
-
| `
|
|
118
|
-
| `
|
|
119
|
-
| `
|
|
120
|
-
| `
|
|
121
|
-
| `
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
106
|
+
| Component | Part props | Slots |
|
|
107
|
+
| -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
108
|
+
| `DialogContent` | `overlayClassName`, `closeButtonClassName` | `dialog-close-button` (the built-in X; your own `DialogClose` keeps `dialog-close`) |
|
|
109
|
+
| `SelectTrigger` | a `className` height replaces `size` | `select-icon`, `select-viewport`, `select-item-indicator` |
|
|
110
|
+
| `TooltipContent` | `arrowClassName` | `tooltip-arrow` |
|
|
111
|
+
| `Combobox` | `contentClassName` (panel, default trigger width), `listClassName`; button props go to trigger | `combobox-search`, `combobox-list`, `combobox-option` |
|
|
112
|
+
| `SearchPicker` | `listClassName`; div props go to the root | `search-picker-option`, `search-picker-empty` |
|
|
113
|
+
| `InfoTip` | `contentClassName`; button props go to the trigger | |
|
|
114
|
+
| `Label`, `FieldLabel` | `requiredClassName` | `label-required` |
|
|
115
|
+
| `DatePickerButton`, `DateTimePickerButton` | `contentClassName`, `calendarProps` (disabled days, `startMonth`, `endMonth`, `weekStartsOn`, `locale`); `minDate` / `maxDate` add to its disabled days | `date-picker-value`, `date-picker-footer`, `date-time-picker-time` |
|
|
116
|
+
| `form/*` fields | `fieldClassName` (wrapper); `className` styles the control; `FormSelectField` `contentClassName`; `FormMarkdownField` `previewClassName` | `form-field`, `form-field-header`, `form-field-description`, `form-field-error`, `form-feedback`, `form-markdown-preview` |
|
|
117
|
+
| `Table` | `containerClassName` (the scroll box, e.g. `max-h-96 overflow-y-auto`); `className` styles the `<table>` | |
|
|
118
|
+
| `TabsList`, `TabsNav` | `scrollerClassName`; `TabsNav` `listClassName`; a list `className` height replaces `h-11` | `tabs-scroller` |
|
|
119
|
+
| `SortableTableHead` | `buttonClassName` | `sortable-table-head-button` |
|
|
120
|
+
| `AccordionTrigger`, `AccordionContent` | `headerClassName`; `AccordionContent` `className` styles the inner box | `accordion-header`, `accordion-content-inner` |
|
|
121
|
+
| `Progress` | `indicatorClassName` | `progress-indicator` |
|
|
122
|
+
| `Meter` | `fillClassName` | `meter-fill`, `meter-marker` |
|
|
123
|
+
| `StatTile` | | `stat-tile-label`, `stat-tile-value`, `stat-tile-sub` |
|
|
124
|
+
| `ColorLegend` | | `color-legend-title`, `-list`, `-item`, `-swatch`, `-range`, `-label`, `-caption` |
|
|
125
|
+
| `RadioGroupItem` | | `radio-group-dot` |
|
|
126
|
+
| `EmptyPanel`, `ErrorFallback`, `TableBodySkeleton` | `className` | `empty-panel`, `error-boundary` |
|
|
126
127
|
|
|
127
128
|
## Forms
|
|
128
129
|
|
|
@@ -155,6 +156,13 @@ description. The date fields label their trigger with `aria-labelledby` (the
|
|
|
155
156
|
label, then the trigger) rather than `<label for>`, so the picked date is
|
|
156
157
|
part of its name.
|
|
157
158
|
|
|
159
|
+
Set `required` on a `form/*` field to mark its label with a visible `*`.
|
|
160
|
+
The marker is hidden from screen readers, which announce the control's own
|
|
161
|
+
`required` (`aria-required` on the select trigger). The date pickers are
|
|
162
|
+
buttons, which cannot be required, so their fields add "Required" to the
|
|
163
|
+
trigger's description instead. Outside the form adapters, `<Label required>`
|
|
164
|
+
draws the same marker; set `required` on the control yourself.
|
|
165
|
+
|
|
158
166
|
`DatePickerButton` is single-day by default. For a from/to range, set
|
|
159
167
|
`mode="range"`; `value` is `{ from, to }` (each side `Date` or `yyyy-MM-dd`)
|
|
160
168
|
and `onChange` receives `DateRange | undefined`. The trigger uses the same
|
|
@@ -163,6 +171,10 @@ one month below `md` and two months at `md+` unless `numberOfMonths` is set,
|
|
|
163
171
|
clamps to the viewport with collision padding and scroll, stays `w-auto` so
|
|
164
172
|
it does not collapse to the default popover width, and includes a Clear
|
|
165
173
|
action. Pass `side` to prefer a placement; Radix still flips on collision.
|
|
174
|
+
`minDate` and `maxDate` (each `Date` or `yyyy-MM-dd`) disable the days
|
|
175
|
+
outside them, on top of any `calendarProps.disabled`, and stop month paging
|
|
176
|
+
there unless `calendarProps` sets `startMonth` / `endMonth`. They limit the
|
|
177
|
+
calendar only; validate a value set some other way in your form.
|
|
166
178
|
The popover is a dialog named "Choose date" ("Choose dates" in range mode),
|
|
167
179
|
and focus moves to the selected day, or today, when it opens. Pass
|
|
168
180
|
`aria-labelledby` and `aria-describedby` to name and describe the trigger;
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ React 19, Tailwind 4, and Node >=20.19 are required. Next 16 is needed only
|
|
|
8
8
|
when using `next/tabs-nav-link`.
|
|
9
9
|
|
|
10
10
|
```sh
|
|
11
|
-
npm install --save-exact atlas-react-kit@0.
|
|
11
|
+
npm install --save-exact atlas-react-kit@0.12.0
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
Import components by subpath:
|
|
@@ -128,7 +128,7 @@ artifact with the package owner's npm login:
|
|
|
128
128
|
```sh
|
|
129
129
|
mkdir -p release-artifacts
|
|
130
130
|
npm pack --pack-destination release-artifacts
|
|
131
|
-
npm publish ./release-artifacts/atlas-react-kit-0.
|
|
131
|
+
npm publish ./release-artifacts/atlas-react-kit-0.12.0.tgz --access public
|
|
132
132
|
```
|
|
133
133
|
|
|
134
134
|
Pin an exact package version in consuming applications. Roll back an upgrade by
|
package/dist/date-picker.d.ts
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import type { DateRange } from "react-day-picker";
|
|
3
|
+
import { toDate, type DateLike } from "./internal/date-like.js";
|
|
3
4
|
import { type PickerCalendarProps, type PickerTriggerProps } from "./picker-shell.js";
|
|
4
5
|
export type { DateRange };
|
|
5
|
-
export type DateLike
|
|
6
|
+
export type { DateLike };
|
|
7
|
+
export { toDate };
|
|
6
8
|
/** From/to value for `DatePickerButton` in range mode; each side is `DateLike`. */
|
|
7
9
|
export type DatePickerRangeValue = {
|
|
8
10
|
from?: DateLike;
|
|
9
11
|
to?: DateLike;
|
|
10
12
|
};
|
|
11
|
-
/**
|
|
12
|
-
* Coerce a date-like value to a local `Date`.
|
|
13
|
-
* Plain calendar dates (`yyyy-MM-dd`) parse as local midnight so they do not
|
|
14
|
-
* shift when the runtime timezone is behind UTC.
|
|
15
|
-
*/
|
|
16
|
-
export declare function toDate(value: DateLike): Date | undefined;
|
|
17
13
|
/** Format a Date as a timezone-safe calendar date (`yyyy-MM-dd`). */
|
|
18
14
|
export declare function formatLocalDate(date: Date): string;
|
|
19
15
|
export type { PickerCalendarProps as DatePickerCalendarProps };
|
package/dist/date-picker.js
CHANGED
|
@@ -1,42 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import { format, isSameDay
|
|
4
|
+
import { format, isSameDay } from "date-fns";
|
|
5
|
+
import { toDate } from "./internal/date-like.js";
|
|
5
6
|
import { MD_MEDIA_QUERY, useMediaQuery } from "./internal/use-media-query.js";
|
|
6
7
|
import { LazyCalendar } from "./lazy-calendar.js";
|
|
7
|
-
import { PickerClearFooter, PickerShell, } from "./picker-shell.js";
|
|
8
|
-
|
|
9
|
-
* Coerce a date-like value to a local `Date`.
|
|
10
|
-
* Plain calendar dates (`yyyy-MM-dd`) parse as local midnight so they do not
|
|
11
|
-
* shift when the runtime timezone is behind UTC.
|
|
12
|
-
*/
|
|
13
|
-
export function toDate(value) {
|
|
14
|
-
if (value == null || value === "")
|
|
15
|
-
return undefined;
|
|
16
|
-
if (value instanceof Date) {
|
|
17
|
-
return Number.isNaN(value.getTime()) ? undefined : value;
|
|
18
|
-
}
|
|
19
|
-
if (typeof value === "number") {
|
|
20
|
-
const date = new Date(value);
|
|
21
|
-
return Number.isNaN(date.getTime()) ? undefined : date;
|
|
22
|
-
}
|
|
23
|
-
const trimmed = value.trim();
|
|
24
|
-
const ymd = /^(\d{4})-(\d{2})-(\d{2})$/.exec(trimmed);
|
|
25
|
-
if (ymd) {
|
|
26
|
-
const year = Number(ymd[1]);
|
|
27
|
-
const month = Number(ymd[2]);
|
|
28
|
-
const day = Number(ymd[3]);
|
|
29
|
-
const date = new Date(year, month - 1, day);
|
|
30
|
-
if (date.getFullYear() !== year ||
|
|
31
|
-
date.getMonth() !== month - 1 ||
|
|
32
|
-
date.getDate() !== day) {
|
|
33
|
-
return undefined;
|
|
34
|
-
}
|
|
35
|
-
return date;
|
|
36
|
-
}
|
|
37
|
-
const parsed = parseISO(trimmed);
|
|
38
|
-
return Number.isNaN(parsed.getTime()) ? undefined : parsed;
|
|
39
|
-
}
|
|
8
|
+
import { PickerClearFooter, PickerShell, limitCalendarProps, } from "./picker-shell.js";
|
|
9
|
+
export { toDate };
|
|
40
10
|
/** Format a Date as a timezone-safe calendar date (`yyyy-MM-dd`). */
|
|
41
11
|
export function formatLocalDate(date) {
|
|
42
12
|
return format(date, "yyyy-MM-dd");
|
|
@@ -52,15 +22,15 @@ function formatRangeTriggerLabel(from, to, placeholder) {
|
|
|
52
22
|
return format(from, "PPP");
|
|
53
23
|
return `${format(from, "PPP")} – ${format(to, "PPP")}`;
|
|
54
24
|
}
|
|
55
|
-
function DatePickerSingleButton({ value, onChange, placeholder = "Pick a date", numberOfMonths, defaultMonth, calendarProps, ...shell }) {
|
|
25
|
+
function DatePickerSingleButton({ value, onChange, placeholder = "Pick a date", numberOfMonths, defaultMonth, calendarProps, minDate, maxDate, ...shell }) {
|
|
56
26
|
const [open, setOpen] = React.useState(false);
|
|
57
27
|
const date = toDate(value);
|
|
58
|
-
return (_jsx(PickerShell, { ...shell, empty: !date, label: date ? format(date, "PPP") : placeholder, dialogLabel: "Choose date", open: open, onOpenChange: setOpen, children: _jsx(LazyCalendar, { ...calendarProps, mode: "single", autoFocus: true, selected: date, defaultMonth: defaultMonth ?? date, numberOfMonths: numberOfMonths, onSelect: (next) => {
|
|
28
|
+
return (_jsx(PickerShell, { ...shell, empty: !date, label: date ? format(date, "PPP") : placeholder, dialogLabel: "Choose date", open: open, onOpenChange: setOpen, children: _jsx(LazyCalendar, { ...limitCalendarProps(calendarProps, minDate, maxDate), mode: "single", autoFocus: true, selected: date, defaultMonth: defaultMonth ?? date, numberOfMonths: numberOfMonths, onSelect: (next) => {
|
|
59
29
|
onChange(next);
|
|
60
30
|
setOpen(false);
|
|
61
31
|
} }) }));
|
|
62
32
|
}
|
|
63
|
-
function DatePickerRangeButton({ value, onChange, placeholder = "Pick a date range", numberOfMonths, defaultMonth, calendarProps, ...shell }) {
|
|
33
|
+
function DatePickerRangeButton({ value, onChange, placeholder = "Pick a date range", numberOfMonths, defaultMonth, calendarProps, minDate, maxDate, ...shell }) {
|
|
64
34
|
const [open, setOpen] = React.useState(false);
|
|
65
35
|
const mdUp = useMediaQuery(MD_MEDIA_QUERY);
|
|
66
36
|
const months = numberOfMonths ?? (mdUp ? 2 : 1);
|
|
@@ -76,7 +46,7 @@ function DatePickerRangeButton({ value, onChange, placeholder = "Pick a date ran
|
|
|
76
46
|
restartRef.current = Boolean(from && to);
|
|
77
47
|
setOpen(next);
|
|
78
48
|
};
|
|
79
|
-
return (_jsxs(PickerShell, { ...shell, empty: !from, label: formatRangeTriggerLabel(from, to, placeholder), dialogLabel: "Choose dates", open: open, onOpenChange: onOpenChange, children: [_jsx(LazyCalendar, { ...calendarProps, mode: "range", autoFocus: true, selected: selected, defaultMonth: defaultMonth ?? from ?? to, numberOfMonths: months, onSelect: (next, day) => {
|
|
49
|
+
return (_jsxs(PickerShell, { ...shell, empty: !from, label: formatRangeTriggerLabel(from, to, placeholder), dialogLabel: "Choose dates", open: open, onOpenChange: onOpenChange, children: [_jsx(LazyCalendar, { ...limitCalendarProps(calendarProps, minDate, maxDate), mode: "range", autoFocus: true, selected: selected, defaultMonth: defaultMonth ?? from ?? to, numberOfMonths: months, onSelect: (next, day) => {
|
|
80
50
|
const range = restartRef.current ? { from: day, to: day } : next;
|
|
81
51
|
restartRef.current = false;
|
|
82
52
|
onChange(range);
|
|
@@ -25,4 +25,4 @@ export declare function resolveTimeStep(step: number | undefined): number;
|
|
|
25
25
|
* stays on-screen at a 390px width. `className` styles the trigger,
|
|
26
26
|
* `contentClassName` the popover, and `calendarProps` reaches Day Picker.
|
|
27
27
|
*/
|
|
28
|
-
export declare function DateTimePickerButton({ value, onChange, timeStep, placeholder, defaultMonth, calendarProps, ...shell }: DateTimePickerButtonProps): React.JSX.Element;
|
|
28
|
+
export declare function DateTimePickerButton({ value, onChange, timeStep, placeholder, defaultMonth, calendarProps, minDate, maxDate, ...shell }: DateTimePickerButtonProps): React.JSX.Element;
|
package/dist/date-time-picker.js
CHANGED
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { format } from "date-fns";
|
|
5
5
|
import { LazyCalendar } from "./lazy-calendar.js";
|
|
6
|
-
import { PickerClearFooter, PickerShell, } from "./picker-shell.js";
|
|
6
|
+
import { PickerClearFooter, PickerShell, limitCalendarProps, } from "./picker-shell.js";
|
|
7
7
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "./select.js";
|
|
8
8
|
/** Default clock increment, in minutes. Must divide 60. */
|
|
9
9
|
export const DEFAULT_TIME_STEP = 15;
|
|
@@ -68,14 +68,14 @@ function preventDismissForPortaledSelect(event) {
|
|
|
68
68
|
* stays on-screen at a 390px width. `className` styles the trigger,
|
|
69
69
|
* `contentClassName` the popover, and `calendarProps` reaches Day Picker.
|
|
70
70
|
*/
|
|
71
|
-
export function DateTimePickerButton({ value, onChange, timeStep, placeholder = "Pick a date and time", defaultMonth, calendarProps, ...shell }) {
|
|
71
|
+
export function DateTimePickerButton({ value, onChange, timeStep, placeholder = "Pick a date and time", defaultMonth, calendarProps, minDate, maxDate, ...shell }) {
|
|
72
72
|
const [open, setOpen] = React.useState(false);
|
|
73
73
|
const date = value ?? null;
|
|
74
74
|
const step = resolveTimeStep(timeStep);
|
|
75
75
|
const empty = date == null;
|
|
76
76
|
const hourId = React.useId();
|
|
77
77
|
const minuteId = React.useId();
|
|
78
|
-
return (_jsxs(PickerShell, { ...shell, empty: empty, label: date ? format(date, "PPP p") : placeholder, dialogLabel: "Choose date and time", open: open, onOpenChange: setOpen, onInteractOutside: preventDismissForPortaledSelect, children: [_jsx(LazyCalendar, { ...calendarProps, mode: "single", autoFocus: true, selected: date ?? undefined, defaultMonth: defaultMonth ?? date ?? undefined, onSelect: (next) => {
|
|
78
|
+
return (_jsxs(PickerShell, { ...shell, empty: empty, label: date ? format(date, "PPP p") : placeholder, dialogLabel: "Choose date and time", open: open, onOpenChange: setOpen, onInteractOutside: preventDismissForPortaledSelect, children: [_jsx(LazyCalendar, { ...limitCalendarProps(calendarProps, minDate, maxDate), mode: "single", autoFocus: true, selected: date ?? undefined, defaultMonth: defaultMonth ?? date ?? undefined, onSelect: (next) => {
|
|
79
79
|
// Re-clicking a selected day would clear it; require Clear instead
|
|
80
80
|
// so a time already set is not dropped by an accidental second tap.
|
|
81
81
|
if (!next)
|
|
@@ -7,6 +7,12 @@ type Props = {
|
|
|
7
7
|
meta?: FieldMetaState;
|
|
8
8
|
placeholder?: string;
|
|
9
9
|
disabled?: boolean;
|
|
10
|
+
/** Marks the label and adds "Required" to the trigger's description. */
|
|
11
|
+
required?: boolean;
|
|
12
|
+
/** Earliest day that can be picked; earlier days are disabled. */
|
|
13
|
+
minDate?: DateLike;
|
|
14
|
+
/** Latest day that can be picked; later days are disabled. */
|
|
15
|
+
maxDate?: DateLike;
|
|
10
16
|
/** Forwarded to the date picker trigger. */
|
|
11
17
|
className?: string;
|
|
12
18
|
/** Classes for the field wrapper (label, trigger, and error). */
|
|
@@ -17,5 +23,5 @@ type Props = {
|
|
|
17
23
|
calendarProps?: DatePickerCalendarProps;
|
|
18
24
|
};
|
|
19
25
|
/** Date picker with built-in field error text and invalid styling. */
|
|
20
|
-
export declare function FormDatePickerField({ onValueChange, label, meta, fieldClassName, ...picker }: Props): import("react").JSX.Element;
|
|
26
|
+
export declare function FormDatePickerField({ onValueChange, label, meta, required, fieldClassName, ...picker }: Props): import("react").JSX.Element;
|
|
21
27
|
export {};
|
|
@@ -4,8 +4,8 @@ import { useId } from "react";
|
|
|
4
4
|
import { DatePickerButton, } from "../date-picker.js";
|
|
5
5
|
import { FormFieldShell, metaErrorText, pickerFieldAria, } from "./field-shell.js";
|
|
6
6
|
/** Date picker with built-in field error text and invalid styling. */
|
|
7
|
-
export function FormDatePickerField({ onValueChange, label, meta, fieldClassName, ...picker }) {
|
|
7
|
+
export function FormDatePickerField({ onValueChange, label, meta, required, fieldClassName, ...picker }) {
|
|
8
8
|
const errorText = metaErrorText(meta);
|
|
9
9
|
const id = useId();
|
|
10
|
-
return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, className: fieldClassName, children: _jsx(DatePickerButton, { ...picker, id: id, onChange: onValueChange, ...pickerFieldAria(id, errorText) }) }));
|
|
10
|
+
return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, required: required, className: fieldClassName, children: _jsx(DatePickerButton, { ...picker, id: id, onChange: onValueChange, ...pickerFieldAria(id, errorText, required) }) }));
|
|
11
11
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DatePickerCalendarProps } from "../date-picker.js";
|
|
1
|
+
import type { DateLike, DatePickerCalendarProps } from "../date-picker.js";
|
|
2
2
|
import type { FieldMetaState } from "./field-error.js";
|
|
3
3
|
type Props = {
|
|
4
4
|
value: Date | null;
|
|
@@ -7,6 +7,12 @@ type Props = {
|
|
|
7
7
|
meta?: FieldMetaState;
|
|
8
8
|
placeholder?: string;
|
|
9
9
|
disabled?: boolean;
|
|
10
|
+
/** Marks the label and adds "Required" to the trigger's description. */
|
|
11
|
+
required?: boolean;
|
|
12
|
+
/** Earliest day that can be picked; earlier days are disabled. */
|
|
13
|
+
minDate?: DateLike;
|
|
14
|
+
/** Latest day that can be picked; later days are disabled. */
|
|
15
|
+
maxDate?: DateLike;
|
|
10
16
|
/** Minute increment forwarded to the picker. */
|
|
11
17
|
timeStep?: number;
|
|
12
18
|
/** Forwarded to the date-time picker trigger. */
|
|
@@ -19,5 +25,5 @@ type Props = {
|
|
|
19
25
|
calendarProps?: DatePickerCalendarProps;
|
|
20
26
|
};
|
|
21
27
|
/** Date-time picker with built-in field error text and invalid styling. */
|
|
22
|
-
export declare function FormDateTimePickerField({ onValueChange, label, meta, fieldClassName, ...picker }: Props): import("react").JSX.Element;
|
|
28
|
+
export declare function FormDateTimePickerField({ onValueChange, label, meta, required, fieldClassName, ...picker }: Props): import("react").JSX.Element;
|
|
23
29
|
export {};
|
|
@@ -4,8 +4,8 @@ import { useId } from "react";
|
|
|
4
4
|
import { DateTimePickerButton } from "../date-time-picker.js";
|
|
5
5
|
import { FormFieldShell, metaErrorText, pickerFieldAria, } from "./field-shell.js";
|
|
6
6
|
/** Date-time picker with built-in field error text and invalid styling. */
|
|
7
|
-
export function FormDateTimePickerField({ onValueChange, label, meta, fieldClassName, ...picker }) {
|
|
7
|
+
export function FormDateTimePickerField({ onValueChange, label, meta, required, fieldClassName, ...picker }) {
|
|
8
8
|
const errorText = metaErrorText(meta);
|
|
9
9
|
const id = useId();
|
|
10
|
-
return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, className: fieldClassName, children: _jsx(DateTimePickerButton, { ...picker, id: id, onChange: onValueChange, ...pickerFieldAria(id, errorText) }) }));
|
|
10
|
+
return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, required: required, className: fieldClassName, children: _jsx(DateTimePickerButton, { ...picker, id: id, onChange: onValueChange, ...pickerFieldAria(id, errorText, required) }) }));
|
|
11
11
|
}
|
|
@@ -12,6 +12,11 @@ export declare function invalidWhen(errorText: string | undefined): true | undef
|
|
|
12
12
|
export declare function labelIdFor(id: string): string;
|
|
13
13
|
/** Id of the error text `FormFieldShell` renders for control `id`. */
|
|
14
14
|
export declare function errorIdFor(id: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Id of the hidden "Required" text `FormFieldShell` renders for a required
|
|
17
|
+
* control `id`, for controls that cannot carry `required` themselves.
|
|
18
|
+
*/
|
|
19
|
+
export declare function requiredIdFor(id: string): string;
|
|
15
20
|
/**
|
|
16
21
|
* `aria-describedby` for the control: the given hint ids, plus the error
|
|
17
22
|
* text while there is one, or `undefined` when there is nothing to read.
|
|
@@ -20,9 +25,10 @@ export declare function describedBy(id: string, errorText: string | undefined, .
|
|
|
20
25
|
/**
|
|
21
26
|
* Trigger ARIA for the date and date-time fields: invalid while there is
|
|
22
27
|
* error text, named "label, value" (a `<label for>` alone would hide the
|
|
23
|
-
* picked value), and described by
|
|
28
|
+
* picked value), and described by "Required" (a button cannot take
|
|
29
|
+
* `aria-required`) and the error.
|
|
24
30
|
*/
|
|
25
|
-
export declare function pickerFieldAria(id: string, errorText: string | undefined): {
|
|
31
|
+
export declare function pickerFieldAria(id: string, errorText: string | undefined, required?: boolean): {
|
|
26
32
|
"aria-invalid": boolean | undefined;
|
|
27
33
|
"aria-labelledby": string;
|
|
28
34
|
"aria-describedby": string | undefined;
|
|
@@ -33,13 +39,15 @@ export declare function pickerFieldAria(id: string, errorText: string | undefine
|
|
|
33
39
|
* Set `labelFor` to false when the control is not rendered (Markdown
|
|
34
40
|
* preview), so the label does not point at a missing element.
|
|
35
41
|
* `className` styles the wrapper (`data-slot="form-field"`), e.g. a width.
|
|
42
|
+
* `required` marks the label; the control still sets its own `required`.
|
|
36
43
|
*/
|
|
37
|
-
export declare function FormFieldShell({ id, label, labelAction, errorText, labelFor, className, children, }: {
|
|
44
|
+
export declare function FormFieldShell({ id, label, labelAction, errorText, labelFor, required, className, children, }: {
|
|
38
45
|
id: string;
|
|
39
46
|
label: string;
|
|
40
47
|
labelAction?: ReactNode;
|
|
41
48
|
errorText: string | undefined;
|
|
42
49
|
labelFor?: boolean;
|
|
50
|
+
required?: boolean;
|
|
43
51
|
className?: string;
|
|
44
52
|
children: ReactNode;
|
|
45
53
|
}): import("react").JSX.Element;
|
package/dist/form/field-shell.js
CHANGED
|
@@ -23,6 +23,13 @@ export function labelIdFor(id) {
|
|
|
23
23
|
export function errorIdFor(id) {
|
|
24
24
|
return `${id}-error`;
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Id of the hidden "Required" text `FormFieldShell` renders for a required
|
|
28
|
+
* control `id`, for controls that cannot carry `required` themselves.
|
|
29
|
+
*/
|
|
30
|
+
export function requiredIdFor(id) {
|
|
31
|
+
return `${id}-required`;
|
|
32
|
+
}
|
|
26
33
|
/**
|
|
27
34
|
* `aria-describedby` for the control: the given hint ids, plus the error
|
|
28
35
|
* text while there is one, or `undefined` when there is nothing to read.
|
|
@@ -34,13 +41,14 @@ export function describedBy(id, errorText, ...hintIds) {
|
|
|
34
41
|
/**
|
|
35
42
|
* Trigger ARIA for the date and date-time fields: invalid while there is
|
|
36
43
|
* error text, named "label, value" (a `<label for>` alone would hide the
|
|
37
|
-
* picked value), and described by
|
|
44
|
+
* picked value), and described by "Required" (a button cannot take
|
|
45
|
+
* `aria-required`) and the error.
|
|
38
46
|
*/
|
|
39
|
-
export function pickerFieldAria(id, errorText) {
|
|
47
|
+
export function pickerFieldAria(id, errorText, required) {
|
|
40
48
|
return {
|
|
41
49
|
"aria-invalid": invalidWhen(errorText),
|
|
42
50
|
"aria-labelledby": `${labelIdFor(id)} ${pickerValueId(id)}`,
|
|
43
|
-
"aria-describedby": describedBy(id, errorText),
|
|
51
|
+
"aria-describedby": describedBy(id, errorText, required ? requiredIdFor(id) : undefined),
|
|
44
52
|
};
|
|
45
53
|
}
|
|
46
54
|
/**
|
|
@@ -49,8 +57,9 @@ export function pickerFieldAria(id, errorText) {
|
|
|
49
57
|
* Set `labelFor` to false when the control is not rendered (Markdown
|
|
50
58
|
* preview), so the label does not point at a missing element.
|
|
51
59
|
* `className` styles the wrapper (`data-slot="form-field"`), e.g. a width.
|
|
60
|
+
* `required` marks the label; the control still sets its own `required`.
|
|
52
61
|
*/
|
|
53
|
-
export function FormFieldShell({ id, label, labelAction, errorText, labelFor = true, className, children, }) {
|
|
54
|
-
const labelNode = (_jsx(Label, { id: labelIdFor(id), htmlFor: labelFor ? id : undefined, children: label }));
|
|
55
|
-
return (_jsxs("div", { "data-slot": "form-field", className: cn("flex flex-col gap-1.5", className), children: [labelAction ? (_jsxs("div", { "data-slot": "form-field-header", className: "flex items-center justify-between gap-2", children: [labelNode, labelAction] })) : (labelNode), children, _jsx(FieldError, { id: errorIdFor(id), text: errorText })] }));
|
|
62
|
+
export function FormFieldShell({ id, label, labelAction, errorText, labelFor = true, required, className, children, }) {
|
|
63
|
+
const labelNode = (_jsx(Label, { id: labelIdFor(id), htmlFor: labelFor ? id : undefined, required: required, children: label }));
|
|
64
|
+
return (_jsxs("div", { "data-slot": "form-field", className: cn("flex flex-col gap-1.5", className), children: [labelAction ? (_jsxs("div", { "data-slot": "form-field-header", className: "flex items-center justify-between gap-2", children: [labelNode, labelAction] })) : (labelNode), children, required ? (_jsx("span", { id: requiredIdFor(id), hidden: true, children: "Required" })) : null, _jsx(FieldError, { id: errorIdFor(id), text: errorText })] }));
|
|
56
65
|
}
|
|
@@ -3,7 +3,7 @@ import type { TextFieldApi } from "./text-field.js";
|
|
|
3
3
|
* GFM body field: Write is a textarea, Preview is the sanitized Markdown
|
|
4
4
|
* renderer. String-in/string-out — not a WYSIWYG document model.
|
|
5
5
|
*/
|
|
6
|
-
export declare function FormMarkdownField({ field, id, label, placeholder, rows, showError, className, fieldClassName, previewClassName, }: {
|
|
6
|
+
export declare function FormMarkdownField({ field, id, label, placeholder, rows, showError, required, className, fieldClassName, previewClassName, }: {
|
|
7
7
|
field: TextFieldApi;
|
|
8
8
|
/**
|
|
9
9
|
* Control id the label points at. Defaults to `field.name`; pass a unique
|
|
@@ -14,6 +14,8 @@ export declare function FormMarkdownField({ field, id, label, placeholder, rows,
|
|
|
14
14
|
placeholder?: string;
|
|
15
15
|
rows?: number;
|
|
16
16
|
showError?: boolean;
|
|
17
|
+
/** Sets `required` on the Write textarea and marks the label. */
|
|
18
|
+
required?: boolean;
|
|
17
19
|
/** Forwarded to the Write textarea. */
|
|
18
20
|
className?: string;
|
|
19
21
|
/** Classes for the field wrapper (label row, body, and error). */
|
|
@@ -16,7 +16,7 @@ const Markdown = lazy(() => loadMarkdown().then((module) => ({ default: module.M
|
|
|
16
16
|
* GFM body field: Write is a textarea, Preview is the sanitized Markdown
|
|
17
17
|
* renderer. String-in/string-out — not a WYSIWYG document model.
|
|
18
18
|
*/
|
|
19
|
-
export function FormMarkdownField({ field, id = field.name, label, placeholder = "Markdown supported", rows = 5, showError = true, className, fieldClassName, previewClassName, }) {
|
|
19
|
+
export function FormMarkdownField({ field, id = field.name, label, placeholder = "Markdown supported", rows = 5, showError = true, required, className, fieldClassName, previewClassName, }) {
|
|
20
20
|
const [mode, setMode] = useState("write");
|
|
21
21
|
const onChange = onInputChange(field.handleChange);
|
|
22
22
|
const errorText = showError ? metaErrorText(field.state.meta) : undefined;
|
|
@@ -25,5 +25,5 @@ export function FormMarkdownField({ field, id = field.name, label, placeholder =
|
|
|
25
25
|
if (value === "write" || value === "preview")
|
|
26
26
|
setMode(value);
|
|
27
27
|
}
|
|
28
|
-
return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, labelFor: mode === "write", className: fieldClassName, labelAction: _jsxs(ToggleGroup, { type: "single", value: mode, onValueChange: handleModeChange, variant: "outline", size: "sm", spacing: 0, "aria-label": `${label} mode`, children: [_jsx(ToggleGroupItem, { value: "write", children: "Write" }), _jsx(ToggleGroupItem, { value: "preview", onPointerEnter: preloadMarkdown, onFocus: preloadMarkdown, children: "Preview" })] }), children: mode === "write" ? (_jsx(Textarea, { id: id, value: field.state.value, onChange: onChange, onBlur: field.handleBlur, placeholder: placeholder, rows: rows, className: className, "aria-invalid": invalidWhen(errorText), "aria-describedby": describedBy(id, errorText) })) : (_jsx("div", { "data-slot": "form-markdown-preview", className: cn(fieldSurface, "min-h-16", previewClassName), "aria-live": "polite", children: trimmed ? (_jsx(Suspense, { fallback: _jsx(Spinner, { "aria-label": "Loading preview" }), children: _jsx(Markdown, { content: trimmed }) })) : (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nothing to preview" })) })) }));
|
|
28
|
+
return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, labelFor: mode === "write", required: required, className: fieldClassName, labelAction: _jsxs(ToggleGroup, { type: "single", value: mode, onValueChange: handleModeChange, variant: "outline", size: "sm", spacing: 0, "aria-label": `${label} mode`, children: [_jsx(ToggleGroupItem, { value: "write", children: "Write" }), _jsx(ToggleGroupItem, { value: "preview", onPointerEnter: preloadMarkdown, onFocus: preloadMarkdown, children: "Preview" })] }), children: mode === "write" ? (_jsx(Textarea, { id: id, value: field.state.value, onChange: onChange, onBlur: field.handleBlur, placeholder: placeholder, rows: rows, required: required, className: className, "aria-invalid": invalidWhen(errorText), "aria-describedby": describedBy(id, errorText) })) : (_jsx("div", { "data-slot": "form-markdown-preview", className: cn(fieldSurface, "min-h-16", previewClassName), "aria-live": "polite", children: trimmed ? (_jsx(Suspense, { fallback: _jsx(Spinner, { "aria-label": "Loading preview" }), children: _jsx(Markdown, { content: trimmed }) })) : (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nothing to preview" })) })) }));
|
|
29
29
|
}
|
|
@@ -6,6 +6,8 @@ type Props = {
|
|
|
6
6
|
label: string;
|
|
7
7
|
onValueChange: (value: string) => void;
|
|
8
8
|
placeholder?: string;
|
|
9
|
+
/** Requires a value (native form validation) and marks the label. */
|
|
10
|
+
required?: boolean;
|
|
9
11
|
/** Forwarded to the select trigger. */
|
|
10
12
|
className?: string;
|
|
11
13
|
/** Classes for the field wrapper (label, trigger, and error). */
|
|
@@ -15,5 +17,5 @@ type Props = {
|
|
|
15
17
|
children: ReactNode;
|
|
16
18
|
};
|
|
17
19
|
/** Select control with built-in field error text and invalid styling. */
|
|
18
|
-
export declare function FormSelectField({ value, meta, label, onValueChange, placeholder, className, fieldClassName, contentClassName, children, }: Props): import("react").JSX.Element;
|
|
20
|
+
export declare function FormSelectField({ value, meta, label, onValueChange, placeholder, required, className, fieldClassName, contentClassName, children, }: Props): import("react").JSX.Element;
|
|
19
21
|
export {};
|
|
@@ -4,8 +4,8 @@ import { useId } from "react";
|
|
|
4
4
|
import { Select, SelectContent, SelectTrigger, SelectValue } from "../select.js";
|
|
5
5
|
import { describedBy, FormFieldShell, invalidWhen, metaErrorText, } from "./field-shell.js";
|
|
6
6
|
/** Select control with built-in field error text and invalid styling. */
|
|
7
|
-
export function FormSelectField({ value, meta, label, onValueChange, placeholder, className, fieldClassName, contentClassName, children, }) {
|
|
7
|
+
export function FormSelectField({ value, meta, label, onValueChange, placeholder, required, className, fieldClassName, contentClassName, children, }) {
|
|
8
8
|
const errorText = metaErrorText(meta);
|
|
9
9
|
const id = useId();
|
|
10
|
-
return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, className: fieldClassName, children: _jsxs(Select, { value: value, onValueChange: onValueChange, children: [_jsx(SelectTrigger, { id: id, className: className, "aria-invalid": invalidWhen(errorText), "aria-describedby": describedBy(id, errorText), children: _jsx(SelectValue, { placeholder: placeholder }) }), _jsx(SelectContent, { className: contentClassName, children: children })] }) }));
|
|
10
|
+
return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, required: required, className: fieldClassName, children: _jsxs(Select, { value: value, onValueChange: onValueChange, required: required, children: [_jsx(SelectTrigger, { id: id, className: className, "aria-required": required || undefined, "aria-invalid": invalidWhen(errorText), "aria-describedby": describedBy(id, errorText), children: _jsx(SelectValue, { placeholder: placeholder }) }), _jsx(SelectContent, { className: contentClassName, children: children })] }) }));
|
|
11
11
|
}
|
|
@@ -28,6 +28,7 @@ export declare function FormTextField({ field, id, label, placeholder, descripti
|
|
|
28
28
|
/** When true (default), shows meta.errors and aria-invalid styling. */
|
|
29
29
|
showError?: boolean;
|
|
30
30
|
autoComplete?: string;
|
|
31
|
+
/** Sets `required` on the control and marks the label. */
|
|
31
32
|
required?: boolean;
|
|
32
33
|
minLength?: number;
|
|
33
34
|
/** Forwarded to the input or textarea. */
|
package/dist/form/text-field.js
CHANGED
|
@@ -21,5 +21,5 @@ export function FormTextField({ field, id = field.name, label, placeholder, desc
|
|
|
21
21
|
"aria-invalid": invalidWhen(errorText),
|
|
22
22
|
"aria-describedby": describedBy(id, errorText, descriptionId),
|
|
23
23
|
};
|
|
24
|
-
return (_jsxs(FormFieldShell, { id: id, label: label, errorText: errorText, className: fieldClassName, children: [description ? (_jsx("p", { id: descriptionId, "data-slot": "form-field-description", className: "text-xs text-muted-foreground", children: description })) : null, multiline ? (_jsx(Textarea, { ...controlProps, rows: rows })) : (_jsx(Input, { ...controlProps, type: inputType, autoFocus: autoFocus }))] }));
|
|
24
|
+
return (_jsxs(FormFieldShell, { id: id, label: label, errorText: errorText, required: required, className: fieldClassName, children: [description ? (_jsx("p", { id: descriptionId, "data-slot": "form-field-description", className: "text-xs text-muted-foreground", children: description })) : null, multiline ? (_jsx(Textarea, { ...controlProps, rows: rows })) : (_jsx(Input, { ...controlProps, type: inputType, autoFocus: autoFocus }))] }));
|
|
25
25
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** A date the pickers accept: a `Date`, `yyyy-MM-dd`, ISO text, or a timestamp. */
|
|
2
|
+
export type DateLike = Date | string | number | null | undefined;
|
|
3
|
+
/**
|
|
4
|
+
* Coerce a date-like value to a local `Date`.
|
|
5
|
+
* Plain calendar dates (`yyyy-MM-dd`) parse as local midnight so they do not
|
|
6
|
+
* shift when the runtime timezone is behind UTC.
|
|
7
|
+
*/
|
|
8
|
+
export declare function toDate(value: DateLike): Date | undefined;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { parseISO } from "date-fns";
|
|
2
|
+
/**
|
|
3
|
+
* Coerce a date-like value to a local `Date`.
|
|
4
|
+
* Plain calendar dates (`yyyy-MM-dd`) parse as local midnight so they do not
|
|
5
|
+
* shift when the runtime timezone is behind UTC.
|
|
6
|
+
*/
|
|
7
|
+
export function toDate(value) {
|
|
8
|
+
if (value == null || value === "")
|
|
9
|
+
return undefined;
|
|
10
|
+
if (value instanceof Date) {
|
|
11
|
+
return Number.isNaN(value.getTime()) ? undefined : value;
|
|
12
|
+
}
|
|
13
|
+
if (typeof value === "number") {
|
|
14
|
+
const date = new Date(value);
|
|
15
|
+
return Number.isNaN(date.getTime()) ? undefined : date;
|
|
16
|
+
}
|
|
17
|
+
const trimmed = value.trim();
|
|
18
|
+
const ymd = /^(\d{4})-(\d{2})-(\d{2})$/.exec(trimmed);
|
|
19
|
+
if (ymd) {
|
|
20
|
+
const year = Number(ymd[1]);
|
|
21
|
+
const month = Number(ymd[2]);
|
|
22
|
+
const day = Number(ymd[3]);
|
|
23
|
+
const date = new Date(year, month - 1, day);
|
|
24
|
+
if (date.getFullYear() !== year ||
|
|
25
|
+
date.getMonth() !== month - 1 ||
|
|
26
|
+
date.getDate() !== day) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
return date;
|
|
30
|
+
}
|
|
31
|
+
const parsed = parseISO(trimmed);
|
|
32
|
+
return Number.isNaN(parsed.getTime()) ? undefined : parsed;
|
|
33
|
+
}
|
package/dist/label.d.ts
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { Label as LabelPrimitive } from "radix-ui";
|
|
3
|
-
|
|
3
|
+
type LabelProps = React.ComponentProps<typeof LabelPrimitive.Root> & {
|
|
4
|
+
/**
|
|
5
|
+
* Shows a visible required marker after the text. The marker is hidden
|
|
6
|
+
* from assistive tech, so set `required` (or `aria-required`) on the
|
|
7
|
+
* control too; screen readers announce it from there.
|
|
8
|
+
*/
|
|
9
|
+
required?: boolean;
|
|
10
|
+
/** Classes for the required marker (`data-slot="label-required"`). */
|
|
11
|
+
requiredClassName?: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Form label. `required` adds a visible marker; style it with
|
|
15
|
+
* `requiredClassName` or the `label-required` slot.
|
|
16
|
+
*/
|
|
17
|
+
declare function Label({ className, required, requiredClassName, children, ...props }: LabelProps): React.JSX.Element;
|
|
4
18
|
export { Label };
|
|
19
|
+
export type { LabelProps };
|
package/dist/label.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Label as LabelPrimitive } from "radix-ui";
|
|
5
5
|
import { cn } from "./utils.js";
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Form label. `required` adds a visible marker; style it with
|
|
8
|
+
* `requiredClassName` or the `label-required` slot.
|
|
9
|
+
*/
|
|
10
|
+
function Label({ className, required, requiredClassName, children, ...props }) {
|
|
11
|
+
return (_jsxs(LabelPrimitive.Root, { "data-slot": "label", className: cn("flex items-center gap-2 text-xs font-semibold tracking-wide uppercase select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50 peer-data-[slot=checkbox]:text-sm peer-data-[slot=checkbox]:font-normal peer-data-[slot=checkbox]:tracking-normal peer-data-[slot=checkbox]:normal-case peer-data-[slot=radio-group-item]:text-sm peer-data-[slot=radio-group-item]:font-normal peer-data-[slot=radio-group-item]:tracking-normal peer-data-[slot=radio-group-item]:normal-case peer-data-[slot=switch]:text-sm peer-data-[slot=switch]:font-normal peer-data-[slot=switch]:tracking-normal peer-data-[slot=switch]:normal-case", className), ...props, children: [children, required ? (_jsx("span", { "data-slot": "label-required", "aria-hidden": "true", className: cn("-ml-1.5 text-destructive", requiredClassName), children: "*" })) : null] }));
|
|
8
12
|
}
|
|
9
13
|
export { Label };
|
package/dist/picker-shell.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import type { PropsBase } from "react-day-picker";
|
|
3
|
+
import { type DateLike } from "./internal/date-like.js";
|
|
3
4
|
import { PopoverContent } from "./popover.js";
|
|
4
5
|
/**
|
|
5
6
|
* Not a public entry. The trigger, popover, and Clear footer that
|
|
@@ -34,10 +35,26 @@ export type PickerTriggerProps = {
|
|
|
34
35
|
side?: React.ComponentProps<typeof PopoverContent>["side"];
|
|
35
36
|
/** Month shown when the popover opens; falls back to the selected date. */
|
|
36
37
|
defaultMonth?: Date;
|
|
38
|
+
/**
|
|
39
|
+
* Earliest day that can be picked. Earlier days are disabled and the
|
|
40
|
+
* calendar does not page before its month.
|
|
41
|
+
*/
|
|
42
|
+
minDate?: DateLike;
|
|
43
|
+
/**
|
|
44
|
+
* Latest day that can be picked. Later days are disabled and the calendar
|
|
45
|
+
* does not page past its month.
|
|
46
|
+
*/
|
|
47
|
+
maxDate?: DateLike;
|
|
37
48
|
/** Day Picker options passed through to the calendar. */
|
|
38
49
|
calendarProps?: PickerCalendarProps;
|
|
39
50
|
};
|
|
40
|
-
|
|
51
|
+
/**
|
|
52
|
+
* `calendarProps` with `minDate` / `maxDate` applied: days outside them join
|
|
53
|
+
* any `disabled` matchers, and they bound paging unless `startMonth` /
|
|
54
|
+
* `endMonth` are set.
|
|
55
|
+
*/
|
|
56
|
+
export declare function limitCalendarProps(calendarProps: PickerCalendarProps | undefined, minDate: DateLike, maxDate: DateLike): PickerCalendarProps | undefined;
|
|
57
|
+
type PickerShellProps = Omit<PickerTriggerProps, "placeholder" | "defaultMonth" | "calendarProps" | "minDate" | "maxDate"> & {
|
|
41
58
|
/** Muted trigger text while nothing is picked. */
|
|
42
59
|
empty: boolean;
|
|
43
60
|
/** Trigger text: the formatted value or the placeholder. */
|
package/dist/picker-shell.js
CHANGED
|
@@ -3,10 +3,38 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { CalendarIcon } from "lucide-react";
|
|
5
5
|
import { Button } from "./button.js";
|
|
6
|
+
import { toDate } from "./internal/date-like.js";
|
|
6
7
|
import { PICKER_COLLISION_PADDING, pickerContentClassName, pickerTriggerClassName, pickerValueId, } from "./internal/form-control.js";
|
|
7
8
|
import { preloadCalendar } from "./lazy-calendar.js";
|
|
8
9
|
import { Popover, PopoverContent, PopoverTrigger } from "./popover.js";
|
|
9
10
|
import { cn } from "./utils.js";
|
|
11
|
+
/**
|
|
12
|
+
* `calendarProps` with `minDate` / `maxDate` applied: days outside them join
|
|
13
|
+
* any `disabled` matchers, and they bound paging unless `startMonth` /
|
|
14
|
+
* `endMonth` are set.
|
|
15
|
+
*/
|
|
16
|
+
export function limitCalendarProps(calendarProps, minDate, maxDate) {
|
|
17
|
+
const min = toDate(minDate);
|
|
18
|
+
const max = toDate(maxDate);
|
|
19
|
+
if (!min && !max)
|
|
20
|
+
return calendarProps;
|
|
21
|
+
const disabled = calendarProps?.disabled;
|
|
22
|
+
const matchers = [
|
|
23
|
+
...(disabled === undefined
|
|
24
|
+
? []
|
|
25
|
+
: Array.isArray(disabled)
|
|
26
|
+
? disabled
|
|
27
|
+
: [disabled]),
|
|
28
|
+
...(min ? [{ before: min }] : []),
|
|
29
|
+
...(max ? [{ after: max }] : []),
|
|
30
|
+
];
|
|
31
|
+
return {
|
|
32
|
+
...calendarProps,
|
|
33
|
+
disabled: matchers,
|
|
34
|
+
startMonth: calendarProps?.startMonth ?? min,
|
|
35
|
+
endMonth: calendarProps?.endMonth ?? max,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
10
38
|
/** Form-control trigger plus the viewport-clamped calendar popover. */
|
|
11
39
|
export function PickerShell({ id, disabled, className, contentClassName, "aria-invalid": ariaInvalid, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, side, empty, label, dialogLabel, open, onOpenChange, onInteractOutside, children, }) {
|
|
12
40
|
return (_jsxs(Popover, { open: open, onOpenChange: onOpenChange, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { id: id, type: "button", variant: "outline", disabled: disabled, "aria-invalid": ariaInvalid, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "data-empty": empty, className: cn(pickerTriggerClassName, className), onPointerEnter: preloadCalendar, onFocus: preloadCalendar, children: [_jsx(CalendarIcon, { "data-icon": "inline-start" }), _jsx("span", { "data-slot": "date-picker-value", id: id ? pickerValueId(id) : undefined, children: label })] }) }), _jsx(PopoverContent, { align: "start", side: side, collisionPadding: PICKER_COLLISION_PADDING, "aria-label": dialogLabel,
|
package/dist/toast.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
2
|
/** Identifies a toast, to replace it in place or dismiss it. */
|
|
3
3
|
type ToastId = string | number;
|
|
4
4
|
/** A button in a toast. Clicking it runs `onClick`, then closes the toast. */
|
|
@@ -50,7 +50,9 @@ interface ToasterProps {
|
|
|
50
50
|
declare const toast: Toast;
|
|
51
51
|
/**
|
|
52
52
|
* Mount one near the app root, and show toasts with `toast()` from this
|
|
53
|
-
* module. Only the props declared here reach the library.
|
|
53
|
+
* module. Only the props declared here reach the library. The stack
|
|
54
|
+
* expands while keyboard focus is inside it, as it does on hover, so every
|
|
55
|
+
* toast's buttons are visible and reachable.
|
|
54
56
|
*/
|
|
55
57
|
declare const Toaster: ({ position, closeButton, expand, visibleToasts, duration, }: ToasterProps) => import("react").JSX.Element;
|
|
56
58
|
export { Toaster, toast };
|
package/dist/toast.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
3
4
|
import { CircleAlertIcon, CircleCheckIcon, InfoIcon, LoaderCircleIcon, XIcon, } from "lucide-react";
|
|
4
5
|
import { useTheme } from "next-themes";
|
|
5
6
|
import { Toaster as Sonner, toast as sonnerToast, } from "sonner";
|
|
@@ -52,12 +53,18 @@ const toast = Object.assign(show(sonnerToast), {
|
|
|
52
53
|
* is the badge itself; the X is 12px. `overflow-visible` on the toast and
|
|
53
54
|
* toaster keeps stacked badges from being clipped.
|
|
54
55
|
*
|
|
56
|
+
* Stacking: while the stack is collapsed, every part of the toasts behind
|
|
57
|
+
* the front one is `invisible`. Their badges would otherwise peek out above
|
|
58
|
+
* the front toast, closer together than the 24px target spacing, and their
|
|
59
|
+
* buttons would stay in the tab order unseen. Hovering or focusing the stack
|
|
60
|
+
* expands it (see `Toaster`), which shows them again.
|
|
61
|
+
*
|
|
55
62
|
* Padding is asymmetric: `pl-4` matches Alert; `pr-6` clears the badge
|
|
56
63
|
* (about 16px of it sits inside the border). Icon-to-text is `gap-x-2.5`
|
|
57
64
|
* so the status icon and copy read as one unit.
|
|
58
65
|
*/
|
|
59
66
|
const classNames = {
|
|
60
|
-
toast: "relative flex w-max max-w-[min(356px,calc(100vw-2rem))] flex-wrap items-center justify-end gap-x-2.5 gap-y-2 overflow-visible rounded-none border border-border bg-popover py-3 pl-4 pr-6 text-sm text-popover-foreground shadow-md",
|
|
67
|
+
toast: "relative flex w-max max-w-[min(356px,calc(100vw-2rem))] flex-wrap items-center justify-end gap-x-2.5 gap-y-2 overflow-visible rounded-none border border-border bg-popover py-3 pl-4 pr-6 text-sm text-popover-foreground shadow-md data-[expanded=false]:data-[front=false]:*:invisible",
|
|
61
68
|
// Fills the row beside the icon, so action buttons wrap to their own row
|
|
62
69
|
// (aligned right by the toast's justify-end). Without an icon the basis
|
|
63
70
|
// falls back to the title's width, so a one-word toast stays compact.
|
|
@@ -84,10 +91,18 @@ const icons = {
|
|
|
84
91
|
};
|
|
85
92
|
/**
|
|
86
93
|
* Mount one near the app root, and show toasts with `toast()` from this
|
|
87
|
-
* module. Only the props declared here reach the library.
|
|
94
|
+
* module. Only the props declared here reach the library. The stack
|
|
95
|
+
* expands while keyboard focus is inside it, as it does on hover, so every
|
|
96
|
+
* toast's buttons are visible and reachable.
|
|
88
97
|
*/
|
|
89
98
|
const Toaster = ({ position, closeButton = true, expand, visibleToasts, duration, }) => {
|
|
90
99
|
const { theme = "system" } = useTheme();
|
|
91
|
-
|
|
100
|
+
const [focusWithin, setFocusWithin] = useState(false);
|
|
101
|
+
const onBlur = (event) => {
|
|
102
|
+
if (!event.currentTarget.contains(event.relatedTarget)) {
|
|
103
|
+
setFocusWithin(false);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
return (_jsx("div", { "data-slot": "toaster", className: "contents", onFocus: () => setFocusWithin(true), onBlur: onBlur, children: _jsx(Sonner, { theme: theme, className: "toaster group overflow-visible", position: position, closeButton: closeButton, expand: expand || focusWithin, visibleToasts: visibleToasts, duration: duration, icons: icons, toastOptions: { unstyled: true, classNames } }) }));
|
|
92
107
|
};
|
|
93
108
|
export { Toaster, toast };
|