@sixthshift/design-system 0.2.1 → 0.3.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/README.md +135 -28
- package/dist/components/Badge/Badge.d.ts +5 -2
- package/dist/components/Badge/Badge.d.ts.map +1 -1
- package/dist/components/Badge/Badge.js +5 -3
- package/dist/components/Badge/Badge.js.map +1 -1
- package/dist/components/BarChart/BarChart.d.ts +8 -2
- package/dist/components/BarChart/BarChart.d.ts.map +1 -1
- package/dist/components/BarChart/BarChart.js +5 -3
- package/dist/components/BarChart/BarChart.js.map +1 -1
- package/dist/components/Calendar/Calendar.d.ts +17 -1
- package/dist/components/Calendar/Calendar.d.ts.map +1 -1
- package/dist/components/Calendar/Calendar.js +80 -225
- package/dist/components/Calendar/Calendar.js.map +1 -1
- package/dist/components/Calendar/CalendarView.d.ts +15 -0
- package/dist/components/Calendar/CalendarView.d.ts.map +1 -0
- package/dist/components/Calendar/CalendarView.js +255 -0
- package/dist/components/Calendar/CalendarView.js.map +1 -0
- package/dist/components/Calendar/calendar.types.d.ts +78 -27
- package/dist/components/Calendar/calendar.types.d.ts.map +1 -1
- package/dist/components/Calendar/calendar.types.js +10 -3
- package/dist/components/Calendar/calendar.types.js.map +1 -1
- package/dist/components/Calendar/index.d.ts +16 -2
- package/dist/components/Calendar/index.d.ts.map +1 -1
- package/dist/components/Calendar/index.js +15 -1
- package/dist/components/Calendar/index.js.map +1 -1
- package/dist/components/ColorDot/ColorDot.d.ts +9 -3
- package/dist/components/ColorDot/ColorDot.d.ts.map +1 -1
- package/dist/components/ColorDot/ColorDot.js +5 -3
- package/dist/components/ColorDot/ColorDot.js.map +1 -1
- package/dist/components/DatePicker/DatePicker.d.ts +2 -2
- package/dist/components/DatePicker/DatePicker.d.ts.map +1 -1
- package/dist/components/DatePicker/DatePicker.js +65 -10
- package/dist/components/DatePicker/DatePicker.js.map +1 -1
- package/dist/components/DatePicker/datepicker.types.d.ts +34 -36
- package/dist/components/DatePicker/datepicker.types.d.ts.map +1 -1
- package/dist/components/DatePicker/datepicker.types.js +5 -3
- package/dist/components/DatePicker/datepicker.types.js.map +1 -1
- package/dist/components/DatePicker/index.d.ts +10 -1
- package/dist/components/DatePicker/index.d.ts.map +1 -1
- package/dist/components/DatePicker/index.js +8 -0
- package/dist/components/DatePicker/index.js.map +1 -1
- package/dist/components/DateRangePicker/DateRangePicker.d.ts +3 -2
- package/dist/components/DateRangePicker/DateRangePicker.d.ts.map +1 -1
- package/dist/components/DateRangePicker/DateRangePicker.js +17 -21
- package/dist/components/DateRangePicker/DateRangePicker.js.map +1 -1
- package/dist/components/DateRangePicker/daterangepicker.types.d.ts +20 -20
- package/dist/components/DateRangePicker/daterangepicker.types.d.ts.map +1 -1
- package/dist/components/DateRangePicker/daterangepicker.types.js +6 -0
- package/dist/components/DateRangePicker/daterangepicker.types.js.map +1 -1
- package/dist/components/DateRangePicker/index.d.ts +8 -1
- package/dist/components/DateRangePicker/index.d.ts.map +1 -1
- package/dist/components/DateRangePicker/index.js +6 -0
- package/dist/components/DateRangePicker/index.js.map +1 -1
- package/dist/components/DateTimePicker/DateTimePicker.d.ts +3 -3
- package/dist/components/DateTimePicker/DateTimePicker.d.ts.map +1 -1
- package/dist/components/DateTimePicker/DateTimePicker.js +30 -12
- package/dist/components/DateTimePicker/DateTimePicker.js.map +1 -1
- package/dist/components/DateTimePicker/datetimepicker.types.d.ts +20 -17
- package/dist/components/DateTimePicker/datetimepicker.types.d.ts.map +1 -1
- package/dist/components/DateTimePicker/datetimepicker.types.js +10 -0
- package/dist/components/DateTimePicker/datetimepicker.types.js.map +1 -1
- package/dist/components/DateTimePicker/index.d.ts +8 -1
- package/dist/components/DateTimePicker/index.d.ts.map +1 -1
- package/dist/components/DateTimePicker/index.js +6 -0
- package/dist/components/DateTimePicker/index.js.map +1 -1
- package/dist/components/DateTimeRangePicker/DateTimeRangePicker.d.ts +3 -3
- package/dist/components/DateTimeRangePicker/DateTimeRangePicker.d.ts.map +1 -1
- package/dist/components/DateTimeRangePicker/DateTimeRangePicker.js +41 -20
- package/dist/components/DateTimeRangePicker/DateTimeRangePicker.js.map +1 -1
- package/dist/components/DateTimeRangePicker/datetimerangepicker.types.d.ts +22 -18
- package/dist/components/DateTimeRangePicker/datetimerangepicker.types.d.ts.map +1 -1
- package/dist/components/DateTimeRangePicker/datetimerangepicker.types.js +8 -0
- package/dist/components/DateTimeRangePicker/datetimerangepicker.types.js.map +1 -1
- package/dist/components/DateTimeRangePicker/index.d.ts +8 -0
- package/dist/components/DateTimeRangePicker/index.d.ts.map +1 -1
- package/dist/components/DateTimeRangePicker/index.js +7 -0
- package/dist/components/DateTimeRangePicker/index.js.map +1 -1
- package/dist/components/EmptyState/EmptyState.d.ts +16 -5
- package/dist/components/EmptyState/EmptyState.d.ts.map +1 -1
- package/dist/components/EmptyState/EmptyState.js +5 -3
- package/dist/components/EmptyState/EmptyState.js.map +1 -1
- package/dist/components/Field/Field.d.ts +3 -3
- package/dist/components/Field/Field.d.ts.map +1 -1
- package/dist/components/Field/Field.js +6 -4
- package/dist/components/Field/Field.js.map +1 -1
- package/dist/components/LineChart/LineChart.d.ts +18 -2
- package/dist/components/LineChart/LineChart.d.ts.map +1 -1
- package/dist/components/LineChart/LineChart.js +5 -3
- package/dist/components/LineChart/LineChart.js.map +1 -1
- package/dist/components/Markdown/Markdown.d.ts +2 -4
- package/dist/components/Markdown/Markdown.d.ts.map +1 -1
- package/dist/components/Markdown/Markdown.js +3 -2
- package/dist/components/Markdown/Markdown.js.map +1 -1
- package/dist/components/Message/Message.d.ts.map +1 -1
- package/dist/components/Message/Message.js +16 -1
- package/dist/components/Message/Message.js.map +1 -1
- package/dist/components/MetricList/MetricList.d.ts +3 -3
- package/dist/components/MetricList/MetricList.d.ts.map +1 -1
- package/dist/components/MetricList/MetricList.js +5 -3
- package/dist/components/MetricList/MetricList.js.map +1 -1
- package/dist/components/Modal/Modal.d.ts +24 -5
- package/dist/components/Modal/Modal.d.ts.map +1 -1
- package/dist/components/Modal/Modal.js +16 -6
- package/dist/components/Modal/Modal.js.map +1 -1
- package/dist/components/Modal/components/ModalContext.d.ts +14 -0
- package/dist/components/Modal/components/ModalContext.d.ts.map +1 -1
- package/dist/components/Modal/components/ModalContext.js.map +1 -1
- package/dist/components/Modal/components/ModalHeader.d.ts.map +1 -1
- package/dist/components/Modal/components/ModalHeader.js +13 -2
- package/dist/components/Modal/components/ModalHeader.js.map +1 -1
- package/dist/components/NavBottom/NavBottom.d.ts +2 -1
- package/dist/components/NavBottom/NavBottom.d.ts.map +1 -1
- package/dist/components/NavBottom/NavBottom.js +6 -4
- package/dist/components/NavBottom/NavBottom.js.map +1 -1
- package/dist/components/NavSide/NavItem.js +1 -1
- package/dist/components/NavSide/NavItem.js.map +1 -1
- package/dist/components/NavSide/NavSide.d.ts +2 -1
- package/dist/components/NavSide/NavSide.d.ts.map +1 -1
- package/dist/components/NavSide/NavSide.js +5 -3
- package/dist/components/NavSide/NavSide.js.map +1 -1
- package/dist/components/Popover/Popover.d.ts.map +1 -1
- package/dist/components/Popover/Popover.js +7 -0
- package/dist/components/Popover/Popover.js.map +1 -1
- package/dist/components/Popover/components/PopoverBody.d.ts.map +1 -1
- package/dist/components/Popover/components/PopoverBody.js +5 -2
- package/dist/components/Popover/components/PopoverBody.js.map +1 -1
- package/dist/components/Popover/components/PopoverContext.d.ts +1 -0
- package/dist/components/Popover/components/PopoverContext.d.ts.map +1 -1
- package/dist/components/Popover/components/PopoverContext.js.map +1 -1
- package/dist/components/Popover/components/PopoverTrigger.d.ts.map +1 -1
- package/dist/components/Popover/components/PopoverTrigger.js +2 -2
- package/dist/components/Popover/components/PopoverTrigger.js.map +1 -1
- package/dist/components/ProgressBar/ProgressBar.d.ts +2 -1
- package/dist/components/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/components/ProgressBar/ProgressBar.js +5 -3
- package/dist/components/ProgressBar/ProgressBar.js.map +1 -1
- package/dist/components/Select/Select.d.ts +11 -2
- package/dist/components/Select/Select.d.ts.map +1 -1
- package/dist/components/Select/Select.js +64 -18
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/Select/SelectDropdown.d.ts +14 -2
- package/dist/components/Select/SelectDropdown.d.ts.map +1 -1
- package/dist/components/Select/SelectDropdown.js +19 -4
- package/dist/components/Select/SelectDropdown.js.map +1 -1
- package/dist/components/Select/SelectTrigger.d.ts +10 -3
- package/dist/components/Select/SelectTrigger.d.ts.map +1 -1
- package/dist/components/Select/SelectTrigger.js +12 -3
- package/dist/components/Select/SelectTrigger.js.map +1 -1
- package/dist/components/Select/useSelectKeyboard.d.ts +15 -1
- package/dist/components/Select/useSelectKeyboard.d.ts.map +1 -1
- package/dist/components/Select/useSelectKeyboard.js +73 -65
- package/dist/components/Select/useSelectKeyboard.js.map +1 -1
- package/dist/components/Sheet/Sheet.d.ts +19 -5
- package/dist/components/Sheet/Sheet.d.ts.map +1 -1
- package/dist/components/Sheet/Sheet.js +18 -6
- package/dist/components/Sheet/Sheet.js.map +1 -1
- package/dist/components/Sheet/components/SheetContext.d.ts +10 -0
- package/dist/components/Sheet/components/SheetContext.d.ts.map +1 -1
- package/dist/components/Sheet/components/SheetContext.js.map +1 -1
- package/dist/components/Sheet/components/SheetHeader.d.ts.map +1 -1
- package/dist/components/Sheet/components/SheetHeader.js +11 -2
- package/dist/components/Sheet/components/SheetHeader.js.map +1 -1
- package/dist/components/Skeleton/Skeleton.d.ts +2 -1
- package/dist/components/Skeleton/Skeleton.d.ts.map +1 -1
- package/dist/components/Skeleton/Skeleton.js +5 -3
- package/dist/components/Skeleton/Skeleton.js.map +1 -1
- package/dist/components/Sparkline/Sparkline.d.ts +10 -2
- package/dist/components/Sparkline/Sparkline.d.ts.map +1 -1
- package/dist/components/Sparkline/Sparkline.js +5 -3
- package/dist/components/Sparkline/Sparkline.js.map +1 -1
- package/dist/components/StatsCard/StatsCard.d.ts +4 -4
- package/dist/components/StatsCard/StatsCard.d.ts.map +1 -1
- package/dist/components/StatsCard/StatsCard.js +5 -3
- package/dist/components/StatsCard/StatsCard.js.map +1 -1
- package/dist/components/Tabs/Tabs.d.ts +2 -2
- package/dist/components/Tabs/components/TabsList.d.ts +1 -1
- package/dist/components/Tabs/components/TabsList.d.ts.map +1 -1
- package/dist/components/Tabs/components/TabsList.js +5 -4
- package/dist/components/Tabs/components/TabsList.js.map +1 -1
- package/dist/components/Tabs/components/TabsPanels.d.ts +1 -1
- package/dist/components/Tabs/components/TabsPanels.d.ts.map +1 -1
- package/dist/components/Tabs/components/TabsPanels.js +5 -3
- package/dist/components/Tabs/components/TabsPanels.js.map +1 -1
- package/dist/components/TagChip/TagChip.d.ts +2 -1
- package/dist/components/TagChip/TagChip.d.ts.map +1 -1
- package/dist/components/TagChip/TagChip.js +5 -3
- package/dist/components/TagChip/TagChip.js.map +1 -1
- package/dist/components/TagInput/TagInput.d.ts +1 -1
- package/dist/components/TagInput/TagInput.d.ts.map +1 -1
- package/dist/components/TagInput/TagInput.js +5 -4
- package/dist/components/TagInput/TagInput.js.map +1 -1
- package/dist/components/TimePicker/TimePicker.d.ts +2 -2
- package/dist/components/TimePicker/TimePicker.d.ts.map +1 -1
- package/dist/components/TimePicker/TimePicker.js +27 -11
- package/dist/components/TimePicker/TimePicker.js.map +1 -1
- package/dist/components/TimePicker/timepicker.types.d.ts +14 -13
- package/dist/components/TimePicker/timepicker.types.d.ts.map +1 -1
- package/dist/components/TimePicker/timepicker.types.js +4 -3
- package/dist/components/TimePicker/timepicker.types.js.map +1 -1
- package/dist/date-time/index.d.ts +2 -0
- package/dist/date-time/index.d.ts.map +1 -1
- package/dist/date-time/index.js +9 -0
- package/dist/date-time/index.js.map +1 -1
- package/dist/date-time/iso.d.ts +261 -0
- package/dist/date-time/iso.d.ts.map +1 -0
- package/dist/date-time/iso.js +358 -0
- package/dist/date-time/iso.js.map +1 -0
- package/dist/date-time/now.d.ts +7 -6
- package/dist/date-time/now.d.ts.map +1 -1
- package/dist/date-time/now.js +3 -3
- package/dist/date-time/now.js.map +1 -1
- package/dist/theme/index.d.ts +4 -3
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +3 -2
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/schema.d.ts +70 -32
- package/dist/theme/schema.d.ts.map +1 -1
- package/dist/theme/schema.js +36 -18
- package/dist/theme/schema.js.map +1 -1
- package/package.json +9 -15
- package/src/components/Badge/Badge.tsx +5 -4
- package/src/components/BarChart/BarChart.tsx +25 -30
- package/src/components/Calendar/Calendar.tsx +91 -401
- package/src/components/Calendar/CalendarView.tsx +445 -0
- package/src/components/Calendar/calendar.types.ts +94 -29
- package/src/components/Calendar/index.ts +16 -17
- package/src/components/ColorDot/ColorDot.tsx +6 -4
- package/src/components/DatePicker/DatePicker.tsx +92 -16
- package/src/components/DatePicker/datepicker.types.ts +38 -39
- package/src/components/DatePicker/index.ts +11 -10
- package/src/components/DateRangePicker/DateRangePicker.tsx +17 -21
- package/src/components/DateRangePicker/daterangepicker.types.ts +22 -22
- package/src/components/DateRangePicker/index.ts +9 -1
- package/src/components/DateTimePicker/DateTimePicker.tsx +46 -18
- package/src/components/DateTimePicker/datetimepicker.types.ts +22 -18
- package/src/components/DateTimePicker/index.ts +9 -1
- package/src/components/DateTimeRangePicker/DateTimeRangePicker.tsx +62 -28
- package/src/components/DateTimeRangePicker/datetimerangepicker.types.ts +24 -19
- package/src/components/DateTimeRangePicker/index.ts +9 -0
- package/src/components/EmptyState/EmptyState.tsx +8 -7
- package/src/components/Field/Field.tsx +7 -6
- package/src/components/LineChart/LineChart.tsx +147 -141
- package/src/components/Markdown/Markdown.tsx +4 -3
- package/src/components/Message/Message.tsx +17 -1
- package/src/components/MetricList/MetricList.tsx +10 -5
- package/src/components/Modal/Modal.tsx +119 -87
- package/src/components/Modal/components/ModalContext.tsx +14 -0
- package/src/components/Modal/components/ModalHeader.tsx +14 -2
- package/src/components/NavBottom/NavBottom.tsx +6 -4
- package/src/components/NavSide/NavItem.tsx +1 -1
- package/src/components/NavSide/NavSide.tsx +5 -3
- package/src/components/Popover/Popover.tsx +7 -0
- package/src/components/Popover/components/PopoverBody.tsx +6 -1
- package/src/components/Popover/components/PopoverContext.tsx +1 -0
- package/src/components/Popover/components/PopoverTrigger.tsx +2 -1
- package/src/components/ProgressBar/ProgressBar.tsx +32 -28
- package/src/components/Select/Select.tsx +76 -19
- package/src/components/Select/SelectDropdown.tsx +32 -3
- package/src/components/Select/SelectTrigger.tsx +26 -3
- package/src/components/Select/useSelectKeyboard.ts +20 -10
- package/src/components/Sheet/Sheet.tsx +108 -83
- package/src/components/Sheet/components/SheetContext.tsx +10 -0
- package/src/components/Sheet/components/SheetHeader.tsx +17 -2
- package/src/components/Skeleton/Skeleton.tsx +5 -3
- package/src/components/Sparkline/Sparkline.tsx +29 -36
- package/src/components/StatsCard/StatsCard.tsx +7 -6
- package/src/components/Tabs/components/TabsList.tsx +5 -3
- package/src/components/Tabs/components/TabsPanels.tsx +5 -3
- package/src/components/TagChip/TagChip.tsx +10 -3
- package/src/components/TagInput/TagInput.tsx +5 -3
- package/src/components/TimePicker/TimePicker.tsx +31 -13
- package/src/components/TimePicker/timepicker.types.ts +14 -13
- package/src/date-time/index.ts +61 -0
- package/src/date-time/iso.ts +511 -0
- package/src/date-time/now.ts +10 -9
- package/src/theme/index.ts +11 -3
- package/src/theme/schema.ts +82 -54
- package/src/theme/tokens.css +807 -0
- package/dist/styles.css +0 -2
- package/dist/theme.generated.css +0 -438
- package/src/styles/base.css +0 -41
- package/src/theme/palette.json +0 -100
- package/src/theme/palette.schema.json +0 -40
- package/src/theme/theme.json +0 -349
- package/src/theme/theme.schema.json +0 -876
- package/src/theme/typography.json +0 -7
- package/src/theme/typography.schema.json +0 -36
- package/tailwind.config.ts +0 -261
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn } from "@sixthshift/design-system/utils";
|
|
2
2
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
-
import
|
|
3
|
+
import * as React from "react";
|
|
4
4
|
|
|
5
5
|
const INTENT_CLASSES: Record<string, string> = {
|
|
6
6
|
neutral: "bg-fg-subtle",
|
|
@@ -29,22 +29,24 @@ const colorDotVariants = cva("inline-block shrink-0 rounded-full", {
|
|
|
29
29
|
},
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
-
export type ColorDotProps = HTMLAttributes<HTMLSpanElement> &
|
|
32
|
+
export type ColorDotProps = React.HTMLAttributes<HTMLSpanElement> &
|
|
33
33
|
VariantProps<typeof colorDotVariants> & {
|
|
34
34
|
/** Intent name (neutral, brand, success, warning, danger) or arbitrary CSS color */
|
|
35
35
|
color: string;
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
-
export const ColorDot = ({ color, size, pulse, className, style, ...props }
|
|
38
|
+
export const ColorDot = React.forwardRef<HTMLSpanElement, ColorDotProps>(({ color, size, pulse, className, style, ...props }, ref) => {
|
|
39
39
|
const intentClass = INTENT_CLASSES[color];
|
|
40
40
|
|
|
41
41
|
return (
|
|
42
42
|
<span
|
|
43
|
+
ref={ref}
|
|
43
44
|
className={cn(colorDotVariants({ size, pulse }), intentClass, className)}
|
|
44
45
|
style={intentClass ? style : { ...style, backgroundColor: color }}
|
|
45
46
|
{...props}
|
|
46
47
|
/>
|
|
47
48
|
);
|
|
48
|
-
};
|
|
49
|
+
});
|
|
50
|
+
ColorDot.displayName = "ColorDot";
|
|
49
51
|
|
|
50
52
|
export { colorDotVariants };
|
|
@@ -2,16 +2,40 @@ import { autoUpdate, FloatingPortal, flip, offset, shift, useClick, useDismiss,
|
|
|
2
2
|
import { useControllableState } from "@sixthshift/design-system/hooks";
|
|
3
3
|
import { cn } from "@sixthshift/design-system/utils";
|
|
4
4
|
import { Calendar as CalendarIcon, X } from "lucide-react";
|
|
5
|
-
import
|
|
6
|
-
import { useCallback, useId, useState } from "react";
|
|
7
|
-
import {
|
|
8
|
-
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
import { useCallback, useId, useMemo, useState } from "react";
|
|
7
|
+
import {
|
|
8
|
+
adaptDisabledDates,
|
|
9
|
+
fromISODate,
|
|
10
|
+
fromISODateOrUndefined,
|
|
11
|
+
fromISODateRange,
|
|
12
|
+
type ISODate,
|
|
13
|
+
type ISODateRange,
|
|
14
|
+
isPlainDate,
|
|
15
|
+
type Temporal,
|
|
16
|
+
today,
|
|
17
|
+
toISODate,
|
|
18
|
+
toISODateOrUndefined,
|
|
19
|
+
toISODateRange,
|
|
20
|
+
} from "../../date-time";
|
|
21
|
+
import { CalendarView } from "../Calendar/CalendarView";
|
|
22
|
+
import type { PresetOption } from "../Calendar/calendar.types";
|
|
9
23
|
import { defaultFormatDisplay, getDisplayValue, temporalToISO } from "./datepicker.hooks";
|
|
10
24
|
import type { DatePickerProps, DateRangeValue } from "./datepicker.types";
|
|
11
25
|
|
|
12
26
|
export type { DatePickerProps };
|
|
13
27
|
|
|
14
|
-
|
|
28
|
+
/** The Temporal-shaped value the picker holds internally, across all modes. */
|
|
29
|
+
type InternalValue = Temporal.PlainDate | DateRangeValue | Temporal.PlainDate[] | undefined;
|
|
30
|
+
|
|
31
|
+
/** Widen a public, mode-appropriate ISO value to {@link InternalValue}. */
|
|
32
|
+
function toTemporal(mode: DatePickerProps["mode"], value: ISODate | ISODateRange | ISODate[] | undefined): InternalValue {
|
|
33
|
+
if (mode === "multiple") return ((value as ISODate[] | undefined) ?? []).map(fromISODate);
|
|
34
|
+
if (mode === "range") return fromISODateRange(value as ISODateRange | undefined);
|
|
35
|
+
return fromISODateOrUndefined(value as ISODate | undefined);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const DatePicker = React.forwardRef<HTMLDivElement, DatePickerProps>((props, ref) => {
|
|
15
39
|
const {
|
|
16
40
|
mode = "single",
|
|
17
41
|
weekStartsOn = 0,
|
|
@@ -37,11 +61,62 @@ export const DatePicker = (props: DatePickerProps) => {
|
|
|
37
61
|
// Open state
|
|
38
62
|
const [open, setOpen] = useState(false);
|
|
39
63
|
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
// The ISO boundary.
|
|
66
|
+
//
|
|
67
|
+
// Props arrive as canonical ISO strings and callbacks emit them; everything
|
|
68
|
+
// below this block — state, the calendar grid, display formatting — works in
|
|
69
|
+
// Temporal, exactly as before.
|
|
70
|
+
//
|
|
71
|
+
// Each conversion is memoised on the string it derives from. Temporal values
|
|
72
|
+
// have no value identity, so converting unmemoised would hand CalendarView a
|
|
73
|
+
// fresh object every render and invalidate its grid memos. Strings compare by
|
|
74
|
+
// value, which makes this strictly more stable than passing Temporal in.
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
const temporalMinDate = useMemo(() => fromISODateOrUndefined(minDate), [minDate]);
|
|
78
|
+
const temporalMaxDate = useMemo(() => fromISODateOrUndefined(maxDate), [maxDate]);
|
|
79
|
+
const temporalDisabled = useMemo(() => adaptDisabledDates(disabled), [disabled]);
|
|
80
|
+
|
|
81
|
+
const temporalPresets = useMemo(() => {
|
|
82
|
+
if (!presets) return undefined;
|
|
83
|
+
if (mode === "single") {
|
|
84
|
+
return (presets as PresetOption<ISODate>[]).map((preset) => ({ label: preset.label, value: fromISODate(preset.value) }));
|
|
85
|
+
}
|
|
86
|
+
if (mode === "multiple") {
|
|
87
|
+
return (presets as PresetOption<ISODate[]>[]).map((preset) => ({ label: preset.label, value: preset.value.map(fromISODate) }));
|
|
88
|
+
}
|
|
89
|
+
return (presets as PresetOption<ISODateRange>[]).map((preset) => ({
|
|
90
|
+
label: preset.label,
|
|
91
|
+
value: fromISODateRange(preset.value) ?? { from: undefined, to: undefined },
|
|
92
|
+
}));
|
|
93
|
+
}, [mode, presets]);
|
|
94
|
+
|
|
95
|
+
const temporalValue = useMemo(() => toTemporal(mode, value), [mode, value]);
|
|
96
|
+
// Read once, on mount, by useControllableState's useState initialiser.
|
|
97
|
+
const temporalDefaultValue = useMemo(() => toTemporal(mode, defaultValue), [mode, defaultValue]);
|
|
98
|
+
|
|
99
|
+
const handleChange = useCallback(
|
|
100
|
+
(next: InternalValue) => {
|
|
101
|
+
if (!onChange) return;
|
|
102
|
+
if (mode === "single") {
|
|
103
|
+
(onChange as (date: ISODate | undefined) => void)(toISODateOrUndefined(next as Temporal.PlainDate | undefined));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (mode === "multiple") {
|
|
107
|
+
(onChange as (dates: ISODate[]) => void)(((next as Temporal.PlainDate[] | undefined) ?? []).map(toISODate));
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
(onChange as (range: ISODateRange | undefined) => void)(toISODateRange(next as DateRangeValue | undefined));
|
|
111
|
+
},
|
|
112
|
+
[mode, onChange]
|
|
113
|
+
);
|
|
114
|
+
|
|
40
115
|
// Controllable value state (external/committed value)
|
|
41
|
-
const [committedValue, setCommittedValue] = useControllableState({
|
|
42
|
-
value:
|
|
43
|
-
defaultValue:
|
|
44
|
-
onChange:
|
|
116
|
+
const [committedValue, setCommittedValue] = useControllableState<InternalValue>({
|
|
117
|
+
value: temporalValue,
|
|
118
|
+
defaultValue: temporalDefaultValue,
|
|
119
|
+
onChange: handleChange,
|
|
45
120
|
});
|
|
46
121
|
|
|
47
122
|
// Draft value (internal state while popup is open)
|
|
@@ -147,7 +222,7 @@ export const DatePicker = (props: DatePickerProps) => {
|
|
|
147
222
|
return (
|
|
148
223
|
<>
|
|
149
224
|
{/* Trigger Input */}
|
|
150
|
-
<div className={cn("relative flex items-center", mode === "range" && "min-w-91.25", mode !== "range" && "min-w-65")}>
|
|
225
|
+
<div ref={ref} className={cn("relative flex items-center", mode === "range" && "min-w-91.25", mode !== "range" && "min-w-65")}>
|
|
151
226
|
<CalendarIcon className="pointer-events-none absolute left-3 h-4 w-4 text-fg-subtle" />
|
|
152
227
|
<input
|
|
153
228
|
ref={refs.setReference as React.Ref<HTMLInputElement>}
|
|
@@ -211,17 +286,17 @@ export const DatePicker = (props: DatePickerProps) => {
|
|
|
211
286
|
{...getFloatingProps()}
|
|
212
287
|
>
|
|
213
288
|
{/* @ts-expect-error - TypeScript can't properly narrow discriminated union props across component boundaries */}
|
|
214
|
-
<
|
|
289
|
+
<CalendarView
|
|
215
290
|
mode={mode}
|
|
216
291
|
value={draftValue}
|
|
217
292
|
onSelect={setDraftValue}
|
|
218
293
|
month={month}
|
|
219
294
|
onMonthChange={setMonth}
|
|
220
|
-
minDate={
|
|
221
|
-
maxDate={
|
|
222
|
-
disabled={
|
|
295
|
+
minDate={temporalMinDate}
|
|
296
|
+
maxDate={temporalMaxDate}
|
|
297
|
+
disabled={temporalDisabled}
|
|
223
298
|
weekStartsOn={weekStartsOn}
|
|
224
|
-
presets={
|
|
299
|
+
presets={temporalPresets}
|
|
225
300
|
showFooter
|
|
226
301
|
showToday={mode !== "range"}
|
|
227
302
|
onApply={handleApply}
|
|
@@ -233,4 +308,5 @@ export const DatePicker = (props: DatePickerProps) => {
|
|
|
233
308
|
)}
|
|
234
309
|
</>
|
|
235
310
|
);
|
|
236
|
-
};
|
|
311
|
+
});
|
|
312
|
+
DatePicker.displayName = "DatePicker";
|
|
@@ -1,44 +1,43 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DatePicker types
|
|
2
|
+
* DatePicker types.
|
|
3
3
|
*
|
|
4
|
-
* All date values
|
|
5
|
-
*
|
|
4
|
+
* All date values at the public boundary are canonical ISO 8601 strings
|
|
5
|
+
* (`"2026-08-26"`). Temporal is an implementation detail of the calendar grid;
|
|
6
|
+
* the `*Internal` types below describe that layer and are not exported from
|
|
7
|
+
* `./index.ts`.
|
|
6
8
|
*/
|
|
7
9
|
|
|
8
|
-
import type { Temporal, WeekStartsOn } from "../../date-time";
|
|
10
|
+
import type { DisabledDates, ISODate, ISODateRange, Temporal, TemporalDisabledMatcher, WeekStartsOn } from "../../date-time";
|
|
9
11
|
import type { PresetOption as CalendarPresetOption } from "../Calendar/calendar.types";
|
|
10
12
|
|
|
11
13
|
// =============================================================================
|
|
12
14
|
// Value Types
|
|
13
15
|
// =============================================================================
|
|
14
16
|
|
|
15
|
-
/**
|
|
16
|
-
* Date range value for range mode
|
|
17
|
-
*/
|
|
18
|
-
export type DateRangeValue = {
|
|
19
|
-
from: Temporal.PlainDate | undefined;
|
|
20
|
-
to: Temporal.PlainDate | undefined;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
17
|
/**
|
|
24
18
|
* Selection mode for the DatePicker
|
|
25
19
|
*/
|
|
26
20
|
export type DatePickerMode = "single" | "range" | "multiple";
|
|
27
21
|
|
|
28
22
|
// =============================================================================
|
|
29
|
-
//
|
|
23
|
+
// Internal (Temporal) types
|
|
30
24
|
// =============================================================================
|
|
31
25
|
|
|
32
26
|
/**
|
|
33
|
-
*
|
|
27
|
+
* Date range value in Temporal terms, used by `datepicker.hooks`.
|
|
28
|
+
*
|
|
29
|
+
* The public equivalent is `ISODateRange`.
|
|
30
|
+
*/
|
|
31
|
+
export type DateRangeValue = {
|
|
32
|
+
from: Temporal.PlainDate | undefined;
|
|
33
|
+
to: Temporal.PlainDate | undefined;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Temporal-shaped disabled matchers, produced from the public
|
|
38
|
+
* {@link DisabledDates} by `adaptDisabledDates`.
|
|
34
39
|
*/
|
|
35
|
-
export type DisabledDateMatcher =
|
|
36
|
-
| Temporal.PlainDate // Single date
|
|
37
|
-
| Temporal.PlainDate[] // Array of dates
|
|
38
|
-
| ((date: Temporal.PlainDate) => boolean) // Custom function
|
|
39
|
-
| { before: Temporal.PlainDate } // All dates before
|
|
40
|
-
| { after: Temporal.PlainDate } // All dates after
|
|
41
|
-
| { from: Temporal.PlainDate; to: Temporal.PlainDate }; // Date range
|
|
40
|
+
export type DisabledDateMatcher = TemporalDisabledMatcher;
|
|
42
41
|
|
|
43
42
|
// =============================================================================
|
|
44
43
|
// Props (discriminated union by mode)
|
|
@@ -48,14 +47,14 @@ type DatePickerBaseProps = {
|
|
|
48
47
|
/** First day of week (0 = Sunday, 1 = Monday) */
|
|
49
48
|
weekStartsOn?: WeekStartsOn;
|
|
50
49
|
|
|
51
|
-
/**
|
|
52
|
-
minDate?:
|
|
50
|
+
/** Earliest selectable date, inclusive */
|
|
51
|
+
minDate?: ISODate;
|
|
53
52
|
|
|
54
|
-
/**
|
|
55
|
-
maxDate?:
|
|
53
|
+
/** Latest selectable date, inclusive */
|
|
54
|
+
maxDate?: ISODate;
|
|
56
55
|
|
|
57
|
-
/**
|
|
58
|
-
disabled?:
|
|
56
|
+
/** Which dates to refuse. See `DisabledDates`. */
|
|
57
|
+
disabled?: DisabledDates | DisabledDates[];
|
|
59
58
|
|
|
60
59
|
/** Placeholder text */
|
|
61
60
|
placeholder?: string;
|
|
@@ -82,39 +81,39 @@ type DatePickerBaseProps = {
|
|
|
82
81
|
export type DatePickerSingleProps = DatePickerBaseProps & {
|
|
83
82
|
mode?: "single";
|
|
84
83
|
/** Controlled value */
|
|
85
|
-
value?:
|
|
84
|
+
value?: ISODate | undefined;
|
|
86
85
|
/** Default value for uncontrolled mode */
|
|
87
|
-
defaultValue?:
|
|
86
|
+
defaultValue?: ISODate | undefined;
|
|
88
87
|
/** Called when date changes */
|
|
89
|
-
onChange?: (date:
|
|
88
|
+
onChange?: (date: ISODate | undefined) => void;
|
|
90
89
|
/** Preset options (shown in left sidebar) */
|
|
91
|
-
presets?: CalendarPresetOption<
|
|
90
|
+
presets?: CalendarPresetOption<ISODate>[];
|
|
92
91
|
};
|
|
93
92
|
|
|
94
93
|
export type DatePickerRangeProps = DatePickerBaseProps & {
|
|
95
94
|
mode: "range";
|
|
96
95
|
/** Controlled value */
|
|
97
|
-
value?:
|
|
96
|
+
value?: ISODateRange | undefined;
|
|
98
97
|
/** Default value for uncontrolled mode */
|
|
99
|
-
defaultValue?:
|
|
98
|
+
defaultValue?: ISODateRange | undefined;
|
|
100
99
|
/** Called when range changes */
|
|
101
|
-
onChange?: (range:
|
|
100
|
+
onChange?: (range: ISODateRange | undefined) => void;
|
|
102
101
|
/** Preset options (shown in left sidebar) */
|
|
103
|
-
presets?: CalendarPresetOption<
|
|
102
|
+
presets?: CalendarPresetOption<ISODateRange>[];
|
|
104
103
|
};
|
|
105
104
|
|
|
106
105
|
export type DatePickerMultipleProps = DatePickerBaseProps & {
|
|
107
106
|
mode: "multiple";
|
|
108
107
|
/** Controlled value (array of dates) */
|
|
109
|
-
value?:
|
|
108
|
+
value?: ISODate[] | undefined;
|
|
110
109
|
/** Default value for uncontrolled mode */
|
|
111
|
-
defaultValue?:
|
|
110
|
+
defaultValue?: ISODate[] | undefined;
|
|
112
111
|
/** Called when selection changes */
|
|
113
|
-
onChange?: (dates:
|
|
112
|
+
onChange?: (dates: ISODate[]) => void;
|
|
114
113
|
/** Maximum number of dates that can be selected */
|
|
115
114
|
max?: number | undefined;
|
|
116
115
|
/** Preset options (shown in left sidebar) */
|
|
117
|
-
presets?: CalendarPresetOption<
|
|
116
|
+
presets?: CalendarPresetOption<ISODate[]>[];
|
|
118
117
|
};
|
|
119
118
|
|
|
120
119
|
/**
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Public surface of `@sixthshift/design-system/date-picker`.
|
|
3
|
+
*
|
|
4
|
+
* `DateRangeValue` and `DisabledDateMatcher` used to be exported from here as
|
|
5
|
+
* Temporal-shaped types. They are now internal; the public equivalents are
|
|
6
|
+
* `ISODateRange` and `DisabledDates`, re-exported below so a consumer typing
|
|
7
|
+
* their own range state does not need a second import.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type { DisabledDates, ISODate, ISODateRange } from "../../date-time";
|
|
2
11
|
export type { PresetOption } from "../Calendar/calendar.types";
|
|
3
12
|
export { DatePicker } from "./DatePicker";
|
|
4
|
-
export type {
|
|
5
|
-
DatePickerMode,
|
|
6
|
-
DatePickerMultipleProps,
|
|
7
|
-
DatePickerProps,
|
|
8
|
-
DatePickerRangeProps,
|
|
9
|
-
DatePickerSingleProps,
|
|
10
|
-
DateRangeValue,
|
|
11
|
-
DisabledDateMatcher,
|
|
12
|
-
} from "./datepicker.types";
|
|
13
|
+
export type { DatePickerMode, DatePickerMultipleProps, DatePickerProps, DatePickerRangeProps, DatePickerSingleProps } from "./datepicker.types";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import * as React from "react";
|
|
1
2
|
import { useMemo } from "react";
|
|
2
|
-
import {
|
|
3
|
+
import { addDaysISO, startOfMonthISO, todayISO } from "../../date-time";
|
|
3
4
|
|
|
4
5
|
import { DatePicker } from "../DatePicker";
|
|
5
6
|
import type { DatePickerRangeProps } from "../DatePicker/datepicker.types";
|
|
@@ -14,49 +15,43 @@ function getDefaultPresets(): PresetOption[] {
|
|
|
14
15
|
{
|
|
15
16
|
label: "Today",
|
|
16
17
|
value: () => {
|
|
17
|
-
const t =
|
|
18
|
+
const t = todayISO();
|
|
18
19
|
return { from: t, to: t };
|
|
19
20
|
},
|
|
20
21
|
},
|
|
21
22
|
{
|
|
22
23
|
label: "Yesterday",
|
|
23
24
|
value: () => {
|
|
24
|
-
const yesterday =
|
|
25
|
+
const yesterday = addDaysISO(todayISO(), -1);
|
|
25
26
|
return { from: yesterday, to: yesterday };
|
|
26
27
|
},
|
|
27
28
|
},
|
|
28
29
|
{
|
|
29
30
|
label: "Last 7 days",
|
|
30
31
|
value: () => {
|
|
31
|
-
const t =
|
|
32
|
-
|
|
33
|
-
return { from: weekAgo, to: t };
|
|
32
|
+
const t = todayISO();
|
|
33
|
+
return { from: addDaysISO(t, -6), to: t };
|
|
34
34
|
},
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
37
|
label: "Last 30 days",
|
|
38
38
|
value: () => {
|
|
39
|
-
const t =
|
|
40
|
-
|
|
41
|
-
return { from: monthAgo, to: t };
|
|
39
|
+
const t = todayISO();
|
|
40
|
+
return { from: addDaysISO(t, -29), to: t };
|
|
42
41
|
},
|
|
43
42
|
},
|
|
44
43
|
{
|
|
45
44
|
label: "This month",
|
|
46
45
|
value: () => {
|
|
47
|
-
const t =
|
|
48
|
-
|
|
49
|
-
return { from: firstDay, to: t };
|
|
46
|
+
const t = todayISO();
|
|
47
|
+
return { from: startOfMonthISO(t), to: t };
|
|
50
48
|
},
|
|
51
49
|
},
|
|
52
50
|
{
|
|
53
51
|
label: "Last month",
|
|
54
52
|
value: () => {
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
const lastDayLastMonth = firstDayThisMonth.subtract({ days: 1 });
|
|
58
|
-
const firstDayLastMonth = lastDayLastMonth.with({ day: 1 });
|
|
59
|
-
return { from: firstDayLastMonth, to: lastDayLastMonth };
|
|
53
|
+
const lastDayLastMonth = addDaysISO(startOfMonthISO(todayISO()), -1);
|
|
54
|
+
return { from: startOfMonthISO(lastDayLastMonth), to: lastDayLastMonth };
|
|
60
55
|
},
|
|
61
56
|
},
|
|
62
57
|
];
|
|
@@ -66,9 +61,9 @@ function getDefaultPresets(): PresetOption[] {
|
|
|
66
61
|
* DateRangePicker - A specialized date range picker component
|
|
67
62
|
*
|
|
68
63
|
* Wraps DatePicker with mode="range" and adds common range presets.
|
|
69
|
-
*
|
|
64
|
+
* All date values are canonical ISO 8601 strings ("2026-08-26").
|
|
70
65
|
*/
|
|
71
|
-
export const DateRangePicker = (props
|
|
66
|
+
export const DateRangePicker = React.forwardRef<HTMLDivElement, DateRangePickerProps>((props, ref) => {
|
|
72
67
|
const {
|
|
73
68
|
value,
|
|
74
69
|
defaultValue,
|
|
@@ -123,5 +118,6 @@ export const DateRangePicker = (props: DateRangePickerProps) => {
|
|
|
123
118
|
...(clearable !== undefined && { clearable }),
|
|
124
119
|
};
|
|
125
120
|
|
|
126
|
-
return <DatePicker {...(datePickerProps as DatePickerRangeProps)} />;
|
|
127
|
-
};
|
|
121
|
+
return <DatePicker ref={ref} {...(datePickerProps as DatePickerRangeProps)} />;
|
|
122
|
+
});
|
|
123
|
+
DateRangePicker.displayName = "DateRangePicker";
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
/**
|
|
2
|
+
* DateRangePicker types.
|
|
3
|
+
*
|
|
4
|
+
* All values at the public boundary are canonical ISO 8601 date strings
|
|
5
|
+
* (`"2026-08-26"`).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { DisabledDates, ISODate, ISODateRange, WeekStartsOn } from "../../date-time";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A preset range.
|
|
12
|
+
*
|
|
13
|
+
* The value is a function so that relative ranges ("Last 7 days") are computed
|
|
14
|
+
* when the picker renders rather than when the preset list is defined.
|
|
15
|
+
*/
|
|
8
16
|
export type PresetOption = {
|
|
9
17
|
label: string;
|
|
10
|
-
value: () =>
|
|
18
|
+
value: () => ISODateRange;
|
|
11
19
|
};
|
|
12
20
|
|
|
13
|
-
export type DisabledDateMatcher =
|
|
14
|
-
| Temporal.PlainDate
|
|
15
|
-
| Temporal.PlainDate[]
|
|
16
|
-
| ((date: Temporal.PlainDate) => boolean)
|
|
17
|
-
| { before: Temporal.PlainDate }
|
|
18
|
-
| { after: Temporal.PlainDate }
|
|
19
|
-
| { from: Temporal.PlainDate; to: Temporal.PlainDate };
|
|
20
|
-
|
|
21
21
|
export type DateRangePickerProps = {
|
|
22
22
|
// Value (controlled/uncontrolled)
|
|
23
|
-
value?:
|
|
24
|
-
defaultValue?:
|
|
25
|
-
onChange?: (range:
|
|
23
|
+
value?: ISODateRange;
|
|
24
|
+
defaultValue?: ISODateRange;
|
|
25
|
+
onChange?: (range: ISODateRange | undefined) => void;
|
|
26
26
|
|
|
27
27
|
// Constraints
|
|
28
|
-
minDate?:
|
|
29
|
-
maxDate?:
|
|
30
|
-
disabled?:
|
|
28
|
+
minDate?: ISODate;
|
|
29
|
+
maxDate?: ISODate;
|
|
30
|
+
disabled?: DisabledDates | DisabledDates[];
|
|
31
31
|
|
|
32
32
|
// Presets
|
|
33
33
|
presets?: PresetOption[];
|
|
@@ -1,2 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public surface of `@sixthshift/design-system/date-range-picker`.
|
|
3
|
+
*
|
|
4
|
+
* `DateRangeValue` and `DisabledDateMatcher` were Temporal-shaped and are gone;
|
|
5
|
+
* the ISO equivalents `ISODateRange` and `DisabledDates` are re-exported here.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type { DisabledDates, ISODate, ISODateRange } from "../../date-time";
|
|
1
9
|
export { DateRangePicker } from "./DateRangePicker";
|
|
2
|
-
export type { DateRangePickerProps,
|
|
10
|
+
export type { DateRangePickerProps, PresetOption } from "./daterangepicker.types";
|
|
@@ -2,12 +2,20 @@ import { autoUpdate, FloatingPortal, flip, offset, shift, useClick, useDismiss,
|
|
|
2
2
|
import { useControllableState } from "@sixthshift/design-system/hooks";
|
|
3
3
|
import { cn } from "@sixthshift/design-system/utils";
|
|
4
4
|
import { Clock, X } from "lucide-react";
|
|
5
|
-
import
|
|
6
|
-
import { useCallback, useId, useState } from "react";
|
|
7
|
-
import {
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
import { useCallback, useId, useMemo, useState } from "react";
|
|
7
|
+
import {
|
|
8
|
+
adaptDisabledDates,
|
|
9
|
+
fromISODateOrUndefined,
|
|
10
|
+
fromISOInstantOrUndefined,
|
|
11
|
+
fromISOTimeOrUndefined,
|
|
12
|
+
Temporal,
|
|
13
|
+
today,
|
|
14
|
+
toISOInstantOrUndefined,
|
|
15
|
+
} from "../../date-time";
|
|
8
16
|
|
|
9
17
|
import { Button } from "../Button";
|
|
10
|
-
import {
|
|
18
|
+
import { CalendarView } from "../Calendar/CalendarView";
|
|
11
19
|
import { Separator } from "../Separator";
|
|
12
20
|
import { PeriodSelector } from "../TimePicker/PeriodSelector";
|
|
13
21
|
import { TimeColumn } from "../TimePicker/TimeColumn";
|
|
@@ -59,9 +67,9 @@ function formatInstantDisplay(instant: Temporal.Instant, clockFormat: "12h" | "2
|
|
|
59
67
|
* DateTimePicker - A component for selecting both date and time
|
|
60
68
|
*
|
|
61
69
|
* Combines date and time selection in a single popup with side-by-side layout.
|
|
62
|
-
*
|
|
70
|
+
* Values cross the boundary as canonical UTC instant strings.
|
|
63
71
|
*/
|
|
64
|
-
export const DateTimePicker = (props
|
|
72
|
+
export const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerProps>((props, ref) => {
|
|
65
73
|
const {
|
|
66
74
|
value,
|
|
67
75
|
defaultValue,
|
|
@@ -87,11 +95,30 @@ export const DateTimePicker = (props: DateTimePickerProps) => {
|
|
|
87
95
|
// Open state
|
|
88
96
|
const [open, setOpen] = useState(false);
|
|
89
97
|
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
// The ISO boundary.
|
|
100
|
+
//
|
|
101
|
+
// The value crosses as a canonical UTC instant string; constraints cross as
|
|
102
|
+
// ISO dates and times. Below this block everything is Temporal, exactly as
|
|
103
|
+
// before. Each conversion is memoised on its source string so the Temporal
|
|
104
|
+
// values stay referentially stable across renders.
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
const temporalValue = useMemo(() => fromISOInstantOrUndefined(value), [value]);
|
|
108
|
+
const temporalDefaultValue = useMemo(() => fromISOInstantOrUndefined(defaultValue), [defaultValue]);
|
|
109
|
+
const temporalMinDate = useMemo(() => fromISODateOrUndefined(minDate), [minDate]);
|
|
110
|
+
const temporalMaxDate = useMemo(() => fromISODateOrUndefined(maxDate), [maxDate]);
|
|
111
|
+
const temporalMinTime = useMemo(() => fromISOTimeOrUndefined(minTime), [minTime]);
|
|
112
|
+
const temporalMaxTime = useMemo(() => fromISOTimeOrUndefined(maxTime), [maxTime]);
|
|
113
|
+
const temporalDisabledDates = useMemo(() => adaptDisabledDates(disabledDates), [disabledDates]);
|
|
114
|
+
|
|
115
|
+
const handleChange = useCallback((next: Temporal.Instant | undefined) => onChange?.(toISOInstantOrUndefined(next)), [onChange]);
|
|
116
|
+
|
|
90
117
|
// Controllable value state (external/committed value)
|
|
91
|
-
const [committedValue, setCommittedValue] = useControllableState({
|
|
92
|
-
value,
|
|
93
|
-
defaultValue,
|
|
94
|
-
onChange,
|
|
118
|
+
const [committedValue, setCommittedValue] = useControllableState<Temporal.Instant | undefined>({
|
|
119
|
+
value: temporalValue,
|
|
120
|
+
defaultValue: temporalDefaultValue,
|
|
121
|
+
onChange: handleChange,
|
|
95
122
|
});
|
|
96
123
|
|
|
97
124
|
// Draft state (date + time parts)
|
|
@@ -250,15 +277,15 @@ export const DateTimePicker = (props: DateTimePickerProps) => {
|
|
|
250
277
|
const checkTimeDisabled = useCallback(
|
|
251
278
|
(hour: number, minute: number, second: number): boolean => {
|
|
252
279
|
const temporal = Temporal.PlainTime.from({ hour, minute, second });
|
|
253
|
-
return isTimeDisabled(temporal,
|
|
280
|
+
return isTimeDisabled(temporal, temporalMinTime, temporalMaxTime);
|
|
254
281
|
},
|
|
255
|
-
[
|
|
282
|
+
[temporalMinTime, temporalMaxTime]
|
|
256
283
|
);
|
|
257
284
|
|
|
258
285
|
return (
|
|
259
286
|
<>
|
|
260
287
|
{/* Trigger Input */}
|
|
261
|
-
<div className="relative flex items-center">
|
|
288
|
+
<div ref={ref} className="relative flex items-center">
|
|
262
289
|
<Clock className="pointer-events-none absolute left-3 h-4 w-4 text-fg-subtle" />
|
|
263
290
|
<input
|
|
264
291
|
ref={refs.setReference as React.Ref<HTMLInputElement>}
|
|
@@ -310,15 +337,15 @@ export const DateTimePicker = (props: DateTimePickerProps) => {
|
|
|
310
337
|
>
|
|
311
338
|
<div className="flex gap-4">
|
|
312
339
|
{/* Date Calendar (left side) */}
|
|
313
|
-
<
|
|
340
|
+
<CalendarView
|
|
314
341
|
mode="single"
|
|
315
342
|
value={draftDate}
|
|
316
343
|
onSelect={setDraftDate as (date: Temporal.PlainDate | undefined) => void}
|
|
317
344
|
month={month}
|
|
318
345
|
onMonthChange={setMonth}
|
|
319
|
-
minDate={
|
|
320
|
-
maxDate={
|
|
321
|
-
disabled={
|
|
346
|
+
minDate={temporalMinDate}
|
|
347
|
+
maxDate={temporalMaxDate}
|
|
348
|
+
disabled={temporalDisabledDates}
|
|
322
349
|
weekStartsOn={weekStartsOn}
|
|
323
350
|
showFooter={false}
|
|
324
351
|
/>
|
|
@@ -396,4 +423,5 @@ export const DateTimePicker = (props: DateTimePickerProps) => {
|
|
|
396
423
|
)}
|
|
397
424
|
</>
|
|
398
425
|
);
|
|
399
|
-
};
|
|
426
|
+
});
|
|
427
|
+
DateTimePicker.displayName = "DateTimePicker";
|