@stll/ui 0.41.0 → 0.41.1
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/dist/components/button.d.ts +2 -2
- package/dist/components/date-picker-popover.d.ts +40 -2
- package/dist/components/date-picker-popover.js +247 -60
- package/dist/components/date-picker-popover.logic.d.ts +39 -0
- package/dist/components/date-picker-popover.logic.js +70 -1
- package/dist/components/input-group.d.ts +1 -1
- package/dist/components/select.js +2 -1
- package/dist/components/sidebar.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/lib/field-trigger.d.ts +9 -0
- package/dist/lib/field-trigger.js +10 -0
- package/package.json +1 -1
|
@@ -13,8 +13,8 @@ import { VariantProps } from "class-variance-authority";
|
|
|
13
13
|
*/
|
|
14
14
|
declare const buttonAccessibleDisabledClass = "cursor-not-allowed opacity-64";
|
|
15
15
|
declare const buttonVariants: (props?: ({
|
|
16
|
-
size?: "
|
|
17
|
-
variant?: "link" | "destructive" | "default" | "destructive-outline" | "destructive-ghost" | "ghost" | "
|
|
16
|
+
size?: "default" | "row" | "chip" | "icon" | "icon-lg" | "icon-sm" | "icon-xl" | "icon-xs" | "lg" | "sm" | "xl" | "xs" | null | undefined;
|
|
17
|
+
variant?: "link" | "destructive" | "muted" | "default" | "destructive-outline" | "destructive-ghost" | "ghost" | "outline" | "secondary" | null | undefined;
|
|
18
18
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
19
19
|
type ButtonProps = {
|
|
20
20
|
variant?: VariantProps<typeof buttonVariants>["variant"];
|
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
import { OverlayLayer } from "../lib/overlay-layer.js";
|
|
2
|
+
import { DatePickerMode } from "./date-picker-popover.logic.js";
|
|
2
3
|
//#region src/components/date-picker-popover.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* `compact` keeps the dense 32px day cells everywhere. `touch` grows day,
|
|
6
|
+
* month, and year cells to 44px targets under a coarse pointer and leaves a
|
|
7
|
+
* fine pointer on the compact metric.
|
|
8
|
+
*/
|
|
9
|
+
type DatePickerPopoverSize = "compact" | "touch";
|
|
10
|
+
/**
|
|
11
|
+
* `inline` is the borderless trigger for property rows and table cells;
|
|
12
|
+
* `field` is the bordered box form controls share with `SelectTrigger`.
|
|
13
|
+
*/
|
|
14
|
+
type DatePickerPopoverVariant = "inline" | "field";
|
|
3
15
|
type DatePickerPopoverProps = {
|
|
16
|
+
/**
|
|
17
|
+
* `date` (default) reads and writes `YYYY-MM-DD`. `date-time` reads and
|
|
18
|
+
* writes `YYYY-MM-DDTHH:mm`, a zone-less wall-clock time the host converts
|
|
19
|
+
* to and from an instant in the time zone it owns. A `Date` is read in UTC
|
|
20
|
+
* in both modes, so a string is the way to keep a local wall-clock time.
|
|
21
|
+
*/
|
|
22
|
+
mode?: DatePickerMode;
|
|
23
|
+
value: string | Date | null;
|
|
24
|
+
onChange: (value: string | null) => void;
|
|
4
25
|
id?: string;
|
|
5
26
|
/** ID of the visible field label, when the picker is part of a form field. */
|
|
6
27
|
labelledBy?: string;
|
|
7
|
-
|
|
8
|
-
|
|
28
|
+
/** Merged into the trigger's classes. */
|
|
29
|
+
className?: string;
|
|
30
|
+
disabled?: boolean;
|
|
31
|
+
/** Hide the clear button, for fields that must keep a value. */
|
|
32
|
+
hideClear?: boolean;
|
|
33
|
+
size?: DatePickerPopoverSize;
|
|
34
|
+
variant?: DatePickerPopoverVariant;
|
|
9
35
|
locale?: string;
|
|
10
36
|
isOverdue?: boolean;
|
|
11
37
|
showIcon?: boolean;
|
|
@@ -19,6 +45,18 @@ type DatePickerPopoverProps = {
|
|
|
19
45
|
/** Label for the "go to today" button. Auto-localized from the locale when omitted. */
|
|
20
46
|
todayLabel?: string;
|
|
21
47
|
overdueLabel?: string;
|
|
48
|
+
/** Accessible name of the popup. The package carries no catalogs, so the
|
|
49
|
+
* English defaults below are meant to be replaced by the host. */
|
|
50
|
+
dialogLabel?: string;
|
|
51
|
+
previousMonthLabel?: string;
|
|
52
|
+
nextMonthLabel?: string;
|
|
53
|
+
previousYearLabel?: string;
|
|
54
|
+
nextYearLabel?: string;
|
|
55
|
+
previousDecadeLabel?: string;
|
|
56
|
+
nextDecadeLabel?: string;
|
|
57
|
+
/** Label of the time input in `date-time` mode. */
|
|
58
|
+
timeLabel?: string;
|
|
59
|
+
/** Inclusive `YYYY-MM-DD` bounds; in `date-time` mode they bound the day. */
|
|
22
60
|
minDate?: string;
|
|
23
61
|
maxDate?: string;
|
|
24
62
|
isDateDisabled?: (date: string) => boolean;
|
|
@@ -3,11 +3,14 @@ import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from "../icons.js";
|
|
|
3
3
|
import { cn } from "../lib/utils.js";
|
|
4
4
|
import { Button } from "./button.js";
|
|
5
5
|
import { DirectionalIcon } from "./directional-icon.js";
|
|
6
|
+
import { FIELD_TRIGGER_CLASS_NAME } from "../lib/field-trigger.js";
|
|
6
7
|
import { getLocaleWeekInfo, getWeekendDays } from "../lib/week.js";
|
|
7
|
-
import { localDateFromTimestamp, millisecondsUntilNextLocalDate, resolveCalendarViewMonth, shiftCalendarDate } from "./date-picker-popover.logic.js";
|
|
8
|
+
import { DATE_PICKER_MODE, formatDateTimeValue, getHourOptions, getMinuteOptions, getTimeFieldNames, joinPickerTime, localDateFromTimestamp, millisecondsUntilNextLocalDate, parseDateTimeValue, resolveCalendarViewMonth, shiftCalendarDate, splitPickerTime } from "./date-picker-popover.logic.js";
|
|
8
9
|
import { Popover, PopoverContent as PopoverPopup, PopoverTrigger } from "./popover.js";
|
|
10
|
+
import { Select, SelectContent as SelectPopup, SelectItem, SelectTrigger, SelectValue } from "./select.js";
|
|
9
11
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
10
12
|
import { useCallback, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
13
|
+
import { panic } from "better-result";
|
|
11
14
|
import { Temporal } from "temporal-polyfill/full";
|
|
12
15
|
//#region src/components/date-picker-popover.tsx
|
|
13
16
|
const toISODate = (date) => date.toString();
|
|
@@ -154,6 +157,7 @@ const deriveTodayLabel = (locale) => {
|
|
|
154
157
|
const raw = getRelativeTimeFormatter(locale).format(0, "day");
|
|
155
158
|
return raw.charAt(0).toUpperCase() + raw.slice(1);
|
|
156
159
|
};
|
|
160
|
+
const utcDateTimeFromDate = (date) => Temporal.Instant.fromEpochMilliseconds(date.getTime()).toZonedDateTimeISO("UTC").toPlainDateTime().toString({ smallestUnit: "minute" });
|
|
157
161
|
const normalizeDate = (v) => {
|
|
158
162
|
if (v === null || v === void 0) return "";
|
|
159
163
|
if (v instanceof Date) return Temporal.Instant.fromEpochMilliseconds(v.getTime()).toZonedDateTimeISO("UTC").toPlainDate().toString();
|
|
@@ -165,8 +169,94 @@ const isAfter = (a, b) => a > b;
|
|
|
165
169
|
/** Round down to the start of a decade (e.g. 2026 → 2020). */
|
|
166
170
|
const decadeStart = (year) => Math.floor(year / 10) * 10;
|
|
167
171
|
const DECADE_SIZE = 12;
|
|
168
|
-
const
|
|
169
|
-
|
|
172
|
+
const DISPLAY_DATE_FORMAT = {
|
|
173
|
+
month: "short",
|
|
174
|
+
day: "numeric",
|
|
175
|
+
year: "numeric",
|
|
176
|
+
calendar: "gregory",
|
|
177
|
+
timeZone: "UTC"
|
|
178
|
+
};
|
|
179
|
+
const DISPLAY_DATE_TIME_FORMAT = {
|
|
180
|
+
...DISPLAY_DATE_FORMAT,
|
|
181
|
+
hour: "numeric",
|
|
182
|
+
minute: "2-digit"
|
|
183
|
+
};
|
|
184
|
+
const resolveSelection = (mode, value) => {
|
|
185
|
+
switch (mode) {
|
|
186
|
+
case DATE_PICKER_MODE.date: return {
|
|
187
|
+
mode,
|
|
188
|
+
date: normalizeDate(value)
|
|
189
|
+
};
|
|
190
|
+
case DATE_PICKER_MODE.dateTime: {
|
|
191
|
+
const parsed = parseDateTimeValue(value instanceof Date ? utcDateTimeFromDate(value) : value);
|
|
192
|
+
return {
|
|
193
|
+
mode: DATE_PICKER_MODE.dateTime,
|
|
194
|
+
date: parsed?.date ?? "",
|
|
195
|
+
time: parsed?.time ?? null
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
default: return panic(`Unhandled date picker mode: ${String(mode)}`);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
/** Locale-formatted trigger text, or null while nothing is set. */
|
|
202
|
+
const formatSelection = (locale, selection) => {
|
|
203
|
+
if (selection.date === "") return null;
|
|
204
|
+
const date = Temporal.PlainDate.from(selection.date);
|
|
205
|
+
switch (selection.mode) {
|
|
206
|
+
case DATE_PICKER_MODE.date: return getDateFormatter(locale, DISPLAY_DATE_FORMAT).format(toUTCDateTime(date));
|
|
207
|
+
case DATE_PICKER_MODE.dateTime: {
|
|
208
|
+
const instant = date.toZonedDateTime({
|
|
209
|
+
plainTime: Temporal.PlainTime.from(selection.time ?? "00:00"),
|
|
210
|
+
timeZone: "UTC"
|
|
211
|
+
}).epochMilliseconds;
|
|
212
|
+
return getDateFormatter(locale, DISPLAY_DATE_TIME_FORMAT).format(instant);
|
|
213
|
+
}
|
|
214
|
+
default: return panic(`Unhandled date picker mode: ${String(selection)}`);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
const TRIGGER_VARIANT_CLASS_NAMES = {
|
|
218
|
+
inline: cn("flex h-auto min-h-7 w-full min-w-0 items-center gap-1.5", "rounded-md px-1.5 text-sm", "hover:bg-muted", "data-disabled:pointer-events-none data-disabled:opacity-64"),
|
|
219
|
+
field: cn(FIELD_TRIGGER_CLASS_NAME, "min-w-0 justify-start gap-1.5")
|
|
220
|
+
};
|
|
221
|
+
/** The package carries no catalogs: English defaults the host replaces. */
|
|
222
|
+
const resolvePopupLabels = (props) => ({
|
|
223
|
+
dialog: props.dialogLabel ?? "Date picker",
|
|
224
|
+
navigation: {
|
|
225
|
+
days: {
|
|
226
|
+
next: props.nextMonthLabel ?? "Next month",
|
|
227
|
+
previous: props.previousMonthLabel ?? "Previous month"
|
|
228
|
+
},
|
|
229
|
+
months: {
|
|
230
|
+
next: props.nextYearLabel ?? "Next year",
|
|
231
|
+
previous: props.previousYearLabel ?? "Previous year"
|
|
232
|
+
},
|
|
233
|
+
years: {
|
|
234
|
+
next: props.nextDecadeLabel ?? "Next decade",
|
|
235
|
+
previous: props.previousDecadeLabel ?? "Previous decade"
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
time: props.timeLabel ?? "Time"
|
|
239
|
+
});
|
|
240
|
+
const PICKER_SIZE_CLASS_NAMES = {
|
|
241
|
+
compact: {
|
|
242
|
+
popup: "w-60",
|
|
243
|
+
heading: "",
|
|
244
|
+
day: "",
|
|
245
|
+
cell: ""
|
|
246
|
+
},
|
|
247
|
+
touch: {
|
|
248
|
+
popup: "w-60 pointer-coarse:w-77",
|
|
249
|
+
heading: "pointer-coarse:min-h-11 pointer-coarse:text-sm",
|
|
250
|
+
day: "pointer-coarse:size-11 pointer-coarse:text-sm",
|
|
251
|
+
cell: "pointer-coarse:min-h-11 pointer-coarse:text-sm"
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
const DatePickerPopoverContent = (props) => {
|
|
255
|
+
const { id, labelledBy, className, disabled, hideClear, size = "compact", variant = "inline", mode = DATE_PICKER_MODE.date, value: rawValue, onChange, locale, isOverdue = false, showIcon = true, placeholderLabel, clearLabel = "Clear date", defaultOpen = false, onOpenChange, todayLabel: todayLabelProp, overdueLabel, minDate, maxDate, isDateDisabled, layer = "default", today } = props;
|
|
256
|
+
const selection = resolveSelection(mode, rawValue);
|
|
257
|
+
const value = selection.date;
|
|
258
|
+
const labels = resolvePopupLabels(props);
|
|
259
|
+
const sizeClassNames = PICKER_SIZE_CLASS_NAMES[size];
|
|
170
260
|
const displayValueId = useId();
|
|
171
261
|
const todayLabel = todayLabelProp ?? deriveTodayLabel(locale);
|
|
172
262
|
const firstDow = useMemo(() => getFirstDayOfWeek(locale), [locale]);
|
|
@@ -204,13 +294,28 @@ const DatePickerPopoverContent = ({ id, labelledBy, value: rawValue, onChange, l
|
|
|
204
294
|
maxDate,
|
|
205
295
|
isDateDisabled
|
|
206
296
|
]);
|
|
207
|
-
const displayLabel =
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
297
|
+
const displayLabel = formatSelection(locale, selection) ?? placeholderLabel ?? "—";
|
|
298
|
+
const selectDay = (date) => {
|
|
299
|
+
switch (selection.mode) {
|
|
300
|
+
case DATE_PICKER_MODE.date:
|
|
301
|
+
onChange(date);
|
|
302
|
+
return;
|
|
303
|
+
case DATE_PICKER_MODE.dateTime:
|
|
304
|
+
onChange(formatDateTimeValue({
|
|
305
|
+
date,
|
|
306
|
+
time: selection.time ?? "00:00"
|
|
307
|
+
}));
|
|
308
|
+
return;
|
|
309
|
+
default: panic(`Unhandled date picker mode: ${String(selection)}`);
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
const handleTimeChange = (clock) => {
|
|
313
|
+
if (value === "") return;
|
|
314
|
+
onChange(formatDateTimeValue({
|
|
315
|
+
date: value,
|
|
316
|
+
time: joinPickerTime(clock)
|
|
317
|
+
}));
|
|
318
|
+
};
|
|
214
319
|
const formatDayLabel = useCallback((iso) => getDateFormatter(locale, {
|
|
215
320
|
weekday: "long",
|
|
216
321
|
month: "long",
|
|
@@ -240,7 +345,7 @@ const DatePickerPopoverContent = ({ id, labelledBy, value: rawValue, onChange, l
|
|
|
240
345
|
else {
|
|
241
346
|
if (e.key === "Enter" || e.key === " ") {
|
|
242
347
|
e.preventDefault();
|
|
243
|
-
if (!isDayDisabled(current))
|
|
348
|
+
if (!isDayDisabled(current)) selectDay(current);
|
|
244
349
|
}
|
|
245
350
|
return;
|
|
246
351
|
}
|
|
@@ -327,18 +432,20 @@ const DatePickerPopoverContent = ({ id, labelledBy, value: rawValue, onChange, l
|
|
|
327
432
|
setDecadeBaseOverride(decadeStart(viewYear));
|
|
328
433
|
}
|
|
329
434
|
};
|
|
435
|
+
const triggerToneClassName = (() => {
|
|
436
|
+
if (isOverdue) return "text-destructive";
|
|
437
|
+
if (value) return "text-foreground";
|
|
438
|
+
return "text-muted-foreground";
|
|
439
|
+
})();
|
|
330
440
|
return /* @__PURE__ */ jsxs(Popover, {
|
|
331
441
|
defaultOpen,
|
|
332
442
|
onOpenChange: handleOpenChange,
|
|
333
443
|
children: [/* @__PURE__ */ jsxs(PopoverTrigger, {
|
|
444
|
+
disabled,
|
|
334
445
|
render: /* @__PURE__ */ jsx("button", {
|
|
335
446
|
"aria-label": !labelledBy && value ? displayLabel : void 0,
|
|
336
447
|
"aria-labelledby": labelledBy ? `${labelledBy} ${displayValueId}` : void 0,
|
|
337
|
-
className: cn(
|
|
338
|
-
if (isOverdue) return "text-destructive";
|
|
339
|
-
if (value) return "text-foreground";
|
|
340
|
-
return "text-muted-foreground";
|
|
341
|
-
})()),
|
|
448
|
+
className: cn(TRIGGER_VARIANT_CLASS_NAMES[variant], triggerToneClassName, className),
|
|
342
449
|
id,
|
|
343
450
|
type: "button"
|
|
344
451
|
}),
|
|
@@ -355,48 +462,25 @@ const DatePickerPopoverContent = ({ id, labelledBy, value: rawValue, onChange, l
|
|
|
355
462
|
})
|
|
356
463
|
]
|
|
357
464
|
}), /* @__PURE__ */ jsx(PopoverPopup, {
|
|
465
|
+
className: "data-starting-style:scale-100",
|
|
358
466
|
layer,
|
|
359
467
|
padding: "sm",
|
|
360
468
|
side: "bottom",
|
|
361
469
|
sideOffset: 4,
|
|
362
470
|
children: /* @__PURE__ */ jsxs("div", {
|
|
363
|
-
|
|
471
|
+
"aria-label": labels.dialog,
|
|
472
|
+
className: sizeClassNames.popup,
|
|
473
|
+
"data-slot": "date-picker-popup",
|
|
364
474
|
role: "dialog",
|
|
365
|
-
"aria-label": "Date picker",
|
|
366
475
|
children: [
|
|
367
|
-
/* @__PURE__ */
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
})(),
|
|
376
|
-
onClick: handlePrev,
|
|
377
|
-
size: "icon-xs",
|
|
378
|
-
variant: "ghost",
|
|
379
|
-
children: /* @__PURE__ */ jsx(DirectionalIcon, { icon: ChevronLeftIcon })
|
|
380
|
-
}),
|
|
381
|
-
/* @__PURE__ */ jsx("button", {
|
|
382
|
-
className: cn("text-xs font-medium", view !== "years" && "hover:bg-muted cursor-pointer rounded-md px-2 py-0.5", view === "years" && "cursor-default"),
|
|
383
|
-
onClick: handleHeaderClick,
|
|
384
|
-
tabIndex: view === "years" ? -1 : 0,
|
|
385
|
-
type: "button",
|
|
386
|
-
children: headerLabel
|
|
387
|
-
}),
|
|
388
|
-
/* @__PURE__ */ jsx(Button, {
|
|
389
|
-
"aria-label": (() => {
|
|
390
|
-
if (view === "days") return "Next month";
|
|
391
|
-
if (view === "months") return "Next year";
|
|
392
|
-
return "Next decade";
|
|
393
|
-
})(),
|
|
394
|
-
onClick: handleNext,
|
|
395
|
-
size: "icon-xs",
|
|
396
|
-
variant: "ghost",
|
|
397
|
-
children: /* @__PURE__ */ jsx(DirectionalIcon, { icon: ChevronRightIcon })
|
|
398
|
-
})
|
|
399
|
-
]
|
|
476
|
+
/* @__PURE__ */ jsx(PickerHeader, {
|
|
477
|
+
headingClassName: sizeClassNames.heading,
|
|
478
|
+
label: headerLabel,
|
|
479
|
+
labels: labels.navigation[view],
|
|
480
|
+
onHeadingClick: handleHeaderClick,
|
|
481
|
+
onNext: handleNext,
|
|
482
|
+
onPrevious: handlePrev,
|
|
483
|
+
view
|
|
400
484
|
}),
|
|
401
485
|
view === "days" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
|
|
402
486
|
className: "grid grid-cols-7 gap-0",
|
|
@@ -416,16 +500,16 @@ const DatePickerPopoverContent = ({ id, labelledBy, value: rawValue, onChange, l
|
|
|
416
500
|
children: days.map((day) => {
|
|
417
501
|
const isSelected = day.date === value;
|
|
418
502
|
const isFocused = day.date === focusedDate || !focusedDate && isSelected || !focusedDate && !value && day.isToday;
|
|
419
|
-
const
|
|
503
|
+
const isUnavailable = isDayDisabled(day.date);
|
|
420
504
|
return /* @__PURE__ */ jsx("button", {
|
|
421
505
|
"aria-current": day.isToday ? "date" : void 0,
|
|
422
|
-
"aria-disabled":
|
|
506
|
+
"aria-disabled": isUnavailable || void 0,
|
|
423
507
|
"aria-label": formatDayLabel(day.date),
|
|
424
508
|
"aria-selected": isSelected || void 0,
|
|
425
|
-
className: cn("flex size-8 items-center justify-center", "rounded-full text-xs", "focus-visible:ring-ring focus-visible:ring-1 focus-visible:outline-none",
|
|
509
|
+
className: cn("flex size-8 items-center justify-center", "rounded-full text-xs", sizeClassNames.day, "focus-visible:ring-ring focus-visible:ring-1 focus-visible:outline-none", isUnavailable ? "text-foreground-disabled cursor-not-allowed" : "hover:bg-muted cursor-pointer", !day.isCurrentMonth && !isUnavailable && "text-foreground-disabled", day.isWeekend && day.isCurrentMonth && !isUnavailable && !isSelected && "text-foreground-label", day.isToday && !isSelected && !isUnavailable && "ring-foreground font-medium ring-1", isSelected && "bg-primary text-primary-foreground hover:bg-primary/90"),
|
|
426
510
|
"data-date": day.date,
|
|
427
511
|
onClick: () => {
|
|
428
|
-
if (!
|
|
512
|
+
if (!isUnavailable) selectDay(day.date);
|
|
429
513
|
},
|
|
430
514
|
role: "gridcell",
|
|
431
515
|
tabIndex: isFocused ? 0 : -1,
|
|
@@ -437,6 +521,7 @@ const DatePickerPopoverContent = ({ id, labelledBy, value: rawValue, onChange, l
|
|
|
437
521
|
view === "months" && /* @__PURE__ */ jsx(MonthGrid, {
|
|
438
522
|
currentMonth: selectedMonth,
|
|
439
523
|
currentYear: selectedYear,
|
|
524
|
+
cellClassName: sizeClassNames.cell,
|
|
440
525
|
locale,
|
|
441
526
|
onSelect: handleMonthSelect,
|
|
442
527
|
today,
|
|
@@ -445,9 +530,17 @@ const DatePickerPopoverContent = ({ id, labelledBy, value: rawValue, onChange, l
|
|
|
445
530
|
view === "years" && /* @__PURE__ */ jsx(YearGrid, {
|
|
446
531
|
currentYear: selectedYear,
|
|
447
532
|
decadeBase,
|
|
533
|
+
cellClassName: sizeClassNames.cell,
|
|
448
534
|
onSelect: handleYearSelect,
|
|
449
535
|
today
|
|
450
536
|
}),
|
|
537
|
+
selection.mode === DATE_PICKER_MODE.dateTime && /* @__PURE__ */ jsx(TimeField, {
|
|
538
|
+
disabled: value === "",
|
|
539
|
+
label: labels.time,
|
|
540
|
+
locale,
|
|
541
|
+
onChange: handleTimeChange,
|
|
542
|
+
time: selection.time
|
|
543
|
+
}),
|
|
451
544
|
/* @__PURE__ */ jsxs("div", {
|
|
452
545
|
className: "mt-1 flex items-center gap-1 border-t pt-1",
|
|
453
546
|
children: [/* @__PURE__ */ jsx(Button, {
|
|
@@ -463,7 +556,7 @@ const DatePickerPopoverContent = ({ id, labelledBy, value: rawValue, onChange, l
|
|
|
463
556
|
size: "xs",
|
|
464
557
|
variant: "ghost",
|
|
465
558
|
children: todayLabel
|
|
466
|
-
}), value && /* @__PURE__ */ jsx(Button, {
|
|
559
|
+
}), value && !hideClear && /* @__PURE__ */ jsx(Button, {
|
|
467
560
|
className: "flex-1",
|
|
468
561
|
onClick: () => onChange(null),
|
|
469
562
|
size: "xs",
|
|
@@ -486,8 +579,102 @@ const DatePickerPopover = (props) => {
|
|
|
486
579
|
today
|
|
487
580
|
});
|
|
488
581
|
};
|
|
582
|
+
const PickerHeader = ({ headingClassName, label, labels, onHeadingClick, onNext, onPrevious, view }) => {
|
|
583
|
+
const isTopLevel = view === "years";
|
|
584
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
585
|
+
className: "flex items-center justify-between gap-1 pb-1",
|
|
586
|
+
children: [
|
|
587
|
+
/* @__PURE__ */ jsx(Button, {
|
|
588
|
+
"aria-label": labels.previous,
|
|
589
|
+
onClick: onPrevious,
|
|
590
|
+
size: "icon-xs",
|
|
591
|
+
variant: "ghost",
|
|
592
|
+
children: /* @__PURE__ */ jsx(DirectionalIcon, { icon: ChevronLeftIcon })
|
|
593
|
+
}),
|
|
594
|
+
/* @__PURE__ */ jsx("button", {
|
|
595
|
+
className: cn("text-xs font-medium", isTopLevel ? "cursor-default" : "hover:bg-muted cursor-pointer rounded-md px-2 py-0.5", headingClassName),
|
|
596
|
+
"data-slot": "date-picker-heading",
|
|
597
|
+
onClick: onHeadingClick,
|
|
598
|
+
tabIndex: isTopLevel ? -1 : 0,
|
|
599
|
+
type: "button",
|
|
600
|
+
children: label
|
|
601
|
+
}),
|
|
602
|
+
/* @__PURE__ */ jsx(Button, {
|
|
603
|
+
"aria-label": labels.next,
|
|
604
|
+
onClick: onNext,
|
|
605
|
+
size: "icon-xs",
|
|
606
|
+
variant: "ghost",
|
|
607
|
+
children: /* @__PURE__ */ jsx(DirectionalIcon, { icon: ChevronRightIcon })
|
|
608
|
+
})
|
|
609
|
+
]
|
|
610
|
+
});
|
|
611
|
+
};
|
|
612
|
+
/**
|
|
613
|
+
* Hour and minute selects in the locale's own clock and numerals. Inert
|
|
614
|
+
* until a day is picked, since a time alone is not a value.
|
|
615
|
+
*/
|
|
616
|
+
const TimeField = ({ disabled, label, locale, onChange, time }) => {
|
|
617
|
+
const labelId = useId();
|
|
618
|
+
const hourOptions = useMemo(() => getHourOptions(locale), [locale]);
|
|
619
|
+
const minuteOptions = useMemo(() => getMinuteOptions(locale), [locale]);
|
|
620
|
+
const fieldNames = useMemo(() => getTimeFieldNames(locale), [locale]);
|
|
621
|
+
const clock = time === null ? null : splitPickerTime(time);
|
|
622
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
623
|
+
"aria-labelledby": labelId,
|
|
624
|
+
className: "mt-1 flex items-center gap-1.5 border-t pt-1",
|
|
625
|
+
role: "group",
|
|
626
|
+
children: [
|
|
627
|
+
/* @__PURE__ */ jsx("span", {
|
|
628
|
+
className: "text-foreground-label shrink-0 text-xs",
|
|
629
|
+
id: labelId,
|
|
630
|
+
children: label
|
|
631
|
+
}),
|
|
632
|
+
/* @__PURE__ */ jsx(TimePartSelect, {
|
|
633
|
+
disabled,
|
|
634
|
+
label: fieldNames.hour,
|
|
635
|
+
onChange: (hour) => onChange({
|
|
636
|
+
hour,
|
|
637
|
+
minute: clock?.minute ?? 0
|
|
638
|
+
}),
|
|
639
|
+
options: hourOptions,
|
|
640
|
+
value: clock?.hour ?? null
|
|
641
|
+
}),
|
|
642
|
+
/* @__PURE__ */ jsx("span", {
|
|
643
|
+
"aria-hidden": "true",
|
|
644
|
+
className: "text-muted-foreground text-xs",
|
|
645
|
+
children: ":"
|
|
646
|
+
}),
|
|
647
|
+
/* @__PURE__ */ jsx(TimePartSelect, {
|
|
648
|
+
disabled,
|
|
649
|
+
label: fieldNames.minute,
|
|
650
|
+
onChange: (minute) => onChange({
|
|
651
|
+
hour: clock?.hour ?? 0,
|
|
652
|
+
minute
|
|
653
|
+
}),
|
|
654
|
+
options: minuteOptions,
|
|
655
|
+
value: clock?.minute ?? null
|
|
656
|
+
})
|
|
657
|
+
]
|
|
658
|
+
});
|
|
659
|
+
};
|
|
660
|
+
const TimePartSelect = ({ disabled, label, onChange, options, value }) => /* @__PURE__ */ jsxs(Select, {
|
|
661
|
+
disabled,
|
|
662
|
+
onValueChange: (next) => {
|
|
663
|
+
if (typeof next === "number") onChange(next);
|
|
664
|
+
},
|
|
665
|
+
value,
|
|
666
|
+
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|
|
667
|
+
"aria-label": label,
|
|
668
|
+
className: "min-w-0 flex-1",
|
|
669
|
+
size: "sm",
|
|
670
|
+
children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "—" })
|
|
671
|
+
}), /* @__PURE__ */ jsx(SelectPopup, { children: options.map((option) => /* @__PURE__ */ jsx(SelectItem, {
|
|
672
|
+
value: option.value,
|
|
673
|
+
children: option.label
|
|
674
|
+
}, option.value)) })]
|
|
675
|
+
});
|
|
489
676
|
const MONTHS_PER_ROW = 3;
|
|
490
|
-
const MonthGrid = ({ locale, viewYear, currentMonth, currentYear, onSelect, today }) => {
|
|
677
|
+
const MonthGrid = ({ locale, viewYear, currentMonth, currentYear, cellClassName, onSelect, today }) => {
|
|
491
678
|
const labels = useMemo(() => getMonthLabels(locale, "short"), [locale]);
|
|
492
679
|
const now = Temporal.PlainDate.from(today);
|
|
493
680
|
const todayMonth = now.month - 1;
|
|
@@ -504,7 +691,7 @@ const MonthGrid = ({ locale, viewYear, currentMonth, currentYear, onSelect, toda
|
|
|
504
691
|
const isSelected = currentMonth !== null && currentYear !== null && month === currentMonth && viewYear === currentYear;
|
|
505
692
|
return /* @__PURE__ */ jsxs("button", {
|
|
506
693
|
"aria-selected": isSelected || void 0,
|
|
507
|
-
className: cn("rounded-md px-2 py-1.5 text-xs", "hover:bg-muted cursor-pointer", "focus-visible:ring-ring focus-visible:ring-1 focus-visible:outline-none", month === todayMonth && viewYear === todayYear && !isSelected && "ring-foreground font-medium ring-1", isSelected && "bg-primary text-primary-foreground hover:bg-primary/90"),
|
|
694
|
+
className: cn("rounded-md px-2 py-1.5 text-xs", cellClassName, "hover:bg-muted cursor-pointer", "focus-visible:ring-ring focus-visible:ring-1 focus-visible:outline-none", month === todayMonth && viewYear === todayYear && !isSelected && "ring-foreground font-medium ring-1", isSelected && "bg-primary text-primary-foreground hover:bg-primary/90"),
|
|
508
695
|
onClick: () => onSelect(month),
|
|
509
696
|
role: "gridcell",
|
|
510
697
|
type: "button",
|
|
@@ -519,7 +706,7 @@ const MonthGrid = ({ locale, viewYear, currentMonth, currentYear, onSelect, toda
|
|
|
519
706
|
});
|
|
520
707
|
};
|
|
521
708
|
const YEARS_PER_ROW = 3;
|
|
522
|
-
const YearGrid = ({ decadeBase, currentYear, onSelect, today }) => {
|
|
709
|
+
const YearGrid = ({ decadeBase, currentYear, cellClassName, onSelect, today }) => {
|
|
523
710
|
const todayYear = Temporal.PlainDate.from(today).year;
|
|
524
711
|
const startYear = decadeBase - 1;
|
|
525
712
|
const rows = [];
|
|
@@ -536,7 +723,7 @@ const YearGrid = ({ decadeBase, currentYear, onSelect, today }) => {
|
|
|
536
723
|
const isSelected = currentYear !== null && year === currentYear;
|
|
537
724
|
return /* @__PURE__ */ jsx("button", {
|
|
538
725
|
"aria-selected": isSelected || void 0,
|
|
539
|
-
className: cn("rounded-md px-2 py-1.5 text-xs", "hover:bg-muted cursor-pointer", "focus-visible:ring-ring focus-visible:ring-1 focus-visible:outline-none", isOutside && !isSelected && "text-foreground-subtle", year === todayYear && !isSelected && "ring-foreground font-medium ring-1", isSelected && "bg-primary text-primary-foreground hover:bg-primary/90"),
|
|
726
|
+
className: cn("rounded-md px-2 py-1.5 text-xs", cellClassName, "hover:bg-muted cursor-pointer", "focus-visible:ring-ring focus-visible:ring-1 focus-visible:outline-none", isOutside && !isSelected && "text-foreground-subtle", year === todayYear && !isSelected && "ring-foreground font-medium ring-1", isSelected && "bg-primary text-primary-foreground hover:bg-primary/90"),
|
|
540
727
|
onClick: () => onSelect(year),
|
|
541
728
|
role: "gridcell",
|
|
542
729
|
type: "button",
|
|
@@ -14,4 +14,43 @@ export declare const shiftCalendarDate: (date: string, options: {
|
|
|
14
14
|
years?: number;
|
|
15
15
|
}) => string;
|
|
16
16
|
export declare const millisecondsUntilNextLocalDate: (timestamp: number) => number;
|
|
17
|
+
export declare const DATE_PICKER_MODE: Readonly<{
|
|
18
|
+
readonly date: "date";
|
|
19
|
+
readonly dateTime: "date-time";
|
|
20
|
+
}>;
|
|
21
|
+
export type DatePickerMode = (typeof DATE_PICKER_MODE)[keyof typeof DATE_PICKER_MODE];
|
|
22
|
+
/** The time a date-time value takes when a day is picked before any time. */
|
|
23
|
+
export declare const DEFAULT_PICKER_TIME = "00:00";
|
|
24
|
+
/** A wall-clock selection: an ISO date plus an `HH:mm` time. */
|
|
25
|
+
export type DateTimeSelection = {
|
|
26
|
+
date: string;
|
|
27
|
+
time: string;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Read a date-time value (`YYYY-MM-DDTHH:mm`, a zone-less
|
|
31
|
+
* `Temporal.PlainDateTime`). A bare date reads as midnight and seconds are
|
|
32
|
+
* dropped. An instant (`…Z`) is not a wall-clock time and throws, like any
|
|
33
|
+
* other malformed value.
|
|
34
|
+
*/
|
|
35
|
+
export declare const parseDateTimeValue: (value: string | null) => DateTimeSelection | null;
|
|
36
|
+
export declare const formatDateTimeValue: ({ date, time }: DateTimeSelection) => string;
|
|
37
|
+
export type PickerClock = {
|
|
38
|
+
hour: number;
|
|
39
|
+
minute: number;
|
|
40
|
+
};
|
|
41
|
+
export declare const splitPickerTime: (time: string) => PickerClock;
|
|
42
|
+
export declare const joinPickerTime: (clock: PickerClock) => string;
|
|
43
|
+
export type PickerTimeOption = {
|
|
44
|
+
value: number;
|
|
45
|
+
label: string;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Hour choices in the locale's own clock: `2 PM` where the locale counts
|
|
49
|
+
* twelve hours, `14` where it counts twenty-four.
|
|
50
|
+
*/
|
|
51
|
+
export declare const getHourOptions: (locale: string) => PickerTimeOption[];
|
|
52
|
+
/** Two-digit minute choices in the locale's numbering system. */
|
|
53
|
+
export declare const getMinuteOptions: (locale: string) => PickerTimeOption[];
|
|
54
|
+
/** Accessible names of the hour and minute fields, from the locale itself. */
|
|
55
|
+
export declare const getTimeFieldNames: (locale: string) => Record<keyof PickerClock, string>;
|
|
17
56
|
//#endregion
|
|
@@ -27,5 +27,74 @@ const millisecondsUntilNextLocalDate = (timestamp) => {
|
|
|
27
27
|
});
|
|
28
28
|
return Math.max(0, nextLocalDate.epochMilliseconds - timestamp) + DATE_ROLLOVER_EPSILON_MS;
|
|
29
29
|
};
|
|
30
|
+
const DATE_PICKER_MODE = Object.freeze({
|
|
31
|
+
date: "date",
|
|
32
|
+
dateTime: "date-time"
|
|
33
|
+
});
|
|
34
|
+
/** The time a date-time value takes when a day is picked before any time. */
|
|
35
|
+
const DEFAULT_PICKER_TIME = "00:00";
|
|
36
|
+
/**
|
|
37
|
+
* Read a date-time value (`YYYY-MM-DDTHH:mm`, a zone-less
|
|
38
|
+
* `Temporal.PlainDateTime`). A bare date reads as midnight and seconds are
|
|
39
|
+
* dropped. An instant (`…Z`) is not a wall-clock time and throws, like any
|
|
40
|
+
* other malformed value.
|
|
41
|
+
*/
|
|
42
|
+
const parseDateTimeValue = (value) => {
|
|
43
|
+
if (value === null || value === "") return null;
|
|
44
|
+
const dateTime = Temporal.PlainDateTime.from(value);
|
|
45
|
+
return {
|
|
46
|
+
date: dateTime.toPlainDate().toString(),
|
|
47
|
+
time: dateTime.toPlainTime().toString({ smallestUnit: "minute" })
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
const formatDateTimeValue = ({ date, time }) => `${date}T${time}`;
|
|
51
|
+
const splitPickerTime = (time) => {
|
|
52
|
+
const { hour, minute } = Temporal.PlainTime.from(time);
|
|
53
|
+
return {
|
|
54
|
+
hour,
|
|
55
|
+
minute
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
const joinPickerTime = (clock) => Temporal.PlainTime.from(clock).toString({ smallestUnit: "minute" });
|
|
59
|
+
const HOURS = Array.from({ length: 24 }, (_, hour) => hour);
|
|
60
|
+
const MINUTES = Array.from({ length: 60 }, (_, minute) => minute);
|
|
61
|
+
/**
|
|
62
|
+
* Hour choices in the locale's own clock: `2 PM` where the locale counts
|
|
63
|
+
* twelve hours, `14` where it counts twenty-four.
|
|
64
|
+
*/
|
|
65
|
+
const getHourOptions = (locale) => {
|
|
66
|
+
const formatter = new Intl.DateTimeFormat(locale, {
|
|
67
|
+
hour: "numeric",
|
|
68
|
+
timeZone: "UTC"
|
|
69
|
+
});
|
|
70
|
+
const epoch = Temporal.PlainDate.from("1970-01-01");
|
|
71
|
+
return HOURS.map((hour) => ({
|
|
72
|
+
value: hour,
|
|
73
|
+
label: formatter.format(epoch.toZonedDateTime({
|
|
74
|
+
plainTime: Temporal.PlainTime.from({ hour }),
|
|
75
|
+
timeZone: "UTC"
|
|
76
|
+
}).epochMilliseconds)
|
|
77
|
+
}));
|
|
78
|
+
};
|
|
79
|
+
/** Two-digit minute choices in the locale's numbering system. */
|
|
80
|
+
const getMinuteOptions = (locale) => {
|
|
81
|
+
const formatter = new Intl.NumberFormat(locale, {
|
|
82
|
+
minimumIntegerDigits: 2,
|
|
83
|
+
useGrouping: false
|
|
84
|
+
});
|
|
85
|
+
return MINUTES.map((minute) => ({
|
|
86
|
+
value: minute,
|
|
87
|
+
label: formatter.format(minute)
|
|
88
|
+
}));
|
|
89
|
+
};
|
|
90
|
+
const capitalize = (locale, text) => text.charAt(0).toLocaleUpperCase(locale) + text.slice(1);
|
|
91
|
+
/** Accessible names of the hour and minute fields, from the locale itself. */
|
|
92
|
+
const getTimeFieldNames = (locale) => {
|
|
93
|
+
const names = new Intl.DisplayNames(locale, { type: "dateTimeField" });
|
|
94
|
+
return {
|
|
95
|
+
hour: capitalize(locale, names.of("hour") ?? "hour"),
|
|
96
|
+
minute: capitalize(locale, names.of("minute") ?? "minute")
|
|
97
|
+
};
|
|
98
|
+
};
|
|
30
99
|
//#endregion
|
|
31
|
-
export { localDateFromTimestamp, millisecondsUntilNextLocalDate, resolveCalendarViewMonth, shiftCalendarDate };
|
|
100
|
+
export { DATE_PICKER_MODE, DEFAULT_PICKER_TIME, formatDateTimeValue, getHourOptions, getMinuteOptions, getTimeFieldNames, joinPickerTime, localDateFromTimestamp, millisecondsUntilNextLocalDate, parseDateTimeValue, resolveCalendarViewMonth, shiftCalendarDate, splitPickerTime };
|
|
@@ -5,7 +5,7 @@ import { VariantProps } from "class-variance-authority";
|
|
|
5
5
|
//#region src/components/input-group.d.ts
|
|
6
6
|
declare const InputGroup: ({ className, ...props }: React$1.ComponentProps<"div">) => React$1.JSX.Element;
|
|
7
7
|
declare const inputGroupAddonVariants: (props?: ({
|
|
8
|
-
align?: "inline-
|
|
8
|
+
align?: "inline-end" | "inline-start" | "block-end" | "block-start" | null | undefined;
|
|
9
9
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
10
10
|
declare const InputGroupAddon: ({ className, align, ...props }: React$1.ComponentProps<"div"> & VariantProps<typeof inputGroupAddonVariants>) => React$1.JSX.Element;
|
|
11
11
|
declare const InputGroupText: ({ className, ...props }: React$1.ComponentProps<"span">) => React$1.JSX.Element;
|
|
@@ -3,6 +3,7 @@ import { ChevronDownIcon, ChevronUpIcon, ChevronsUpDownIcon } from "../icons.js"
|
|
|
3
3
|
import { cn } from "../lib/utils.js";
|
|
4
4
|
import { OVERLAY_LAYER_CLASS_NAMES } from "../lib/overlay-layer.js";
|
|
5
5
|
import { CONTROL_SIZE } from "../lib/control-size.js";
|
|
6
|
+
import { FIELD_TRIGGER_CLASS_NAME } from "../lib/field-trigger.js";
|
|
6
7
|
import { useLatest } from "../hooks/use-latest.js";
|
|
7
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
9
|
import * as React$1 from "react";
|
|
@@ -31,7 +32,7 @@ const Select = ({ children, isItemEqualToValue, itemToStringLabel, itemToStringV
|
|
|
31
32
|
});
|
|
32
33
|
};
|
|
33
34
|
const SelectTrigger = ({ className, size = CONTROL_SIZE.default, children, ...props }) => /* @__PURE__ */ jsxs(Select$1.Trigger, {
|
|
34
|
-
className: cn(
|
|
35
|
+
className: cn(FIELD_TRIGGER_CLASS_NAME, SELECT_TRIGGER_SIZE_CLASS_NAMES[size], className),
|
|
35
36
|
"data-slot": "select-trigger",
|
|
36
37
|
...props,
|
|
37
38
|
children: [children, /* @__PURE__ */ jsx(Select$1.Icon, {
|
|
@@ -33,7 +33,7 @@ declare function useSidebarLayout(): SidebarLayout;
|
|
|
33
33
|
* left for the content column. Mobile reports 0: there the sidebar is an
|
|
34
34
|
* overlay sheet and occupies no layout width.
|
|
35
35
|
*/
|
|
36
|
-
declare function useSidebarInlineSize(): 0 |
|
|
36
|
+
declare function useSidebarInlineSize(): 0 | 256 | 48;
|
|
37
37
|
declare const SidebarProvider: ({ defaultOpen, forceCollapsed, open: openProp, onOpenChange: setOpenProp, className, style, children, ...props }: React.ComponentProps<"div"> & {
|
|
38
38
|
defaultOpen?: boolean;
|
|
39
39
|
forceCollapsed?: boolean;
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,8 @@ import { assertConsecutiveCalendarDates, getResourceCalendarPlacement, layoutRes
|
|
|
31
31
|
import { ResourceCalendar } from "./calendar/resource-calendar.js";
|
|
32
32
|
import { getFirstWeekday, getLocaleWeekInfo, getWeekendDays } from "./lib/week.js";
|
|
33
33
|
import { Popover, PopoverClose, PopoverContent as PopoverPopup, PopoverCreateHandle, PopoverDescription, PopoverPanel, PopoverTitle, PopoverTrigger } from "./components/popover.js";
|
|
34
|
+
import { useLatest } from "./hooks/use-latest.js";
|
|
35
|
+
import { Select, SelectContent as SelectPopup, SelectGroup, SelectGroupLabel, SelectItem, SelectSeparator, SelectTrigger, SelectValue } from "./components/select.js";
|
|
34
36
|
import { DatePickerPopover } from "./components/date-picker-popover.js";
|
|
35
37
|
import { Field, FieldControl, FieldDescription, FieldError, FieldItem, FieldLabel, FieldValidity } from "./components/field.js";
|
|
36
38
|
import { DestructiveActionConfirmation, useDestructiveActionConfirmation } from "./components/destructive-action-confirmation.js";
|
|
@@ -38,7 +40,6 @@ import { DestructiveConfirmDialog } from "./components/destructive-confirm-dialo
|
|
|
38
40
|
import { FileInput } from "./components/file-input.js";
|
|
39
41
|
import { Form } from "./components/form.js";
|
|
40
42
|
import { Frame, FrameDescription, FrameFooter, FrameHeader, FramePanel, FrameTitle } from "./components/frame.js";
|
|
41
|
-
import { useLatest } from "./hooks/use-latest.js";
|
|
42
43
|
import { HexColorPicker } from "./components/hex-color-picker.js";
|
|
43
44
|
import { Textarea } from "./components/textarea.js";
|
|
44
45
|
import { InputGroup, InputGroupAddon, InputGroupInput, InputGroupText, InputGroupTextarea } from "./components/input-group.js";
|
|
@@ -55,7 +56,6 @@ import { MenuPreviewLayout, PreviewPane } from "./components/preview-pane.js";
|
|
|
55
56
|
import { ScrollToTop } from "./components/scroll-to-top.js";
|
|
56
57
|
import { SecretInput } from "./components/secret-input.js";
|
|
57
58
|
import { SegmentedIconToggle } from "./components/segmented-icon-toggle.js";
|
|
58
|
-
import { Select, SelectContent as SelectPopup, SelectGroup, SelectGroupLabel, SelectItem, SelectSeparator, SelectTrigger, SelectValue } from "./components/select.js";
|
|
59
59
|
import { Separator, TextSeparator } from "./components/separator.js";
|
|
60
60
|
import { Skeleton } from "./components/skeleton.js";
|
|
61
61
|
import { SIDEBAR_WIDTH_ICON_PX, SIDEBAR_WIDTH_PX, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, sidebarIdentityTriggerClassName, useSidebar, useSidebarInlineSize, useSidebarLayout } from "./components/sidebar.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/lib/field-trigger.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The bordered box of a button that opens a picker inside a form (a select
|
|
4
|
+
* trigger, a date field). One string so every popup-backed field shares the
|
|
5
|
+
* input metric, focus ring, invalid and disabled treatment, and the 44px
|
|
6
|
+
* coarse-pointer hit area.
|
|
7
|
+
*/
|
|
8
|
+
export declare const FIELD_TRIGGER_CLASS_NAME = "border-input bg-background text-foreground ring-ring/24 focus-visible:border-ring aria-invalid:border-destructive/36 focus-visible:aria-invalid:border-destructive/64 focus-visible:aria-invalid:ring-destructive/16 dark:bg-input/32 dark:aria-invalid:ring-destructive/24 relative inline-flex min-h-9 w-full min-w-36 items-center justify-center gap-2 rounded-lg border px-[calc(--spacing(3)-1px)] text-start text-base shadow-xs/5 outline-none select-none not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] not-data-disabled:not-focus-visible:not-aria-invalid:not-data-pressed:before:shadow-[0_1px_--theme(--color-black/4%)] focus-visible:ring-[3px] data-disabled:pointer-events-none data-disabled:opacity-64 sm:min-h-8 sm:text-sm dark:not-data-disabled:not-focus-visible:not-aria-invalid:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/6%)] pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [[data-disabled],:focus-visible,[aria-invalid],[data-pressed]]:shadow-none";
|
|
9
|
+
//#endregion
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/lib/field-trigger.ts
|
|
2
|
+
/**
|
|
3
|
+
* The bordered box of a button that opens a picker inside a form (a select
|
|
4
|
+
* trigger, a date field). One string so every popup-backed field shares the
|
|
5
|
+
* input metric, focus ring, invalid and disabled treatment, and the 44px
|
|
6
|
+
* coarse-pointer hit area.
|
|
7
|
+
*/
|
|
8
|
+
const FIELD_TRIGGER_CLASS_NAME = "border-input bg-background text-foreground ring-ring/24 focus-visible:border-ring aria-invalid:border-destructive/36 focus-visible:aria-invalid:border-destructive/64 focus-visible:aria-invalid:ring-destructive/16 dark:bg-input/32 dark:aria-invalid:ring-destructive/24 relative inline-flex min-h-9 w-full min-w-36 items-center justify-center gap-2 rounded-lg border px-[calc(--spacing(3)-1px)] text-start text-base shadow-xs/5 outline-none select-none not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] not-data-disabled:not-focus-visible:not-aria-invalid:not-data-pressed:before:shadow-[0_1px_--theme(--color-black/4%)] focus-visible:ring-[3px] data-disabled:pointer-events-none data-disabled:opacity-64 sm:min-h-8 sm:text-sm dark:not-data-disabled:not-focus-visible:not-aria-invalid:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/6%)] pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [[data-disabled],:focus-visible,[aria-invalid],[data-pressed]]:shadow-none";
|
|
9
|
+
//#endregion
|
|
10
|
+
export { FIELD_TRIGGER_CLASS_NAME };
|
package/package.json
CHANGED