@teamofsilicons/silicon-ui 0.0.0-stage → 0.2.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 (354) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +81 -2
  3. package/check.mjs +236 -0
  4. package/components/accordion/accordion.d.ts +13 -0
  5. package/components/accordion/accordion.js +35 -0
  6. package/components/accordion/accordion.module.css +15 -0
  7. package/components/action-button/action-button.d.ts +11 -0
  8. package/components/action-button/action-button.js +57 -0
  9. package/components/action-button/action-button.module.css +15 -0
  10. package/components/activity-heatmap/activity-heatmap.d.ts +37 -0
  11. package/components/activity-heatmap/activity-heatmap.js +303 -0
  12. package/components/activity-heatmap/activity-heatmap.module.css +83 -0
  13. package/components/alert/alert.d.ts +13 -0
  14. package/components/alert/alert.js +79 -0
  15. package/components/alert/alert.module.css +15 -0
  16. package/components/animated-counter/animated-counter.d.ts +13 -0
  17. package/components/animated-counter/animated-counter.js +73 -0
  18. package/components/animated-counter/animated-counter.module.css +12 -0
  19. package/components/announcement-bar/announcement-bar.d.ts +53 -0
  20. package/components/announcement-bar/announcement-bar.js +187 -0
  21. package/components/announcement-bar/announcement-bar.module.css +60 -0
  22. package/components/avatar/avatar.d.ts +8 -0
  23. package/components/avatar/avatar.js +18 -0
  24. package/components/avatar/avatar.module.css +60 -0
  25. package/components/avatar-group/avatar-group.d.ts +13 -0
  26. package/components/avatar-group/avatar-group.js +24 -0
  27. package/components/avatar-group/avatar-group.module.css +27 -0
  28. package/components/badge/badge.d.ts +10 -0
  29. package/components/badge/badge.js +66 -0
  30. package/components/badge/badge.module.css +52 -0
  31. package/components/bar-chart/bar-chart.d.ts +32 -0
  32. package/components/bar-chart/bar-chart.js +327 -0
  33. package/components/bar-chart/bar-chart.module.css +49 -0
  34. package/components/billing-toggle/billing-toggle.d.ts +35 -0
  35. package/components/billing-toggle/billing-toggle.js +83 -0
  36. package/components/billing-toggle/billing-toggle.module.css +139 -0
  37. package/components/bottom-sheet/bottom-sheet.d.ts +27 -0
  38. package/components/bottom-sheet/bottom-sheet.js +386 -0
  39. package/components/bottom-sheet/bottom-sheet.module.css +69 -0
  40. package/components/breadcrumb/breadcrumb.d.ts +13 -0
  41. package/components/breadcrumb/breadcrumb.js +19 -0
  42. package/components/breadcrumb/breadcrumb.module.css +12 -0
  43. package/components/brush-chart/brush-chart.d.ts +45 -0
  44. package/components/brush-chart/brush-chart.js +426 -0
  45. package/components/brush-chart/brush-chart.module.css +69 -0
  46. package/components/button/button.d.ts +10 -0
  47. package/components/button/button.js +97 -0
  48. package/components/button/button.module.css +39 -0
  49. package/components/button-group/button-group.d.ts +59 -0
  50. package/components/button-group/button-group.js +280 -0
  51. package/components/button-group/button-group.module.css +87 -0
  52. package/components/button-link/button-link.d.ts +24 -0
  53. package/components/button-link/button-link.js +11 -0
  54. package/components/button-link/button-link.module.css +7 -0
  55. package/components/calendar/calendar.d.ts +22 -0
  56. package/components/calendar/calendar.js +253 -0
  57. package/components/calendar/calendar.module.css +51 -0
  58. package/components/card/card.d.ts +19 -0
  59. package/components/card/card.js +108 -0
  60. package/components/card/card.module.css +51 -0
  61. package/components/card-stack/card-stack.d.ts +34 -0
  62. package/components/card-stack/card-stack.js +291 -0
  63. package/components/card-stack/card-stack.module.css +63 -0
  64. package/components/carousel/carousel.d.ts +28 -0
  65. package/components/carousel/carousel.js +332 -0
  66. package/components/carousel/carousel.module.css +42 -0
  67. package/components/chat-thread/chat-thread.d.ts +135 -0
  68. package/components/chat-thread/chat-thread.js +406 -0
  69. package/components/chat-thread/chat-thread.module.css +242 -0
  70. package/components/checkbox/checkbox.d.ts +7 -0
  71. package/components/checkbox/checkbox.js +23 -0
  72. package/components/checkbox/checkbox.module.css +15 -0
  73. package/components/chip-group/chip-group.d.ts +24 -0
  74. package/components/chip-group/chip-group.js +196 -0
  75. package/components/chip-group/chip-group.module.css +38 -0
  76. package/components/code-block/code-block.d.ts +11 -0
  77. package/components/code-block/code-block.js +133 -0
  78. package/components/code-block/code-block.module.css +43 -0
  79. package/components/color-picker/color-picker.d.ts +40 -0
  80. package/components/color-picker/color-picker.js +449 -0
  81. package/components/color-picker/color-picker.module.css +111 -0
  82. package/components/combobox/combobox.d.ts +19 -0
  83. package/components/combobox/combobox.js +167 -0
  84. package/components/combobox/combobox.module.css +28 -0
  85. package/components/command-palette/command-palette.d.ts +20 -0
  86. package/components/command-palette/command-palette.js +151 -0
  87. package/components/command-palette/command-palette.module.css +94 -0
  88. package/components/comment-thread/comment-thread.d.ts +120 -0
  89. package/components/comment-thread/comment-thread.js +343 -0
  90. package/components/comment-thread/comment-thread.module.css +195 -0
  91. package/components/confirm-morph/confirm-morph.d.ts +65 -0
  92. package/components/confirm-morph/confirm-morph.js +355 -0
  93. package/components/confirm-morph/confirm-morph.module.css +146 -0
  94. package/components/context-menu/context-menu.d.ts +18 -0
  95. package/components/context-menu/context-menu.js +148 -0
  96. package/components/context-menu/context-menu.module.css +17 -0
  97. package/components/copy-button/copy-button.d.ts +10 -0
  98. package/components/copy-button/copy-button.js +69 -0
  99. package/components/copy-button/copy-button.module.css +21 -0
  100. package/components/countdown/countdown.d.ts +31 -0
  101. package/components/countdown/countdown.js +177 -0
  102. package/components/countdown/countdown.module.css +49 -0
  103. package/components/date-picker/date-picker.d.ts +17 -0
  104. package/components/date-picker/date-picker.js +100 -0
  105. package/components/date-picker/date-picker.module.css +34 -0
  106. package/components/date-range-picker/date-range-picker.d.ts +44 -0
  107. package/components/date-range-picker/date-range-picker.js +534 -0
  108. package/components/date-range-picker/date-range-picker.module.css +214 -0
  109. package/components/dialog/dialog.d.ts +11 -0
  110. package/components/dialog/dialog.js +52 -0
  111. package/components/dialog/dialog.module.css +36 -0
  112. package/components/donut-chart/donut-chart.d.ts +46 -0
  113. package/components/donut-chart/donut-chart.js +382 -0
  114. package/components/donut-chart/donut-chart.module.css +48 -0
  115. package/components/drawer/drawer.d.ts +20 -0
  116. package/components/drawer/drawer.js +111 -0
  117. package/components/drawer/drawer.module.css +97 -0
  118. package/components/dropdown-menu/dropdown-menu.d.ts +16 -0
  119. package/components/dropdown-menu/dropdown-menu.js +39 -0
  120. package/components/dropdown-menu/dropdown-menu.module.css +38 -0
  121. package/components/elastic-slider/elastic-slider.d.ts +39 -0
  122. package/components/elastic-slider/elastic-slider.js +261 -0
  123. package/components/elastic-slider/elastic-slider.module.css +40 -0
  124. package/components/empty-state/empty-state.d.ts +12 -0
  125. package/components/empty-state/empty-state.js +69 -0
  126. package/components/empty-state/empty-state.module.css +13 -0
  127. package/components/expandable-card/expandable-card.d.ts +13 -0
  128. package/components/expandable-card/expandable-card.js +89 -0
  129. package/components/expandable-card/expandable-card.module.css +22 -0
  130. package/components/expanding-button-group/expanding-button-group.d.ts +41 -0
  131. package/components/expanding-button-group/expanding-button-group.js +258 -0
  132. package/components/expanding-button-group/expanding-button-group.module.css +44 -0
  133. package/components/expanding-search/expanding-search.d.ts +38 -0
  134. package/components/expanding-search/expanding-search.js +242 -0
  135. package/components/expanding-search/expanding-search.module.css +52 -0
  136. package/components/file-dropzone/file-dropzone.d.ts +44 -0
  137. package/components/file-dropzone/file-dropzone.js +379 -0
  138. package/components/file-dropzone/file-dropzone.module.css +115 -0
  139. package/components/file-upload/file-upload.d.ts +22 -0
  140. package/components/file-upload/file-upload.js +192 -0
  141. package/components/file-upload/file-upload.module.css +49 -0
  142. package/components/filter-toolbar/filter-toolbar.d.ts +48 -0
  143. package/components/filter-toolbar/filter-toolbar.js +446 -0
  144. package/components/filter-toolbar/filter-toolbar.module.css +70 -0
  145. package/components/floating-button-group/floating-button-group.d.ts +51 -0
  146. package/components/floating-button-group/floating-button-group.js +215 -0
  147. package/components/floating-button-group/floating-button-group.module.css +74 -0
  148. package/components/gauge/gauge.d.ts +18 -0
  149. package/components/gauge/gauge.js +101 -0
  150. package/components/gauge/gauge.module.css +22 -0
  151. package/components/hold-to-confirm/hold-to-confirm.d.ts +25 -0
  152. package/components/hold-to-confirm/hold-to-confirm.js +165 -0
  153. package/components/hold-to-confirm/hold-to-confirm.module.css +20 -0
  154. package/components/hover-card/hover-card.d.ts +38 -0
  155. package/components/hover-card/hover-card.js +145 -0
  156. package/components/hover-card/hover-card.module.css +20 -0
  157. package/components/image-compare/image-compare.d.ts +27 -0
  158. package/components/image-compare/image-compare.js +267 -0
  159. package/components/image-compare/image-compare.module.css +25 -0
  160. package/components/in-view-title/in-view-title.d.ts +17 -0
  161. package/components/in-view-title/in-view-title.js +70 -0
  162. package/components/in-view-title/in-view-title.module.css +13 -0
  163. package/components/inline-edit/inline-edit.d.ts +27 -0
  164. package/components/inline-edit/inline-edit.js +315 -0
  165. package/components/inline-edit/inline-edit.module.css +73 -0
  166. package/components/input/input.d.ts +7 -0
  167. package/components/input/input.js +60 -0
  168. package/components/input/input.module.css +22 -0
  169. package/components/json-viewer/json-viewer.d.ts +48 -0
  170. package/components/json-viewer/json-viewer.js +463 -0
  171. package/components/json-viewer/json-viewer.module.css +92 -0
  172. package/components/line-chart/line-chart.d.ts +58 -0
  173. package/components/line-chart/line-chart.js +383 -0
  174. package/components/line-chart/line-chart.module.css +60 -0
  175. package/components/mention-input/mention-input.d.ts +72 -0
  176. package/components/mention-input/mention-input.js +417 -0
  177. package/components/mention-input/mention-input.module.css +134 -0
  178. package/components/metric-card/metric-card.d.ts +8 -0
  179. package/components/metric-card/metric-card.js +44 -0
  180. package/components/metric-card/metric-card.module.css +18 -0
  181. package/components/money-input/money-input.d.ts +48 -0
  182. package/components/money-input/money-input.js +503 -0
  183. package/components/money-input/money-input.module.css +77 -0
  184. package/components/morph-select/morph-select.d.ts +50 -0
  185. package/components/morph-select/morph-select.js +395 -0
  186. package/components/morph-select/morph-select.module.css +88 -0
  187. package/components/multi-select/multi-select.d.ts +18 -0
  188. package/components/multi-select/multi-select.js +105 -0
  189. package/components/multi-select/multi-select.module.css +37 -0
  190. package/components/notification-center/notification-center.d.ts +24 -0
  191. package/components/notification-center/notification-center.js +146 -0
  192. package/components/notification-center/notification-center.module.css +79 -0
  193. package/components/number-field/number-field.d.ts +41 -0
  194. package/components/number-field/number-field.js +455 -0
  195. package/components/number-field/number-field.module.css +71 -0
  196. package/components/otp-input/otp-input.d.ts +13 -0
  197. package/components/otp-input/otp-input.js +129 -0
  198. package/components/otp-input/otp-input.module.css +28 -0
  199. package/components/pagination/pagination.d.ts +7 -0
  200. package/components/pagination/pagination.js +60 -0
  201. package/components/pagination/pagination.module.css +16 -0
  202. package/components/password-field/password-field.d.ts +14 -0
  203. package/components/password-field/password-field.js +51 -0
  204. package/components/password-field/password-field.module.css +27 -0
  205. package/components/password-strength/password-strength.d.ts +107 -0
  206. package/components/password-strength/password-strength.js +161 -0
  207. package/components/password-strength/password-strength.module.css +57 -0
  208. package/components/phone-input/phone-input.d.ts +73 -0
  209. package/components/phone-input/phone-input.js +612 -0
  210. package/components/phone-input/phone-input.module.css +98 -0
  211. package/components/popover/popover.d.ts +6 -0
  212. package/components/popover/popover.js +19 -0
  213. package/components/popover/popover.module.css +32 -0
  214. package/components/progress/progress.d.ts +8 -0
  215. package/components/progress/progress.js +42 -0
  216. package/components/progress/progress.module.css +1 -0
  217. package/components/radio-cards/radio-cards.d.ts +35 -0
  218. package/components/radio-cards/radio-cards.js +110 -0
  219. package/components/radio-cards/radio-cards.module.css +84 -0
  220. package/components/radio-group/radio-group.d.ts +13 -0
  221. package/components/radio-group/radio-group.js +55 -0
  222. package/components/radio-group/radio-group.module.css +30 -0
  223. package/components/resizable-panels/resizable-panels.d.ts +37 -0
  224. package/components/resizable-panels/resizable-panels.js +338 -0
  225. package/components/resizable-panels/resizable-panels.module.css +40 -0
  226. package/components/rich-text-editor/rich-text-editor.d.ts +55 -0
  227. package/components/rich-text-editor/rich-text-editor.js +1733 -0
  228. package/components/rich-text-editor/rich-text-editor.module.css +201 -0
  229. package/components/ridgeline/ridgeline.d.ts +37 -0
  230. package/components/ridgeline/ridgeline.js +346 -0
  231. package/components/ridgeline/ridgeline.module.css +58 -0
  232. package/components/scroll-area/scroll-area.d.ts +40 -0
  233. package/components/scroll-area/scroll-area.js +153 -0
  234. package/components/scroll-area/scroll-area.module.css +79 -0
  235. package/components/search-field/search-field.d.ts +7 -0
  236. package/components/search-field/search-field.js +20 -0
  237. package/components/search-field/search-field.module.css +17 -0
  238. package/components/segmented-control/segmented-control.d.ts +16 -0
  239. package/components/segmented-control/segmented-control.js +56 -0
  240. package/components/segmented-control/segmented-control.module.css +15 -0
  241. package/components/select/select.d.ts +15 -0
  242. package/components/select/select.js +36 -0
  243. package/components/select/select.module.css +35 -0
  244. package/components/shortcut-recorder/shortcut-recorder.d.ts +108 -0
  245. package/components/shortcut-recorder/shortcut-recorder.js +380 -0
  246. package/components/shortcut-recorder/shortcut-recorder.module.css +85 -0
  247. package/components/signature-pad/signature-pad.d.ts +72 -0
  248. package/components/signature-pad/signature-pad.js +450 -0
  249. package/components/signature-pad/signature-pad.module.css +84 -0
  250. package/components/skeleton/skeleton.d.ts +13 -0
  251. package/components/skeleton/skeleton.js +53 -0
  252. package/components/skeleton/skeleton.module.css +11 -0
  253. package/components/slider/slider.d.ts +45 -0
  254. package/components/slider/slider.js +347 -0
  255. package/components/slider/slider.module.css +45 -0
  256. package/components/slope-chart/slope-chart.d.ts +38 -0
  257. package/components/slope-chart/slope-chart.js +183 -0
  258. package/components/slope-chart/slope-chart.module.css +51 -0
  259. package/components/slot-text/slot-text.d.ts +26 -0
  260. package/components/slot-text/slot-text.js +119 -0
  261. package/components/slot-text/slot-text.module.css +19 -0
  262. package/components/sortable-data-table/sortable-data-table.d.ts +38 -0
  263. package/components/sortable-data-table/sortable-data-table.js +240 -0
  264. package/components/sortable-data-table/sortable-data-table.module.css +116 -0
  265. package/components/sparkline/sparkline.d.ts +19 -0
  266. package/components/sparkline/sparkline.js +239 -0
  267. package/components/sparkline/sparkline.module.css +26 -0
  268. package/components/split-button/split-button.d.ts +19 -0
  269. package/components/split-button/split-button.js +33 -0
  270. package/components/split-button/split-button.module.css +51 -0
  271. package/components/stepper/stepper.d.ts +36 -0
  272. package/components/stepper/stepper.js +108 -0
  273. package/components/stepper/stepper.module.css +74 -0
  274. package/components/streamgraph/streamgraph.d.ts +51 -0
  275. package/components/streamgraph/streamgraph.js +376 -0
  276. package/components/streamgraph/streamgraph.module.css +55 -0
  277. package/components/swipe-actions/swipe-actions.d.ts +35 -0
  278. package/components/swipe-actions/swipe-actions.js +330 -0
  279. package/components/swipe-actions/swipe-actions.module.css +64 -0
  280. package/components/switch/switch.d.ts +7 -0
  281. package/components/switch/switch.js +45 -0
  282. package/components/switch/switch.module.css +12 -0
  283. package/components/tabs/tabs.d.ts +8 -0
  284. package/components/tabs/tabs.js +138 -0
  285. package/components/tabs/tabs.module.css +27 -0
  286. package/components/tag-input/tag-input.d.ts +15 -0
  287. package/components/tag-input/tag-input.js +150 -0
  288. package/components/tag-input/tag-input.module.css +34 -0
  289. package/components/text-morph/text-morph.d.ts +13 -0
  290. package/components/text-morph/text-morph.js +69 -0
  291. package/components/text-morph/text-morph.module.css +8 -0
  292. package/components/text-reveal/text-reveal.d.ts +14 -0
  293. package/components/text-reveal/text-reveal.js +20 -0
  294. package/components/text-reveal/text-reveal.module.css +13 -0
  295. package/components/text-shimmer/text-shimmer.d.ts +18 -0
  296. package/components/text-shimmer/text-shimmer.js +90 -0
  297. package/components/text-shimmer/text-shimmer.module.css +9 -0
  298. package/components/textarea/textarea.d.ts +7 -0
  299. package/components/textarea/textarea.js +56 -0
  300. package/components/textarea/textarea.module.css +22 -0
  301. package/components/theme-switch/theme-switch.d.ts +11 -0
  302. package/components/theme-switch/theme-switch.js +37 -0
  303. package/components/theme-switch/theme-switch.module.css +31 -0
  304. package/components/time-picker/time-picker.d.ts +13 -0
  305. package/components/time-picker/time-picker.js +95 -0
  306. package/components/time-picker/time-picker.module.css +23 -0
  307. package/components/timeline/timeline.d.ts +47 -0
  308. package/components/timeline/timeline.js +171 -0
  309. package/components/timeline/timeline.module.css +51 -0
  310. package/components/toast/toast.d.ts +8 -0
  311. package/components/toast/toast.js +118 -0
  312. package/components/toast/toast.module.css +100 -0
  313. package/components/toast-stack/toast-stack.d.ts +57 -0
  314. package/components/toast-stack/toast-stack.js +399 -0
  315. package/components/toast-stack/toast-stack.module.css +61 -0
  316. package/components/tooltip/tooltip.d.ts +8 -0
  317. package/components/tooltip/tooltip.js +62 -0
  318. package/components/tooltip/tooltip.module.css +18 -0
  319. package/components/tree-view/tree-view.d.ts +17 -0
  320. package/components/tree-view/tree-view.js +147 -0
  321. package/components/tree-view/tree-view.module.css +22 -0
  322. package/components/treemap/treemap.d.ts +35 -0
  323. package/components/treemap/treemap.js +375 -0
  324. package/components/treemap/treemap.module.css +59 -0
  325. package/components/usage-meter/usage-meter.d.ts +24 -0
  326. package/components/usage-meter/usage-meter.js +296 -0
  327. package/components/usage-meter/usage-meter.module.css +61 -0
  328. package/components/user-menu/user-menu.d.ts +62 -0
  329. package/components/user-menu/user-menu.js +316 -0
  330. package/components/user-menu/user-menu.module.css +97 -0
  331. package/components/waffle-chart/waffle-chart.d.ts +39 -0
  332. package/components/waffle-chart/waffle-chart.js +244 -0
  333. package/components/waffle-chart/waffle-chart.module.css +52 -0
  334. package/components.json +1330 -0
  335. package/eslint.js +53 -0
  336. package/lib/escape.d.ts +19 -0
  337. package/lib/escape.js +50 -0
  338. package/lib/media.d.ts +353 -0
  339. package/lib/media.js +65 -0
  340. package/lib/menu-highlight.d.ts +28 -0
  341. package/lib/menu-highlight.js +43 -0
  342. package/lib/motion-tokens.d.ts +60 -0
  343. package/lib/motion-tokens.js +25 -0
  344. package/lib/return-focus.d.ts +9 -0
  345. package/lib/return-focus.js +53 -0
  346. package/lib/silicon-provider.d.ts +60 -0
  347. package/lib/silicon-provider.js +47 -0
  348. package/lib/use-copy-feedback.d.ts +8 -0
  349. package/lib/use-copy-feedback.js +36 -0
  350. package/lib/use-today.d.ts +2 -0
  351. package/lib/use-today.js +29 -0
  352. package/package.json +76 -4
  353. package/styles.css +218 -0
  354. package/styles.css.d.ts +1 -0
