@datum-cloud/datum-ui 0.8.0 → 0.9.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.
Files changed (225) hide show
  1. package/dist/{adapter-context-NyGTDZYq.mjs → adapter-context-DemTWhel.mjs} +3 -3
  2. package/dist/alert/index.mjs +1 -1
  3. package/dist/{alert-BDj6od5I.mjs → alert-Cz56SqdN.mjs} +2 -2
  4. package/dist/app-navigation/index.mjs +2 -2
  5. package/dist/{app-navigation-rSOfo1KV.mjs → app-navigation-CnGkJvwF.mjs} +1 -1
  6. package/dist/autocomplete/index.mjs +1 -1
  7. package/dist/{autocomplete-DdbTQe6u.mjs → autocomplete-ChuPNldi.mjs} +6 -6
  8. package/dist/autosearch/index.mjs +12 -12
  9. package/dist/avatar-stack/index.mjs +1 -1
  10. package/dist/{avatar-stack-CDhlA1Nm.mjs → avatar-stack-WqoQ3l06.mjs} +1 -1
  11. package/dist/{button-D3RrsMfQ.mjs → button-Rw1xI-k9.mjs} +2 -2
  12. package/dist/calendar/index.mjs +1 -1
  13. package/dist/calendar-D_DriVHT.mjs +77 -0
  14. package/dist/{calendar-C-Hbf74r.mjs → calendar-Dwvq3Vu_.mjs} +6 -6
  15. package/dist/calendar-date-picker-CchHKodM.mjs +52 -0
  16. package/dist/chart/index.mjs +1 -1
  17. package/dist/{chart-CUa21ynK.mjs → chart-DgcOwkOh.mjs} +5 -5
  18. package/dist/code-editor/index.mjs +1 -1
  19. package/dist/{col-DISdGlqY.mjs → col-Q6yazFwL.mjs} +2 -1
  20. package/dist/components/base/button/button.d.ts.map +1 -1
  21. package/dist/components/features/calendar-date-picker/calendar-date-picker.d.ts +6 -5
  22. package/dist/components/features/calendar-date-picker/calendar-date-picker.d.ts.map +1 -1
  23. package/dist/components/features/calendar-date-picker/types.d.ts +19 -0
  24. package/dist/components/features/calendar-date-picker/types.d.ts.map +1 -1
  25. package/dist/components/features/code-editor/code-editor.d.ts +1 -1
  26. package/dist/components/features/code-editor/code-editor.d.ts.map +1 -1
  27. package/dist/components/features/code-editor/index.d.ts +1 -1
  28. package/dist/components/features/code-editor/index.d.ts.map +1 -1
  29. package/dist/components/features/code-editor/types.d.ts +25 -2
  30. package/dist/components/features/code-editor/types.d.ts.map +1 -1
  31. package/dist/components/features/data-table/core/client-provider.d.ts.map +1 -1
  32. package/dist/components/features/date-time-picker/date-time-picker.d.ts +12 -4
  33. package/dist/components/features/date-time-picker/date-time-picker.d.ts.map +1 -1
  34. package/dist/components/features/date-time-picker/types.d.ts +9 -0
  35. package/dist/components/features/date-time-picker/types.d.ts.map +1 -1
  36. package/dist/components/features/grid/components/col.d.ts +1 -1
  37. package/dist/components/features/grid/components/col.d.ts.map +1 -1
  38. package/dist/components/features/grid/components/row.d.ts +1 -1
  39. package/dist/components/features/grid/components/row.d.ts.map +1 -1
  40. package/dist/components/features/picker/components/calendar.d.ts +14 -0
  41. package/dist/components/features/picker/components/calendar.d.ts.map +1 -0
  42. package/dist/components/features/picker/components/content.d.ts +18 -0
  43. package/dist/components/features/picker/components/content.d.ts.map +1 -0
  44. package/dist/components/features/picker/components/context.d.ts +57 -0
  45. package/dist/components/features/picker/components/context.d.ts.map +1 -0
  46. package/dist/components/features/picker/components/footer.d.ts +35 -0
  47. package/dist/components/features/picker/components/footer.d.ts.map +1 -0
  48. package/dist/components/features/picker/components/index.d.ts +36 -0
  49. package/dist/components/features/picker/components/index.d.ts.map +1 -0
  50. package/dist/components/features/picker/components/presets.d.ts +9 -0
  51. package/dist/components/features/picker/components/presets.d.ts.map +1 -0
  52. package/dist/components/features/picker/components/root.d.ts +40 -0
  53. package/dist/components/features/picker/components/root.d.ts.map +1 -0
  54. package/dist/components/features/picker/components/time-input-field.d.ts +13 -0
  55. package/dist/components/features/picker/components/time-input-field.d.ts.map +1 -0
  56. package/dist/components/features/picker/components/time-input.d.ts +13 -0
  57. package/dist/components/features/picker/components/time-input.d.ts.map +1 -0
  58. package/dist/components/features/picker/components/timezone-indicator.d.ts +14 -0
  59. package/dist/components/features/picker/components/timezone-indicator.d.ts.map +1 -0
  60. package/dist/components/features/picker/components/timezone-select.d.ts +16 -0
  61. package/dist/components/features/picker/components/timezone-select.d.ts.map +1 -0
  62. package/dist/components/features/picker/components/trigger.d.ts +23 -0
  63. package/dist/components/features/picker/components/trigger.d.ts.map +1 -0
  64. package/dist/components/features/picker/hooks/index.d.ts +5 -0
  65. package/dist/components/features/picker/hooks/index.d.ts.map +1 -0
  66. package/dist/components/features/picker/hooks/use-keyboard-shortcuts.d.ts +25 -0
  67. package/dist/components/features/picker/hooks/use-keyboard-shortcuts.d.ts.map +1 -0
  68. package/dist/components/features/picker/hooks/use-picker-state.d.ts +51 -0
  69. package/dist/components/features/picker/hooks/use-picker-state.d.ts.map +1 -0
  70. package/dist/components/features/picker/hooks/use-presets.d.ts +24 -0
  71. package/dist/components/features/picker/hooks/use-presets.d.ts.map +1 -0
  72. package/dist/components/features/picker/hooks/use-time-slots.d.ts +21 -0
  73. package/dist/components/features/picker/hooks/use-time-slots.d.ts.map +1 -0
  74. package/dist/components/features/picker/index.d.ts +8 -0
  75. package/dist/components/features/picker/index.d.ts.map +1 -0
  76. package/dist/components/features/picker/internal/use-deprecation-warning.d.ts +9 -0
  77. package/dist/components/features/picker/internal/use-deprecation-warning.d.ts.map +1 -0
  78. package/dist/components/features/picker/presets/defaults.d.ts +16 -0
  79. package/dist/components/features/picker/presets/defaults.d.ts.map +1 -0
  80. package/dist/components/features/picker/presets/index.d.ts +2 -0
  81. package/dist/components/features/picker/presets/index.d.ts.map +1 -0
  82. package/dist/components/features/picker/types.d.ts +70 -0
  83. package/dist/components/features/picker/types.d.ts.map +1 -0
  84. package/dist/components/features/picker/utils/commit.d.ts +16 -0
  85. package/dist/components/features/picker/utils/commit.d.ts.map +1 -0
  86. package/dist/components/features/picker/utils/compare.d.ts +12 -0
  87. package/dist/components/features/picker/utils/compare.d.ts.map +1 -0
  88. package/dist/components/features/picker/utils/format-value.d.ts +25 -0
  89. package/dist/components/features/picker/utils/format-value.d.ts.map +1 -0
  90. package/dist/components/features/picker/utils/format.d.ts +38 -0
  91. package/dist/components/features/picker/utils/format.d.ts.map +1 -0
  92. package/dist/components/features/picker/utils/index.d.ts +7 -0
  93. package/dist/components/features/picker/utils/index.d.ts.map +1 -0
  94. package/dist/components/features/picker/utils/range.d.ts +26 -0
  95. package/dist/components/features/picker/utils/range.d.ts.map +1 -0
  96. package/dist/components/features/picker/utils/timezone.d.ts +38 -0
  97. package/dist/components/features/picker/utils/timezone.d.ts.map +1 -0
  98. package/dist/components/features/picker/wrappers/date-picker.d.ts +10 -0
  99. package/dist/components/features/picker/wrappers/date-picker.d.ts.map +1 -0
  100. package/dist/components/features/picker/wrappers/date-range-picker.d.ts +16 -0
  101. package/dist/components/features/picker/wrappers/date-range-picker.d.ts.map +1 -0
  102. package/dist/components/features/picker/wrappers/date-range-time-picker.d.ts +16 -0
  103. package/dist/components/features/picker/wrappers/date-range-time-picker.d.ts.map +1 -0
  104. package/dist/components/features/picker/wrappers/date-time-picker.d.ts +20 -0
  105. package/dist/components/features/picker/wrappers/date-time-picker.d.ts.map +1 -0
  106. package/dist/components/features/picker/wrappers/date-time-range-picker.d.ts +16 -0
  107. package/dist/components/features/picker/wrappers/date-time-range-picker.d.ts.map +1 -0
  108. package/dist/components/features/picker/wrappers/index.d.ts +16 -0
  109. package/dist/components/features/picker/wrappers/index.d.ts.map +1 -0
  110. package/dist/components/features/picker/wrappers/internal/default-footer.d.ts +7 -0
  111. package/dist/components/features/picker/wrappers/internal/default-footer.d.ts.map +1 -0
  112. package/dist/components/features/picker/wrappers/time-picker.d.ts +14 -0
  113. package/dist/components/features/picker/wrappers/time-picker.d.ts.map +1 -0
  114. package/dist/components/features/picker/wrappers/time-range-picker.d.ts +16 -0
  115. package/dist/components/features/picker/wrappers/time-range-picker.d.ts.map +1 -0
  116. package/dist/components/features/picker/wrappers/types.d.ts +118 -0
  117. package/dist/components/features/picker/wrappers/types.d.ts.map +1 -0
  118. package/dist/components/features/tag-input/tag-input.d.ts +1 -1
  119. package/dist/components/features/tag-input/tag-input.d.ts.map +1 -1
  120. package/dist/components/features/time-picker/time-picker.d.ts +5 -0
  121. package/dist/components/features/time-picker/time-picker.d.ts.map +1 -1
  122. package/dist/components/features/time-picker/types.d.ts +5 -0
  123. package/dist/components/features/time-picker/types.d.ts.map +1 -1
  124. package/dist/components/features/time-range-picker/index.d.ts +5 -0
  125. package/dist/components/features/time-range-picker/index.d.ts.map +1 -1
  126. package/dist/components/features/time-range-picker/time-range-picker.d.ts +19 -14
  127. package/dist/components/features/time-range-picker/time-range-picker.d.ts.map +1 -1
  128. package/dist/components/features/time-range-picker/types.d.ts +20 -0
  129. package/dist/components/features/time-range-picker/types.d.ts.map +1 -1
  130. package/dist/data-table/index.mjs +22 -15
  131. package/dist/date-picker/index.mjs +2 -2
  132. package/dist/date-range-picker-DNZh06zo.mjs +77 -0
  133. package/dist/date-time-picker/index.mjs +1 -1
  134. package/dist/date-time-picker-BiobghTJ.mjs +43 -0
  135. package/dist/date-time-picker-C0fF7s_e.mjs +109 -0
  136. package/dist/date-time-range-picker-cW4rbuFn.mjs +82 -0
  137. package/dist/default-footer-DbpemJVl.mjs +1037 -0
  138. package/dist/dropzone/index.mjs +1 -1
  139. package/dist/{dropzone-Bt0plEuw.mjs → dropzone-DXH0xHso.mjs} +1 -1
  140. package/dist/empty-content/index.mjs +1 -1
  141. package/dist/form/adapters/conform/index.mjs +19 -19
  142. package/dist/form/adapters/rhf/index.mjs +18 -18
  143. package/dist/form/index.mjs +2 -2
  144. package/dist/form/stepper/index.mjs +23 -23
  145. package/dist/{form-CCNN9VtJ.mjs → form-DvgKb4s4.mjs} +49 -42
  146. package/dist/{form-context-CeKyvO-A.mjs → form-context-0usxTumz.mjs} +3 -3
  147. package/dist/grid/index.mjs +1 -1
  148. package/dist/hooks/index.mjs +2 -2
  149. package/dist/index.mjs +41 -41
  150. package/dist/input-group/index.mjs +1 -1
  151. package/dist/{input-group-DDtz-RT7.mjs → input-group-CeMsDhOk.mjs} +1 -1
  152. package/dist/input-number/index.mjs +1 -1
  153. package/dist/{input-number-Diu-C6d5.mjs → input-number-BU4nno8J.mjs} +1 -1
  154. package/dist/loader-overlay/index.mjs +1 -1
  155. package/dist/map/index.mjs +1 -1
  156. package/dist/{map-qo7bY_g_.mjs → map-l7iwhEwM.mjs} +24 -27
  157. package/dist/more-actions/index.mjs +2 -2
  158. package/dist/{more-actions-Ca5qqd0H.mjs → more-actions-Baqor0yr.mjs} +2 -2
  159. package/dist/multi-select/index.mjs +1 -1
  160. package/dist/{multi-select-DM_dxnSV.mjs → multi-select-BHmtkQIi.mjs} +6 -6
  161. package/dist/nprogress/nprogress.css +59 -11
  162. package/dist/page-title/index.mjs +1 -1
  163. package/dist/picker/index.mjs +273 -0
  164. package/dist/rich-text-editor/index.mjs +1 -1
  165. package/dist/{rich-text-editor-CQH_U4T5.mjs → rich-text-editor-aWuLRaj9.mjs} +1 -1
  166. package/dist/select/index.mjs +1 -1
  167. package/dist/select-By1ZqPgr.mjs +94 -0
  168. package/dist/select-Ck0szhAH.mjs +73 -0
  169. package/dist/sidebar/index.mjs +1 -1
  170. package/dist/{sidebar-B8LQJiNI.mjs → sidebar-CUnFkH7o.mjs} +32 -32
  171. package/dist/skeleton/index.mjs +1 -1
  172. package/dist/{skeleton-D4HOEiOZ.mjs → skeleton-CxAhQT6T.mjs} +1 -1
  173. package/dist/stepper/index.mjs +1 -1
  174. package/dist/{stepper-Beb-zbdL.mjs → stepper-adDdiJMx.mjs} +7 -7
  175. package/dist/styles/root.css +4 -2
  176. package/dist/styles/shadcn/shadcn.css +212 -213
  177. package/dist/switch/index.mjs +1 -1
  178. package/dist/table/index.mjs +1 -1
  179. package/dist/tag-input/index.mjs +1 -1
  180. package/dist/{tag-input-Bf4GMptp.mjs → tag-input-D3NYy9-_.mjs} +1 -1
  181. package/dist/task-queue/index.mjs +1 -1
  182. package/dist/{task-queue-dropdown-D-LncEWm.mjs → task-queue-dropdown-t9KKIL6j.mjs} +3 -3
  183. package/dist/textarea/index.mjs +1 -1
  184. package/dist/theme/index.mjs +1 -1
  185. package/dist/{themes-CAiN4b6G.mjs → themes-08-znmBI.mjs} +16 -16
  186. package/dist/time-input-9LdNL0Us.mjs +196 -0
  187. package/dist/time-picker/index.mjs +1 -1
  188. package/dist/time-picker-B-vpUClR.mjs +45 -0
  189. package/dist/time-picker-C840fLl6.mjs +49 -0
  190. package/dist/timezone-indicator-COoeMKKs.mjs +27 -0
  191. package/dist/{to-api-format-CgKcC9SK.mjs → to-api-format-CNqrcrz7.mjs} +58 -243
  192. package/dist/toast/index.mjs +2 -2
  193. package/dist/{toast-DDdLgY53.mjs → toast-Bd5Kk7BB.mjs} +1 -1
  194. package/dist/tooltip/index.mjs +1 -1
  195. package/dist/transfer/index.mjs +1 -1
  196. package/dist/{transfer-CoGPwOc6.mjs → transfer-CuJGL420.mjs} +6 -6
  197. package/dist/{types-CKIe2WlV.mjs → types-DvMIxilw.mjs} +9 -3
  198. package/dist/typography/index.mjs +1 -1
  199. package/dist/{typography-TRKP_CLT.mjs → typography-CBwfg-vz.mjs} +5 -5
  200. package/dist/{use-copy-to-clipboard-D7KyLIAt.mjs → use-copy-to-clipboard-ZHDvfV3W.mjs} +1 -1
  201. package/dist/use-deprecation-warning-Dy_DOyLu.mjs +20 -0
  202. package/dist/{use-display-touched-C-afz17j.mjs → use-display-touched-DpX3fsOl.mjs} +5 -5
  203. package/dist/{use-option-picker-BXQOfyrK.mjs → use-option-picker-DV5O68eV.mjs} +1 -1
  204. package/dist/utils/index.mjs +1 -1
  205. package/dist/visually-hidden/index.mjs +1 -1
  206. package/package.json +30 -25
  207. package/dist/calendar-date-picker-BaykEs6j.mjs +0 -749
  208. package/dist/date-time-picker-DKOxrhmc.mjs +0 -193
  209. package/dist/select-zxwykvQn.mjs +0 -163
  210. package/dist/time-picker-BZF5jbF6.mjs +0 -99
  211. package/dist/use-date-constraints-R3H4lIoT.mjs +0 -41
  212. /package/dist/{action-row-DnhBhMtt.mjs → action-row-ZaMsJ8cP.mjs} +0 -0
  213. /package/dist/{empty-content-CBh5bbtJ.mjs → empty-content-DTk_lwnh.mjs} +0 -0
  214. /package/dist/{hooks-DQXVwbrs.mjs → hooks-CnphNpwz.mjs} +0 -0
  215. /package/dist/{loader-overlay-C2WDla6V.mjs → loader-overlay-B3YEoOFJ.mjs} +0 -0
  216. /package/dist/{map-leaflet-imports-yWwH4CHB.mjs → map-leaflet-imports-Ix8F7E8j.mjs} +0 -0
  217. /package/dist/{page-title-ChLiv6gB.mjs → page-title-D5JhOpxV.mjs} +0 -0
  218. /package/dist/{skeleton-D2xuJdE1.mjs → skeleton-B1NOdZGM.mjs} +0 -0
  219. /package/dist/{switch-DcSF42Kc.mjs → switch-BcQaR1Bp.mjs} +0 -0
  220. /package/dist/{table-DWGT4cqh.mjs → table-ouGk4Vxe.mjs} +0 -0
  221. /package/dist/{textarea-BoChBcFz.mjs → textarea-q-k7DYwi.mjs} +0 -0
  222. /package/dist/{toast-D5XD7goD.mjs → toast-BWol1pyV.mjs} +0 -0
  223. /package/dist/{tooltip-a7NTDCWw.mjs → tooltip-CHuzXR_O.mjs} +0 -0
  224. /package/dist/{utils-BwB1mIdZ.mjs → utils-bUueJ2r9.mjs} +0 -0
  225. /package/dist/{visuallyhidden-BHOPczmW.mjs → visuallyhidden-CoIJOJ9z.mjs} +0 -0
