@sixthshift/design-system 0.1.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/LICENSE +21 -0
- package/README.md +222 -0
- package/dist/styles.css +2 -0
- package/dist/theme.generated.css +438 -0
- package/package.json +238 -0
- package/src/components/Avatar/Avatar.tsx +19 -0
- package/src/components/Avatar/index.ts +1 -0
- package/src/components/Badge/Badge.tsx +59 -0
- package/src/components/Badge/index.ts +1 -0
- package/src/components/BarChart/BarChart.tsx +51 -0
- package/src/components/BarChart/index.ts +1 -0
- package/src/components/Breadcrumb/Breadcrumb.tsx +47 -0
- package/src/components/Breadcrumb/index.ts +5 -0
- package/src/components/Button/Button.tsx +157 -0
- package/src/components/Button/index.ts +1 -0
- package/src/components/Calendar/Calendar.tsx +433 -0
- package/src/components/Calendar/calendar.hooks.ts +329 -0
- package/src/components/Calendar/calendar.types.ts +117 -0
- package/src/components/Calendar/index.ts +28 -0
- package/src/components/Card/Card.tsx +47 -0
- package/src/components/Card/index.ts +1 -0
- package/src/components/Checkbox/Checkbox.tsx +144 -0
- package/src/components/Checkbox/index.ts +1 -0
- package/src/components/CheckboxGroup/CheckboxGroup.tsx +173 -0
- package/src/components/CheckboxGroup/index.ts +5 -0
- package/src/components/Code/Editor/Editor.tsx +161 -0
- package/src/components/Code/Editor/index.ts +2 -0
- package/src/components/Code/Editor/semanticTokenProvider.ts +69 -0
- package/src/components/Code/Editor/themes.ts +112 -0
- package/src/components/Code/ValidationStatus/ValidationStatus.tsx +81 -0
- package/src/components/Code/ValidationStatus/index.ts +1 -0
- package/src/components/Code/Workspace/StatusBar.tsx +47 -0
- package/src/components/Code/Workspace/Toolbar.tsx +47 -0
- package/src/components/Code/Workspace/Workspace.tsx +109 -0
- package/src/components/Code/Workspace/index.ts +3 -0
- package/src/components/ColorDot/ColorDot.tsx +50 -0
- package/src/components/ColorDot/index.ts +1 -0
- package/src/components/DatePicker/DatePicker.tsx +236 -0
- package/src/components/DatePicker/datepicker.hooks.ts +381 -0
- package/src/components/DatePicker/datepicker.types.ts +140 -0
- package/src/components/DatePicker/index.ts +12 -0
- package/src/components/DateRangePicker/DateRangePicker.tsx +127 -0
- package/src/components/DateRangePicker/daterangepicker.types.ts +53 -0
- package/src/components/DateRangePicker/index.ts +2 -0
- package/src/components/DateTimePicker/DateTimePicker.tsx +399 -0
- package/src/components/DateTimePicker/datetimepicker.types.ts +48 -0
- package/src/components/DateTimePicker/index.ts +2 -0
- package/src/components/DateTimeRangePicker/DateTimeRangePicker.tsx +679 -0
- package/src/components/DateTimeRangePicker/datetimerangepicker.types.ts +62 -0
- package/src/components/DateTimeRangePicker/index.ts +2 -0
- package/src/components/EmptyState/EmptyState.tsx +29 -0
- package/src/components/EmptyState/index.ts +1 -0
- package/src/components/Field/Field.tsx +60 -0
- package/src/components/Field/index.ts +1 -0
- package/src/components/FormField/FormField.tsx +55 -0
- package/src/components/FormField/index.ts +2 -0
- package/src/components/HeatMap/HeatMapCalendar.tsx +127 -0
- package/src/components/HeatMap/HeatMapMatrix.tsx +75 -0
- package/src/components/HeatMap/heat-map.utils.ts +15 -0
- package/src/components/HeatMap/index.ts +3 -0
- package/src/components/HoverCard/HoverCard.tsx +83 -0
- package/src/components/HoverCard/components/HoverCardContent.tsx +30 -0
- package/src/components/HoverCard/components/HoverCardContext.tsx +22 -0
- package/src/components/HoverCard/components/HoverCardTrigger.tsx +32 -0
- package/src/components/HoverCard/index.ts +3 -0
- package/src/components/Input/Input.tsx +48 -0
- package/src/components/Input/index.ts +1 -0
- package/src/components/LineChart/LineChart.tsx +251 -0
- package/src/components/LineChart/index.ts +1 -0
- package/src/components/Markdown/Markdown.tsx +40 -0
- package/src/components/Markdown/index.ts +1 -0
- package/src/components/Message/Message.tsx +59 -0
- package/src/components/Message/components/MessageBody.tsx +9 -0
- package/src/components/Message/components/MessageDescription.tsx +9 -0
- package/src/components/Message/components/MessageIcon.tsx +13 -0
- package/src/components/Message/components/MessageTitle.tsx +9 -0
- package/src/components/Message/index.ts +10 -0
- package/src/components/MetricList/MetricList.tsx +13 -0
- package/src/components/MetricList/MetricRow.tsx +30 -0
- package/src/components/MetricList/index.ts +2 -0
- package/src/components/Modal/Modal.tsx +146 -0
- package/src/components/Modal/components/ModalBody.tsx +9 -0
- package/src/components/Modal/components/ModalContext.tsx +27 -0
- package/src/components/Modal/components/ModalFooter.tsx +9 -0
- package/src/components/Modal/components/ModalHeader.tsx +29 -0
- package/src/components/Modal/components/index.ts +9 -0
- package/src/components/Modal/index.ts +13 -0
- package/src/components/NavBottom/NavBottom.tsx +53 -0
- package/src/components/NavBottom/index.ts +3 -0
- package/src/components/NavSide/NavItem.tsx +39 -0
- package/src/components/NavSide/NavSide.tsx +40 -0
- package/src/components/NavSide/index.ts +2 -0
- package/src/components/NavSide/types.ts +36 -0
- package/src/components/Pagination/Pagination.tsx +86 -0
- package/src/components/Pagination/index.ts +2 -0
- package/src/components/Popover/Popover.tsx +68 -0
- package/src/components/Popover/components/PopoverBody.tsx +32 -0
- package/src/components/Popover/components/PopoverClose.tsx +26 -0
- package/src/components/Popover/components/PopoverContext.tsx +24 -0
- package/src/components/Popover/components/PopoverTrigger.tsx +31 -0
- package/src/components/Popover/index.ts +10 -0
- package/src/components/ProgressBar/ProgressBar.tsx +42 -0
- package/src/components/ProgressBar/index.ts +1 -0
- package/src/components/RadioButton/RadioButton.tsx +89 -0
- package/src/components/RadioButton/index.ts +1 -0
- package/src/components/RadioButtonGroup/RadioButtonGroup.tsx +193 -0
- package/src/components/RadioButtonGroup/index.ts +5 -0
- package/src/components/SearchInput/SearchInput.tsx +45 -0
- package/src/components/SearchInput/index.ts +2 -0
- package/src/components/Select/Select.tsx +289 -0
- package/src/components/Select/SelectDropdown.tsx +90 -0
- package/src/components/Select/SelectTrigger.tsx +130 -0
- package/src/components/Select/index.ts +3 -0
- package/src/components/Select/useSelectKeyboard.ts +111 -0
- package/src/components/Separator/Separator.tsx +20 -0
- package/src/components/Separator/index.ts +1 -0
- package/src/components/Sheet/Sheet.tsx +136 -0
- package/src/components/Sheet/components/SheetBody.tsx +9 -0
- package/src/components/Sheet/components/SheetContext.tsx +23 -0
- package/src/components/Sheet/components/SheetFooter.tsx +9 -0
- package/src/components/Sheet/components/SheetHeader.tsx +29 -0
- package/src/components/Sheet/components/index.ts +9 -0
- package/src/components/Sheet/index.ts +13 -0
- package/src/components/Skeleton/Skeleton.tsx +7 -0
- package/src/components/Skeleton/index.ts +1 -0
- package/src/components/Sparkline/Sparkline.tsx +93 -0
- package/src/components/Sparkline/index.ts +1 -0
- package/src/components/Spinner/Spinner.tsx +41 -0
- package/src/components/Spinner/index.ts +1 -0
- package/src/components/StatsCard/StatsCard.tsx +40 -0
- package/src/components/StatsCard/index.ts +1 -0
- package/src/components/Switch/Switch.tsx +128 -0
- package/src/components/Switch/index.ts +1 -0
- package/src/components/Tabs/Tabs.tsx +49 -0
- package/src/components/Tabs/components/TabsContext.tsx +35 -0
- package/src/components/Tabs/components/TabsList.tsx +98 -0
- package/src/components/Tabs/components/TabsPanels.tsx +26 -0
- package/src/components/Tabs/components/TabsTrigger.tsx +69 -0
- package/src/components/Tabs/index.ts +3 -0
- package/src/components/TagChip/TagChip.tsx +46 -0
- package/src/components/TagChip/index.ts +1 -0
- package/src/components/TagInput/TagInput.tsx +60 -0
- package/src/components/TagInput/index.ts +1 -0
- package/src/components/Text/Text.tsx +35 -0
- package/src/components/Text/index.ts +1 -0
- package/src/components/TextInline/TextInline.tsx +35 -0
- package/src/components/TextInline/index.ts +1 -0
- package/src/components/Textarea/Textarea.tsx +20 -0
- package/src/components/Textarea/index.ts +1 -0
- package/src/components/TimePicker/PeriodSelector.tsx +28 -0
- package/src/components/TimePicker/TimeColumn.tsx +62 -0
- package/src/components/TimePicker/TimePicker.tsx +323 -0
- package/src/components/TimePicker/index.ts +7 -0
- package/src/components/TimePicker/timepicker.hooks.ts +173 -0
- package/src/components/TimePicker/timepicker.types.ts +101 -0
- package/src/components/Toast/Toast.tsx +111 -0
- package/src/components/Toast/index.ts +1 -0
- package/src/components/Toggle/Toggle.tsx +72 -0
- package/src/components/Toggle/index.ts +1 -0
- package/src/components/ToggleGroup/ToggleGroup.tsx +14 -0
- package/src/components/ToggleGroup/ToggleGroupContainer.tsx +19 -0
- package/src/components/ToggleGroup/ToggleGroupItem.tsx +51 -0
- package/src/components/ToggleGroup/ToggleGroupMultiple.tsx +61 -0
- package/src/components/ToggleGroup/ToggleGroupSingle.tsx +105 -0
- package/src/components/ToggleGroup/index.ts +2 -0
- package/src/components/ToggleGroup/toggleGroup.types.ts +55 -0
- package/src/components/Tooltip/Tooltip.tsx +66 -0
- package/src/components/Tooltip/components/TooltipBody.tsx +30 -0
- package/src/components/Tooltip/components/TooltipContext.tsx +22 -0
- package/src/components/Tooltip/components/TooltipTrigger.tsx +27 -0
- package/src/components/Tooltip/index.ts +9 -0
- package/src/date-time/arithmetic.ts +101 -0
- package/src/date-time/boundaries.ts +91 -0
- package/src/date-time/compare.ts +85 -0
- package/src/date-time/duration.ts +63 -0
- package/src/date-time/elapsed.ts +18 -0
- package/src/date-time/format.ts +722 -0
- package/src/date-time/index.ts +279 -0
- package/src/date-time/now.ts +70 -0
- package/src/date-time/parse.ts +170 -0
- package/src/date-time/serialize.ts +90 -0
- package/src/date-time/timezone.ts +47 -0
- package/src/date-time/validate.ts +278 -0
- package/src/hooks/index.ts +10 -0
- package/src/hooks/useCollapsible.ts +16 -0
- package/src/hooks/useControllableState.ts +54 -0
- package/src/hooks/useDebouncedCallback.ts +21 -0
- package/src/hooks/useDebouncedState.ts +20 -0
- package/src/hooks/useDeviceFormFactor.ts +49 -0
- package/src/hooks/useLocalStorage.ts +67 -0
- package/src/hooks/useMergedFloatingRef.ts +29 -0
- package/src/hooks/usePresence.ts +127 -0
- package/src/hooks/useStack.ts +99 -0
- package/src/hooks/useTheme.ts +105 -0
- package/src/internal/Slot.tsx +79 -0
- package/src/internal/index.ts +2 -0
- package/src/internal/rovingFocus.ts +39 -0
- package/src/internal/types.ts +10 -0
- package/src/lib/EmptyBoundary.tsx +12 -0
- package/src/lib/ErrorBoundary.tsx +48 -0
- package/src/lib/contexts/ComponentsContext/ComponentsContext.tsx +34 -0
- package/src/lib/contexts/ComponentsContext/index.ts +1 -0
- package/src/lib/contexts/OverlayContext/OverlayContext.tsx +93 -0
- package/src/lib/contexts/OverlayContext/hooks/useModal.tsx +125 -0
- package/src/lib/contexts/OverlayContext/hooks/useToast.ts +80 -0
- package/src/lib/contexts/OverlayContext/index.ts +6 -0
- package/src/lib/format.ts +51 -0
- package/src/lib/utils.ts +10 -0
- package/src/lib/withEmpty.tsx +14 -0
- package/src/lib/withErrorBoundary.tsx +16 -0
- package/src/lib/withSuspense.tsx +16 -0
- package/src/lib/withSuspenseAndErrorBoundary.tsx +26 -0
- package/src/styles/base.css +41 -0
- package/src/theme/index.ts +37 -0
- package/src/theme/palette.json +100 -0
- package/src/theme/palette.schema.json +40 -0
- package/src/theme/schema.ts +122 -0
- package/src/theme/theme.json +349 -0
- package/src/theme/theme.schema.json +876 -0
- package/src/theme/typography.json +7 -0
- package/src/theme/typography.schema.json +36 -0
- package/src/typography/Body.tsx +8 -0
- package/src/typography/Caption.tsx +8 -0
- package/src/typography/Code.tsx +8 -0
- package/src/typography/Display.tsx +8 -0
- package/src/typography/Emphasis.tsx +8 -0
- package/src/typography/Heading.tsx +23 -0
- package/src/typography/Label.tsx +10 -0
- package/src/typography/Lead.tsx +8 -0
- package/src/typography/Mono.tsx +8 -0
- package/src/typography/Muted.tsx +8 -0
- package/src/typography/SectionTitle.tsx +8 -0
- package/src/typography/Subtitle.tsx +8 -0
- package/src/typography/Timestamp.tsx +8 -0
- package/src/typography/index.ts +10 -0
- package/tailwind.config.ts +261 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @sixthshift/design-system/date-time - Date/time utilities using Temporal API
|
|
3
|
+
*
|
|
4
|
+
* This package provides:
|
|
5
|
+
* - Re-export of Temporal from @js-temporal/polyfill
|
|
6
|
+
* - Parsing utilities (ISO string → Temporal)
|
|
7
|
+
* - Serialization utilities (Temporal → ISO strings for APIs/storage)
|
|
8
|
+
* - Formatting utilities (Temporal → human-readable strings for UI)
|
|
9
|
+
* - Common utility functions
|
|
10
|
+
*
|
|
11
|
+
* When native Temporal lands in browsers/Node, update the Temporal export
|
|
12
|
+
* in this file and all consuming packages get native support automatically.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* import { Temporal, parseDate, serialize, formatDateShort, isToday } from '@sixthshift/design-system/date-time';
|
|
16
|
+
*
|
|
17
|
+
* // Parse ISO string from API
|
|
18
|
+
* const dueDate = parseDate('2025-01-15');
|
|
19
|
+
*
|
|
20
|
+
* // Work with Temporal
|
|
21
|
+
* const tomorrow = dueDate.add({ days: 1 });
|
|
22
|
+
* const overdue = isToday(dueDate);
|
|
23
|
+
*
|
|
24
|
+
* // Serialize back to ISO for API
|
|
25
|
+
* const isoString = serialize(tomorrow); // '2025-01-16'
|
|
26
|
+
*
|
|
27
|
+
* // Format for UI display
|
|
28
|
+
* const display = formatDateShort(tomorrow); // 'Jan 16'
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
// =============================================================================
|
|
32
|
+
// Temporal API (re-export from polyfill)
|
|
33
|
+
// =============================================================================
|
|
34
|
+
// When native Temporal lands, change this to:
|
|
35
|
+
// export const { Temporal } = globalThis;
|
|
36
|
+
|
|
37
|
+
// Re-export the Temporal namespace type for convenience
|
|
38
|
+
export type { Temporal as TemporalNamespace } from "@js-temporal/polyfill";
|
|
39
|
+
export { Temporal } from "@js-temporal/polyfill";
|
|
40
|
+
|
|
41
|
+
// =============================================================================
|
|
42
|
+
// Parsing: ISO 8601 string → Temporal
|
|
43
|
+
// =============================================================================
|
|
44
|
+
|
|
45
|
+
export {
|
|
46
|
+
fromDate,
|
|
47
|
+
parseDate,
|
|
48
|
+
parseDateOrUndefined,
|
|
49
|
+
parseDateTime,
|
|
50
|
+
parseDateTimeOrUndefined,
|
|
51
|
+
parseDuration,
|
|
52
|
+
parseDurationOrUndefined,
|
|
53
|
+
parseInstant,
|
|
54
|
+
parseInstantOrUndefined,
|
|
55
|
+
parseTime,
|
|
56
|
+
parseTimeOrUndefined,
|
|
57
|
+
parseZoned,
|
|
58
|
+
parseZonedOrUndefined,
|
|
59
|
+
} from "./parse";
|
|
60
|
+
|
|
61
|
+
// =============================================================================
|
|
62
|
+
// Formatting: Temporal → strings
|
|
63
|
+
// =============================================================================
|
|
64
|
+
|
|
65
|
+
export {
|
|
66
|
+
// Pattern-based formatting
|
|
67
|
+
formatDate,
|
|
68
|
+
formatDateFull,
|
|
69
|
+
formatDateLong,
|
|
70
|
+
formatDateMedium,
|
|
71
|
+
formatDateMediumYear,
|
|
72
|
+
formatDateNumeric,
|
|
73
|
+
formatDateNumericYear,
|
|
74
|
+
// Date range formats
|
|
75
|
+
formatDateRange,
|
|
76
|
+
// Date formats
|
|
77
|
+
formatDateShort,
|
|
78
|
+
formatDateShortWeekday,
|
|
79
|
+
formatDateShortWeekdayYear,
|
|
80
|
+
formatDateShortYear,
|
|
81
|
+
// Composite type formats
|
|
82
|
+
formatDateTimeTime,
|
|
83
|
+
formatDurationShort,
|
|
84
|
+
formatInstantDateTime,
|
|
85
|
+
formatInstantShortYear,
|
|
86
|
+
formatInstantTime,
|
|
87
|
+
// Duration formats
|
|
88
|
+
formatMilliseconds,
|
|
89
|
+
formatMonthYear,
|
|
90
|
+
formatMonthYearShort,
|
|
91
|
+
// Compact recency (1d/60d/2mo/1y) for index rows
|
|
92
|
+
formatRecency,
|
|
93
|
+
// Relative formats
|
|
94
|
+
formatRelative,
|
|
95
|
+
formatRelativeInstant,
|
|
96
|
+
formatRelativeToNow,
|
|
97
|
+
// Time formats
|
|
98
|
+
formatTime,
|
|
99
|
+
formatTime24,
|
|
100
|
+
formatTimeCompact,
|
|
101
|
+
formatTimeHour,
|
|
102
|
+
formatTimePadded,
|
|
103
|
+
formatWeekRange,
|
|
104
|
+
formatZonedTime,
|
|
105
|
+
} from "./format";
|
|
106
|
+
|
|
107
|
+
// =============================================================================
|
|
108
|
+
// Validation
|
|
109
|
+
// =============================================================================
|
|
110
|
+
|
|
111
|
+
export type { ValidationResult } from "./validate";
|
|
112
|
+
export {
|
|
113
|
+
isDuration,
|
|
114
|
+
isInstant,
|
|
115
|
+
// String validation
|
|
116
|
+
isISODate,
|
|
117
|
+
isISODateTime,
|
|
118
|
+
isISODuration,
|
|
119
|
+
isISOInstant,
|
|
120
|
+
isISOString,
|
|
121
|
+
isISOTime,
|
|
122
|
+
isISOZoned,
|
|
123
|
+
// Type guards
|
|
124
|
+
isPlainDate,
|
|
125
|
+
isPlainDateTime,
|
|
126
|
+
isPlainTime,
|
|
127
|
+
isTemporal,
|
|
128
|
+
isZonedDateTime,
|
|
129
|
+
// Validation with errors
|
|
130
|
+
validateISODate,
|
|
131
|
+
validateISODuration,
|
|
132
|
+
validateISOInstant,
|
|
133
|
+
validateISOZoned,
|
|
134
|
+
} from "./validate";
|
|
135
|
+
|
|
136
|
+
// =============================================================================
|
|
137
|
+
// Current time
|
|
138
|
+
// =============================================================================
|
|
139
|
+
|
|
140
|
+
export {
|
|
141
|
+
currentTime,
|
|
142
|
+
now,
|
|
143
|
+
nowDateTime,
|
|
144
|
+
nowDateTimeISO,
|
|
145
|
+
nowInTimezone,
|
|
146
|
+
nowISO,
|
|
147
|
+
nowZoned,
|
|
148
|
+
today,
|
|
149
|
+
todayISO,
|
|
150
|
+
} from "./now";
|
|
151
|
+
|
|
152
|
+
// =============================================================================
|
|
153
|
+
// Comparison
|
|
154
|
+
// =============================================================================
|
|
155
|
+
|
|
156
|
+
export {
|
|
157
|
+
isAfter,
|
|
158
|
+
isBefore,
|
|
159
|
+
isFuture,
|
|
160
|
+
isInstantAfter,
|
|
161
|
+
isInstantBefore,
|
|
162
|
+
isInstantFuture,
|
|
163
|
+
isInstantPast,
|
|
164
|
+
isPast,
|
|
165
|
+
isSameDay,
|
|
166
|
+
isSameMonth,
|
|
167
|
+
isToday,
|
|
168
|
+
} from "./compare";
|
|
169
|
+
|
|
170
|
+
// =============================================================================
|
|
171
|
+
// Arithmetic
|
|
172
|
+
// =============================================================================
|
|
173
|
+
|
|
174
|
+
export {
|
|
175
|
+
addDays,
|
|
176
|
+
addMonths,
|
|
177
|
+
addWeeks,
|
|
178
|
+
differenceInMinutes,
|
|
179
|
+
diffInDays,
|
|
180
|
+
diffInMonths,
|
|
181
|
+
eachDayOfInterval,
|
|
182
|
+
subtractDays,
|
|
183
|
+
subtractMonths,
|
|
184
|
+
subtractWeeks,
|
|
185
|
+
} from "./arithmetic";
|
|
186
|
+
|
|
187
|
+
// =============================================================================
|
|
188
|
+
// Boundaries
|
|
189
|
+
// =============================================================================
|
|
190
|
+
|
|
191
|
+
export type { WeekStartsOn } from "./boundaries";
|
|
192
|
+
export {
|
|
193
|
+
endOfDay,
|
|
194
|
+
endOfMonth,
|
|
195
|
+
endOfWeek,
|
|
196
|
+
endOfYear,
|
|
197
|
+
startOfDay,
|
|
198
|
+
startOfMonth,
|
|
199
|
+
startOfWeek,
|
|
200
|
+
startOfYear,
|
|
201
|
+
} from "./boundaries";
|
|
202
|
+
|
|
203
|
+
// =============================================================================
|
|
204
|
+
// Serialization: Temporal → ISO 8601 strings
|
|
205
|
+
// =============================================================================
|
|
206
|
+
|
|
207
|
+
export {
|
|
208
|
+
serialize,
|
|
209
|
+
serializeDate,
|
|
210
|
+
serializeDateTime,
|
|
211
|
+
serializeDuration,
|
|
212
|
+
serializeInstant,
|
|
213
|
+
serializeOptional,
|
|
214
|
+
serializeTime,
|
|
215
|
+
serializeZonedDateTime,
|
|
216
|
+
} from "./serialize";
|
|
217
|
+
|
|
218
|
+
// =============================================================================
|
|
219
|
+
// Timezone
|
|
220
|
+
// =============================================================================
|
|
221
|
+
|
|
222
|
+
export {
|
|
223
|
+
changeTimezone,
|
|
224
|
+
plainDateTimeToZonedDateTime,
|
|
225
|
+
plainDateToZonedDateTime,
|
|
226
|
+
toTimezone,
|
|
227
|
+
} from "./timezone";
|
|
228
|
+
|
|
229
|
+
// =============================================================================
|
|
230
|
+
// API Boundary Pattern
|
|
231
|
+
// =============================================================================
|
|
232
|
+
//
|
|
233
|
+
// When sending Temporal types to the API, use these patterns:
|
|
234
|
+
//
|
|
235
|
+
// For Instant (timezone-aware absolute time) values:
|
|
236
|
+
// serialize(instant)
|
|
237
|
+
// → "2026-01-24T23:30:00Z"
|
|
238
|
+
//
|
|
239
|
+
// For PlainDate (date-only, timezone-agnostic) values:
|
|
240
|
+
// serialize(plainDateToZonedDateTime(date))
|
|
241
|
+
// → "2026-01-24T00:00:00-08:00[America/Los_Angeles]"
|
|
242
|
+
//
|
|
243
|
+
// Why?
|
|
244
|
+
// - Instant is already absolute UTC time - serialize directly
|
|
245
|
+
// - PlainDate needs timezone context - interpret in user's timezone, then serialize
|
|
246
|
+
// - Server parseInstant() handles both formats correctly
|
|
247
|
+
//
|
|
248
|
+
// PREFER Instant for all datetime values in UI components and API hooks.
|
|
249
|
+
// PlainDate is only for date-only values (birthdays, all-day events).
|
|
250
|
+
//
|
|
251
|
+
// Example in API hook (CURRENT PATTERN):
|
|
252
|
+
// const params = {
|
|
253
|
+
// since: dateRange?.from ? serialize(dateRange.from) : undefined, // dateRange uses Instant
|
|
254
|
+
// until: dateRange?.to ? serialize(dateRange.to) : undefined,
|
|
255
|
+
// };
|
|
256
|
+
//
|
|
257
|
+
// See: packages/web/src/api/activity-log/useActivityLog.ts for reference implementation
|
|
258
|
+
// =============================================================================
|
|
259
|
+
|
|
260
|
+
// =============================================================================
|
|
261
|
+
// Duration
|
|
262
|
+
// =============================================================================
|
|
263
|
+
|
|
264
|
+
export {
|
|
265
|
+
days,
|
|
266
|
+
hours,
|
|
267
|
+
minutes,
|
|
268
|
+
toHours,
|
|
269
|
+
toMilliseconds,
|
|
270
|
+
toMinutes,
|
|
271
|
+
toSeconds,
|
|
272
|
+
weeks,
|
|
273
|
+
} from "./duration";
|
|
274
|
+
|
|
275
|
+
// =============================================================================
|
|
276
|
+
// Elapsed time
|
|
277
|
+
// =============================================================================
|
|
278
|
+
|
|
279
|
+
export { elapsedMs, epochMs } from "./elapsed";
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Current time/date utilities
|
|
3
|
+
*
|
|
4
|
+
* Functions that answer "what time/date is it right now?"
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get the current instant (point in time)
|
|
11
|
+
*/
|
|
12
|
+
export function now(): Temporal.Instant {
|
|
13
|
+
return Temporal.Now.instant();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get the current instant as an ISO 8601 string
|
|
18
|
+
*/
|
|
19
|
+
export function nowISO(): string {
|
|
20
|
+
return Temporal.Now.instant().toString();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Get today's date in the system timezone
|
|
25
|
+
*/
|
|
26
|
+
export function today(): Temporal.PlainDate {
|
|
27
|
+
return Temporal.Now.plainDateISO();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get today's date as an ISO 8601 string
|
|
32
|
+
*/
|
|
33
|
+
export function todayISO(): string {
|
|
34
|
+
return Temporal.Now.plainDateISO().toString();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get the current time in the system timezone
|
|
39
|
+
*/
|
|
40
|
+
export function currentTime(): Temporal.PlainTime {
|
|
41
|
+
return Temporal.Now.plainTimeISO();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get the current date/time in the system timezone (without timezone info)
|
|
46
|
+
*/
|
|
47
|
+
export function nowDateTime(): Temporal.PlainDateTime {
|
|
48
|
+
return Temporal.Now.plainDateTimeISO();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get the current date/time as an ISO 8601 string (without timezone info)
|
|
53
|
+
*/
|
|
54
|
+
export function nowDateTimeISO(): string {
|
|
55
|
+
return Temporal.Now.plainDateTimeISO().toString();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get the current zoned date/time in the system timezone
|
|
60
|
+
*/
|
|
61
|
+
export function nowZoned(): Temporal.ZonedDateTime {
|
|
62
|
+
return Temporal.Now.zonedDateTimeISO();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get the current zoned date/time in a specific timezone
|
|
67
|
+
*/
|
|
68
|
+
export function nowInTimezone(timezone: string): Temporal.ZonedDateTime {
|
|
69
|
+
return Temporal.Now.zonedDateTimeISO(timezone);
|
|
70
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parsing utilities: External data → Temporal
|
|
3
|
+
*
|
|
4
|
+
* Use these at integration boundaries when receiving data from external sources:
|
|
5
|
+
* - ISO 8601 strings from APIs
|
|
6
|
+
* - Date objects from Node.js APIs or third-party libraries
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Parse an ISO 8601 instant string to Temporal.Instant
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* parseInstant('2025-01-15T14:30:00.000Z')
|
|
16
|
+
* // → Temporal.Instant
|
|
17
|
+
*/
|
|
18
|
+
export function parseInstant(iso: string): Temporal.Instant {
|
|
19
|
+
return Temporal.Instant.from(iso);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Parse an ISO 8601 instant string to Temporal.Instant, or undefined if input is nullish
|
|
24
|
+
*/
|
|
25
|
+
export function parseInstantOrUndefined(iso: string | null | undefined): Temporal.Instant | undefined {
|
|
26
|
+
return iso ? Temporal.Instant.from(iso) : undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Parse an ISO 8601 date string to Temporal.PlainDate
|
|
31
|
+
*
|
|
32
|
+
* Accepts formats:
|
|
33
|
+
* - '2025-01-15' (date only)
|
|
34
|
+
* - '2025-01-15T14:30:00' (datetime, time portion ignored)
|
|
35
|
+
* - '2025-01-15T14:30:00Z' (datetime with Z, time portion ignored)
|
|
36
|
+
* - '2025-01-15T14:30:00.000Z' (datetime with milliseconds and Z)
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* parseDate('2025-01-15')
|
|
40
|
+
* // → Temporal.PlainDate
|
|
41
|
+
*/
|
|
42
|
+
export function parseDate(iso: string): Temporal.PlainDate {
|
|
43
|
+
// Extract just the date portion (first 10 characters: YYYY-MM-DD)
|
|
44
|
+
const datePart = iso.slice(0, 10);
|
|
45
|
+
return Temporal.PlainDate.from(datePart);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Parse an ISO 8601 date string to Temporal.PlainDate, or undefined if input is nullish
|
|
50
|
+
*
|
|
51
|
+
* Accepts same formats as parseDate.
|
|
52
|
+
*/
|
|
53
|
+
export function parseDateOrUndefined(iso: string | null | undefined): Temporal.PlainDate | undefined {
|
|
54
|
+
if (!iso) return undefined;
|
|
55
|
+
const datePart = iso.slice(0, 10);
|
|
56
|
+
return Temporal.PlainDate.from(datePart);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Parse an ISO 8601 time string to Temporal.PlainTime
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* parseTime('14:30:00')
|
|
64
|
+
* // → Temporal.PlainTime
|
|
65
|
+
*/
|
|
66
|
+
export function parseTime(iso: string): Temporal.PlainTime {
|
|
67
|
+
return Temporal.PlainTime.from(iso);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Parse an ISO 8601 time string to Temporal.PlainTime, or undefined if input is nullish
|
|
72
|
+
*/
|
|
73
|
+
export function parseTimeOrUndefined(iso: string | null | undefined): Temporal.PlainTime | undefined {
|
|
74
|
+
return iso ? Temporal.PlainTime.from(iso) : undefined;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Parse an ISO 8601 datetime string to Temporal.PlainDateTime
|
|
79
|
+
*
|
|
80
|
+
* Accepts formats:
|
|
81
|
+
* - '2025-01-15T14:30:00' (datetime)
|
|
82
|
+
* - '2025-01-15T14:30:00Z' (datetime with Z, Z is stripped)
|
|
83
|
+
* - '2025-01-15T14:30:00.000Z' (datetime with milliseconds and Z)
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* parseDateTime('2025-01-15T14:30:00')
|
|
87
|
+
* // → Temporal.PlainDateTime
|
|
88
|
+
*/
|
|
89
|
+
export function parseDateTime(iso: string): Temporal.PlainDateTime {
|
|
90
|
+
// Strip trailing Z if present (PlainDateTime doesn't accept timezone designators)
|
|
91
|
+
const normalized = iso.endsWith("Z") ? iso.slice(0, -1) : iso;
|
|
92
|
+
return Temporal.PlainDateTime.from(normalized);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Parse an ISO 8601 datetime string to Temporal.PlainDateTime, or undefined if input is nullish
|
|
97
|
+
*
|
|
98
|
+
* Accepts same formats as parseDateTime.
|
|
99
|
+
*/
|
|
100
|
+
export function parseDateTimeOrUndefined(iso: string | null | undefined): Temporal.PlainDateTime | undefined {
|
|
101
|
+
if (!iso) return undefined;
|
|
102
|
+
const normalized = iso.endsWith("Z") ? iso.slice(0, -1) : iso;
|
|
103
|
+
return Temporal.PlainDateTime.from(normalized);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Parse an ISO 8601 zoned datetime string to Temporal.ZonedDateTime
|
|
108
|
+
*
|
|
109
|
+
* Accepts formats:
|
|
110
|
+
* - '2025-01-15T14:30:00[America/New_York]'
|
|
111
|
+
* - '2025-01-15T14:30:00-05:00[America/New_York]'
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* parseZoned('2025-01-15T14:30:00[America/New_York]')
|
|
115
|
+
* // → Temporal.ZonedDateTime
|
|
116
|
+
*/
|
|
117
|
+
export function parseZoned(iso: string): Temporal.ZonedDateTime {
|
|
118
|
+
return Temporal.ZonedDateTime.from(iso);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Parse an ISO 8601 zoned datetime string to Temporal.ZonedDateTime, or undefined if input is nullish
|
|
123
|
+
*/
|
|
124
|
+
export function parseZonedOrUndefined(iso: string | null | undefined): Temporal.ZonedDateTime | undefined {
|
|
125
|
+
return iso ? Temporal.ZonedDateTime.from(iso) : undefined;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// =============================================================================
|
|
129
|
+
// Date object conversion
|
|
130
|
+
// =============================================================================
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Convert a JavaScript Date object to Temporal.Instant
|
|
134
|
+
*
|
|
135
|
+
* Use this at API boundaries where external libraries return Date objects
|
|
136
|
+
* (e.g., Node.js fs.Stats.mtime, pg driver timestamptz columns, pg-boss next_run_on)
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* // Converting fs.Stats mtime
|
|
140
|
+
* const instant = fromDate(stats.mtime);
|
|
141
|
+
*
|
|
142
|
+
* // Converting pg driver Date
|
|
143
|
+
* const instant = fromDate(pgResult.created_at);
|
|
144
|
+
*/
|
|
145
|
+
// biome-ignore lint/style/noRestrictedGlobals: Date parameter required for converting from external APIs
|
|
146
|
+
export function fromDate(date: Date): Temporal.Instant {
|
|
147
|
+
return Temporal.Instant.fromEpochMilliseconds(date.getTime());
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// =============================================================================
|
|
151
|
+
// Duration parsing
|
|
152
|
+
// =============================================================================
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Parse an ISO 8601 duration string to Temporal.Duration
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* parseDuration('PT1H30M')
|
|
159
|
+
* // → Temporal.Duration (1 hour 30 minutes)
|
|
160
|
+
*/
|
|
161
|
+
export function parseDuration(iso: string): Temporal.Duration {
|
|
162
|
+
return Temporal.Duration.from(iso);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Parse an ISO 8601 duration string to Temporal.Duration, or undefined if input is nullish
|
|
167
|
+
*/
|
|
168
|
+
export function parseDurationOrUndefined(iso: string | null | undefined): Temporal.Duration | undefined {
|
|
169
|
+
return iso ? Temporal.Duration.from(iso) : undefined;
|
|
170
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic serialization utilities for Temporal types
|
|
3
|
+
*
|
|
4
|
+
* Provides a unified way to convert any Temporal type to its ISO string representation.
|
|
5
|
+
* These functions are simple wrappers around `.toString()` but provide:
|
|
6
|
+
* - Type safety
|
|
7
|
+
* - Consistent naming
|
|
8
|
+
* - Optional value handling
|
|
9
|
+
* - A single import point
|
|
10
|
+
*
|
|
11
|
+
* ## API Boundary Pattern
|
|
12
|
+
*
|
|
13
|
+
* When sending Temporal types to API endpoints, use this composition:
|
|
14
|
+
*
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { plainDateTimeToZonedDateTime, serialize } from "@sixthshift/design-system/date-time";
|
|
17
|
+
*
|
|
18
|
+
* // For PlainDateTime (timezone-naive) → API string
|
|
19
|
+
* const apiValue = serialize(plainDateTimeToZonedDateTime(dateTime));
|
|
20
|
+
* // → "2026-01-24T15:30:00-08:00[America/Los_Angeles]"
|
|
21
|
+
*
|
|
22
|
+
* // For PlainDate → API string (midnight in user's timezone)
|
|
23
|
+
* const apiValue = serialize(plainDateToZonedDateTime(date));
|
|
24
|
+
* // → "2026-01-24T00:00:00-08:00[America/Los_Angeles]"
|
|
25
|
+
*
|
|
26
|
+
* // Server receives and parses to UTC Instant
|
|
27
|
+
* parseInstant(apiValue) → Instant('2026-01-24T23:30:00Z')
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import type { Temporal } from "@js-temporal/polyfill";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Serialize any Temporal type to its ISO 8601 string representation
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* serialize(Temporal.PlainDate.from('2026-01-24'))
|
|
39
|
+
* // → '2026-01-24'
|
|
40
|
+
*
|
|
41
|
+
* serialize(Temporal.PlainDateTime.from('2026-01-24T15:30'))
|
|
42
|
+
* // → '2026-01-24T15:30:00'
|
|
43
|
+
*
|
|
44
|
+
* serialize(Temporal.Instant.from('2026-01-24T04:30:00Z'))
|
|
45
|
+
* // → '2026-01-24T04:30:00Z'
|
|
46
|
+
*/
|
|
47
|
+
export function serialize(
|
|
48
|
+
temporal: Temporal.Instant | Temporal.PlainDate | Temporal.PlainDateTime | Temporal.PlainTime | Temporal.ZonedDateTime | Temporal.Duration
|
|
49
|
+
): string {
|
|
50
|
+
return temporal.toString();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Serialize optional Temporal type to ISO string or undefined
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* serializeOptional(undefined) // → undefined
|
|
58
|
+
* serializeOptional(Temporal.PlainDate.from('2026-01-24')) // → '2026-01-24'
|
|
59
|
+
*/
|
|
60
|
+
export function serializeOptional(
|
|
61
|
+
temporal: Temporal.Instant | Temporal.PlainDate | Temporal.PlainDateTime | Temporal.PlainTime | Temporal.ZonedDateTime | Temporal.Duration | null | undefined
|
|
62
|
+
): string | undefined {
|
|
63
|
+
return temporal?.toString();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Type-specific convenience functions (optional, for clarity)
|
|
67
|
+
|
|
68
|
+
export function serializeInstant(instant: Temporal.Instant): string {
|
|
69
|
+
return instant.toString();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function serializeDate(date: Temporal.PlainDate): string {
|
|
73
|
+
return date.toString();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function serializeDateTime(dateTime: Temporal.PlainDateTime): string {
|
|
77
|
+
return dateTime.toString();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function serializeTime(time: Temporal.PlainTime): string {
|
|
81
|
+
return time.toString();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function serializeZonedDateTime(zoned: Temporal.ZonedDateTime): string {
|
|
85
|
+
return zoned.toString();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function serializeDuration(duration: Temporal.Duration): string {
|
|
89
|
+
return duration.toString();
|
|
90
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timezone conversion utilities
|
|
3
|
+
*
|
|
4
|
+
* Functions for converting between timezones and working with ZonedDateTime
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Convert an instant to a zoned date/time in a specific timezone
|
|
11
|
+
*/
|
|
12
|
+
export function toTimezone(instant: Temporal.Instant, timezone: string): Temporal.ZonedDateTime {
|
|
13
|
+
return instant.toZonedDateTimeISO(timezone);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Convert a zoned date/time to a different timezone
|
|
18
|
+
*/
|
|
19
|
+
export function changeTimezone(zoned: Temporal.ZonedDateTime, timezone: string): Temporal.ZonedDateTime {
|
|
20
|
+
return zoned.withTimeZone(timezone);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Interpret PlainDateTime in a specific timezone, returning ZonedDateTime
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* const dt = Temporal.PlainDateTime.from('2026-01-24T15:30');
|
|
28
|
+
* const zoned = plainDateTimeToZonedDateTime(dt, 'America/Los_Angeles');
|
|
29
|
+
* // → 2026-01-24T15:30:00-08:00[America/Los_Angeles]
|
|
30
|
+
*/
|
|
31
|
+
export function plainDateTimeToZonedDateTime(dateTime: Temporal.PlainDateTime, timezone?: string): Temporal.ZonedDateTime {
|
|
32
|
+
const tz = timezone ?? Temporal.Now.timeZoneId();
|
|
33
|
+
return dateTime.toZonedDateTime(tz);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Interpret PlainDate as midnight in a specific timezone, returning ZonedDateTime
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* const date = Temporal.PlainDate.from('2026-01-24');
|
|
41
|
+
* const zoned = plainDateToZonedDateTime(date, 'America/Los_Angeles');
|
|
42
|
+
* // → 2026-01-24T00:00:00-08:00[America/Los_Angeles]
|
|
43
|
+
*/
|
|
44
|
+
export function plainDateToZonedDateTime(date: Temporal.PlainDate, timezone?: string): Temporal.ZonedDateTime {
|
|
45
|
+
const tz = timezone ?? Temporal.Now.timeZoneId();
|
|
46
|
+
return date.toPlainDateTime(Temporal.PlainTime.from("00:00")).toZonedDateTime(tz);
|
|
47
|
+
}
|