@@ -0,0 +1,534 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
4
+ import { AnimatePresence, animate, motion, useIsPresent, useMotionValue, useReducedMotion, useTransform } from "motion/react";
5
+ import { CalendarDays, ChevronDown, ChevronLeft, ChevronRight } from "lucide-react";
6
+ import { motionTokens } from "../../lib/motion-tokens.js";
7
+ import { useToday } from "../../lib/use-today.js";
8
+ import styles from "./date-range-picker.module.css";
9
+ const DAY = 864e5;
10
+ const startOfDay = (date) => new Date(date.getFullYear(), date.getMonth(), date.getDate());
11
+ const monthStart = (date) => new Date(date.getFullYear(), date.getMonth(), 1);
12
+ const monthEnd = (date) => new Date(date.getFullYear(), date.getMonth() + 1, 0);
13
+ const addDays = (date, amount) => new Date(date.getFullYear(), date.getMonth(), date.getDate() + amount);
14
+ const addMonths = (date, amount) => new Date(date.getFullYear(), date.getMonth() + amount, 1);
15
+ const shiftMonths = (date, amount) => new Date(date.getFullYear(), date.getMonth() + amount, Math.min(date.getDate(), new Date(date.getFullYear(), date.getMonth() + amount + 1, 0).getDate()));
16
+ const dayDiff = (a, b) => Math.round((startOfDay(a).getTime() - startOfDay(b).getTime()) / DAY);
17
+ const monthDiff = (a, b) => (a.getFullYear() - b.getFullYear()) * 12 + a.getMonth() - b.getMonth();
18
+ const sameDay = (a, b) => Boolean(a && b && dayDiff(a, b) === 0);
19
+ const keyOf = (date) => `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
20
+ const fromKey = (key) => { const [year, month, day] = key.split("-").map(Number); return new Date(year, month - 1, day); };
21
+ const ordered = (a, b) => dayDiff(a, b) <= 0 ? { start: a, end: b } : { start: b, end: a };
22
+ const sameRange = (a, b) => Boolean(a && b && sameDay(a.start, b.start) && sameDay(a.end, b.end));
23
+ const clampDate = (date, min, max) => min && dayDiff(date, min) < 0 ? startOfDay(min) : max && dayDiff(date, max) > 0 ? startOfDay(max) : date;
24
+ export const defaultDateRangePresets = [
25
+ { label: "Today", range: today => ({ start: today, end: today }) },
26
+ { label: "Yesterday", range: today => ({ start: addDays(today, -1), end: addDays(today, -1) }) },
27
+ { label: "Last 7 days", range: today => ({ start: addDays(today, -6), end: today }) },
28
+ { label: "Last 30 days", range: today => ({ start: addDays(today, -29), end: today }) },
29
+ { label: "This month", range: today => ({ start: monthStart(today), end: today }) },
30
+ { label: "Last month", range: today => ({ start: addMonths(today, -1), end: monthEnd(addMonths(today, -1)) }) },
31
+ { label: "This quarter", range: today => ({ start: new Date(today.getFullYear(), Math.floor(today.getMonth() / 3) * 3, 1), end: today }) },
32
+ { label: "Year to date", range: today => ({ start: new Date(today.getFullYear(), 0, 1), end: today }) },
33
+ ];
34
+ /** The viewer's local date (lib/use-today.ts, shared with calendar). Re-exported so existing imports from date-range-picker keep working. */
35
+ export { useToday } from "../../lib/use-today.js";
36
+ const noop = () => () => { };
37
+ function useReducedFlag() {
38
+ const hydrated = useSyncExternalStore(noop, () => true, () => false);
39
+ return !!useReducedMotion() && hydrated;
40
+ }
41
+ const { blur } = motionTokens;
42
+ const enterEase = [...motionTokens.ease.enter];
43
+ const standardEase = [...motionTokens.ease.standard];
44
+ /** Duration springs restated as stiffness and damping, so every retarget keeps the velocity already in flight. */
45
+ const physical = (visualDuration, bounce) => {
46
+ const root = 2 * Math.PI / (visualDuration * 1.2);
47
+ return { type: "spring", stiffness: root * root, damping: 2 * (1 - bounce) * root, mass: 1 };
48
+ };
49
+ const GROW = physical(.48, .12), SHRINK = physical(.38, 0), STRETCH = physical(.3, .04), GLIDE = physical(.3, .1), SLIDE = physical(.4, .06);
50
+ const TRIGGER_RADIUS = 18, PANEL_RADIUS = 26, WIDE_CELL = 36, WIDE_MIN = 712, EDGE = 8;
51
+ const roll = {
52
+ enter: (direction) => ({ opacity: 0, y: `${direction * .45}em`, filter: `blur(${blur.subtle}px)` }),
53
+ center: { opacity: 1, y: "0em", filter: "blur(0px)", transition: { duration: .26, ease: enterEase } },
54
+ exit: (direction) => ({ opacity: 0, y: `${direction * -.45}em`, filter: `blur(${blur.subtle}px)`, transition: { duration: .14, ease: standardEase } }),
55
+ };
56
+ const fade = { enter: { opacity: 0 }, center: { opacity: 1, y: "0em", filter: "blur(0px)", transition: { duration: .12 } }, exit: { opacity: 0, transition: { duration: .08 } } };
57
+ /** Panel content arrives just behind the growing surface and leaves ahead of it. */
58
+ const faceIn = {
59
+ hidden: { opacity: 0, y: -4, filter: `blur(${blur.soft}px)` },
60
+ shown: { opacity: 1, y: 0, filter: "blur(0px)", transition: { duration: .26, ease: enterEase, delay: .07 } },
61
+ gone: { opacity: 0, y: -2, filter: `blur(${blur.soft}px)`, transition: { duration: .12, ease: standardEase } },
62
+ };
63
+ const faceFade = { hidden: { opacity: 0 }, shown: { opacity: 1, transition: { duration: .12 } }, gone: { opacity: 0, transition: { duration: .08 } } };
64
+ const slide = {
65
+ enter: (direction) => ({ opacity: 0, x: direction * 48, filter: `blur(${blur.soft}px)` }),
66
+ center: { opacity: 1, x: 0, filter: "blur(0px)", transition: { x: SLIDE, opacity: { duration: .22, ease: enterEase }, filter: { duration: .24, ease: enterEase } } },
67
+ exit: (direction) => ({ opacity: 0, x: direction * -36, filter: `blur(${blur.soft}px)`, transition: { x: SLIDE, opacity: { duration: .14, ease: standardEase }, filter: { duration: .14, ease: standardEase } } }),
68
+ };
69
+ /** Words roll one by one: a later date rises from below, an earlier one drops from above; words that stay the same hold still. */
70
+ function Rolling({ text, direction, reduced }) {
71
+ const words = text.split(/\s+/).filter(Boolean);
72
+ return _jsx("span", { className: styles.rolling, "aria-hidden": "true", children: words.map((word, index) => _jsx("span", { className: styles.word, children: _jsx(AnimatePresence, { initial: false, mode: "popLayout", custom: direction, children: _jsx(motion.span, { className: styles.wordInner, custom: direction, variants: reduced ? fade : roll, initial: "enter", animate: "center", exit: "exit", children: word }, word) }) }, index)) });
73
+ }
74
+ /** Remembers which way a range moved, so labels roll in time's direction. */
75
+ function useDirection(range) {
76
+ const time = range ? range.start.getTime() * 2 + range.end.getTime() : null;
77
+ const [previous, setPrevious] = useState(time);
78
+ const [direction, setDirection] = useState(1);
79
+ if (time !== previous) {
80
+ setPrevious(time);
81
+ setDirection(time === null || previous === null || time >= previous ? 1 : -1);
82
+ }
83
+ return direction;
84
+ }
85
+ const cellIn = (date, first) => { const index = dayDiff(date, first); return { row: Math.floor(index / 7), col: index % 7 }; };
86
+ /** One week's slice of the highlight. It grows out of the edge the range arrives from, so hovering reads as stretching. */
87
+ function Bar({ row, segment, reduced }) {
88
+ const left = useMotionValue(segment?.left ?? 0), width = useMotionValue(segment?.width ?? 0), opacity = useMotionValue(segment ? 1 : 0);
89
+ const leftPct = useTransform(left, value => `${value}%`), widthPct = useTransform(width, value => `${value}%`);
90
+ const wasEmpty = useRef(!segment);
91
+ useLayoutEffect(() => {
92
+ if (!segment) {
93
+ wasEmpty.current = true;
94
+ animate(opacity, 0, { duration: reduced ? 0 : .14 });
95
+ return;
96
+ }
97
+ if (wasEmpty.current || reduced) {
98
+ left.jump(segment.left);
99
+ width.jump(segment.width);
100
+ }
101
+ else {
102
+ animate(left, segment.left, STRETCH);
103
+ animate(width, segment.width, STRETCH);
104
+ }
105
+ wasEmpty.current = false;
106
+ animate(opacity, 1, { duration: reduced ? 0 : .16 });
107
+ }, [left, opacity, reduced, segment, width]);
108
+ return _jsx(motion.span, { className: styles.bar, style: { left: leftPct, width: widthPct, opacity, top: `calc(${row} * var(--cell) + 3px)` } });
109
+ }
110
+ /** A solid end of the range. It glides from day to day and only fades when the end leaves this month. */
111
+ function Thumb({ cell, reduced }) {
112
+ const x = useMotionValue(cell?.col ?? 0), y = useMotionValue(cell?.row ?? 0), opacity = useMotionValue(cell ? 1 : 0), scale = useMotionValue(cell ? 1 : .8);
113
+ const transform = useTransform(() => `translate(${x.get() * 100}%, ${y.get() * 100}%) scale(${scale.get()})`);
114
+ const hidden = useRef(!cell);
115
+ useLayoutEffect(() => {
116
+ if (!cell) {
117
+ hidden.current = true;
118
+ animate(opacity, 0, { duration: reduced ? 0 : .12 });
119
+ animate(scale, .8, { duration: reduced ? 0 : .12 });
120
+ return;
121
+ }
122
+ if (hidden.current || reduced) {
123
+ x.jump(cell.col);
124
+ y.jump(cell.row);
125
+ }
126
+ else {
127
+ animate(x, cell.col, GLIDE);
128
+ animate(y, cell.row, GLIDE);
129
+ }
130
+ hidden.current = false;
131
+ animate(opacity, 1, { duration: reduced ? 0 : .16 });
132
+ animate(scale, 1, reduced ? { duration: 0 } : GLIDE);
133
+ }, [cell, opacity, reduced, scale, x, y]);
134
+ return _jsx(motion.span, { className: styles.thumb, style: { transform, opacity } });
135
+ }
136
+ function Month({ month, range, tabbable, today, minDate, maxDate, weekStartsOn, reduced, formatters, onPick, onHover, onKey, onFocusDay, idBase }) {
137
+ const first = useMemo(() => addDays(month, -((month.getDay() - weekStartsOn + 7) % 7)), [month, weekStartsOn]);
138
+ const rows = useMemo(() => Array.from({ length: 6 }, (_, row) => Array.from({ length: 7 }, (_, col) => addDays(first, row * 7 + col))), [first]);
139
+ const titleId = `${idBase}-${keyOf(month)}`;
140
+ const inMonth = (date) => monthDiff(date, month) === 0;
141
+ const lo = range?.start, hi = range?.end;
142
+ const loKey = lo ? keyOf(lo) : "", hiKey = hi ? keyOf(hi) : "";
143
+ /** Each week's slice of the range, in percent of the row. Keyed by the range's days so bars only move when it does. */
144
+ const segments = useMemo(() => {
145
+ if (!loKey || !hiKey)
146
+ return rows.map(() => null);
147
+ const start = fromKey(loKey), end = fromKey(hiKey);
148
+ const unit = 100 / 7;
149
+ const colOf = (date) => dayDiff(date, first) % 7;
150
+ return rows.map(row => {
151
+ const days = row.filter(date => monthDiff(date, month) === 0);
152
+ if (!days.length)
153
+ return null;
154
+ const rowFirst = days[0], rowLast = days[days.length - 1];
155
+ const from = dayDiff(start, rowFirst) > 0 ? start : rowFirst, to = dayDiff(end, rowLast) < 0 ? end : rowLast;
156
+ if (dayDiff(from, to) <= 0) {
157
+ const a = colOf(from), b = colOf(to);
158
+ return { left: a * unit, width: (b - a + 1) * unit };
159
+ }
160
+ // The week sits wholly before or after the range: collapse at the edge the range would grow in from.
161
+ if (dayDiff(rowLast, start) < 0)
162
+ return { left: (colOf(rowLast) + 1) * unit, width: 0 };
163
+ return { left: colOf(rowFirst) * unit, width: 0 };
164
+ });
165
+ }, [first, hiKey, loKey, month, rows]);
166
+ const startCell = useMemo(() => loKey && monthDiff(fromKey(loKey), month) === 0 ? cellIn(fromKey(loKey), first) : null, [loKey, month, first]);
167
+ const endCell = useMemo(() => hiKey && hiKey !== loKey && monthDiff(fromKey(hiKey), month) === 0 ? cellIn(fromKey(hiKey), first) : null, [hiKey, loKey, month, first]);
168
+ return _jsxs("div", { className: styles.month, children: [_jsx("p", { id: titleId, className: styles.monthTitle, children: formatters.title.format(month) }), _jsxs("div", { role: "grid", "aria-labelledby": titleId, className: styles.grid, children: [_jsx("div", { role: "row", className: styles.weekdays, children: rows[0].map(date => _jsx("span", { role: "columnheader", "aria-label": formatters.weekdayLong.format(date), children: formatters.weekday.format(date).slice(0, 2) }, date.getDay())) }), _jsxs("div", { className: styles.weeks, children: [_jsxs("span", { className: styles.layer, "aria-hidden": "true", children: [segments.map((segment, row) => _jsx(Bar, { row: row, segment: segment, reduced: reduced }, row)), _jsx(Thumb, { cell: startCell, reduced: reduced }), _jsx(Thumb, { cell: endCell, reduced: reduced })] }), rows.map((row, index) => _jsx("div", { role: "row", className: styles.week, children: row.map(date => {
169
+ if (!inMonth(date))
170
+ return _jsx("span", { role: "gridcell", className: styles.blank }, keyOf(date));
171
+ const key = keyOf(date);
172
+ const disabled = Boolean((minDate && dayDiff(date, minDate) < 0) || (maxDate && dayDiff(date, maxDate) > 0));
173
+ const inRange = Boolean(lo && hi && dayDiff(date, lo) >= 0 && dayDiff(date, hi) <= 0);
174
+ const edge = sameDay(date, lo) || sameDay(date, hi);
175
+ const isToday = sameDay(date, today);
176
+ return _jsx("span", { role: "gridcell", "aria-selected": inRange, className: styles.cell, children: _jsx("button", { type: "button", className: styles.day, "data-date": key, "data-edge": edge || undefined, "data-range": inRange || undefined, "data-today": isToday || undefined, tabIndex: key === tabbable ? 0 : -1, disabled: disabled, "aria-current": isToday ? "date" : undefined, "aria-label": formatters.day.format(date), onClick: () => onPick(date), onPointerEnter: () => { if (!disabled)
177
+ onHover(date); }, onFocus: () => onFocusDay(date), onKeyDown: event => onKey(event, date), children: _jsx("span", { children: date.getDate() }) }) }, key);
178
+ }) }, index))] })] })] });
179
+ }
180
+ /** The visible months travel together; a leaving set turns inert so focus and queries only find the current one. */
181
+ function Months({ children, direction, reduced }) {
182
+ const present = useIsPresent();
183
+ return _jsx(motion.div, { className: styles.months, "data-current": present || undefined, inert: !present || undefined, custom: direction, variants: reduced ? fade : slide, initial: "enter", animate: "center", exit: "exit", children: children });
184
+ }
185
+ export function DateRangePicker({ value, defaultValue = null, onChange, label = "Date range", placeholder = "Select dates", presets = defaultDateRangePresets, minDate, maxDate, weekStartsOn = 0, locale = "en-US", months = "auto", boundary, className }) {
186
+ const reduced = useReducedFlag();
187
+ const today = useToday();
188
+ const uid = useId();
189
+ const [inner, setInner] = useState(defaultValue);
190
+ const committed = value !== undefined ? value : inner;
191
+ const [open, setOpen] = useState(false);
192
+ const [compact, setCompact] = useState(false);
193
+ const [panelWidth, setPanelWidth] = useState(0);
194
+ const [view, setView] = useState(() => monthStart(committed?.end ?? new Date(2000, 0, 1)));
195
+ const [direction, setDirection] = useState(1);
196
+ const [draft, setDraft] = useState(committed);
197
+ const [anchor, setAnchor] = useState(null);
198
+ const [hover, setHover] = useState(null);
199
+ const [focusKey, setFocusKey] = useState("");
200
+ const formatters = useMemo(() => ({
201
+ label: new Intl.DateTimeFormat(locale, { month: "short", day: "numeric", year: "numeric" }),
202
+ title: new Intl.DateTimeFormat(locale, { month: "long", year: "numeric" }),
203
+ day: new Intl.DateTimeFormat(locale, { weekday: "long", month: "long", day: "numeric", year: "numeric" }),
204
+ weekday: new Intl.DateTimeFormat(locale, { weekday: "short" }),
205
+ weekdayLong: new Intl.DateTimeFormat(locale, { weekday: "long" }),
206
+ }), [locale]);
207
+ const format = useCallback((range) => !range ? placeholder : sameDay(range.start, range.end) ? formatters.label.format(range.start) : formatters.label.formatRange(range.start, range.end), [formatters, placeholder]);
208
+ const count = compact ? 1 : 2;
209
+ const visible = useMemo(() => Array.from({ length: count }, (_, index) => addMonths(view, index)), [count, view]);
210
+ const shown = anchor ? ordered(anchor, hover ?? anchor) : draft;
211
+ const activePreset = today && !anchor ? presets.findIndex(preset => sameRange(preset.range(today), draft)) : -1;
212
+ const days = shown ? dayDiff(shown.end, shown.start) + 1 : 0;
213
+ const committedDirection = useDirection(committed);
214
+ const shownDirection = useDirection(shown);
215
+ const rootRef = useRef(null);
216
+ const triggerRef = useRef(null);
217
+ const panelRef = useRef(null);
218
+ const railRef = useRef(null);
219
+ const pendingFocus = useRef(null);
220
+ /* ---------- One surface: the trigger's box springs to the panel's box and back ---------- */
221
+ // Before the first measurement the surface simply fills the root and the root wraps the trigger, so server markup already looks right.
222
+ const width = useMotionValue("100%"), height = useMotionValue("100%"), radius = useMotionValue(TRIGGER_RADIUS), x = useMotionValue(0), rootWidth = useMotionValue("auto");
223
+ const sizes = useRef({ trigger: { w: 0, h: 0 }, panel: null });
224
+ const live = useRef({ open: false, reduced: false, ready: false });
225
+ const queued = useRef(false);
226
+ const boundaryRef = useRef(boundary);
227
+ useLayoutEffect(() => { boundaryRef.current = boundary; }, [boundary]);
228
+ const bounds = useCallback(() => {
229
+ const element = boundaryRef.current?.();
230
+ const viewport = { left: 0, right: document.documentElement.clientWidth };
231
+ if (!element)
232
+ return viewport;
233
+ const rect = element.getBoundingClientRect();
234
+ return { left: Math.max(viewport.left, rect.left), right: Math.min(viewport.right, rect.right) };
235
+ }, []);
236
+ const update = useCallback(() => {
237
+ queued.current = false;
238
+ const { trigger, panel } = sizes.current, state = live.current;
239
+ if (!trigger.w)
240
+ return;
241
+ const openPanel = state.open && panel ? panel : null;
242
+ const target = openPanel ?? trigger;
243
+ let offset = 0;
244
+ if (openPanel && rootRef.current) {
245
+ const rect = rootRef.current.getBoundingClientRect(), box = bounds();
246
+ offset = Math.min(0, box.right - EDGE - (rect.left + openPanel.w));
247
+ offset = Math.max(offset, box.left + EDGE - rect.left);
248
+ }
249
+ const r = openPanel ? PANEL_RADIUS : TRIGGER_RADIUS;
250
+ if (!state.ready || state.reduced) {
251
+ width.jump(target.w);
252
+ height.jump(target.h);
253
+ radius.jump(r);
254
+ x.jump(offset);
255
+ rootWidth.jump(trigger.w);
256
+ state.ready = true;
257
+ return;
258
+ }
259
+ const spring = openPanel ? GROW : state.open ? GROW : SHRINK;
260
+ animate(width, target.w, spring);
261
+ animate(height, target.h, spring);
262
+ animate(radius, r, spring);
263
+ animate(x, offset, spring);
264
+ animate(rootWidth, trigger.w, motionTokens.spring.morph);
265
+ }, [bounds, height, radius, rootWidth, width, x]);
266
+ const schedule = useCallback(() => {
267
+ if (queued.current)
268
+ return;
269
+ queued.current = true;
270
+ queueMicrotask(update);
271
+ }, [update]);
272
+ useLayoutEffect(() => { live.current.open = open; live.current.reduced = reduced; if (!open)
273
+ sizes.current.panel = null; schedule(); }, [open, reduced, schedule]);
274
+ useLayoutEffect(() => {
275
+ const node = triggerRef.current;
276
+ if (!node)
277
+ return;
278
+ const read = () => { sizes.current.trigger = { w: node.offsetWidth, h: node.offsetHeight }; schedule(); };
279
+ read();
280
+ if (typeof ResizeObserver === "undefined")
281
+ return;
282
+ const observer = new ResizeObserver(read);
283
+ observer.observe(node);
284
+ return () => observer.disconnect();
285
+ }, [schedule]);
286
+ useLayoutEffect(() => {
287
+ const node = panelRef.current;
288
+ if (!open || !node)
289
+ return;
290
+ const read = () => { sizes.current.panel = { w: node.offsetWidth, h: node.offsetHeight }; schedule(); };
291
+ read();
292
+ if (typeof ResizeObserver === "undefined")
293
+ return;
294
+ const observer = new ResizeObserver(read);
295
+ observer.observe(node);
296
+ return () => observer.disconnect();
297
+ }, [open, schedule]);
298
+ // On a short screen (a phone, a trigger low on the page) the panel can open below the fold, with Apply out of reach.
299
+ // Once it has its size, scroll just enough to bring its bottom into view, never past its top. After closing, the
300
+ // trigger comes back into view if that scroll left it off screen.
301
+ useEffect(() => {
302
+ if (!open)
303
+ return;
304
+ const frame = requestAnimationFrame(() => {
305
+ const node = panelRef.current;
306
+ if (!node)
307
+ return;
308
+ const rect = node.getBoundingClientRect();
309
+ const overflow = rect.bottom - (window.innerHeight - EDGE);
310
+ if (overflow <= 0)
311
+ return;
312
+ window.scrollBy({ top: Math.min(overflow, Math.max(0, rect.top - EDGE)), behavior: reduced ? "instant" : "smooth" });
313
+ });
314
+ return () => cancelAnimationFrame(frame);
315
+ }, [open, reduced]);
316
+ useEffect(() => {
317
+ if (open)
318
+ return;
319
+ const trigger = triggerRef.current;
320
+ if (!trigger)
321
+ return;
322
+ const rect = trigger.getBoundingClientRect();
323
+ if (rect.bottom < 0 || rect.top > window.innerHeight)
324
+ trigger.scrollIntoView({ block: "center", behavior: reduced ? "instant" : "smooth" });
325
+ }, [open, reduced]);
326
+ /* ---------- Layout: two months when there is room, one compact month otherwise ---------- */
327
+ const measureLayout = useCallback(() => {
328
+ const box = bounds();
329
+ const available = Math.max(0, box.right - box.left - EDGE * 2);
330
+ const single = months === 1 || (months === "auto" && available < WIDE_MIN);
331
+ return { single, width: Math.min(352, available) };
332
+ }, [bounds, months]);
333
+ useEffect(() => {
334
+ if (!open)
335
+ return;
336
+ const onResize = () => { const next = measureLayout(); setCompact(next.single); setPanelWidth(next.width); schedule(); };
337
+ window.addEventListener("resize", onResize);
338
+ return () => window.removeEventListener("resize", onResize);
339
+ }, [measureLayout, open, schedule]);
340
+ const viewFor = (range, single, fallback) => {
341
+ const end = monthStart(range?.end ?? fallback);
342
+ return single ? end : addMonths(end, -1);
343
+ };
344
+ const openPanel = () => {
345
+ if (!today)
346
+ return;
347
+ const layout = measureLayout();
348
+ setCompact(layout.single);
349
+ setPanelWidth(layout.width);
350
+ setDraft(committed);
351
+ setAnchor(null);
352
+ setHover(null);
353
+ setView(viewFor(committed, layout.single, today));
354
+ setDirection(0);
355
+ setFocusKey(keyOf(committed?.start ?? today));
356
+ pendingFocus.current = "day";
357
+ setOpen(true);
358
+ };
359
+ const close = useCallback((focus) => {
360
+ if (focus)
361
+ pendingFocus.current = "trigger";
362
+ setDirection(0);
363
+ setOpen(false);
364
+ setAnchor(null);
365
+ setHover(null);
366
+ }, []);
367
+ const apply = () => {
368
+ const next = anchor ? { start: anchor, end: anchor } : draft;
369
+ if (!next)
370
+ return;
371
+ if (value === undefined)
372
+ setInner(next);
373
+ onChange?.(next);
374
+ close(true);
375
+ };
376
+ /* ---------- Focus management ---------- */
377
+ useLayoutEffect(() => {
378
+ const target = pendingFocus.current;
379
+ if (!target)
380
+ return;
381
+ if (target === "trigger" && !open) {
382
+ pendingFocus.current = null;
383
+ triggerRef.current?.focus({ preventScroll: true });
384
+ return;
385
+ }
386
+ if (target === "day" && open) {
387
+ const node = panelRef.current?.querySelector(`[data-current] [data-date="${focusKey}"]`);
388
+ if (node) {
389
+ pendingFocus.current = null;
390
+ node.focus({ preventScroll: true });
391
+ }
392
+ }
393
+ }, [focusKey, open, view, compact]);
394
+ useEffect(() => {
395
+ if (!open)
396
+ return;
397
+ const down = (event) => { if (!rootRef.current?.contains(event.target))
398
+ close(false); };
399
+ document.addEventListener("pointerdown", down);
400
+ return () => document.removeEventListener("pointerdown", down);
401
+ }, [close, open]);
402
+ const onRootKey = (event) => {
403
+ if (event.key === "Escape" && open) {
404
+ event.preventDefault();
405
+ event.stopPropagation();
406
+ close(true);
407
+ }
408
+ };
409
+ const onRootBlur = (event) => {
410
+ const next = event.relatedTarget;
411
+ if (open && next && !event.currentTarget.contains(next))
412
+ close(false);
413
+ };
414
+ /* ---------- Month navigation and picking ---------- */
415
+ const goTo = (next) => {
416
+ const delta = monthDiff(next, view);
417
+ if (!delta)
418
+ return;
419
+ setDirection(Math.sign(delta));
420
+ setView(next);
421
+ };
422
+ /** Keeps a range on screen: nothing moves when it already fits, otherwise its last month lands on the right. */
423
+ const reveal = (range) => {
424
+ const first = view, last = addMonths(view, count - 1);
425
+ if (monthDiff(range.start, first) >= 0 && monthDiff(range.end, last) <= 0)
426
+ return;
427
+ goTo(viewFor(range, compact, range.end));
428
+ };
429
+ const pick = (date) => {
430
+ setFocusKey(keyOf(date));
431
+ if (!anchor) {
432
+ setAnchor(date);
433
+ setHover(date);
434
+ return;
435
+ }
436
+ setDraft(ordered(anchor, date));
437
+ setAnchor(null);
438
+ setHover(null);
439
+ };
440
+ const choosePreset = (preset) => {
441
+ if (!today)
442
+ return;
443
+ const range = preset.range(today);
444
+ setDraft(range);
445
+ setAnchor(null);
446
+ setHover(null);
447
+ setFocusKey(keyOf(range.start));
448
+ reveal(range);
449
+ };
450
+ const onDayKey = (event, date) => {
451
+ const weekday = (date.getDay() - weekStartsOn + 7) % 7;
452
+ const moves = {
453
+ ArrowLeft: () => addDays(date, -1), ArrowRight: () => addDays(date, 1), ArrowUp: () => addDays(date, -7), ArrowDown: () => addDays(date, 7),
454
+ Home: () => addDays(date, -weekday), End: () => addDays(date, 6 - weekday),
455
+ PageUp: () => shiftMonths(date, event.shiftKey ? -12 : -1), PageDown: () => shiftMonths(date, event.shiftKey ? 12 : 1),
456
+ };
457
+ const move = moves[event.key];
458
+ if (!move)
459
+ return;
460
+ event.preventDefault();
461
+ const next = clampDate(move(), minDate, maxDate);
462
+ setFocusKey(keyOf(next));
463
+ if (anchor)
464
+ setHover(next);
465
+ pendingFocus.current = "day";
466
+ const first = view, last = addMonths(view, count - 1);
467
+ if (monthDiff(next, first) < 0)
468
+ goTo(monthStart(next));
469
+ else if (monthDiff(next, last) > 0)
470
+ goTo(addMonths(monthStart(next), -(count - 1)));
471
+ };
472
+ const onRailKey = (event) => {
473
+ const keys = compact ? ["ArrowLeft", "ArrowRight"] : ["ArrowUp", "ArrowDown"];
474
+ if (!keys.includes(event.key))
475
+ return;
476
+ const buttons = Array.from(event.currentTarget.querySelectorAll("[data-preset]"));
477
+ const at = buttons.indexOf(document.activeElement);
478
+ if (at < 0)
479
+ return;
480
+ event.preventDefault();
481
+ buttons[(at + (event.key === keys[1] ? 1 : -1) + buttons.length) % buttons.length]?.focus();
482
+ };
483
+ /** The day that owns the tab stop: the focused day when it is on screen, else the first visible day of the range, else the 1st. */
484
+ const tabbable = useMemo(() => {
485
+ const onScreen = (date) => visible.some(month => monthDiff(date, month) === 0);
486
+ if (focusKey && onScreen(fromKey(focusKey)))
487
+ return focusKey;
488
+ if (shown && onScreen(shown.start))
489
+ return keyOf(shown.start);
490
+ return keyOf(visible[0]);
491
+ }, [focusKey, shown, visible]);
492
+ /* ---------- Preset highlight glides between presets ---------- */
493
+ const gx = useMotionValue(0), gy = useMotionValue(0), gw = useMotionValue(0), gh = useMotionValue(0), go = useMotionValue(0);
494
+ useLayoutEffect(() => {
495
+ const rail = railRef.current;
496
+ const node = activePreset < 0 ? null : rail?.querySelector(`[data-preset="${activePreset}"]`);
497
+ if (!rail || !node) {
498
+ animate(go, 0, { duration: reduced ? 0 : .14 });
499
+ return;
500
+ }
501
+ const place = [node.offsetLeft, node.offsetTop, node.offsetWidth, node.offsetHeight];
502
+ if (go.get() < .05 || reduced) {
503
+ gx.jump(place[0]);
504
+ gy.jump(place[1]);
505
+ gw.jump(place[2]);
506
+ gh.jump(place[3]);
507
+ }
508
+ else {
509
+ animate(gx, place[0], GLIDE);
510
+ animate(gy, place[1], GLIDE);
511
+ animate(gw, place[2], GLIDE);
512
+ animate(gh, place[3], GLIDE);
513
+ }
514
+ animate(go, 1, { duration: reduced ? 0 : .16 });
515
+ if (compact && (node.offsetLeft < rail.scrollLeft || node.offsetLeft + node.offsetWidth > rail.scrollLeft + rail.clientWidth)) {
516
+ rail.scrollTo({ left: node.offsetLeft - 12, behavior: reduced ? "auto" : "smooth" });
517
+ }
518
+ }, [activePreset, compact, go, gh, gw, gx, gy, open, reduced]);
519
+ /* ---------- Copy ---------- */
520
+ const committedText = format(committed);
521
+ const shownText = shown ? format(shown) : "No dates";
522
+ const countText = shown ? `${days} ${days === 1 ? "day" : "days"}` : "";
523
+ const status = !open ? "" : anchor ? `Start ${formatters.label.format(anchor)}. Choose an end date.` : shown ? `${shownText}, ${countText}` : "";
524
+ const layoutId = reduced ? undefined : `${uid}-value`;
525
+ const cellSize = compact && panelWidth ? Math.max(32, Math.min(42, Math.floor((panelWidth - 24) / 7))) : WIDE_CELL;
526
+ const quiet = open ? (reduced ? { opacity: 0 } : { opacity: 0, filter: `blur(${blur.subtle}px)` }) : { opacity: 1, filter: "blur(0px)" };
527
+ const quietTransition = open ? { duration: .12, ease: standardEase } : { duration: .22, ease: enterEase, delay: reduced ? 0 : .1 };
528
+ return _jsxs(motion.div, { ref: rootRef, className: [styles.root, className].filter(Boolean).join(" "), style: { width: rootWidth }, "data-open": open || undefined, onKeyDown: onRootKey, onBlur: onRootBlur, children: [_jsxs("button", { ref: triggerRef, type: "button", className: styles.trigger, "aria-haspopup": "dialog", "aria-expanded": open, "aria-controls": open ? `${uid}-panel` : undefined, "aria-label": `${label}: ${committedText}`, disabled: !today, inert: open || undefined, onClick: openPanel, children: [_jsx(motion.span, { className: styles.triggerIcon, initial: false, animate: quiet, transition: quietTransition, children: _jsx(CalendarDays, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) }), open || !layoutId
529
+ ? _jsx(motion.span, { className: styles.value, "data-empty": !committed || undefined, initial: false, animate: open ? { opacity: 0 } : { opacity: 1 }, transition: quietTransition, children: _jsx(Rolling, { text: committedText, direction: committedDirection, reduced: reduced }) }, "resting")
530
+ : _jsx(motion.span, { layoutId: layoutId, layout: "position", className: styles.value, "data-empty": !committed || undefined, transition: SHRINK, children: _jsx(Rolling, { text: committedText, direction: committedDirection, reduced: reduced }) }, "shared"), _jsx(motion.span, { className: styles.chevron, initial: false, animate: quiet, transition: quietTransition, children: _jsx(ChevronDown, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) })] }), _jsx(motion.div, { className: styles.surface, style: { width, height, borderRadius: radius, x }, children: _jsx(AnimatePresence, { children: open && today && _jsxs(motion.div, { ref: panelRef, id: `${uid}-panel`, className: styles.panel, role: "dialog", "aria-label": label, "data-compact": compact || undefined, style: { "--cell": `${cellSize}px`, width: compact && panelWidth ? panelWidth : undefined }, exit: { opacity: 1, transition: { duration: .14 } }, children: [_jsxs(motion.div, { className: styles.body, variants: reduced ? faceFade : faceIn, initial: "hidden", animate: "shown", exit: "gone", children: [_jsxs("div", { ref: railRef, className: styles.rail, role: "group", "aria-label": "Presets", onKeyDown: onRailKey, children: [_jsx(motion.span, { className: styles.railHighlight, style: { x: gx, y: gy, width: gw, height: gh, opacity: go }, "aria-hidden": "true" }), presets.map((preset, index) => _jsx("button", { type: "button", className: styles.preset, "data-preset": index, "aria-pressed": index === activePreset, onClick: () => choosePreset(preset), children: preset.label }, preset.label))] }), _jsxs("div", { className: styles.calendars, onPointerLeave: () => { if (anchor)
531
+ setHover(null); }, children: [_jsx("button", { type: "button", className: styles.navButton, "data-side": "prev", "aria-label": "Previous month", onClick: () => goTo(addMonths(view, -1)), disabled: Boolean(minDate && monthDiff(view, minDate) <= 0), children: _jsx(ChevronLeft, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) }), _jsx("button", { type: "button", className: styles.navButton, "data-side": "next", "aria-label": "Next month", onClick: () => goTo(addMonths(view, 1)), disabled: Boolean(maxDate && monthDiff(addMonths(view, count - 1), maxDate) >= 0), children: _jsx(ChevronRight, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) }), _jsx("div", { className: styles.viewport, children: _jsx(AnimatePresence, { initial: false, mode: "popLayout", custom: direction, children: _jsx(Months, { direction: direction, reduced: reduced, children: visible.map(month => _jsx(Month, { month: month, range: shown, tabbable: tabbable, today: today, minDate: minDate, maxDate: maxDate, weekStartsOn: weekStartsOn, reduced: reduced, formatters: formatters, idBase: uid, onPick: pick, onHover: date => { if (anchor)
532
+ setHover(date); }, onKey: onDayKey, onFocusDay: date => setFocusKey(keyOf(date)) }, keyOf(month))) }, `${keyOf(view)}-${count}`) }) })] })] }), _jsxs("div", { className: styles.footer, children: [_jsxs("div", { className: styles.summary, children: [_jsx(motion.span, { layoutId: layoutId, layout: layoutId ? "position" : undefined, className: styles.summaryValue, transition: GROW, variants: layoutId ? undefined : faceFade, initial: layoutId ? undefined : "hidden", animate: layoutId ? undefined : "shown", exit: layoutId ? undefined : "gone", children: _jsx(Rolling, { text: shownText, direction: shownDirection, reduced: reduced }) }), _jsx(motion.span, { className: styles.summaryCount, variants: reduced ? faceFade : faceIn, initial: "hidden", animate: "shown", exit: "gone", children: countText && _jsx(Rolling, { text: anchor && sameDay(anchor, hover) ? "Pick an end date" : countText, direction: shownDirection, reduced: reduced }) })] }), _jsxs(motion.div, { className: styles.actions, variants: reduced ? faceFade : faceIn, initial: "hidden", animate: "shown", exit: "gone", children: [_jsx("button", { type: "button", className: styles.ghost, onClick: () => close(true), children: "Cancel" }), _jsx("button", { type: "button", className: styles.primary, onClick: apply, disabled: !shown, children: "Apply" })] })] }), _jsx("p", { className: styles.srOnly, "aria-live": "polite", children: status })] }, "panel") }) })] });
533
+ }
534
+ export default DateRangePicker;