@@ -0,0 +1,1037 @@
1
+ import { t as cn } from "./cn-D2KYQ917.mjs";
2
+ import { t as Button } from "./button-BllvE9Lm.mjs";
3
+ import { t as Button$1 } from "./button-Rw1xI-k9.mjs";
4
+ import { t as useBreakpoint } from "./use-breakpoint-DGcVmB3c.mjs";
5
+ import { t as ResponsivePopover } from "./responsive-popover-D-t9bxSN.mjs";
6
+ import { CalendarIcon, X } from "lucide-react";
7
+ import { createContext, useCallback, useContext, useEffect, useMemo, useReducer, useRef } from "react";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ import { endOfDay, endOfMonth, endOfWeek, endOfYear, format, startOfDay, startOfMonth, startOfWeek, startOfYear, subDays, subHours, subMinutes } from "date-fns";
10
+ import { formatInTimeZone, fromZonedTime, toZonedTime } from "date-fns-tz";
11
+ //#region src/components/base/date-picker/use-date-constraints.ts
12
+ function useDateConstraints({ minDate, maxDate, disablePast, disableFuture }) {
13
+ const today = useMemo(() => /* @__PURE__ */ new Date(), []);
14
+ const effectiveMinDate = useMemo(() => {
15
+ let min = minDate;
16
+ if (disablePast) {
17
+ const todayStart = startOfDay(today);
18
+ min = min ? min > todayStart ? min : todayStart : todayStart;
19
+ }
20
+ return min;
21
+ }, [
22
+ minDate,
23
+ disablePast,
24
+ today
25
+ ]);
26
+ const effectiveMaxDate = useMemo(() => {
27
+ let max = maxDate;
28
+ if (disableFuture) {
29
+ const todayEnd = endOfDay(today);
30
+ max = max ? max < todayEnd ? max : todayEnd : todayEnd;
31
+ }
32
+ return max;
33
+ }, [
34
+ maxDate,
35
+ disableFuture,
36
+ today
37
+ ]);
38
+ return {
39
+ effectiveMinDate,
40
+ effectiveMaxDate,
41
+ isDateDisabled: useCallback((date) => {
42
+ if (effectiveMinDate && date < effectiveMinDate) return true;
43
+ if (effectiveMaxDate && date > effectiveMaxDate) return true;
44
+ return false;
45
+ }, [effectiveMinDate, effectiveMaxDate])
46
+ };
47
+ }
48
+ //#endregion
49
+ //#region src/components/features/picker/types.ts
50
+ const RANGE_MODES = [
51
+ "date-range",
52
+ "time-range",
53
+ "datetime-range",
54
+ "date-range-time"
55
+ ];
56
+ const TIME_BEARING_MODES = [
57
+ "time",
58
+ "time-range",
59
+ "datetime",
60
+ "datetime-range",
61
+ "date-range-time"
62
+ ];
63
+ const DATE_BEARING_MODES = [
64
+ "date",
65
+ "date-range",
66
+ "datetime",
67
+ "datetime-range",
68
+ "date-range-time"
69
+ ];
70
+ function isRangeMode(mode) {
71
+ return RANGE_MODES.includes(mode);
72
+ }
73
+ function isTimeBearingMode(mode) {
74
+ return TIME_BEARING_MODES.includes(mode);
75
+ }
76
+ function isDateBearingMode(mode) {
77
+ return DATE_BEARING_MODES.includes(mode);
78
+ }
79
+ const ALLOWED_STEPS = [
80
+ 1,
81
+ 5,
82
+ 10,
83
+ 15,
84
+ 30,
85
+ 60
86
+ ];
87
+ //#endregion
88
+ //#region src/components/features/picker/utils/timezone.ts
89
+ /** Browser-detected IANA timezone, falling back to UTC if unavailable. */
90
+ function getBrowserTimezone() {
91
+ try {
92
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
93
+ } catch {
94
+ return "UTC";
95
+ }
96
+ }
97
+ /**
98
+ * UTC offset for a timezone in `±HH:MM` format.
99
+ * Returns `+00:00` for UTC or any timezone the runtime cannot resolve.
100
+ */
101
+ function getTimezoneOffset(timezone) {
102
+ try {
103
+ const offsetPart = new Intl.DateTimeFormat("en-US", {
104
+ timeZone: timezone,
105
+ timeZoneName: "shortOffset"
106
+ }).formatToParts(/* @__PURE__ */ new Date()).find((p) => p.type === "timeZoneName");
107
+ if (!offsetPart) return "+00:00";
108
+ const match = /GMT([+-])(\d+)(?::(\d+))?/.exec(offsetPart.value);
109
+ if (match) return `${match[1]}${match[2].padStart(2, "0")}:${match[3]?.padStart(2, "0") ?? "00"}`;
110
+ if (offsetPart.value === "GMT") return "+00:00";
111
+ return "+00:00";
112
+ } catch {
113
+ return "+00:00";
114
+ }
115
+ }
116
+ /**
117
+ * Human-readable timezone label.
118
+ * Example: `'Asia/Jakarta'` → `'Asia/Jakarta (UTC+07:00)'`.
119
+ */
120
+ function formatTimezoneLabel(timezone) {
121
+ const offset = getTimezoneOffset(timezone);
122
+ return `${timezone.replace(/_/g, " ")} (UTC${offset})`;
123
+ }
124
+ /**
125
+ * Convert a UTC ISO string to a Date object whose wall-clock components
126
+ * (year/month/day/hours/minutes) reflect the value as seen in `timezone`.
127
+ *
128
+ * Use this to feed Calendar / time slot UI that reads `getHours()` etc.
129
+ *
130
+ * INVARIANT: `zonedDateToIso(isoToZonedDate(iso, tz), tz) === iso` for any
131
+ * valid ISO and tz.
132
+ */
133
+ function isoToZonedDate(iso, timezone) {
134
+ return toZonedTime(new Date(iso), timezone);
135
+ }
136
+ /**
137
+ * Date-input variant of `isoToZonedDate`. Skips the ISO round-trip when the
138
+ * caller already has a Date instance (e.g. preset `getRange(tz)` results).
139
+ */
140
+ function dateToZoned(date, timezone) {
141
+ return toZonedTime(date, timezone);
142
+ }
143
+ /**
144
+ * Inverse of `isoToZonedDate`. Treat the Date's wall-clock components
145
+ * as `timezone`-local and convert back to a UTC ISO instant.
146
+ */
147
+ function zonedDateToIso(date, timezone) {
148
+ return fromZonedTime(date, timezone).toISOString();
149
+ }
150
+ /**
151
+ * Format a UTC ISO string in the given timezone using a date-fns format pattern.
152
+ * Returns empty string if the input is not a valid date.
153
+ */
154
+ function formatInTimezone(iso, timezone, pattern) {
155
+ try {
156
+ const d = new Date(iso);
157
+ if (Number.isNaN(d.getTime())) return "";
158
+ return formatInTimeZone(d, timezone, pattern);
159
+ } catch {
160
+ return "";
161
+ }
162
+ }
163
+ //#endregion
164
+ //#region src/components/features/picker/components/context.ts
165
+ const PickerContext = createContext(null);
166
+ /** Throws when used outside `<Picker.Root>` — enforces compound-component discipline. */
167
+ function usePickerContext() {
168
+ const ctx = useContext(PickerContext);
169
+ if (!ctx) throw new Error("Picker primitives must be rendered inside <Picker.Root>");
170
+ return ctx;
171
+ }
172
+ //#endregion
173
+ //#region src/components/features/picker/components/content.tsx
174
+ function PickerContent({ trigger, sheetTitle, sheetDescription, footer, align = "start", side = "bottom", modal, contentClassName, children }) {
175
+ const { state, actions, effectiveCommit, responsive, breakpoint } = usePickerContext();
176
+ const isMobileSheet = responsive && breakpoint === "mobile";
177
+ const desktopFooter = !isMobileSheet && effectiveCommit === "apply" ? footer : null;
178
+ const sheetFooter = isMobileSheet ? footer : void 0;
179
+ return /* @__PURE__ */ jsx(ResponsivePopover, {
180
+ open: state.open,
181
+ onOpenChange: (open) => open ? actions.open() : actions.close(),
182
+ trigger,
183
+ sheetTitle,
184
+ sheetDescription,
185
+ sheetFooter,
186
+ align,
187
+ side,
188
+ modal,
189
+ responsive,
190
+ contentClassName: cn("w-auto", contentClassName),
191
+ children: /* @__PURE__ */ jsxs("div", {
192
+ className: "flex flex-col",
193
+ children: [/* @__PURE__ */ jsx("div", {
194
+ className: "flex flex-col",
195
+ children
196
+ }), desktopFooter && /* @__PURE__ */ jsx("div", {
197
+ className: "border-t p-3",
198
+ children: desktopFooter
199
+ })]
200
+ })
201
+ });
202
+ }
203
+ PickerContent.displayName = "Picker.Content";
204
+ //#endregion
205
+ //#region src/components/features/picker/hooks/use-keyboard-shortcuts.ts
206
+ /**
207
+ * Bind preset keyboard shortcuts to a scoped root element (NOT document).
208
+ *
209
+ * Why root-scoped: binding on `document` causes presets to fire when the
210
+ * user is typing in any input on the page — a well-known bug in the legacy
211
+ * TimeRangePicker. Scoping to the picker's root element fixes it.
212
+ *
213
+ * Modifier keys (Ctrl, Meta, Alt) are ignored to avoid colliding with
214
+ * browser/OS shortcuts.
215
+ */
216
+ function useKeyboardShortcuts({ rootRef, presets, onSelect, enabled }) {
217
+ useEffect(() => {
218
+ if (!enabled) return;
219
+ const root = rootRef.current;
220
+ if (!root) return;
221
+ function handler(e) {
222
+ if (e.ctrlKey || e.metaKey || e.altKey) return;
223
+ const matched = presets.find((p) => p.shortcut && p.shortcut.toLowerCase() === e.key.toLowerCase());
224
+ if (matched) {
225
+ e.preventDefault();
226
+ onSelect(matched);
227
+ }
228
+ }
229
+ root.addEventListener("keydown", handler);
230
+ return () => root.removeEventListener("keydown", handler);
231
+ }, [
232
+ rootRef,
233
+ presets,
234
+ onSelect,
235
+ enabled
236
+ ]);
237
+ }
238
+ //#endregion
239
+ //#region src/components/features/picker/utils/commit.ts
240
+ /**
241
+ * Resolves the effective commit semantics for a picker.
242
+ *
243
+ * - Date + time combined modes (`datetime` / `datetime-range` /
244
+ * `date-range-time`) **always** commit on Apply, regardless of the
245
+ * caller-supplied override. Picking a date alone shouldn't publish a
246
+ * value with an unconfirmed 00:00 time — the user needs to set the
247
+ * time too, then explicitly Apply. The wrappers don't expose a
248
+ * `commit` prop for these modes; primitive consumers can still pass
249
+ * one through `Picker.Root` but it's silently overridden here.
250
+ * - Range modes (`date-range`, `time-range`) default to `'apply'`.
251
+ * - Everything else defaults to `'immediate'`.
252
+ */
253
+ function resolveCommit(mode, commit) {
254
+ if (isDateBearingMode(mode) && isTimeBearingMode(mode)) return "apply";
255
+ return commit ?? (isRangeMode(mode) ? "apply" : "immediate");
256
+ }
257
+ //#endregion
258
+ //#region src/components/features/picker/utils/format.ts
259
+ /**
260
+ * Format a Date as YYYY-MM-DD using local-time components.
261
+ *
262
+ * Note: this is the consumer-friendly format for date-only modes.
263
+ * It does NOT round-trip through timezones; that's intentional —
264
+ * a calendar day has no timezone.
265
+ */
266
+ function dateToYYYYMMDD(d) {
267
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
268
+ }
269
+ /**
270
+ * Extract `"HH:mm"` from a Date using its wall-clock components.
271
+ *
272
+ * Caller is responsible for ensuring those components reflect the
273
+ * intended timezone (typically via `dateToZoned` / `isoToZonedDate`
274
+ * upstream). Pads single-digit hour/minute to two characters.
275
+ */
276
+ function dateToHHmm(d) {
277
+ return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
278
+ }
279
+ const TIME_REGEX = /^(\d{2}):(\d{2})$/;
280
+ /**
281
+ * Strict HH:mm validation. Rejects single-digit components,
282
+ * out-of-range hours/minutes, and any non-numeric content.
283
+ */
284
+ function isValidTimeString(s) {
285
+ const match = TIME_REGEX.exec(s);
286
+ if (!match) return false;
287
+ const h = Number(match[1]);
288
+ const m = Number(match[2]);
289
+ return h >= 0 && h <= 23 && m >= 0 && m <= 59;
290
+ }
291
+ /**
292
+ * Parse a HH:mm string. Returns null for invalid input.
293
+ */
294
+ function parseTimeString(s) {
295
+ if (!isValidTimeString(s)) return null;
296
+ const match = TIME_REGEX.exec(s);
297
+ return {
298
+ h: Number(match[1]),
299
+ m: Number(match[2])
300
+ };
301
+ }
302
+ /**
303
+ * Format a HH:mm string for user display, respecting hour cycle.
304
+ *
305
+ * - `'24'`: returns the input as-is (`"14:30"`)
306
+ * - `'12'`: returns `"2:30 PM"` style with AM/PM and no leading zero on hour
307
+ *
308
+ * Returns empty string for invalid input — callers should validate first.
309
+ */
310
+ function formatTimeLabel(time, hourCycle) {
311
+ const parsed = parseTimeString(time);
312
+ if (!parsed) return "";
313
+ if (hourCycle === "24") return time;
314
+ const period = parsed.h >= 12 ? "PM" : "AM";
315
+ return `${parsed.h === 0 ? 12 : parsed.h > 12 ? parsed.h - 12 : parsed.h}:${String(parsed.m).padStart(2, "0")} ${period}`;
316
+ }
317
+ //#endregion
318
+ //#region src/components/features/picker/hooks/use-picker-state.ts
319
+ function reducer(state, action) {
320
+ switch (action.type) {
321
+ case "SET_SINGLE_DATE":
322
+ case "SET_SINGLE_TIME":
323
+ case "SET_SINGLE_DATETIME":
324
+ case "SET_RANGE":
325
+ case "SET_TIME_RANGE":
326
+ case "SET_DATETIME_RANGE": return {
327
+ ...state,
328
+ pendingValue: action.payload,
329
+ selectedPresetKey: void 0
330
+ };
331
+ case "SELECT_PRESET": return {
332
+ ...state,
333
+ pendingValue: action.payload.pendingValue,
334
+ selectedPresetKey: action.payload.key,
335
+ monthFrom: action.payload.monthFrom,
336
+ monthTo: action.payload.monthTo
337
+ };
338
+ case "SET_MONTH_FROM": return {
339
+ ...state,
340
+ monthFrom: action.payload
341
+ };
342
+ case "SET_MONTH_TO": return {
343
+ ...state,
344
+ monthTo: action.payload
345
+ };
346
+ case "OPEN": return {
347
+ ...state,
348
+ open: true
349
+ };
350
+ case "CLOSE": return {
351
+ ...state,
352
+ open: false
353
+ };
354
+ case "RESET_TO": return {
355
+ ...state,
356
+ pendingValue: action.payload,
357
+ selectedPresetKey: void 0
358
+ };
359
+ case "CLEAR_PRESET": return {
360
+ ...state,
361
+ selectedPresetKey: void 0
362
+ };
363
+ default: return state;
364
+ }
365
+ }
366
+ /**
367
+ * Convert a preset's `{ from: Date, to: Date }` (real UTC instants) into the
368
+ * value shape that the picker's `mode` actually emits.
369
+ *
370
+ * - `date-range`: pass through (Dates).
371
+ * - `datetime-range` / `date-range-time`: `.toISOString()` — instants already match.
372
+ * - `time-range`: extract wall-clock `HH:mm` in the active timezone.
373
+ * - others (date / time / datetime): no preset value — return `null`.
374
+ */
375
+ function presetValueForMode(mode, range, key, timezone) {
376
+ switch (mode) {
377
+ case "date-range": return {
378
+ from: range.from,
379
+ to: range.to,
380
+ preset: key
381
+ };
382
+ case "datetime-range":
383
+ case "date-range-time": return {
384
+ from: range.from.toISOString(),
385
+ to: range.to.toISOString(),
386
+ preset: key
387
+ };
388
+ case "time-range": return {
389
+ from: dateToHHmm(dateToZoned(range.from, timezone)),
390
+ to: dateToHHmm(dateToZoned(range.to, timezone)),
391
+ preset: key
392
+ };
393
+ default: return null;
394
+ }
395
+ }
396
+ function usePickerState({ mode, value, onChange, commit, timezone }) {
397
+ const effectiveCommit = resolveCommit(mode, commit);
398
+ const [state, dispatch] = useReducer(reducer, void 0, () => ({
399
+ pendingValue: value,
400
+ open: false,
401
+ selectedPresetKey: void 0,
402
+ monthFrom: void 0,
403
+ monthTo: void 0
404
+ }));
405
+ useEffect(() => {
406
+ dispatch({
407
+ type: "RESET_TO",
408
+ payload: value
409
+ });
410
+ }, [value]);
411
+ const onChangeRef = useRef(onChange);
412
+ const valueRef = useRef(value);
413
+ const pendingValueRef = useRef(state.pendingValue);
414
+ const effectiveCommitRef = useRef(effectiveCommit);
415
+ const modeRef = useRef(mode);
416
+ const timezoneRef = useRef(timezone);
417
+ useEffect(() => {
418
+ onChangeRef.current = onChange;
419
+ valueRef.current = value;
420
+ pendingValueRef.current = state.pendingValue;
421
+ effectiveCommitRef.current = effectiveCommit;
422
+ modeRef.current = mode;
423
+ timezoneRef.current = timezone;
424
+ });
425
+ const commitValue = useCallback((next) => {
426
+ onChangeRef.current(next);
427
+ }, []);
428
+ const actions = useMemo(() => {
429
+ const setSingleValue = (action) => {
430
+ dispatch(action);
431
+ if (effectiveCommitRef.current === "immediate") {
432
+ commitValue(action.payload);
433
+ dispatch({ type: "CLOSE" });
434
+ }
435
+ };
436
+ return {
437
+ setSingleDate: (d) => setSingleValue({
438
+ type: "SET_SINGLE_DATE",
439
+ payload: d
440
+ }),
441
+ setSingleTime: (t) => setSingleValue({
442
+ type: "SET_SINGLE_TIME",
443
+ payload: t
444
+ }),
445
+ setSingleDatetime: (iso) => setSingleValue({
446
+ type: "SET_SINGLE_DATETIME",
447
+ payload: iso
448
+ }),
449
+ setRange: (r) => {
450
+ dispatch({
451
+ type: "SET_RANGE",
452
+ payload: r
453
+ });
454
+ if (effectiveCommitRef.current === "immediate" && r !== null) {
455
+ commitValue(r);
456
+ dispatch({ type: "CLOSE" });
457
+ }
458
+ },
459
+ setTimeRange: (r) => {
460
+ dispatch({
461
+ type: "SET_TIME_RANGE",
462
+ payload: r
463
+ });
464
+ if (effectiveCommitRef.current === "immediate" && r !== null) {
465
+ commitValue(r);
466
+ dispatch({ type: "CLOSE" });
467
+ }
468
+ },
469
+ setDatetimeRange: (r) => {
470
+ dispatch({
471
+ type: "SET_DATETIME_RANGE",
472
+ payload: r
473
+ });
474
+ if (effectiveCommitRef.current === "immediate" && r !== null) {
475
+ commitValue(r);
476
+ dispatch({ type: "CLOSE" });
477
+ }
478
+ },
479
+ selectPreset: (range, key) => {
480
+ const pendingValue = presetValueForMode(modeRef.current, range, key, timezoneRef.current);
481
+ dispatch({
482
+ type: "SELECT_PRESET",
483
+ payload: {
484
+ pendingValue,
485
+ key,
486
+ monthFrom: range.from,
487
+ monthTo: range.to
488
+ }
489
+ });
490
+ commitValue(pendingValue);
491
+ dispatch({ type: "CLOSE" });
492
+ },
493
+ setMonthFrom: (d) => dispatch({
494
+ type: "SET_MONTH_FROM",
495
+ payload: d
496
+ }),
497
+ setMonthTo: (d) => dispatch({
498
+ type: "SET_MONTH_TO",
499
+ payload: d
500
+ }),
501
+ open: () => dispatch({ type: "OPEN" }),
502
+ close: () => dispatch({ type: "CLOSE" }),
503
+ apply: () => {
504
+ commitValue(pendingValueRef.current);
505
+ dispatch({ type: "CLOSE" });
506
+ },
507
+ reset: () => dispatch({
508
+ type: "RESET_TO",
509
+ payload: valueRef.current
510
+ }),
511
+ cancel: () => {
512
+ dispatch({
513
+ type: "RESET_TO",
514
+ payload: valueRef.current
515
+ });
516
+ dispatch({ type: "CLOSE" });
517
+ },
518
+ clear: () => {
519
+ dispatch({
520
+ type: "RESET_TO",
521
+ payload: null
522
+ });
523
+ commitValue(null);
524
+ }
525
+ };
526
+ }, [commitValue]);
527
+ return {
528
+ state: {
529
+ pendingValue: state.pendingValue,
530
+ open: state.open,
531
+ selectedPresetKey: state.selectedPresetKey,
532
+ monthFrom: state.monthFrom,
533
+ monthTo: state.monthTo
534
+ },
535
+ actions
536
+ };
537
+ }
538
+ //#endregion
539
+ //#region src/components/features/picker/presets/defaults.ts
540
+ function startOfDayInTz(d, tz) {
541
+ const zoned = toZonedTime(d, tz);
542
+ zoned.setHours(0, 0, 0, 0);
543
+ return fromZonedTime(zoned, tz);
544
+ }
545
+ function endOfDayInTz(d, tz) {
546
+ const zoned = toZonedTime(d, tz);
547
+ zoned.setHours(23, 59, 59, 999);
548
+ return fromZonedTime(zoned, tz);
549
+ }
550
+ /**
551
+ * Date-bearing presets. Operate on calendar days; ignore time-of-day.
552
+ */
553
+ const DATE_PRESETS = [
554
+ {
555
+ key: "today",
556
+ label: "Today",
557
+ shortcut: "D",
558
+ getRange: (tz) => ({
559
+ from: startOfDayInTz(/* @__PURE__ */ new Date(), tz),
560
+ to: endOfDayInTz(/* @__PURE__ */ new Date(), tz)
561
+ })
562
+ },
563
+ {
564
+ key: "yesterday",
565
+ label: "Yesterday",
566
+ shortcut: "Y",
567
+ getRange: (tz) => {
568
+ const y = subDays(/* @__PURE__ */ new Date(), 1);
569
+ return {
570
+ from: startOfDayInTz(y, tz),
571
+ to: endOfDayInTz(y, tz)
572
+ };
573
+ }
574
+ },
575
+ {
576
+ key: "this-week",
577
+ label: "This Week",
578
+ getRange: () => {
579
+ const now = /* @__PURE__ */ new Date();
580
+ return {
581
+ from: startOfWeek(now, { weekStartsOn: 1 }),
582
+ to: endOfWeek(now, { weekStartsOn: 1 })
583
+ };
584
+ }
585
+ },
586
+ {
587
+ key: "last-7-days",
588
+ label: "Last 7 Days",
589
+ shortcut: "W",
590
+ getRange: (tz) => ({
591
+ from: startOfDayInTz(subDays(/* @__PURE__ */ new Date(), 6), tz),
592
+ to: endOfDayInTz(/* @__PURE__ */ new Date(), tz)
593
+ })
594
+ },
595
+ {
596
+ key: "this-month",
597
+ label: "This Month",
598
+ getRange: () => {
599
+ const now = /* @__PURE__ */ new Date();
600
+ return {
601
+ from: startOfMonth(now),
602
+ to: endOfMonth(now)
603
+ };
604
+ }
605
+ },
606
+ {
607
+ key: "last-30-days",
608
+ label: "Last 30 Days",
609
+ shortcut: "M",
610
+ getRange: (tz) => ({
611
+ from: startOfDayInTz(subDays(/* @__PURE__ */ new Date(), 29), tz),
612
+ to: endOfDayInTz(/* @__PURE__ */ new Date(), tz)
613
+ })
614
+ },
615
+ {
616
+ key: "this-year",
617
+ label: "This Year",
618
+ getRange: () => {
619
+ const now = /* @__PURE__ */ new Date();
620
+ return {
621
+ from: startOfYear(now),
622
+ to: endOfYear(now)
623
+ };
624
+ }
625
+ }
626
+ ];
627
+ /**
628
+ * Datetime-bearing presets. Use "now" as the upper bound for past windows
629
+ * (so the picker doesn't propose future timestamps).
630
+ */
631
+ const DATETIME_PRESETS = [
632
+ {
633
+ key: "last-15m",
634
+ label: "Last 15 minutes",
635
+ shortcut: "1",
636
+ getRange: () => {
637
+ const now = /* @__PURE__ */ new Date();
638
+ return {
639
+ from: subMinutes(now, 15),
640
+ to: now
641
+ };
642
+ }
643
+ },
644
+ {
645
+ key: "last-1h",
646
+ label: "Last hour",
647
+ shortcut: "H",
648
+ getRange: () => {
649
+ const now = /* @__PURE__ */ new Date();
650
+ return {
651
+ from: subHours(now, 1),
652
+ to: now
653
+ };
654
+ }
655
+ },
656
+ {
657
+ key: "last-24h",
658
+ label: "Last 24 hours",
659
+ getRange: () => {
660
+ const now = /* @__PURE__ */ new Date();
661
+ return {
662
+ from: subHours(now, 24),
663
+ to: now
664
+ };
665
+ }
666
+ },
667
+ {
668
+ key: "today",
669
+ label: "Today",
670
+ shortcut: "D",
671
+ getRange: (tz) => ({
672
+ from: startOfDayInTz(/* @__PURE__ */ new Date(), tz),
673
+ to: /* @__PURE__ */ new Date()
674
+ })
675
+ },
676
+ {
677
+ key: "last-7-days",
678
+ label: "Last 7 days",
679
+ shortcut: "W",
680
+ getRange: () => {
681
+ const now = /* @__PURE__ */ new Date();
682
+ return {
683
+ from: subDays(now, 7),
684
+ to: now
685
+ };
686
+ }
687
+ },
688
+ {
689
+ key: "last-30-days",
690
+ label: "Last 30 days",
691
+ shortcut: "M",
692
+ getRange: () => {
693
+ const now = /* @__PURE__ */ new Date();
694
+ return {
695
+ from: subDays(now, 30),
696
+ to: now
697
+ };
698
+ }
699
+ }
700
+ ];
701
+ const EMPTY = [];
702
+ /**
703
+ * Default preset set for a given mode. Time-only modes return empty —
704
+ * presets rarely make sense for picking a wall-clock time.
705
+ */
706
+ function getDefaultPresets(mode) {
707
+ switch (mode) {
708
+ case "date":
709
+ case "date-range": return DATE_PRESETS;
710
+ case "datetime":
711
+ case "datetime-range":
712
+ case "date-range-time": return DATETIME_PRESETS;
713
+ default: return EMPTY;
714
+ }
715
+ }
716
+ //#endregion
717
+ //#region src/components/features/picker/hooks/use-presets.ts
718
+ /**
719
+ * Resolve the effective list of presets for a picker.
720
+ *
721
+ * Resolution order:
722
+ * 1. If `custom` is provided, use it as-is (no exclusion, no constraint check).
723
+ * 2. Otherwise: defaults for `mode`, minus `excludeKeys`, minus presets
724
+ * whose `getRange(timezone).from` or `.to` is rejected by `isDateDisabled`.
725
+ *
726
+ * Returns a memoized array; identity-stable when inputs haven't changed.
727
+ */
728
+ function usePresets({ mode, timezone, custom, excludeKeys, isDateDisabled }) {
729
+ return useMemo(() => {
730
+ if (custom) return custom;
731
+ const base = getDefaultPresets(mode);
732
+ const excludeSet = new Set(excludeKeys ?? []);
733
+ return base.filter((preset) => {
734
+ if (excludeSet.has(preset.key)) return false;
735
+ const range = preset.getRange(timezone);
736
+ if (isDateDisabled(range.from)) return false;
737
+ if (isDateDisabled(range.to)) return false;
738
+ return true;
739
+ });
740
+ }, [
741
+ mode,
742
+ timezone,
743
+ custom,
744
+ excludeKeys,
745
+ isDateDisabled
746
+ ]);
747
+ }
748
+ //#endregion
749
+ //#region src/components/features/picker/components/root.tsx
750
+ function detectHourCycle() {
751
+ try {
752
+ const opts = new Intl.DateTimeFormat(void 0, { hour: "numeric" }).resolvedOptions();
753
+ if (opts.hourCycle === "h23" || opts.hourCycle === "h24") return "24";
754
+ return "12";
755
+ } catch {
756
+ return "24";
757
+ }
758
+ }
759
+ function PickerRoot({ mode, value, onChange, commit, timezone: timezoneProp, step = 15, hourCycle: hourCycleProp, hideTimezone = false, responsive = true, presets: customPresets, excludePresets, minDate, maxDate, disablePast, disableFuture, children }) {
760
+ const rootRef = useRef(null);
761
+ const breakpoint = useBreakpoint();
762
+ const timezone = timezoneProp ?? getBrowserTimezone();
763
+ const hourCycle = hourCycleProp ?? detectHourCycle();
764
+ const { isDateDisabled } = useDateConstraints({
765
+ minDate,
766
+ maxDate,
767
+ disablePast,
768
+ disableFuture
769
+ });
770
+ const presets = usePresets({
771
+ mode,
772
+ timezone,
773
+ custom: customPresets,
774
+ excludeKeys: excludePresets,
775
+ isDateDisabled
776
+ });
777
+ const { state, actions } = usePickerState({
778
+ mode,
779
+ value,
780
+ onChange,
781
+ commit,
782
+ timezone
783
+ });
784
+ const effectiveCommit = resolveCommit(mode, commit);
785
+ useKeyboardShortcuts({
786
+ rootRef,
787
+ presets,
788
+ onSelect: useCallback((preset) => {
789
+ const range = preset.getRange(timezone);
790
+ actions.selectPreset(range, preset.key);
791
+ }, [timezone, actions]),
792
+ enabled: state.open
793
+ });
794
+ const ctx = useMemo(() => ({
795
+ mode,
796
+ timezone,
797
+ effectiveCommit,
798
+ hourCycle,
799
+ step,
800
+ hideTimezone,
801
+ responsive,
802
+ presets,
803
+ rootRef,
804
+ breakpoint,
805
+ state,
806
+ actions
807
+ }), [
808
+ mode,
809
+ timezone,
810
+ effectiveCommit,
811
+ hourCycle,
812
+ step,
813
+ hideTimezone,
814
+ responsive,
815
+ presets,
816
+ breakpoint,
817
+ state,
818
+ actions
819
+ ]);
820
+ return /* @__PURE__ */ jsx(PickerContext.Provider, {
821
+ value: ctx,
822
+ children: /* @__PURE__ */ jsx("div", {
823
+ ref: rootRef,
824
+ children
825
+ })
826
+ });
827
+ }
828
+ PickerRoot.displayName = "Picker.Root";
829
+ /**
830
+ * Public namespace export. Subsequent tasks add Trigger, Content, etc.
831
+ */
832
+ const Picker = { Root: PickerRoot };
833
+ //#endregion
834
+ //#region src/components/features/picker/components/trigger.tsx
835
+ const DEFAULT_ICON = /* @__PURE__ */ jsx(CalendarIcon, { className: "text-muted-foreground size-4 shrink-0" });
836
+ function PickerTrigger({ placeholder = "Pick a date", clearable = false, disabled = false, children, className, triggerClassName, id, icon, onClick, ...slotProps }) {
837
+ const { state, actions } = usePickerContext();
838
+ const hasValue = state.pendingValue !== null && state.pendingValue !== void 0;
839
+ const renderedIcon = icon === false ? null : icon ?? DEFAULT_ICON;
840
+ return /* @__PURE__ */ jsxs(Button$1, {
841
+ id,
842
+ type: "button",
843
+ variant: "outline",
844
+ role: "combobox",
845
+ "aria-label": placeholder,
846
+ "aria-expanded": state.open,
847
+ disabled,
848
+ onClick: (e) => {
849
+ onClick?.(e);
850
+ if (state.open) actions.close();
851
+ else actions.open();
852
+ },
853
+ className: cn("w-full justify-between gap-2 font-normal", triggerClassName, className),
854
+ ...slotProps,
855
+ children: [/* @__PURE__ */ jsxs("span", {
856
+ className: "flex flex-1 items-center gap-2 truncate",
857
+ children: [renderedIcon, /* @__PURE__ */ jsx("span", {
858
+ className: cn("truncate", !hasValue && "text-muted-foreground"),
859
+ children: hasValue ? children ? children(state.pendingValue) : String(state.pendingValue) : placeholder
860
+ })]
861
+ }), clearable && hasValue && /* @__PURE__ */ jsx("button", {
862
+ type: "button",
863
+ "aria-label": "Clear",
864
+ onClick: (e) => {
865
+ e.stopPropagation();
866
+ actions.clear();
867
+ },
868
+ className: "text-muted-foreground hover:text-destructive shrink-0 rounded-sm p-0.5 transition-colors",
869
+ children: /* @__PURE__ */ jsx(X, { className: "size-3.5" })
870
+ })]
871
+ });
872
+ }
873
+ PickerTrigger.displayName = "Picker.Trigger";
874
+ //#endregion
875
+ //#region src/components/features/picker/utils/format-value.ts
876
+ const DATE_OPTS = {
877
+ year: "numeric",
878
+ month: "short",
879
+ day: "2-digit"
880
+ };
881
+ function safeDate(input) {
882
+ if (input instanceof Date && !Number.isNaN(input.getTime())) return input;
883
+ return null;
884
+ }
885
+ function safeIso(input, timezone) {
886
+ if (typeof input !== "string") return null;
887
+ try {
888
+ const d = isoToZonedDate(input, timezone);
889
+ return Number.isNaN(d.getTime()) ? null : d;
890
+ } catch {
891
+ return null;
892
+ }
893
+ }
894
+ function formatDate(d, locale, dateFormat) {
895
+ if (dateFormat) return format(d, dateFormat);
896
+ return d.toLocaleDateString(locale, DATE_OPTS);
897
+ }
898
+ /** Renders `HH:mm` from a Date whose wall-clock fields are already in the target timezone. */
899
+ function formatZonedTimeOfDay(d, hourCycle, timeFormat) {
900
+ if (timeFormat) return format(d, timeFormat);
901
+ return formatTimeLabel(dateToHHmm(d), hourCycle);
902
+ }
903
+ /**
904
+ * Renders a `"HH:mm"` time-of-day string. Honors `timeFormat` (date-fns pattern)
905
+ * by lifting the value into a Date and formatting; falls back to `formatTimeLabel`
906
+ * (hourCycle-aware) otherwise.
907
+ */
908
+ function formatTimeOfDayString(value, hourCycle, timeFormat) {
909
+ if (!timeFormat) return formatTimeLabel(value, hourCycle);
910
+ const parsed = parseTimeString(value);
911
+ if (!parsed) return "";
912
+ const d = /* @__PURE__ */ new Date();
913
+ d.setHours(parsed.h, parsed.m, 0, 0);
914
+ return format(d, timeFormat);
915
+ }
916
+ function isSameZonedDay(a, b) {
917
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
918
+ }
919
+ /**
920
+ * Renders a human-readable trigger label for any picker mode.
921
+ * Returns the empty string when the value is null/undefined or unparseable.
922
+ */
923
+ function formatPickerValue(value, mode, opts = {}) {
924
+ if (value === null || value === void 0) return "";
925
+ const timezone = opts.timezone ?? "UTC";
926
+ const hourCycle = opts.hourCycle ?? "24";
927
+ const locale = opts.locale;
928
+ const dateFormat = opts.dateFormat;
929
+ const timeFormat = opts.timeFormat;
930
+ if (mode === "date") {
931
+ const d = safeDate(value);
932
+ return d ? formatDate(d, locale, dateFormat) : "";
933
+ }
934
+ if (mode === "date-range") {
935
+ const v = value;
936
+ const from = safeDate(v?.from);
937
+ const to = safeDate(v?.to);
938
+ if (!from || !to) return "";
939
+ return `${formatDate(from, locale, dateFormat)} - ${formatDate(to, locale, dateFormat)}`;
940
+ }
941
+ if (mode === "time") return typeof value === "string" ? formatTimeOfDayString(value, hourCycle, timeFormat) : "";
942
+ if (mode === "time-range") {
943
+ const v = value;
944
+ if (typeof v?.from !== "string" || typeof v?.to !== "string") return "";
945
+ return `${formatTimeOfDayString(v.from, hourCycle, timeFormat)} - ${formatTimeOfDayString(v.to, hourCycle, timeFormat)}`;
946
+ }
947
+ if (mode === "datetime") {
948
+ const d = safeIso(value, timezone);
949
+ return d ? `${formatDate(d, locale, dateFormat)} ${formatZonedTimeOfDay(d, hourCycle, timeFormat)}` : "";
950
+ }
951
+ if (mode === "datetime-range") {
952
+ const v = value;
953
+ const from = safeIso(v?.from, timezone);
954
+ const to = safeIso(v?.to, timezone);
955
+ if (!from || !to) return "";
956
+ if (isSameZonedDay(from, to)) return `${formatDate(from, locale, dateFormat)} ${formatZonedTimeOfDay(from, hourCycle, timeFormat)} - ${formatZonedTimeOfDay(to, hourCycle, timeFormat)}`;
957
+ return `${formatDate(from, locale, dateFormat)} ${formatZonedTimeOfDay(from, hourCycle, timeFormat)} - ${formatDate(to, locale, dateFormat)} ${formatZonedTimeOfDay(to, hourCycle, timeFormat)}`;
958
+ }
959
+ if (mode === "date-range-time") {
960
+ const v = value;
961
+ const from = safeIso(v?.from, timezone);
962
+ const to = safeIso(v?.to, timezone);
963
+ if (!from || !to) return "";
964
+ return `${formatDate(from, locale, dateFormat)} - ${formatDate(to, locale, dateFormat)} (${formatZonedTimeOfDay(from, hourCycle, timeFormat)})`;
965
+ }
966
+ return "";
967
+ }
968
+ //#endregion
969
+ //#region src/components/features/picker/components/footer.tsx
970
+ function PickerFooter({ children, className }) {
971
+ return /* @__PURE__ */ jsx("div", {
972
+ className: cn("flex items-center justify-end gap-2", className),
973
+ children
974
+ });
975
+ }
976
+ PickerFooter.displayName = "Picker.Footer";
977
+ function PickerApply({ children = "Apply" }) {
978
+ const { state, actions } = usePickerContext();
979
+ return /* @__PURE__ */ jsx(Button, {
980
+ type: "primary",
981
+ theme: "solid",
982
+ size: "xs",
983
+ disabled: state.pendingValue === null || state.pendingValue === void 0,
984
+ onClick: actions.apply,
985
+ children
986
+ });
987
+ }
988
+ PickerApply.displayName = "Picker.Apply";
989
+ function PickerReset({ children = "Reset" }) {
990
+ const { actions } = usePickerContext();
991
+ return /* @__PURE__ */ jsx(Button, {
992
+ type: "quaternary",
993
+ theme: "outline",
994
+ size: "xs",
995
+ onClick: actions.reset,
996
+ children
997
+ });
998
+ }
999
+ PickerReset.displayName = "Picker.Reset";
1000
+ function PickerCancel({ children = "Cancel" }) {
1001
+ const { actions } = usePickerContext();
1002
+ return /* @__PURE__ */ jsx(Button, {
1003
+ type: "quaternary",
1004
+ theme: "borderless",
1005
+ size: "xs",
1006
+ onClick: actions.cancel,
1007
+ children
1008
+ });
1009
+ }
1010
+ PickerCancel.displayName = "Picker.Cancel";
1011
+ function PickerClear({ children = "Clear" }) {
1012
+ const { actions } = usePickerContext();
1013
+ return /* @__PURE__ */ jsx(Button, {
1014
+ type: "primary",
1015
+ theme: "borderless",
1016
+ size: "xs",
1017
+ onClick: actions.clear,
1018
+ children
1019
+ });
1020
+ }
1021
+ PickerClear.displayName = "Picker.Clear";
1022
+ //#endregion
1023
+ //#region src/components/features/picker/wrappers/internal/default-footer.tsx
1024
+ /**
1025
+ * The standard Cancel / Reset / Apply footer used by every wrapper.
1026
+ * `Picker.Content` decides whether to render it - desktop popover renders
1027
+ * it only when `commit === 'apply'`; mobile sheet always renders it.
1028
+ */
1029
+ function DefaultFooter() {
1030
+ return /* @__PURE__ */ jsxs(PickerFooter, { children: [
1031
+ /* @__PURE__ */ jsx(PickerCancel, {}),
1032
+ /* @__PURE__ */ jsx(PickerReset, {}),
1033
+ /* @__PURE__ */ jsx(PickerApply, {})
1034
+ ] });
1035
+ }
1036
+ //#endregion
1037
+ export { zonedDateToIso as A, PickerContext as C, getBrowserTimezone as D, formatTimezoneLabel as E, isDateBearingMode as F, isRangeMode as I, isTimeBearingMode as L, DATE_BEARING_MODES as M, RANGE_MODES as N, getTimezoneOffset as O, TIME_BEARING_MODES as P, useDateConstraints as R, PickerContent as S, formatInTimezone as T, formatTimeLabel as _, PickerFooter as a, resolveCommit as b, PickerTrigger as c, DATETIME_PRESETS as d, DATE_PRESETS as f, dateToYYYYMMDD as g, dateToHHmm as h, PickerClear as i, ALLOWED_STEPS as j, isoToZonedDate as k, Picker as l, usePickerState as m, PickerApply as n, PickerReset as o, getDefaultPresets as p, PickerCancel as r, formatPickerValue as s, DefaultFooter as t, usePresets as u, isValidTimeString as v, usePickerContext as w, useKeyboardShortcuts as x, parseTimeString as y };