atlas-react-kit 0.10.0 → 0.11.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 +50 -8
- package/DESIGN_RULES.md +12 -0
- package/README.md +2 -2
- package/dist/accordion.d.ts +8 -1
- package/dist/accordion.js +8 -3
- package/dist/alert.js +1 -1
- package/dist/avatar.d.ts +5 -1
- package/dist/avatar.js +7 -2
- package/dist/badge.js +1 -1
- package/dist/breadcrumb.js +3 -2
- package/dist/button.js +6 -6
- package/dist/calendar.js +19 -12
- package/dist/card.d.ts +1 -1
- package/dist/checkbox.js +2 -2
- package/dist/color-legend.d.ts +6 -4
- package/dist/color-legend.js +7 -4
- package/dist/combobox.d.ts +7 -2
- package/dist/combobox.js +35 -25
- package/dist/date-picker.d.ts +7 -13
- package/dist/date-picker.js +14 -20
- package/dist/date-time-picker.d.ts +5 -13
- package/dist/date-time-picker.js +26 -30
- package/dist/dialog.d.ts +5 -1
- package/dist/dialog.js +4 -3
- package/dist/dropdown-menu.js +5 -4
- package/dist/empty-panel.d.ts +4 -3
- package/dist/empty-panel.js +3 -3
- package/dist/error-boundary.d.ts +5 -3
- package/dist/error-boundary.js +9 -4
- package/dist/field.d.ts +14 -1
- package/dist/field.js +16 -3
- package/dist/form/date-picker-field.d.ts +8 -2
- package/dist/form/date-picker-field.js +4 -4
- package/dist/form/date-time-picker-field.d.ts +8 -1
- package/dist/form/date-time-picker-field.js +3 -3
- package/dist/form/feedback-field.d.ts +2 -1
- package/dist/form/feedback-field.js +3 -2
- package/dist/form/field-error.d.ts +4 -1
- package/dist/form/field-error.js +3 -2
- package/dist/form/field-shell.d.ts +25 -1
- package/dist/form/field-shell.js +36 -3
- package/dist/form/markdown-field.d.ts +7 -1
- package/dist/form/markdown-field.js +3 -3
- package/dist/form/select-field.d.ts +7 -1
- package/dist/form/select-field.js +3 -3
- package/dist/form/text-field.d.ts +5 -1
- package/dist/form/text-field.js +6 -3
- package/dist/info-tip.d.ts +2 -2
- package/dist/info-tip.js +34 -16
- package/dist/internal/form-control.d.ts +5 -0
- package/dist/internal/form-control.js +7 -0
- package/dist/internal/listbox-navigation.d.ts +32 -0
- package/dist/internal/listbox-navigation.js +38 -0
- package/dist/internal/listbox-option.d.ts +25 -0
- package/dist/internal/listbox-option.js +23 -0
- package/dist/internal/math.d.ts +5 -0
- package/dist/internal/math.js +7 -0
- package/dist/internal/overlay-styles.d.ts +28 -0
- package/dist/internal/overlay-styles.js +28 -0
- package/dist/internal/styles.d.ts +27 -0
- package/dist/internal/styles.js +27 -0
- package/dist/internal/tabs-styles.d.ts +10 -1
- package/dist/internal/tabs-styles.js +27 -5
- package/dist/lazy-calendar.js +9 -1
- package/dist/markdown.js +10 -4
- package/dist/meter.d.ts +3 -2
- package/dist/meter.js +6 -3
- package/dist/pagination.d.ts +8 -1
- package/dist/pagination.js +15 -9
- package/dist/picker-shell.d.ts +59 -0
- package/dist/picker-shell.js +21 -0
- package/dist/popover.js +2 -1
- package/dist/progress.d.ts +7 -3
- package/dist/progress.js +10 -4
- package/dist/radio-group.js +2 -2
- package/dist/search-picker.d.ts +4 -6
- package/dist/search-picker.js +28 -33
- package/dist/select.js +12 -6
- package/dist/separator.d.ts +4 -0
- package/dist/separator.js +5 -1
- package/dist/slider.d.ts +8 -3
- package/dist/slider.js +7 -5
- package/dist/sortable-table-head.d.ts +3 -1
- package/dist/sortable-table-head.js +3 -2
- package/dist/stat-tile.d.ts +6 -4
- package/dist/stat-tile.js +6 -4
- package/dist/switch.js +2 -2
- package/dist/table-body-skeleton.d.ts +8 -2
- package/dist/table-body-skeleton.js +7 -3
- package/dist/table.d.ts +7 -1
- package/dist/table.js +6 -2
- package/dist/tabs.d.ts +13 -3
- package/dist/tabs.js +29 -10
- package/dist/theme-provider.d.ts +2 -1
- package/dist/theme-provider.js +1 -1
- package/dist/toggle.js +4 -4
- package/dist/tooltip.d.ts +7 -3
- package/dist/tooltip.js +17 -6
- package/package.json +1 -1
- package/theme.css +43 -50
package/dist/combobox.js
CHANGED
|
@@ -5,9 +5,9 @@ import { CheckIcon, ChevronDownIcon } from "lucide-react";
|
|
|
5
5
|
import { Button } from "./button.js";
|
|
6
6
|
import { Input } from "./input.js";
|
|
7
7
|
import { formControlTriggerClassName } from "./internal/form-control.js";
|
|
8
|
-
import {
|
|
8
|
+
import { foldSearchText, handleListboxKey, listboxOptionId, } from "./internal/listbox-navigation.js";
|
|
9
|
+
import { ListboxEmptyStatus, ListboxOption } from "./internal/listbox-option.js";
|
|
9
10
|
import { Popover, PopoverContent, PopoverTrigger } from "./popover.js";
|
|
10
|
-
import { listboxEmpty, optionState } from "./internal/styles.js";
|
|
11
11
|
import { cn, onInputChange } from "./utils.js";
|
|
12
12
|
const CREATE_OPTION_KEY = "__atlas-combobox-create";
|
|
13
13
|
/**
|
|
@@ -26,7 +26,7 @@ function defaultCreateLabel(value) {
|
|
|
26
26
|
/**
|
|
27
27
|
* Value to offer as a new option, or `null` when the query is empty,
|
|
28
28
|
* rejected by `normalize`, or already in the list (value or label,
|
|
29
|
-
* case
|
|
29
|
+
* ignoring case and accents). Domain catalogs stay with the caller.
|
|
30
30
|
*/
|
|
31
31
|
function resolveCreateValue(query, options, normalize) {
|
|
32
32
|
const created = normalize(query);
|
|
@@ -35,9 +35,9 @@ function resolveCreateValue(query, options, normalize) {
|
|
|
35
35
|
const value = created.trim();
|
|
36
36
|
if (!value)
|
|
37
37
|
return null;
|
|
38
|
-
const needle = value
|
|
39
|
-
const exists = options.some((option) => option.value
|
|
40
|
-
option.label
|
|
38
|
+
const needle = foldSearchText(value);
|
|
39
|
+
const exists = options.some((option) => foldSearchText(option.value) === needle ||
|
|
40
|
+
foldSearchText(option.label) === needle);
|
|
41
41
|
return exists ? null : value;
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
@@ -49,12 +49,14 @@ function resolveCreateValue(query, options, normalize) {
|
|
|
49
49
|
* Opt-in `creatable` appends a create action when the query is not already
|
|
50
50
|
* an option; choosing it calls `onValueChange` like a normal selection.
|
|
51
51
|
*/
|
|
52
|
-
export function Combobox({ options, value, onValueChange, placeholder = "Select", searchPlaceholder = "Search", emptyText = "No results", disabled, id, className, listClassName, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, creatable = false, normalizeCreateValue = defaultNormalizeCreateValue, createLabel = defaultCreateLabel, }) {
|
|
52
|
+
export function Combobox({ options, value, onValueChange, placeholder = "Select", searchPlaceholder = "Search", emptyText = "No results", disabled, id, className, listClassName, contentClassName, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, creatable = false, normalizeCreateValue = defaultNormalizeCreateValue, createLabel = defaultCreateLabel, ...props }) {
|
|
53
53
|
const [open, setOpen] = React.useState(false);
|
|
54
54
|
const [query, setQuery] = React.useState("");
|
|
55
55
|
const [highlight, setHighlight] = React.useState(0);
|
|
56
56
|
const inputRef = React.useRef(null);
|
|
57
57
|
const listId = React.useId();
|
|
58
|
+
const generatedId = React.useId();
|
|
59
|
+
const triggerId = id ?? generatedId;
|
|
58
60
|
// A created value may not be in `options` yet (the app owns the catalog).
|
|
59
61
|
// Keep it visible on the trigger and in the list so reopen/Enter still work.
|
|
60
62
|
const resolvedOptions = React.useMemo(() => {
|
|
@@ -66,10 +68,10 @@ export function Combobox({ options, value, onValueChange, placeholder = "Select"
|
|
|
66
68
|
}, [creatable, options, value]);
|
|
67
69
|
const selected = resolvedOptions.find((option) => option.value === value);
|
|
68
70
|
const filtered = React.useMemo(() => {
|
|
69
|
-
const needle = query.trim()
|
|
71
|
+
const needle = foldSearchText(query.trim());
|
|
70
72
|
if (!needle)
|
|
71
73
|
return resolvedOptions;
|
|
72
|
-
return resolvedOptions.filter((option) => option.label
|
|
74
|
+
return resolvedOptions.filter((option) => foldSearchText(option.label).includes(needle));
|
|
73
75
|
}, [resolvedOptions, query]);
|
|
74
76
|
const createValue = creatable
|
|
75
77
|
? resolveCreateValue(query, resolvedOptions, normalizeCreateValue)
|
|
@@ -109,24 +111,32 @@ export function Combobox({ options, value, onValueChange, placeholder = "Select"
|
|
|
109
111
|
setQuery("");
|
|
110
112
|
};
|
|
111
113
|
const activeOption = list[highlight];
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
const empty = list.length === 0;
|
|
115
|
+
// APG combobox: ArrowDown, ArrowUp, and Alt+ArrowDown open the list from
|
|
116
|
+
// the trigger. Radix only opens a popover on Enter, Space, and click.
|
|
117
|
+
const onTriggerKeyDown = (event) => {
|
|
118
|
+
if (open || (event.key !== "ArrowDown" && event.key !== "ArrowUp"))
|
|
117
119
|
return;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
120
|
+
event.preventDefault();
|
|
121
|
+
onOpenChange(true);
|
|
122
|
+
};
|
|
123
|
+
const onSearchKeyDown = (event) => {
|
|
124
|
+
handleListboxKey(event, {
|
|
125
|
+
highlight,
|
|
126
|
+
length: list.length,
|
|
127
|
+
setHighlight,
|
|
128
|
+
onEnter: (index) => {
|
|
129
|
+
const option = list[index];
|
|
130
|
+
if (option)
|
|
131
|
+
selectValue(option.value);
|
|
132
|
+
},
|
|
133
|
+
});
|
|
125
134
|
};
|
|
126
|
-
return (_jsxs(Popover, { open: open, onOpenChange: onOpenChange, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { id:
|
|
135
|
+
return (_jsxs(Popover, { open: open, onOpenChange: onOpenChange, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { ...props, id: triggerId, type: "button", variant: "outline", role: "combobox", "aria-expanded": open, "aria-controls": listId, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, disabled: disabled, onKeyDown: onTriggerKeyDown, className: cn(formControlTriggerClassName, "justify-between", !selected && "text-muted-foreground", className), children: [_jsx("span", { className: "truncate", children: selected?.label ?? placeholder }), _jsx(ChevronDownIcon, { "data-icon": "inline-end", className: "opacity-50" })] }) }), _jsxs(PopoverContent, { align: "start", className: cn("w-[var(--radix-popover-trigger-width)] gap-0 rounded-none p-0", contentClassName), children: [_jsx("div", { "data-slot": "combobox-search", className: "border-b p-2", children: _jsx(Input, { ref: inputRef, value: query, onChange: onQueryChange, onKeyDown: onSearchKeyDown, placeholder: searchPlaceholder, "aria-label": searchPlaceholder, "aria-autocomplete": "list", "aria-controls": listId, "aria-activedescendant": activeOption ? listboxOptionId(listId, highlight) : undefined }) }), _jsx("ul", { id: listId, role: "listbox", "data-slot": "combobox-list", "aria-label": ariaLabelledBy ? undefined : ariaLabel, "aria-labelledby": ariaLabelledBy ?? (ariaLabel ? undefined : triggerId), className: empty
|
|
136
|
+
? "sr-only"
|
|
137
|
+
: cn("max-h-60 overflow-y-auto p-1", listClassName), children: list.map((option, index) => {
|
|
127
138
|
const isCreate = createOption != null && option === createOption;
|
|
128
139
|
const isSelected = option.value === value && !isCreate;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
})) })] })] }));
|
|
140
|
+
return (_jsxs(ListboxOption, { "data-slot": "combobox-option", listId: listId, index: index, active: index === highlight, selected: isSelected, className: "pr-8", onMouseEnter: () => setHighlight(index), onClick: () => selectValue(option.value), children: [_jsx("span", { className: "truncate", children: option.label }), isSelected ? (_jsx(CheckIcon, { className: "pointer-events-none absolute right-2 size-3.5" })) : null] }, isCreate ? CREATE_OPTION_KEY : option.value));
|
|
141
|
+
}) }), _jsx(ListboxEmptyStatus, { empty: empty, children: emptyText })] })] }));
|
|
132
142
|
}
|
package/dist/date-picker.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import type { DateRange } from "react-day-picker";
|
|
3
|
-
import {
|
|
3
|
+
import { type PickerCalendarProps, type PickerTriggerProps } from "./picker-shell.js";
|
|
4
4
|
export type { DateRange };
|
|
5
5
|
export type DateLike = Date | string | number | null | undefined;
|
|
6
6
|
/** From/to value for `DatePickerButton` in range mode; each side is `DateLike`. */
|
|
@@ -16,22 +16,14 @@ export type DatePickerRangeValue = {
|
|
|
16
16
|
export declare function toDate(value: DateLike): Date | undefined;
|
|
17
17
|
/** Format a Date as a timezone-safe calendar date (`yyyy-MM-dd`). */
|
|
18
18
|
export declare function formatLocalDate(date: Date): string;
|
|
19
|
-
type
|
|
20
|
-
|
|
21
|
-
placeholder?: string;
|
|
22
|
-
disabled?: boolean;
|
|
23
|
-
className?: string;
|
|
24
|
-
"aria-invalid"?: boolean;
|
|
19
|
+
export type { PickerCalendarProps as DatePickerCalendarProps };
|
|
20
|
+
type DatePickerSharedProps = PickerTriggerProps & {
|
|
25
21
|
/**
|
|
26
22
|
* Visible months in the popover calendar. Defaults to 1 in single mode.
|
|
27
23
|
* In range mode, defaults to 1 below the `md` breakpoint and 2 at `md`
|
|
28
24
|
* and above so two stacked months do not open off-screen on mobile.
|
|
29
25
|
*/
|
|
30
26
|
numberOfMonths?: number;
|
|
31
|
-
/** Popover side. Defaults to `bottom`; Radix still flips on collision. */
|
|
32
|
-
side?: React.ComponentProps<typeof PopoverContent>["side"];
|
|
33
|
-
/** Month shown when the popover opens; falls back to the selected date. */
|
|
34
|
-
defaultMonth?: Date;
|
|
35
27
|
};
|
|
36
28
|
export type DatePickerSingleProps = DatePickerSharedProps & {
|
|
37
29
|
mode?: "single";
|
|
@@ -50,7 +42,9 @@ export type DatePickerButtonProps = DatePickerSingleProps | DatePickerRangeProps
|
|
|
50
42
|
* `yyyy-MM-dd` and emits `Date | undefined`. `mode="range"` accepts
|
|
51
43
|
* `{ from, to }` and emits `DateRange | undefined`. Range mode shows one
|
|
52
44
|
* month below `md` and two at `md+` unless `numberOfMonths` is set. The
|
|
53
|
-
* popover clamps to the viewport (`side` is overridable).
|
|
54
|
-
*
|
|
45
|
+
* popover clamps to the viewport (`side` is overridable). `className` styles
|
|
46
|
+
* the trigger, `contentClassName` the popover, and `calendarProps` reaches
|
|
47
|
+
* Day Picker (disabled days, `startMonth`, `weekStartsOn`, `locale`). Square
|
|
48
|
+
* corners follow the shared shape rules.
|
|
55
49
|
*/
|
|
56
50
|
export declare function DatePickerButton(props: DatePickerButtonProps): React.JSX.Element;
|
package/dist/date-picker.js
CHANGED
|
@@ -2,13 +2,9 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { format, isSameDay, parseISO } from "date-fns";
|
|
5
|
-
import { CalendarIcon } from "lucide-react";
|
|
6
|
-
import { Button } from "./button.js";
|
|
7
|
-
import { PICKER_COLLISION_PADDING, pickerContentClassName, pickerTriggerClassName, } from "./internal/form-control.js";
|
|
8
5
|
import { MD_MEDIA_QUERY, useMediaQuery } from "./internal/use-media-query.js";
|
|
9
|
-
import { LazyCalendar
|
|
10
|
-
import {
|
|
11
|
-
import { cn } from "./utils.js";
|
|
6
|
+
import { LazyCalendar } from "./lazy-calendar.js";
|
|
7
|
+
import { PickerClearFooter, PickerShell, } from "./picker-shell.js";
|
|
12
8
|
/**
|
|
13
9
|
* Coerce a date-like value to a local `Date`.
|
|
14
10
|
* Plain calendar dates (`yyyy-MM-dd`) parse as local midnight so they do not
|
|
@@ -56,19 +52,15 @@ function formatRangeTriggerLabel(from, to, placeholder) {
|
|
|
56
52
|
return format(from, "PPP");
|
|
57
53
|
return `${format(from, "PPP")} – ${format(to, "PPP")}`;
|
|
58
54
|
}
|
|
59
|
-
|
|
60
|
-
function DatePickerShell({ id, disabled, ariaInvalid, className, empty, label, open, onOpenChange, side, children, }) {
|
|
61
|
-
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, "data-empty": empty, className: cn(pickerTriggerClassName, className), onPointerEnter: preloadCalendar, onFocus: preloadCalendar, children: [_jsx(CalendarIcon, { "data-icon": "inline-start" }), label] }) }), _jsx(PopoverContent, { align: "start", side: side, collisionPadding: PICKER_COLLISION_PADDING, className: cn("w-auto", pickerContentClassName), children: children })] }));
|
|
62
|
-
}
|
|
63
|
-
function DatePickerSingleButton({ id, value, onChange, placeholder = "Pick a date", disabled, className, "aria-invalid": ariaInvalid, numberOfMonths, side, defaultMonth, }) {
|
|
55
|
+
function DatePickerSingleButton({ value, onChange, placeholder = "Pick a date", numberOfMonths, defaultMonth, calendarProps, ...shell }) {
|
|
64
56
|
const [open, setOpen] = React.useState(false);
|
|
65
57
|
const date = toDate(value);
|
|
66
|
-
return (_jsx(
|
|
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) => {
|
|
67
59
|
onChange(next);
|
|
68
60
|
setOpen(false);
|
|
69
61
|
} }) }));
|
|
70
62
|
}
|
|
71
|
-
function DatePickerRangeButton({
|
|
63
|
+
function DatePickerRangeButton({ value, onChange, placeholder = "Pick a date range", numberOfMonths, defaultMonth, calendarProps, ...shell }) {
|
|
72
64
|
const [open, setOpen] = React.useState(false);
|
|
73
65
|
const mdUp = useMediaQuery(MD_MEDIA_QUERY);
|
|
74
66
|
const months = numberOfMonths ?? (mdUp ? 2 : 1);
|
|
@@ -84,7 +76,7 @@ function DatePickerRangeButton({ id, value, onChange, placeholder = "Pick a date
|
|
|
84
76
|
restartRef.current = Boolean(from && to);
|
|
85
77
|
setOpen(next);
|
|
86
78
|
};
|
|
87
|
-
return (_jsxs(
|
|
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) => {
|
|
88
80
|
const range = restartRef.current ? { from: day, to: day } : next;
|
|
89
81
|
restartRef.current = false;
|
|
90
82
|
onChange(range);
|
|
@@ -93,10 +85,10 @@ function DatePickerRangeButton({ id, value, onChange, placeholder = "Pick a date
|
|
|
93
85
|
if (range?.from && range.to && !isSameDay(range.from, range.to)) {
|
|
94
86
|
setOpen(false);
|
|
95
87
|
}
|
|
96
|
-
} }), _jsx(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
88
|
+
} }), _jsx(PickerClearFooter, { disabled: !from && !to, onClear: () => {
|
|
89
|
+
onChange(undefined);
|
|
90
|
+
setOpen(false);
|
|
91
|
+
} })] }));
|
|
100
92
|
}
|
|
101
93
|
/**
|
|
102
94
|
* Calendar popover trigger styled as a form control (same chrome as Input
|
|
@@ -104,8 +96,10 @@ function DatePickerRangeButton({ id, value, onChange, placeholder = "Pick a date
|
|
|
104
96
|
* `yyyy-MM-dd` and emits `Date | undefined`. `mode="range"` accepts
|
|
105
97
|
* `{ from, to }` and emits `DateRange | undefined`. Range mode shows one
|
|
106
98
|
* month below `md` and two at `md+` unless `numberOfMonths` is set. The
|
|
107
|
-
* popover clamps to the viewport (`side` is overridable).
|
|
108
|
-
*
|
|
99
|
+
* popover clamps to the viewport (`side` is overridable). `className` styles
|
|
100
|
+
* the trigger, `contentClassName` the popover, and `calendarProps` reaches
|
|
101
|
+
* Day Picker (disabled days, `startMonth`, `weekStartsOn`, `locale`). Square
|
|
102
|
+
* corners follow the shared shape rules.
|
|
109
103
|
*/
|
|
110
104
|
export function DatePickerButton(props) {
|
|
111
105
|
if (props.mode === "range") {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { type PickerTriggerProps } from "./picker-shell.js";
|
|
3
3
|
/** Default clock increment, in minutes. Must divide 60. */
|
|
4
4
|
export declare const DEFAULT_TIME_STEP = 15;
|
|
5
|
-
export type DateTimePickerButtonProps = {
|
|
5
|
+
export type DateTimePickerButtonProps = PickerTriggerProps & {
|
|
6
6
|
/** Controlled instant. `null` or omitted is empty. */
|
|
7
7
|
value?: Date | null;
|
|
8
8
|
onChange: (date: Date | null) => void;
|
|
@@ -11,15 +11,6 @@ export type DateTimePickerButtonProps = {
|
|
|
11
11
|
* Values that do not divide 60 fall back to 15 so the list stays even.
|
|
12
12
|
*/
|
|
13
13
|
timeStep?: number;
|
|
14
|
-
id?: string;
|
|
15
|
-
placeholder?: string;
|
|
16
|
-
disabled?: boolean;
|
|
17
|
-
className?: string;
|
|
18
|
-
"aria-invalid"?: boolean;
|
|
19
|
-
/** Popover side. Defaults to `bottom`; Radix still flips on collision. */
|
|
20
|
-
side?: React.ComponentProps<typeof PopoverContent>["side"];
|
|
21
|
-
/** Month shown when the popover opens; falls back to the selected date. */
|
|
22
|
-
defaultMonth?: Date;
|
|
23
14
|
};
|
|
24
15
|
/**
|
|
25
16
|
* Normalize a minute step so option lists stay even (1, 5, 15, 30, 60).
|
|
@@ -31,6 +22,7 @@ export declare function resolveTimeStep(step: number | undefined): number;
|
|
|
31
22
|
* chrome as Input, SelectTrigger, and DatePickerButton). `value` is a `Date`
|
|
32
23
|
* or `null`. Picking a day keeps the popover open so the time can be set;
|
|
33
24
|
* Clear emits `null`. One month only, clamped to the viewport, so the panel
|
|
34
|
-
* stays on-screen at a 390px width.
|
|
25
|
+
* stays on-screen at a 390px width. `className` styles the trigger,
|
|
26
|
+
* `contentClassName` the popover, and `calendarProps` reaches Day Picker.
|
|
35
27
|
*/
|
|
36
|
-
export declare function DateTimePickerButton({
|
|
28
|
+
export declare function DateTimePickerButton({ value, onChange, timeStep, placeholder, defaultMonth, calendarProps, ...shell }: DateTimePickerButtonProps): React.JSX.Element;
|
package/dist/date-time-picker.js
CHANGED
|
@@ -2,13 +2,9 @@
|
|
|
2
2
|
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
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { PICKER_COLLISION_PADDING, pickerContentClassName, pickerTriggerClassName, } from "./internal/form-control.js";
|
|
8
|
-
import { LazyCalendar, preloadCalendar } from "./lazy-calendar.js";
|
|
9
|
-
import { Popover, PopoverContent, PopoverTrigger } from "./popover.js";
|
|
5
|
+
import { LazyCalendar } from "./lazy-calendar.js";
|
|
6
|
+
import { PickerClearFooter, PickerShell, } from "./picker-shell.js";
|
|
10
7
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "./select.js";
|
|
11
|
-
import { cn } from "./utils.js";
|
|
12
8
|
/** Default clock increment, in minutes. Must divide 60. */
|
|
13
9
|
export const DEFAULT_TIME_STEP = 15;
|
|
14
10
|
/**
|
|
@@ -69,35 +65,35 @@ function preventDismissForPortaledSelect(event) {
|
|
|
69
65
|
* chrome as Input, SelectTrigger, and DatePickerButton). `value` is a `Date`
|
|
70
66
|
* or `null`. Picking a day keeps the popover open so the time can be set;
|
|
71
67
|
* Clear emits `null`. One month only, clamped to the viewport, so the panel
|
|
72
|
-
* stays on-screen at a 390px width.
|
|
68
|
+
* stays on-screen at a 390px width. `className` styles the trigger,
|
|
69
|
+
* `contentClassName` the popover, and `calendarProps` reaches Day Picker.
|
|
73
70
|
*/
|
|
74
|
-
export function DateTimePickerButton({
|
|
71
|
+
export function DateTimePickerButton({ value, onChange, timeStep, placeholder = "Pick a date and time", defaultMonth, calendarProps, ...shell }) {
|
|
75
72
|
const [open, setOpen] = React.useState(false);
|
|
76
73
|
const date = value ?? null;
|
|
77
74
|
const step = resolveTimeStep(timeStep);
|
|
78
75
|
const empty = date == null;
|
|
79
|
-
const label = date ? format(date, "PPP p") : placeholder;
|
|
80
76
|
const hourId = React.useId();
|
|
81
77
|
const minuteId = React.useId();
|
|
82
|
-
return (_jsxs(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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) => {
|
|
79
|
+
// Re-clicking a selected day would clear it; require Clear instead
|
|
80
|
+
// so a time already set is not dropped by an accidental second tap.
|
|
81
|
+
if (!next)
|
|
82
|
+
return;
|
|
83
|
+
onChange(combineDateAndTime(next, date?.getHours() ?? 0, date?.getMinutes() ?? 0));
|
|
84
|
+
} }), _jsxs("div", { role: "group", "aria-label": "Time", "data-slot": "date-time-picker-time", className: "flex min-w-0 flex-col gap-2 border-t p-3", children: [_jsx("p", { className: "text-xs font-semibold tracking-wide uppercase", children: "Time" }), _jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsxs(Select, { value: date ? padTimePart(date.getHours()) : undefined, onValueChange: (nextHour) => {
|
|
85
|
+
if (!date)
|
|
86
|
+
return;
|
|
87
|
+
onChange(combineDateAndTime(date, Number(nextHour), date.getMinutes()));
|
|
88
|
+
}, disabled: empty, children: [_jsx(SelectTrigger, { id: hourId, "aria-label": "Hour", className: "min-w-0 flex-1", children: _jsx(SelectValue, { placeholder: "\u2013\u2013" }) }), _jsx(SelectContent, { children: HOUR_OPTIONS.map((hour) => (_jsx(SelectItem, { value: hour, children: hour }, hour))) })] }), _jsx("span", { className: "text-muted-foreground", "aria-hidden": "true", children: ":" }), _jsxs(Select, { value: date ? padTimePart(date.getMinutes()) : undefined, onValueChange: (nextMinute) => {
|
|
89
|
+
if (!date)
|
|
90
|
+
return;
|
|
91
|
+
onChange(combineDateAndTime(date, date.getHours(), Number(nextMinute)));
|
|
92
|
+
}, disabled: empty, children: [_jsx(SelectTrigger, { id: minuteId, "aria-label": "Minute", className: "min-w-0 flex-1", children: _jsx(SelectValue, { placeholder: "\u2013\u2013" }) }), _jsx(SelectContent, { children: minuteOptions(step, date?.getMinutes()).map((minute) => {
|
|
93
|
+
const value = padTimePart(minute);
|
|
94
|
+
return (_jsx(SelectItem, { value: value, children: value }, value));
|
|
95
|
+
}) })] })] })] }), _jsx(PickerClearFooter, { disabled: empty, onClear: () => {
|
|
96
|
+
onChange(null);
|
|
97
|
+
setOpen(false);
|
|
98
|
+
} })] }));
|
|
103
99
|
}
|
package/dist/dialog.d.ts
CHANGED
|
@@ -5,8 +5,12 @@ declare function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogP
|
|
|
5
5
|
declare function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>): React.JSX.Element;
|
|
6
6
|
declare function DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>): React.JSX.Element;
|
|
7
7
|
declare function DialogOverlay({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Overlay>): React.JSX.Element;
|
|
8
|
-
declare function DialogContent({ className, children, showCloseButton, ...props }: React.ComponentProps<typeof DialogPrimitive.Content> & {
|
|
8
|
+
declare function DialogContent({ className, children, showCloseButton, overlayClassName, closeButtonClassName, ...props }: React.ComponentProps<typeof DialogPrimitive.Content> & {
|
|
9
9
|
showCloseButton?: boolean;
|
|
10
|
+
/** Classes for the backdrop, e.g. a darker scrim. */
|
|
11
|
+
overlayClassName?: string;
|
|
12
|
+
/** Classes for the built-in close button, e.g. its position. */
|
|
13
|
+
closeButtonClassName?: string;
|
|
10
14
|
}): React.JSX.Element;
|
|
11
15
|
declare function DialogHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
12
16
|
declare function DialogFooter({ className, children, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
package/dist/dialog.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Dialog as DialogPrimitive } from "radix-ui";
|
|
5
|
+
import { overlayFade, overlayFadeZoom, overlaySurface, } from "./internal/overlay-styles.js";
|
|
5
6
|
import { cn } from "./utils.js";
|
|
6
7
|
import { Button } from "./button.js";
|
|
7
8
|
import { XIcon } from "lucide-react";
|
|
@@ -18,10 +19,10 @@ function DialogClose({ ...props }) {
|
|
|
18
19
|
return _jsx(DialogPrimitive.Close, { "data-slot": "dialog-close", ...props });
|
|
19
20
|
}
|
|
20
21
|
function DialogOverlay({ className, ...props }) {
|
|
21
|
-
return (_jsx(DialogPrimitive.Overlay, { "data-slot": "dialog-overlay", className: cn("fixed inset-0 isolate z-50 bg-black/20
|
|
22
|
+
return (_jsx(DialogPrimitive.Overlay, { "data-slot": "dialog-overlay", className: cn(overlayFade, "fixed inset-0 isolate z-50 bg-black/20 supports-backdrop-filter:backdrop-blur-sm", className), ...props }));
|
|
22
23
|
}
|
|
23
|
-
function DialogContent({ className, children, showCloseButton = true, ...props }) {
|
|
24
|
-
return (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, {}), _jsxs(DialogPrimitive.Content, { "data-slot": "dialog-content", className: cn("fixed top-1/2 left-1/2 z-50 grid max-h-[90dvh] w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-6 overflow-y-auto
|
|
24
|
+
function DialogContent({ className, children, showCloseButton = true, overlayClassName, closeButtonClassName, ...props }) {
|
|
25
|
+
return (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, { className: overlayClassName }), _jsxs(DialogPrimitive.Content, { "data-slot": "dialog-content", className: cn(overlaySurface, overlayFadeZoom, "fixed top-1/2 left-1/2 z-50 grid max-h-[90dvh] w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-6 overflow-y-auto p-6 text-sm outline-none", className), ...props, children: [children, showCloseButton && (_jsx(DialogPrimitive.Close, { "data-slot": "dialog-close-button", asChild: true, children: _jsxs(Button, { variant: "ghost", size: "icon-sm", className: cn("absolute top-4 right-4 text-muted-foreground hover:text-foreground", closeButtonClassName), children: [_jsx(XIcon, {}), _jsx("span", { className: "sr-only", children: "Close" })] }) }))] })] }));
|
|
25
26
|
}
|
|
26
27
|
function DialogHeader({ className, ...props }) {
|
|
27
28
|
return (_jsx("div", { "data-slot": "dialog-header", className: cn("flex flex-col gap-2", className), ...props }));
|
package/dist/dropdown-menu.js
CHANGED
|
@@ -3,11 +3,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui";
|
|
5
5
|
import { CheckIcon, ChevronRightIcon } from "lucide-react";
|
|
6
|
+
import { menuLabel, optionIcon, optionRow, overlayFadeZoom, overlaySideSlide, overlaySurface, } from "./internal/overlay-styles.js";
|
|
6
7
|
import { dataDisabledState, optionState } from "./internal/styles.js";
|
|
7
8
|
import { cn } from "./utils.js";
|
|
8
|
-
/**
|
|
9
|
-
const menuItemClassName = cn(optionState.focus, dataDisabledState, "
|
|
10
|
-
const menuContentClassName = "z-50 overflow-x-hidden overflow-y-auto
|
|
9
|
+
/** Menu row: the shared option row with menu padding and the inset slot. */
|
|
10
|
+
const menuItemClassName = cn(optionRow, optionState.focus, dataDisabledState, optionIcon, "pr-3 data-inset:pl-9");
|
|
11
|
+
const menuContentClassName = cn(overlaySurface, overlaySideSlide, overlayFadeZoom, "z-50 overflow-x-hidden overflow-y-auto p-1");
|
|
11
12
|
/** Action menu for commands (row actions, overflow menus). Use `Select` to pick a value. */
|
|
12
13
|
function DropdownMenu({ ...props }) {
|
|
13
14
|
return _jsx(DropdownMenuPrimitive.Root, { "data-slot": "dropdown-menu", ...props });
|
|
@@ -37,7 +38,7 @@ function DropdownMenuRadioItem({ className, children, ...props }) {
|
|
|
37
38
|
return (_jsxs(DropdownMenuPrimitive.RadioItem, { "data-slot": "dropdown-menu-radio-item", className: cn(menuItemClassName, "pl-9", className), ...props, children: [_jsx("span", { className: "pointer-events-none absolute left-3 flex size-3.5 items-center justify-center", children: _jsx(DropdownMenuPrimitive.ItemIndicator, { children: _jsx("span", { className: "block size-1.5 rounded-full bg-current" }) }) }), children] }));
|
|
38
39
|
}
|
|
39
40
|
function DropdownMenuLabel({ className, inset, ...props }) {
|
|
40
|
-
return (_jsx(DropdownMenuPrimitive.Label, { "data-slot": "dropdown-menu-label", "data-inset": inset || undefined, className: cn("
|
|
41
|
+
return (_jsx(DropdownMenuPrimitive.Label, { "data-slot": "dropdown-menu-label", "data-inset": inset || undefined, className: cn(menuLabel, "data-inset:pl-9", className), ...props }));
|
|
41
42
|
}
|
|
42
43
|
function DropdownMenuSeparator({ className, ...props }) {
|
|
43
44
|
return (_jsx(DropdownMenuPrimitive.Separator, { "data-slot": "dropdown-menu-separator", className: cn("-mx-1 my-1 h-px bg-border", className), ...props }));
|
package/dist/empty-panel.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
type
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
type Props = React.ComponentProps<"div"> & {
|
|
2
3
|
children: React.ReactNode;
|
|
3
4
|
/** Tighter padding for compact widgets; default is standard form empty states. */
|
|
4
5
|
size?: "sm" | "md" | "lg";
|
|
5
6
|
muted?: boolean;
|
|
6
7
|
className?: string;
|
|
7
8
|
};
|
|
8
|
-
/** Dashed empty-state panel for lists, widgets, and form sections. */
|
|
9
|
-
export declare function EmptyPanel({ children, size, muted, className, }: Props):
|
|
9
|
+
/** Dashed empty-state panel for lists, widgets, and form sections. Other props go to the root `div`. */
|
|
10
|
+
export declare function EmptyPanel({ children, size, muted, className, ...props }: Props): React.JSX.Element;
|
|
10
11
|
export {};
|
package/dist/empty-panel.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from "./utils.js";
|
|
3
|
-
/** Dashed empty-state panel for lists, widgets, and form sections. */
|
|
4
|
-
export function EmptyPanel({ children, size = "md", muted = true, className, }) {
|
|
5
|
-
return (_jsx("div", { className: cn("rounded-none border border-dashed text-center text-sm text-muted-foreground", muted && "bg-muted/30", size === "sm" && "px-3 py-4", size === "md" && "px-4 py-6", size === "lg" && "px-4 py-8", className), children: children }));
|
|
3
|
+
/** Dashed empty-state panel for lists, widgets, and form sections. Other props go to the root `div`. */
|
|
4
|
+
export function EmptyPanel({ children, size = "md", muted = true, className, ...props }) {
|
|
5
|
+
return (_jsx("div", { "data-slot": "empty-panel", ...props, className: cn("rounded-none border border-dashed text-center text-sm text-muted-foreground", muted && "bg-muted/30", size === "sm" && "px-3 py-4", size === "md" && "px-4 py-6", size === "lg" && "px-4 py-8", className), children: children }));
|
|
6
6
|
}
|
package/dist/error-boundary.d.ts
CHANGED
|
@@ -4,12 +4,14 @@ type ErrorFallbackProps = {
|
|
|
4
4
|
message?: string;
|
|
5
5
|
onRetry?: () => void;
|
|
6
6
|
onReload?: () => void;
|
|
7
|
+
className?: string;
|
|
7
8
|
};
|
|
8
9
|
/**
|
|
9
|
-
* Presentational recovery UI used by `ErrorBoundary
|
|
10
|
-
*
|
|
10
|
+
* Presentational recovery UI used by `ErrorBoundary`: a large `EmptyPanel`
|
|
11
|
+
* with the message and recovery buttons. Exported so the markup can be
|
|
12
|
+
* tested without throwing during render.
|
|
11
13
|
*/
|
|
12
|
-
export declare function ErrorFallback({ message, onRetry, onReload, }: ErrorFallbackProps): import("react").JSX.Element;
|
|
14
|
+
export declare function ErrorFallback({ message, onRetry, onReload, className, }: ErrorFallbackProps): import("react").JSX.Element;
|
|
13
15
|
type ErrorBoundaryProps = {
|
|
14
16
|
children: ReactNode;
|
|
15
17
|
/** Overrides the default recovery message. */
|
package/dist/error-boundary.js
CHANGED
|
@@ -2,12 +2,17 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Component } from "react";
|
|
4
4
|
import { Button } from "./button.js";
|
|
5
|
+
import { EmptyPanel } from "./empty-panel.js";
|
|
6
|
+
import { cn } from "./utils.js";
|
|
5
7
|
/**
|
|
6
|
-
* Presentational recovery UI used by `ErrorBoundary
|
|
7
|
-
*
|
|
8
|
+
* Presentational recovery UI used by `ErrorBoundary`: a large `EmptyPanel`
|
|
9
|
+
* with the message and recovery buttons. Exported so the markup can be
|
|
10
|
+
* tested without throwing during render.
|
|
8
11
|
*/
|
|
9
|
-
export function ErrorFallback({ message = "Something went wrong.", onRetry, onReload, }) {
|
|
10
|
-
return (_jsxs(
|
|
12
|
+
export function ErrorFallback({ message = "Something went wrong.", onRetry, onReload, className, }) {
|
|
13
|
+
return (_jsxs(EmptyPanel, { role: "alert", "data-slot": "error-boundary", size: "lg",
|
|
14
|
+
// The panel's muted text is for its message; the buttons keep their own.
|
|
15
|
+
className: cn("flex flex-col items-center gap-4 text-foreground", className), children: [_jsx("p", { className: "text-sm text-muted-foreground", children: message }), _jsxs("div", { className: "flex flex-wrap items-center justify-center gap-2", children: [onRetry ? (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: onRetry, children: "Try again" })) : null, onReload ? (_jsx(Button, { type: "button", size: "sm", onClick: onReload, children: "Reload" })) : null] })] }));
|
|
11
16
|
}
|
|
12
17
|
/**
|
|
13
18
|
* Generic React error boundary with retry (reset) and full-page reload.
|
package/dist/field.d.ts
CHANGED
|
@@ -4,9 +4,22 @@ declare function FieldSet({ className, ...props }: React.ComponentProps<"fieldse
|
|
|
4
4
|
declare function FieldLegend({ className, variant, ...props }: React.ComponentProps<"legend"> & {
|
|
5
5
|
variant?: "legend" | "label";
|
|
6
6
|
}): import("react").JSX.Element;
|
|
7
|
+
/**
|
|
8
|
+
* Stack of fields and the `@container/field-group` that responsive fields
|
|
9
|
+
* query. It keeps `w-full`: an inline-size container has no intrinsic
|
|
10
|
+
* width, so without it the group collapses to zero in a flex row or any
|
|
11
|
+
* shrink-to-fit parent. Override with `className` (`w-auto`, `max-w-md`).
|
|
12
|
+
*/
|
|
7
13
|
declare function FieldGroup({ className, ...props }: React.ComponentProps<"div">): import("react").JSX.Element;
|
|
14
|
+
/**
|
|
15
|
+
* Vertical (and responsive, below `@md`) fields are a flex column, so
|
|
16
|
+
* children stretch to the field's width by default (`align-items: stretch`)
|
|
17
|
+
* while a child's own width class (`w-32`, `w-fit`) still wins. The field
|
|
18
|
+
* itself sets no width: it fills a block or column parent and sizes to its
|
|
19
|
+
* content in a row.
|
|
20
|
+
*/
|
|
8
21
|
declare const fieldVariants: (props?: ({
|
|
9
|
-
orientation?: "
|
|
22
|
+
orientation?: "horizontal" | "vertical" | "responsive" | null | undefined;
|
|
10
23
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
11
24
|
declare function Field({ className, orientation, ...props }: React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>): import("react").JSX.Element;
|
|
12
25
|
declare function FieldContent({ className, ...props }: React.ComponentProps<"div">): import("react").JSX.Element;
|
package/dist/field.js
CHANGED
|
@@ -11,15 +11,28 @@ function FieldSet({ className, ...props }) {
|
|
|
11
11
|
function FieldLegend({ className, variant = "legend", ...props }) {
|
|
12
12
|
return (_jsx("legend", { "data-slot": "field-legend", "data-variant": variant, className: cn("mb-3 font-semibold uppercase data-[variant=label]:text-xs data-[variant=legend]:text-xs", className), ...props }));
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Stack of fields and the `@container/field-group` that responsive fields
|
|
16
|
+
* query. It keeps `w-full`: an inline-size container has no intrinsic
|
|
17
|
+
* width, so without it the group collapses to zero in a flex row or any
|
|
18
|
+
* shrink-to-fit parent. Override with `className` (`w-auto`, `max-w-md`).
|
|
19
|
+
*/
|
|
14
20
|
function FieldGroup({ className, ...props }) {
|
|
15
21
|
return (_jsx("div", { "data-slot": "field-group", className: cn("group/field-group @container/field-group flex w-full flex-col gap-10 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4", className), ...props }));
|
|
16
22
|
}
|
|
17
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Vertical (and responsive, below `@md`) fields are a flex column, so
|
|
25
|
+
* children stretch to the field's width by default (`align-items: stretch`)
|
|
26
|
+
* while a child's own width class (`w-32`, `w-fit`) still wins. The field
|
|
27
|
+
* itself sets no width: it fills a block or column parent and sizes to its
|
|
28
|
+
* content in a row.
|
|
29
|
+
*/
|
|
30
|
+
const fieldVariants = cva("group/field flex gap-3 data-[invalid=true]:text-destructive", {
|
|
18
31
|
variants: {
|
|
19
32
|
orientation: {
|
|
20
|
-
vertical: "flex-col
|
|
33
|
+
vertical: "flex-col",
|
|
21
34
|
horizontal: "flex-row items-center has-[>[data-slot=field-content]]:items-start *:data-[slot=field-label]:flex-auto has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
|
|
22
|
-
responsive: "flex-col
|
|
35
|
+
responsive: "flex-col @md/field-group:flex-row @md/field-group:items-center @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:*:data-[slot=field-label]:flex-auto @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
|
|
23
36
|
},
|
|
24
37
|
},
|
|
25
38
|
defaultVariants: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type DateLike } from "../date-picker.js";
|
|
1
|
+
import { type DateLike, type DatePickerCalendarProps } from "../date-picker.js";
|
|
2
2
|
import type { FieldMetaState } from "./field-error.js";
|
|
3
3
|
type Props = {
|
|
4
4
|
value: DateLike;
|
|
@@ -9,7 +9,13 @@ type Props = {
|
|
|
9
9
|
disabled?: boolean;
|
|
10
10
|
/** Forwarded to the date picker trigger. */
|
|
11
11
|
className?: string;
|
|
12
|
+
/** Classes for the field wrapper (label, trigger, and error). */
|
|
13
|
+
fieldClassName?: string;
|
|
14
|
+
/** Forwarded to the date picker popover. */
|
|
15
|
+
contentClassName?: string;
|
|
16
|
+
/** Day Picker options forwarded to the calendar (disabled days, etc.). */
|
|
17
|
+
calendarProps?: DatePickerCalendarProps;
|
|
12
18
|
};
|
|
13
19
|
/** Date picker with built-in field error text and invalid styling. */
|
|
14
|
-
export declare function FormDatePickerField({
|
|
20
|
+
export declare function FormDatePickerField({ onValueChange, label, meta, fieldClassName, ...picker }: Props): import("react").JSX.Element;
|
|
15
21
|
export {};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { useId } from "react";
|
|
4
|
-
import { DatePickerButton } from "../date-picker.js";
|
|
5
|
-
import { FormFieldShell,
|
|
4
|
+
import { DatePickerButton, } from "../date-picker.js";
|
|
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({
|
|
7
|
+
export function FormDatePickerField({ onValueChange, label, meta, fieldClassName, ...picker }) {
|
|
8
8
|
const errorText = metaErrorText(meta);
|
|
9
9
|
const id = useId();
|
|
10
|
-
return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, children: _jsx(DatePickerButton, { id: id,
|
|
10
|
+
return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, className: fieldClassName, children: _jsx(DatePickerButton, { ...picker, id: id, onChange: onValueChange, ...pickerFieldAria(id, errorText) }) }));
|
|
11
11
|
}
|