@nsaa/core 0.1.0-alpha.4 → 0.1.0-alpha.5

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.
@@ -95,6 +95,7 @@ function Button$1({ className, variant = "default", size = "default", loading =
95
95
  const placement = loading ? resolvePlacement(loadingPlacement, children, size ?? "default") : null;
96
96
  return /* @__PURE__ */ jsx(Button, {
97
97
  "data-slot": "button",
98
+ "data-variant": variant ?? "default",
98
99
  className: cn(buttonVariants({
99
100
  variant,
100
101
  size
@@ -0,0 +1,55 @@
1
+
2
+ import { DateRangePreset, DateRangeValue, WeekDay } from "./presets.js";
3
+ import * as React from "react";
4
+ import { Locale, Matcher } from "react-day-picker";
5
+ export type DateRangePickerLabels = {
6
+ /** Trigger text when there is no range. */
7
+ placeholder: string;
8
+ /** The presetsOnly option that opens the calendar. */
9
+ custom: string;
10
+ /** The presets listbox's name. */
11
+ presets: string;
12
+ /** The panel's name (Popover) and the Drawer's title. */
13
+ title: string;
14
+ };
15
+ export type DateRangePickerProps = Omit<React.ComponentProps<"div">, "defaultValue" | "onChange" | "children"> & {
16
+ /** The selected range (controlled), or null for none. */
17
+ value?: DateRangeValue | null | undefined;
18
+ /** The range it starts with when uncontrolled. */
19
+ defaultValue?: DateRangeValue | null | undefined;
20
+ /** `preset` is the id of the picked preset, or of the preset a custom range matches; else null. */
21
+ onValueChange?: ((range: DateRangeValue, details: {
22
+ preset: string | null;
23
+ }) => void) | undefined;
24
+ /** Default: every built-in preset, in the order of `presets`. Pass `[]` for none. */
25
+ presets?: readonly DateRangePreset[] | undefined;
26
+ /** Presets in a Select-like menu; "Custom…" opens the calendar. */
27
+ presetsOnly?: boolean | undefined;
28
+ /** Default: the locale's first day (`Intl.Locale` week info), else Monday. */
29
+ weekStartsOn?: WeekDay | undefined;
30
+ /** IANA zone that decides what "today" is. Default: the device zone. */
31
+ timeZone?: string | undefined;
32
+ /**
33
+ * A BCP 47 tag (formats the trigger and the announcement, sets the week start) or a date-fns locale
34
+ * (`import { fr } from "date-fns/locale"`), which also translates the calendar's month and day
35
+ * names. Default: `<html lang>`, else the runtime's; the calendar's names stay English unless a
36
+ * date-fns locale is passed.
37
+ */
38
+ locale?: string | Partial<Locale> | undefined;
39
+ /** The current instant. Default: now. Pin it for tests and stable screenshots. */
40
+ now?: Date | undefined;
41
+ /** The earliest day that can be picked; presets ending before it are disabled. */
42
+ min?: Date | undefined;
43
+ /** The latest day that can be picked; presets starting after it are disabled. */
44
+ max?: Date | undefined;
45
+ /** Days that cannot be picked (react-day-picker matchers). */
46
+ disabled?: Matcher | Matcher[] | undefined;
47
+ /** Default: 2 on containers at least 600 px wide, else 1. 2 also puts presets in a side list. */
48
+ numberOfMonths?: 1 | 2 | undefined;
49
+ /** The trigger Button's size. */
50
+ size?: "default" | "sm" | undefined;
51
+ /** Overrides for the visible and spoken strings: placeholder, custom, preset labels and the panel title. */
52
+ labels?: Partial<DateRangePickerLabels> | undefined;
53
+ };
54
+ declare function DateRangePicker({ value: valueProp, defaultValue, onValueChange, presets: presetList, presetsOnly, weekStartsOn: weekStartsOnProp, timeZone, locale: localeProp, now, min, max, disabled, numberOfMonths: monthsProp, size, labels: labelsProp, id, "aria-label": ariaLabel, className, ...props }: DateRangePickerProps): React.JSX.Element;
55
+ export { DateRangePicker };
@@ -0,0 +1,359 @@
1
+ "use client";
2
+ "use client";
3
+ import { cn } from "../lib/cn.js";
4
+ import { duration } from "../tokens/dist/motion.js";
5
+ import { Button } from "../button/button.js";
6
+ import { Calendar } from "../calendar/calendar.js";
7
+ import { Drawer, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger } from "../drawer/drawer.js";
8
+ import { Popover, PopoverContent, PopoverTrigger } from "../popover/popover.js";
9
+ import { localeWeekStart, matchPreset, presets, todayIn } from "./presets.js";
10
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11
+ import * as React from "react";
12
+ import { useReducedMotion, useReducedMotionConfig } from "motion/react";
13
+ // src/date-range-picker/date-range-picker.tsx
14
+ const DEFAULT_LABELS = {
15
+ placeholder: "Pick a date range",
16
+ custom: "Custom…",
17
+ presets: "Presets",
18
+ title: "Date range"
19
+ };
20
+ const DEFAULT_PRESETS = Object.values(presets);
21
+ const CUSTOM = "__custom";
22
+ const WIDE_PX = 600;
23
+ const DRAWER_QUERY = "(pointer: coarse) and (max-width: 639px)";
24
+ const AXES = [
25
+ "data-mode",
26
+ "data-theme",
27
+ "data-corners",
28
+ "data-surface"
29
+ ];
30
+ /** Lucide `calendar`. */
31
+ function CalendarIcon() {
32
+ return /* @__PURE__ */ jsxs("svg", {
33
+ xmlns: "http://www.w3.org/2000/svg",
34
+ viewBox: "0 0 24 24",
35
+ fill: "none",
36
+ stroke: "currentColor",
37
+ strokeWidth: "2",
38
+ strokeLinecap: "round",
39
+ strokeLinejoin: "round",
40
+ "aria-hidden": "true",
41
+ "data-icon": "inline-start",
42
+ children: [
43
+ /* @__PURE__ */ jsx("path", { d: "M8 2v4" }),
44
+ /* @__PURE__ */ jsx("path", { d: "M16 2v4" }),
45
+ /* @__PURE__ */ jsx("rect", {
46
+ width: "18",
47
+ height: "18",
48
+ x: "3",
49
+ y: "4",
50
+ rx: "2"
51
+ }),
52
+ /* @__PURE__ */ jsx("path", { d: "M3 10h18" })
53
+ ]
54
+ });
55
+ }
56
+ /** Lucide `chevron-down`, for the presetsOnly trigger. */
57
+ function ChevronIcon() {
58
+ return /* @__PURE__ */ jsx("svg", {
59
+ xmlns: "http://www.w3.org/2000/svg",
60
+ viewBox: "0 0 24 24",
61
+ fill: "none",
62
+ stroke: "currentColor",
63
+ strokeWidth: "2",
64
+ strokeLinecap: "round",
65
+ strokeLinejoin: "round",
66
+ "aria-hidden": "true",
67
+ "data-icon": "inline-end",
68
+ className: "ms-auto text-muted-foreground",
69
+ children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
70
+ });
71
+ }
72
+ function useContainerWide(ref) {
73
+ const [wide, setWide] = React.useState(false);
74
+ React.useLayoutEffect(() => {
75
+ const container = ref.current?.parentElement;
76
+ if (!container) return;
77
+ setWide(container.clientWidth >= WIDE_PX);
78
+ const observer = new ResizeObserver(([entry]) => {
79
+ if (entry) setWide(entry.contentRect.width >= WIDE_PX);
80
+ });
81
+ observer.observe(container);
82
+ return () => observer.disconnect();
83
+ }, [ref]);
84
+ return wide;
85
+ }
86
+ function useMedia(query) {
87
+ return React.useSyncExternalStore((onChange) => {
88
+ const list = window.matchMedia(query);
89
+ list.addEventListener("change", onChange);
90
+ return () => list.removeEventListener("change", onChange);
91
+ }, () => window.matchMedia(query).matches, () => false);
92
+ }
93
+ /** Presets as a listbox: roving focus, arrows (along the orientation, RTL-aware), Home, End, Enter. */
94
+ function PresetList({ options, selectedId, orientation, label, onPick }) {
95
+ const baseId = React.useId();
96
+ const refs = React.useRef([]);
97
+ const selected = options.findIndex((o) => o.id === selectedId);
98
+ const [active, setActive] = React.useState(Math.max(selected, 0));
99
+ const move = (index) => {
100
+ setActive(index);
101
+ refs.current[index]?.focus();
102
+ };
103
+ const onKeyDown = (event) => {
104
+ const rtl = event.currentTarget.closest("[dir]")?.getAttribute("dir") === "rtl";
105
+ const vertical = orientation === "vertical";
106
+ const next = vertical ? "ArrowDown" : rtl ? "ArrowLeft" : "ArrowRight";
107
+ const prev = vertical ? "ArrowUp" : rtl ? "ArrowRight" : "ArrowLeft";
108
+ const last = options.length - 1;
109
+ const option = options[active];
110
+ if (event.key === next) move(Math.min(active + 1, last));
111
+ else if (event.key === prev) move(Math.max(active - 1, 0));
112
+ else if (event.key === "Home") move(0);
113
+ else if (event.key === "End") move(last);
114
+ else if (event.key === "Enter" || event.key === " ") {
115
+ if (option && !option.disabled) onPick(option.id);
116
+ } else return;
117
+ event.preventDefault();
118
+ };
119
+ return /* @__PURE__ */ jsx("div", {
120
+ role: "listbox",
121
+ "aria-label": label,
122
+ "aria-orientation": orientation,
123
+ "data-slot": "date-range-picker-presets",
124
+ "data-orientation": orientation,
125
+ className: cn("flex gap-1 p-2", orientation === "vertical" ? "w-40 shrink-0 flex-col" : "overflow-x-auto border-b border-border"),
126
+ children: options.map((option, index) => /* @__PURE__ */ jsx("div", {
127
+ ref: (el) => {
128
+ refs.current[index] = el;
129
+ },
130
+ id: `${baseId}-${option.id}`,
131
+ role: "option",
132
+ tabIndex: index === active ? 0 : -1,
133
+ "aria-selected": option.id === selectedId,
134
+ "aria-disabled": option.disabled || void 0,
135
+ "data-slot": "date-range-picker-preset",
136
+ onFocus: () => setActive(index),
137
+ onKeyDown,
138
+ onClick: () => {
139
+ if (!option.disabled) onPick(option.id);
140
+ },
141
+ className: cn("flex cursor-default items-center rounded-md text-sm whitespace-nowrap text-foreground outline-hidden select-none hover:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 aria-disabled:pointer-events-none aria-disabled:opacity-50 aria-selected:bg-muted aria-selected:font-medium", orientation === "vertical" ? "min-h-8 px-2 pointer-coarse:min-h-11" : "h-7 shrink-0 border border-border px-2.5 pointer-coarse:h-11"),
142
+ children: option.label
143
+ }, option.id))
144
+ });
145
+ }
146
+ function DateRangePicker({ value: valueProp, defaultValue = null, onValueChange, presets: presetList = DEFAULT_PRESETS, presetsOnly = false, weekStartsOn: weekStartsOnProp, timeZone, locale: localeProp, now, min, max, disabled, numberOfMonths: monthsProp, size = "default", labels: labelsProp, id, "aria-label": ariaLabel, className, ...props }) {
147
+ const labels = {
148
+ ...DEFAULT_LABELS,
149
+ ...labelsProp
150
+ };
151
+ const rootRef = React.useRef(null);
152
+ const timer = React.useRef(void 0);
153
+ const [inner, setInner] = React.useState(defaultValue);
154
+ const [open, setOpen] = React.useState(false);
155
+ const [view, setView] = React.useState("calendar");
156
+ const [draft, setDraft] = React.useState(null);
157
+ const [axes, setAxes] = React.useState({});
158
+ const [announcement, setAnnouncement] = React.useState("");
159
+ const wide = useContainerWide(rootRef);
160
+ const drawer = useMedia(DRAWER_QUERY);
161
+ const prefersReduced = useReducedMotion() ?? false;
162
+ const configReduced = useReducedMotionConfig() ?? false;
163
+ const value = valueProp !== void 0 ? valueProp : inner;
164
+ const calendarLocale = typeof localeProp === "object" ? localeProp : void 0;
165
+ const locale = (typeof localeProp === "string" ? localeProp : localeProp?.code) ?? (typeof document !== "undefined" && document.documentElement.lang || void 0);
166
+ const weekStartsOn = weekStartsOnProp ?? calendarLocale?.options?.weekStartsOn ?? localeWeekStart(locale);
167
+ const options = { weekStartsOn };
168
+ const today = todayIn(now, timeZone);
169
+ const months = drawer ? 1 : monthsProp ?? (wide ? 2 : 1);
170
+ const matched = matchPreset(value, presetList, today, options);
171
+ const short = new Intl.DateTimeFormat(locale, {
172
+ day: "numeric",
173
+ month: "short",
174
+ year: "numeric"
175
+ });
176
+ const long = new Intl.DateTimeFormat(locale, { dateStyle: "long" });
177
+ const spoken = (range, preset) => `${preset ? `${preset.label}, ` : ""}${long.formatRange(range.from, range.to)}`;
178
+ React.useEffect(() => () => clearTimeout(timer.current), []);
179
+ const panelRef = React.useRef(null);
180
+ React.useEffect(() => {
181
+ if (open && presetsOnly && view === "calendar") panelRef.current?.querySelector("button[data-day][tabindex=\"0\"]")?.focus();
182
+ }, [
183
+ open,
184
+ presetsOnly,
185
+ view
186
+ ]);
187
+ const changeOpen = (next) => {
188
+ clearTimeout(timer.current);
189
+ if (next) {
190
+ const trigger = rootRef.current;
191
+ const read = {};
192
+ for (const name of [...AXES, "dir"]) {
193
+ const found = trigger?.closest(`[${name}]`)?.getAttribute(name);
194
+ if (found) read[name] = found;
195
+ }
196
+ setAxes(read);
197
+ setView(presetsOnly ? "list" : "calendar");
198
+ }
199
+ setDraft(null);
200
+ setOpen(next);
201
+ };
202
+ const apply = (range, picked) => {
203
+ const preset = picked ?? matchPreset(range, presetList, today, options);
204
+ if (valueProp === void 0) setInner(range);
205
+ onValueChange?.(range, { preset: preset?.id ?? null });
206
+ setAnnouncement(spoken(range, preset));
207
+ changeOpen(false);
208
+ };
209
+ const reduced = () => {
210
+ const html = document.documentElement.dataset.motion;
211
+ if (html === "reduce") return true;
212
+ if (html === "full") return false;
213
+ return prefersReduced || configReduced;
214
+ };
215
+ const onDay = (_range, day) => {
216
+ clearTimeout(timer.current);
217
+ if (!draft || draft.to) {
218
+ setDraft({ from: day });
219
+ return;
220
+ }
221
+ const range = day < draft.from ? {
222
+ from: day,
223
+ to: draft.from
224
+ } : {
225
+ from: draft.from,
226
+ to: day
227
+ };
228
+ setDraft(range);
229
+ if (reduced()) apply(range);
230
+ else timer.current = setTimeout(() => apply(range), duration.chunk);
231
+ };
232
+ const floor = (date) => todayIn(date);
233
+ const presetOptions = presetList.map((preset) => {
234
+ const r = preset.range(today, options);
235
+ return {
236
+ id: preset.id,
237
+ label: preset.label,
238
+ disabled: !!min && r.from < floor(min) || !!max && r.to > floor(max)
239
+ };
240
+ });
241
+ const pickPreset = (presetId) => {
242
+ if (presetId === CUSTOM) return setView("calendar");
243
+ const preset = presetList.find((p) => p.id === presetId);
244
+ if (preset) apply(preset.range(today, options), preset);
245
+ };
246
+ const matchers = [
247
+ ...min ? [{ before: min }] : [],
248
+ ...max ? [{ after: max }] : [],
249
+ ...disabled === void 0 ? [] : Array.isArray(disabled) ? disabled : [disabled]
250
+ ];
251
+ const anchor = value?.to ?? today;
252
+ const defaultMonth = months === 2 ? new Date(anchor.getFullYear(), anchor.getMonth() - 1, 1) : anchor;
253
+ const narrow = months === 1;
254
+ const panel = view === "list" ? /* @__PURE__ */ jsx(PresetList, {
255
+ options: [...presetOptions, {
256
+ id: CUSTOM,
257
+ label: labels.custom
258
+ }],
259
+ selectedId: matched?.id ?? (value ? CUSTOM : void 0),
260
+ orientation: "vertical",
261
+ label: labels.presets,
262
+ onPick: pickPreset
263
+ }) : /* @__PURE__ */ jsxs("div", {
264
+ ref: panelRef,
265
+ "data-slot": "date-range-picker-panel",
266
+ className: cn("flex", narrow ? "flex-col" : "flex-row"),
267
+ children: [!presetsOnly && presetOptions.length > 0 && /* @__PURE__ */ jsx("div", {
268
+ className: cn(!narrow && "border-e border-border"),
269
+ children: /* @__PURE__ */ jsx(PresetList, {
270
+ options: presetOptions,
271
+ selectedId: matched?.id,
272
+ orientation: narrow ? "horizontal" : "vertical",
273
+ label: labels.presets,
274
+ onPick: pickPreset
275
+ })
276
+ }), /* @__PURE__ */ jsx(Calendar, {
277
+ mode: "range",
278
+ locale: calendarLocale,
279
+ selected: draft ?? value ?? void 0,
280
+ onSelect: onDay,
281
+ numberOfMonths: months,
282
+ showOutsideDays: months === 1,
283
+ defaultMonth,
284
+ today,
285
+ weekStartsOn,
286
+ disabled: matchers,
287
+ startMonth: min,
288
+ endMonth: max,
289
+ className: cn(narrow && "mx-auto")
290
+ })]
291
+ });
292
+ const triggerText = matched ? matched.label : value ? short.formatRange(value.from, value.to) : "";
293
+ const trigger = /* @__PURE__ */ jsx(Button, {
294
+ variant: "outline",
295
+ size,
296
+ id,
297
+ "data-slot": "date-range-picker-trigger",
298
+ className: cn("justify-start font-normal text-foreground", presetsOnly && "min-w-40")
299
+ });
300
+ const triggerChildren = /* @__PURE__ */ jsxs(Fragment, { children: [
301
+ ariaLabel && /* @__PURE__ */ jsx("span", {
302
+ className: "sr-only",
303
+ children: `${ariaLabel},`
304
+ }),
305
+ ariaLabel && " ",
306
+ /* @__PURE__ */ jsx(CalendarIcon, {}),
307
+ value ? /* @__PURE__ */ jsx("span", {
308
+ "data-slot": "date-range-picker-value",
309
+ children: triggerText
310
+ }) : /* @__PURE__ */ jsx("span", {
311
+ "data-slot": "date-range-picker-placeholder",
312
+ className: "text-muted-foreground",
313
+ children: labels.placeholder
314
+ }),
315
+ value && matched && /* @__PURE__ */ jsxs("span", {
316
+ className: "sr-only",
317
+ children: [", ", long.formatRange(value.from, value.to)]
318
+ }),
319
+ presetsOnly && /* @__PURE__ */ jsx(ChevronIcon, {})
320
+ ] });
321
+ return /* @__PURE__ */ jsxs("div", {
322
+ ref: rootRef,
323
+ "data-slot": "date-range-picker",
324
+ className: cn("inline-flex", className),
325
+ ...props,
326
+ children: [drawer ? /* @__PURE__ */ jsxs(Drawer, {
327
+ open,
328
+ onOpenChange: changeOpen,
329
+ children: [/* @__PURE__ */ jsx(DrawerTrigger, {
330
+ render: trigger,
331
+ children: triggerChildren
332
+ }), /* @__PURE__ */ jsxs(DrawerContent, {
333
+ "data-slot": "date-range-picker-content",
334
+ ...axes,
335
+ children: [/* @__PURE__ */ jsx(DrawerHeader, { children: /* @__PURE__ */ jsx(DrawerTitle, { children: labels.title }) }), panel]
336
+ })]
337
+ }) : /* @__PURE__ */ jsxs(Popover, {
338
+ open,
339
+ onOpenChange: changeOpen,
340
+ children: [/* @__PURE__ */ jsx(PopoverTrigger, {
341
+ render: trigger,
342
+ children: triggerChildren
343
+ }), /* @__PURE__ */ jsx(PopoverContent, {
344
+ "data-slot": "date-range-picker-content",
345
+ "aria-label": labels.title,
346
+ align: "start",
347
+ className: "w-auto max-w-(--available-width) gap-0 p-0",
348
+ ...axes,
349
+ children: panel
350
+ })]
351
+ }), /* @__PURE__ */ jsx("span", {
352
+ role: "status",
353
+ className: "sr-only",
354
+ "data-slot": "date-range-picker-status",
355
+ children: announcement
356
+ })]
357
+ });
358
+ }
359
+ export { DateRangePicker };
@@ -0,0 +1,4 @@
1
+
2
+ import { BuiltInPresetId, DateRangePreset, DateRangePresetOptions, DateRangeValue, WeekDay, localeWeekStart, matchPreset, presets, todayIn } from "./presets.js";
3
+ import { DateRangePicker, DateRangePickerLabels, DateRangePickerProps } from "./date-range-picker.js";
4
+ export { type BuiltInPresetId, DateRangePicker, type DateRangePickerLabels, type DateRangePickerProps, type DateRangePreset, type DateRangePresetOptions, type DateRangeValue, type WeekDay, localeWeekStart, matchPreset, presets, todayIn };
@@ -0,0 +1,25 @@
1
+
2
+ /** A range of whole days. Both ends are dates at local midnight, the way react-day-picker reads them. */
3
+ export type DateRangeValue = {
4
+ from: Date;
5
+ to: Date;
6
+ };
7
+ /** 0 = Sunday … 6 = Saturday, as react-day-picker's `weekStartsOn`. */
8
+ export type WeekDay = 0 | 1 | 2 | 3 | 4 | 5 | 6;
9
+ export type DateRangePresetOptions = {
10
+ weekStartsOn: WeekDay;
11
+ };
12
+ export type DateRangePreset = {
13
+ id: string;
14
+ label: string;
15
+ /** `now` is today at local midnight, in the picker's `timeZone`. */
16
+ range: (now: Date, options: DateRangePresetOptions) => DateRangeValue;
17
+ };
18
+ /** Today as a local-midnight date, read in `timeZone` (IANA) when given, else the device zone. */
19
+ export declare function todayIn(now?: Date, timeZone?: string): Date;
20
+ export type BuiltInPresetId = "today" | "yesterday" | "thisWeek" | "lastWeek" | "thisMonth" | "lastMonth" | "last7Days" | "last30Days" | "thisYear";
21
+ export declare const presets: Record<BuiltInPresetId, DateRangePreset>;
22
+ /** The first preset whose range is `range`, day for day; list order decides ties. */
23
+ export declare function matchPreset(range: DateRangeValue | null | undefined, list: readonly DateRangePreset[], now: Date, options: DateRangePresetOptions): DateRangePreset | undefined;
24
+ /** The locale's first day of the week (`Intl.Locale` week info), else Monday. */
25
+ export declare function localeWeekStart(locale?: string): WeekDay;
@@ -0,0 +1,115 @@
1
+ "use client";
2
+ // src/date-range-picker/presets.ts
3
+ const day = (y, m, d) => new Date(y, m, d);
4
+ const addDays = (date, n) => day(date.getFullYear(), date.getMonth(), date.getDate() + n);
5
+ const startOfWeek = (date, weekStartsOn) => addDays(date, -((date.getDay() - weekStartsOn + 7) % 7));
6
+ const isSameDay = (a, b) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
7
+ /** Today as a local-midnight date, read in `timeZone` (IANA) when given, else the device zone. */
8
+ function todayIn(now = /* @__PURE__ */ new Date(), timeZone) {
9
+ if (!timeZone) return day(now.getFullYear(), now.getMonth(), now.getDate());
10
+ const parts = new Intl.DateTimeFormat("en-US", {
11
+ timeZone,
12
+ year: "numeric",
13
+ month: "numeric",
14
+ day: "numeric"
15
+ }).formatToParts(now);
16
+ const get = (type) => Number(parts.find((p) => p.type === type)?.value);
17
+ return day(get("year"), get("month") - 1, get("day"));
18
+ }
19
+ const presets = {
20
+ today: {
21
+ id: "today",
22
+ label: "Today",
23
+ range: (now) => ({
24
+ from: now,
25
+ to: now
26
+ })
27
+ },
28
+ yesterday: {
29
+ id: "yesterday",
30
+ label: "Yesterday",
31
+ range: (now) => ({
32
+ from: addDays(now, -1),
33
+ to: addDays(now, -1)
34
+ })
35
+ },
36
+ thisWeek: {
37
+ id: "thisWeek",
38
+ label: "This week",
39
+ range: (now, { weekStartsOn }) => ({
40
+ from: startOfWeek(now, weekStartsOn),
41
+ to: now
42
+ })
43
+ },
44
+ lastWeek: {
45
+ id: "lastWeek",
46
+ label: "Last week",
47
+ range: (now, { weekStartsOn }) => {
48
+ const from = addDays(startOfWeek(now, weekStartsOn), -7);
49
+ return {
50
+ from,
51
+ to: addDays(from, 6)
52
+ };
53
+ }
54
+ },
55
+ thisMonth: {
56
+ id: "thisMonth",
57
+ label: "This month",
58
+ range: (now) => ({
59
+ from: day(now.getFullYear(), now.getMonth(), 1),
60
+ to: now
61
+ })
62
+ },
63
+ lastMonth: {
64
+ id: "lastMonth",
65
+ label: "Last month",
66
+ range: (now) => ({
67
+ from: day(now.getFullYear(), now.getMonth() - 1, 1),
68
+ to: day(now.getFullYear(), now.getMonth(), 0)
69
+ })
70
+ },
71
+ last7Days: {
72
+ id: "last7Days",
73
+ label: "Last 7 days",
74
+ range: (now) => ({
75
+ from: addDays(now, -6),
76
+ to: now
77
+ })
78
+ },
79
+ last30Days: {
80
+ id: "last30Days",
81
+ label: "Last 30 days",
82
+ range: (now) => ({
83
+ from: addDays(now, -29),
84
+ to: now
85
+ })
86
+ },
87
+ thisYear: {
88
+ id: "thisYear",
89
+ label: "This year",
90
+ range: (now) => ({
91
+ from: day(now.getFullYear(), 0, 1),
92
+ to: now
93
+ })
94
+ }
95
+ };
96
+ /** The first preset whose range is `range`, day for day; list order decides ties. */
97
+ function matchPreset(range, list, now, options) {
98
+ if (!range) return void 0;
99
+ return list.find((preset) => {
100
+ const r = preset.range(now, options);
101
+ return isSameDay(r.from, range.from) && isSameDay(r.to, range.to);
102
+ });
103
+ }
104
+ /** The locale's first day of the week (`Intl.Locale` week info), else Monday. */
105
+ function localeWeekStart(locale) {
106
+ try {
107
+ const tag = locale ?? (typeof navigator !== "undefined" ? navigator.language : void 0);
108
+ if (!tag) return 1;
109
+ const info = new Intl.Locale(tag);
110
+ const first = (info.getWeekInfo?.() ?? info.weekInfo)?.firstDay;
111
+ if (first) return first % 7;
112
+ } catch {}
113
+ return 1;
114
+ }
115
+ export { isSameDay, localeWeekStart, matchPreset, presets, todayIn };
package/dist/index.d.ts CHANGED
@@ -26,6 +26,9 @@ import { Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, Comma
26
26
  import "./command/index.js";
27
27
  import { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger } from "./context-menu/context-menu.js";
28
28
  import "./context-menu/index.js";
29
+ import { BuiltInPresetId, DateRangePreset, DateRangePresetOptions, DateRangeValue, WeekDay, localeWeekStart, matchPreset, presets, todayIn } from "./date-range-picker/presets.js";
30
+ import { DateRangePicker, DateRangePickerLabels, DateRangePickerProps } from "./date-range-picker/date-range-picker.js";
31
+ import "./date-range-picker/index.js";
29
32
  import "./dialog/index.js";
30
33
  import { DirectionProvider } from "./direction.js";
31
34
  import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerSwipeHandle, DrawerTitle, DrawerTrigger } from "./drawer/drawer.js";
@@ -48,18 +51,27 @@ import "./kbd/index.js";
48
51
  import "./label/index.js";
49
52
  import { cn } from "./lib/cn.js";
50
53
  import { IconSwap, IconSwapProps } from "./lib/icon-swap.js";
54
+ import { Meter, MeterIndicator, MeterLabel, MeterLegend, MeterProps, MeterSegment, MeterSegmentProps, MeterTrack, MeterValue } from "./meter/meter.js";
55
+ import "./meter/index.js";
56
+ import { Money, MoneyOptions, MoneyProps, formatMoney } from "./money/money.js";
57
+ import "./money/index.js";
51
58
  import { NativeSelect, NativeSelectOptGroup, NativeSelectOption } from "./native-select/native-select.js";
52
59
  import "./native-select/index.js";
53
60
  import { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious } from "./pagination/pagination.js";
54
61
  import "./pagination/index.js";
55
62
  import { Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger } from "./popover/popover.js";
56
63
  import "./popover/index.js";
64
+ import { Progress, ProgressIndicator, ProgressLabel, ProgressTrack, ProgressValue } from "./progress/progress.js";
65
+ import "./progress/index.js";
57
66
  import { RadioGroup, RadioGroupItem } from "./radio-group/radio-group.js";
58
67
  import "./radio-group/index.js";
59
68
  import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue } from "./select/select.js";
60
69
  import "./select/index.js";
61
70
  import { Separator } from "./separator/separator.js";
62
71
  import "./separator/index.js";
72
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./tooltip/tooltip.js";
73
+ import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, useSidebar } from "./sidebar/sidebar.js";
74
+ import "./sidebar/index.js";
63
75
  import { Skeleton, SkeletonContent } from "./skeleton/skeleton.js";
64
76
  import "./skeleton/index.js";
65
77
  import { Slider } from "./slider/slider.js";
@@ -71,6 +83,8 @@ import { LoaderProvider, LoaderProviderProps } from "./spinner/loader-provider.j
71
83
  import { LoaderComponent, registerLoader, registerLoaders } from "./spinner/registry.js";
72
84
  import { Spinner, SpinnerProps, SpinnerVariant } from "./spinner/spinner.js";
73
85
  import "./spinner/index.js";
86
+ import { Stat, StatAction, StatActionProps, StatChange, StatChangeProps, StatDescription, StatGroup, StatGroupProps, StatIcon, StatLabel, StatProps, StatValue } from "./stat/stat.js";
87
+ import "./stat/index.js";
74
88
  import { Switch } from "./switch/switch.js";
75
89
  import "./switch/index.js";
76
90
  import { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow } from "./table/table.js";
@@ -85,7 +99,6 @@ import { ToggleGroup, ToggleGroupItemProps, ToggleGroupMultipleProps, ToggleGrou
85
99
  import "./toggle-group/index.js";
86
100
  import { Toolbar, ToolbarButtonProps, ToolbarGroupProps, ToolbarInputProps, ToolbarLinkProps, ToolbarProps, ToolbarSize } from "./toolbar/toolbar.js";
87
101
  import "./toolbar/index.js";
88
- import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./tooltip/tooltip.js";
89
102
  import "./tooltip/index.js";
90
103
  import "./lib/track-input-modality.js";
91
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertAction, AlertClose, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertLink, AlertLinks, AlertTitle, Avatar, AvatarBadge, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage, Badge, Button, Calendar, CalendarDayButton, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, ComboboxChip, ComboboxChips, ComboboxChipsInput, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxLabel, ComboboxList, ComboboxSeparator, ComboboxTrigger, ComboboxValue, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DirectionProvider, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerSwipeHandle, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, type EmptyIllustrationOptions, EmptyMedia, EmptyTitle, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, IconSwap, type IconSwapProps, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Kbd, KbdGroup, Label, type LoaderComponent, LoaderProvider, type LoaderProviderProps, NativeSelect, NativeSelectOptGroup, NativeSelectOption, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger, RadioGroup, RadioGroupItem, SOUND_MAP, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Skeleton, SkeletonContent, Slider, type SoundComponent, type SoundCue, type SoundPlayer, SoundProvider, type SoundProviderProps, Spinner, type SpinnerProps, type SpinnerVariant, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toast, ToastAction, ToastClose, ToastContent, ToastDescription, ToastPortal, ToastProvider, ToastTitle, ToastViewport, Toaster, ToggleGroup, type ToggleGroupItemProps, type ToggleGroupMultipleProps, type ToggleGroupProps, type ToggleGroupSingleProps, type ToggleGroupSize, type ToggleGroupVariant, Toolbar, type ToolbarButtonProps, type ToolbarGroupProps, type ToolbarInputProps, type ToolbarLinkProps, type ToolbarProps, type ToolbarSize, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, badgeVariants, buttonVariants, cn, createToastManager, lazyUisfxPlayer, registerLoader, registerLoaders, tabsListVariants, toast, useComboboxAnchor, useEmptyIllustration, useSound, useToastManager };
104
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertAction, AlertClose, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertLink, AlertLinks, AlertTitle, Avatar, AvatarBadge, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage, Badge, type BuiltInPresetId, Button, Calendar, CalendarDayButton, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, ComboboxChip, ComboboxChips, ComboboxChipsInput, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxLabel, ComboboxList, ComboboxSeparator, ComboboxTrigger, ComboboxValue, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DateRangePicker, type DateRangePickerLabels, type DateRangePickerProps, type DateRangePreset, type DateRangePresetOptions, type DateRangeValue, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DirectionProvider, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerSwipeHandle, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, type EmptyIllustrationOptions, EmptyMedia, EmptyTitle, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, IconSwap, type IconSwapProps, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Kbd, KbdGroup, Label, type LoaderComponent, LoaderProvider, type LoaderProviderProps, Meter, MeterIndicator, MeterLabel, MeterLegend, type MeterProps, MeterSegment, type MeterSegmentProps, MeterTrack, MeterValue, Money, type MoneyOptions, type MoneyProps, NativeSelect, NativeSelectOptGroup, NativeSelectOption, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger, Progress, ProgressIndicator, ProgressLabel, ProgressTrack, ProgressValue, RadioGroup, RadioGroupItem, SOUND_MAP, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, SkeletonContent, Slider, type SoundComponent, type SoundCue, type SoundPlayer, SoundProvider, type SoundProviderProps, Spinner, type SpinnerProps, type SpinnerVariant, Stat, StatAction, type StatActionProps, StatChange, type StatChangeProps, StatDescription, StatGroup, type StatGroupProps, StatIcon, StatLabel, type StatProps, StatValue, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toast, ToastAction, ToastClose, ToastContent, ToastDescription, ToastPortal, ToastProvider, ToastTitle, ToastViewport, Toaster, ToggleGroup, type ToggleGroupItemProps, type ToggleGroupMultipleProps, type ToggleGroupProps, type ToggleGroupSingleProps, type ToggleGroupSize, type ToggleGroupVariant, Toolbar, type ToolbarButtonProps, type ToolbarGroupProps, type ToolbarInputProps, type ToolbarLinkProps, type ToolbarProps, type ToolbarSize, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type WeekDay, badgeVariants, buttonVariants, cn, createToastManager, formatMoney, lazyUisfxPlayer, localeWeekStart, matchPreset, presets, registerLoader, registerLoaders, tabsListVariants, toast, todayIn, useComboboxAnchor, useEmptyIllustration, useSidebar, useSound, useToastManager };