@mastra/playground-ui 42.0.0-alpha.8 → 42.0.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 (203) hide show
  1. package/CHANGELOG.md +234 -0
  2. package/dist/Card-BjFMX3pD.js +96 -0
  3. package/dist/Card-BjFMX3pD.js.map +1 -0
  4. package/dist/{Card-CHeBhO_f.cjs → Card-WU2qkiHB.cjs} +42 -9
  5. package/dist/Card-WU2qkiHB.cjs.map +1 -0
  6. package/dist/components/Breadcrumb.cjs.js +2 -2
  7. package/dist/components/Breadcrumb.cjs.js.map +1 -1
  8. package/dist/components/Breadcrumb.es.js +2 -2
  9. package/dist/components/Breadcrumb.es.js.map +1 -1
  10. package/dist/components/Card.cjs.js +2 -1
  11. package/dist/components/Card.cjs.js.map +1 -1
  12. package/dist/components/Card.es.js +1 -1
  13. package/dist/components/Command.cjs.js +1 -1
  14. package/dist/components/Command.es.js +1 -1
  15. package/dist/components/Composer.cjs.js +3 -3
  16. package/dist/components/Composer.cjs.js.map +1 -1
  17. package/dist/components/Composer.es.js +3 -3
  18. package/dist/components/Composer.es.js.map +1 -1
  19. package/dist/components/DataCodeSection.cjs.js +1 -1
  20. package/dist/components/DataCodeSection.es.js +1 -1
  21. package/dist/components/DataList.cjs.js +4 -4
  22. package/dist/components/DataList.es.js +4 -4
  23. package/dist/components/DataPanel.cjs.js +1 -1
  24. package/dist/components/DataPanel.es.js +1 -1
  25. package/dist/components/DateRangeTimeline.cjs.js +1011 -0
  26. package/dist/components/DateRangeTimeline.cjs.js.map +1 -0
  27. package/dist/components/DateRangeTimeline.d.ts +2 -0
  28. package/dist/components/DateRangeTimeline.es.js +1005 -0
  29. package/dist/components/DateRangeTimeline.es.js.map +1 -0
  30. package/dist/components/DateTimePicker.cjs.js +4 -3
  31. package/dist/components/DateTimePicker.cjs.js.map +1 -1
  32. package/dist/components/DateTimePicker.es.js +2 -1
  33. package/dist/components/DateTimePicker.es.js.map +1 -1
  34. package/dist/components/DateTimeRangePicker.cjs.js +1 -1
  35. package/dist/components/DateTimeRangePicker.es.js +1 -1
  36. package/dist/components/EnvironmentVariablesEditor.cjs.js +1 -1
  37. package/dist/components/EnvironmentVariablesEditor.es.js +1 -1
  38. package/dist/components/FormFieldBlocks.cjs.js +1 -1
  39. package/dist/components/FormFieldBlocks.es.js +1 -1
  40. package/dist/components/HorizontalBars.cjs.js +1 -1
  41. package/dist/components/HorizontalBars.es.js +1 -1
  42. package/dist/components/ListSearch.cjs.js +1 -1
  43. package/dist/components/ListSearch.es.js +1 -1
  44. package/dist/components/LogsDataList.cjs.js +2 -2
  45. package/dist/components/LogsDataList.es.js +2 -2
  46. package/dist/components/MainSidebar.cjs.js +39 -12
  47. package/dist/components/MainSidebar.cjs.js.map +1 -1
  48. package/dist/components/MainSidebar.es.js +39 -12
  49. package/dist/components/MainSidebar.es.js.map +1 -1
  50. package/dist/components/MetricsLineChart.cjs.js +1 -1
  51. package/dist/components/MetricsLineChart.es.js +1 -1
  52. package/dist/components/SankeyChart.cjs.js +346 -46
  53. package/dist/components/SankeyChart.cjs.js.map +1 -1
  54. package/dist/components/SankeyChart.es.js +348 -49
  55. package/dist/components/SankeyChart.es.js.map +1 -1
  56. package/dist/components/ScrollArea.cjs.js +1 -1
  57. package/dist/components/ScrollArea.es.js +1 -1
  58. package/dist/components/ThreadRail.cjs.js +1 -1
  59. package/dist/components/ThreadRail.es.js +1 -1
  60. package/dist/components/Toaster.cjs.js +1 -0
  61. package/dist/components/Toaster.cjs.js.map +1 -1
  62. package/dist/components/Toaster.es.js +1 -1
  63. package/dist/{data-code-section-CGy0owio.js → data-code-section-BjqDCzy3.js} +2 -2
  64. package/dist/{data-code-section-CGy0owio.js.map → data-code-section-BjqDCzy3.js.map} +1 -1
  65. package/dist/{data-code-section-D5yXGjk5.cjs → data-code-section-D0BwpqvV.cjs} +2 -2
  66. package/dist/{data-code-section-D5yXGjk5.cjs.map → data-code-section-D0BwpqvV.cjs.map} +1 -1
  67. package/dist/{data-list-BwGRWaNd.cjs → data-list-CQWBPREX.cjs} +3 -3
  68. package/dist/{data-list-BwGRWaNd.cjs.map → data-list-CQWBPREX.cjs.map} +1 -1
  69. package/dist/{data-list-By3xmGvT.js → data-list-Ca8j8X2e.js} +3 -3
  70. package/dist/{data-list-By3xmGvT.js.map → data-list-Ca8j8X2e.js.map} +1 -1
  71. package/dist/{data-list-row-wrapper-D0eATMXZ.cjs → data-list-row-wrapper-D-ieH7I9.cjs} +2 -2
  72. package/dist/{data-list-row-wrapper-D0eATMXZ.cjs.map → data-list-row-wrapper-D-ieH7I9.cjs.map} +1 -1
  73. package/dist/{data-list-row-wrapper--77eZufv.js → data-list-row-wrapper-dMY1gERX.js} +2 -2
  74. package/dist/{data-list-row-wrapper--77eZufv.js.map → data-list-row-wrapper-dMY1gERX.js.map} +1 -1
  75. package/dist/{data-list-skeleton-BGvCCBtu.cjs → data-list-skeleton-C-DkvqbS.cjs} +2 -2
  76. package/dist/{data-list-skeleton-BGvCCBtu.cjs.map → data-list-skeleton-C-DkvqbS.cjs.map} +1 -1
  77. package/dist/{data-list-skeleton-rDMLgcEJ.js → data-list-skeleton-CIxMXF73.js} +2 -2
  78. package/dist/{data-list-skeleton-rDMLgcEJ.js.map → data-list-skeleton-CIxMXF73.js.map} +1 -1
  79. package/dist/{data-list-top-cell-CT5c7UVt.cjs → data-list-top-cell-CL-FVkPX.cjs} +9 -4
  80. package/dist/data-list-top-cell-CL-FVkPX.cjs.map +1 -0
  81. package/dist/{data-list-top-cell-B5qdzDE4.js → data-list-top-cell-QZpCLDIZ.js} +9 -4
  82. package/dist/data-list-top-cell-QZpCLDIZ.js.map +1 -0
  83. package/dist/{data-panel-BELvPVPG.js → data-panel-BGpCzY5K.js} +2 -2
  84. package/dist/{data-panel-BELvPVPG.js.map → data-panel-BGpCzY5K.js.map} +1 -1
  85. package/dist/{data-panel-C6Aa1nFn.cjs → data-panel-CsonAnlK.cjs} +2 -2
  86. package/dist/{data-panel-C6Aa1nFn.cjs.map → data-panel-CsonAnlK.cjs.map} +1 -1
  87. package/dist/date-picker-BpKciwOX.js +48 -0
  88. package/dist/date-picker-BpKciwOX.js.map +1 -0
  89. package/dist/date-picker-DrWmUawi.cjs +50 -0
  90. package/dist/date-picker-DrWmUawi.cjs.map +1 -0
  91. package/dist/{date-time-range-picker-BRQx-ox5.cjs → date-time-range-picker-CF4a39P7.cjs} +5 -4
  92. package/dist/{date-time-range-picker-BRQx-ox5.cjs.map → date-time-range-picker-CF4a39P7.cjs.map} +1 -1
  93. package/dist/{date-time-range-picker-Cw4Ac_rW.js → date-time-range-picker-DDNeOyj9.js} +3 -2
  94. package/dist/{date-time-range-picker-Cw4Ac_rW.js.map → date-time-range-picker-DDNeOyj9.js.map} +1 -1
  95. package/dist/domains/logs/components/logs-list-view.cjs.js +2 -2
  96. package/dist/domains/logs/components/logs-list-view.es.js +2 -2
  97. package/dist/domains/metrics/components/date-range-selector.cjs.js +1 -1
  98. package/dist/domains/metrics/components/date-range-selector.es.js +1 -1
  99. package/dist/domains/metrics/components/latency-card-view.cjs.js +1 -1
  100. package/dist/domains/metrics/components/latency-card-view.es.js +1 -1
  101. package/dist/domains/metrics/components/memory-card-view.cjs.js +1 -1
  102. package/dist/domains/metrics/components/memory-card-view.es.js +1 -1
  103. package/dist/domains/metrics/components/model-usage-cost-card-view.cjs.js +1 -1
  104. package/dist/domains/metrics/components/model-usage-cost-card-view.es.js +1 -1
  105. package/dist/domains/metrics/components/scores-card-view.cjs.js +2 -2
  106. package/dist/domains/metrics/components/scores-card-view.es.js +2 -2
  107. package/dist/domains/metrics/components/token-usage-by-agent-card-view.cjs.js +1 -1
  108. package/dist/domains/metrics/components/token-usage-by-agent-card-view.es.js +1 -1
  109. package/dist/domains/metrics/components/token-usage-timeline-card-view.cjs.js +1 -1
  110. package/dist/domains/metrics/components/token-usage-timeline-card-view.es.js +1 -1
  111. package/dist/domains/metrics/components/traces-volume-card-view.cjs.js +1 -1
  112. package/dist/domains/metrics/components/traces-volume-card-view.es.js +1 -1
  113. package/dist/domains/traces/components/span-data-panel-view.cjs.js +1 -1
  114. package/dist/domains/traces/components/span-data-panel-view.es.js +1 -1
  115. package/dist/domains/traces/components/trace-data-panel-view.cjs.js +1 -1
  116. package/dist/domains/traces/components/trace-data-panel-view.es.js +1 -1
  117. package/dist/domains/traces/components/traces-list-view.cjs.js +2 -2
  118. package/dist/domains/traces/components/traces-list-view.es.js +2 -2
  119. package/dist/ee/signals/components/signals-empty-state.cjs.js +1 -1
  120. package/dist/ee/signals/components/signals-empty-state.es.js +1 -1
  121. package/dist/ee/topics/components/topic-trace-summary-list.cjs.js +1 -1
  122. package/dist/ee/topics/components/topic-trace-summary-list.es.js +1 -1
  123. package/dist/{horizontal-bars-DmWxvGBL.js → horizontal-bars-CHoNhl-X.js} +2 -2
  124. package/dist/{horizontal-bars-DmWxvGBL.js.map → horizontal-bars-CHoNhl-X.js.map} +1 -1
  125. package/dist/{horizontal-bars-C39TYAB0.cjs → horizontal-bars-aRhVh7mi.cjs} +2 -2
  126. package/dist/{horizontal-bars-C39TYAB0.cjs.map → horizontal-bars-aRhVh7mi.cjs.map} +1 -1
  127. package/dist/{logs-data-list-BYbBzVcC.cjs → logs-data-list-4R9mXlZ5.cjs} +3 -3
  128. package/dist/{logs-data-list-BYbBzVcC.cjs.map → logs-data-list-4R9mXlZ5.cjs.map} +1 -1
  129. package/dist/{logs-data-list-DbfZpFx8.js → logs-data-list-BXLk5vqv.js} +3 -3
  130. package/dist/{logs-data-list-DbfZpFx8.js.map → logs-data-list-BXLk5vqv.js.map} +1 -1
  131. package/dist/{metrics-line-chart-ByY_VXfb.js → metrics-line-chart-COIxay_u.js} +6 -3
  132. package/dist/metrics-line-chart-COIxay_u.js.map +1 -0
  133. package/dist/{metrics-line-chart-FvsUXcL7.cjs → metrics-line-chart-kZ1Ed_ga.cjs} +6 -3
  134. package/dist/metrics-line-chart-kZ1Ed_ga.cjs.map +1 -0
  135. package/dist/{scroll-area-C3deZ3yj.cjs → scroll-area-BIaiA4t3.cjs} +7 -5
  136. package/dist/scroll-area-BIaiA4t3.cjs.map +1 -0
  137. package/dist/{scroll-area-DcY6f0GS.js → scroll-area-h3DvBR0U.js} +7 -5
  138. package/dist/scroll-area-h3DvBR0U.js.map +1 -0
  139. package/dist/{search-field-block-DaXYck0w.js → search-field-block-CMoQ9fjl.js} +3 -2
  140. package/dist/search-field-block-CMoQ9fjl.js.map +1 -0
  141. package/dist/{search-field-block-BFY5jn1F.cjs → search-field-block-DmqZsrrI.cjs} +3 -2
  142. package/dist/search-field-block-DmqZsrrI.cjs.map +1 -0
  143. package/dist/src/ds/components/Card/Card.d.ts +15 -2
  144. package/dist/src/ds/components/Card/index.d.ts +2 -2
  145. package/dist/src/ds/components/Composer/composer.d.ts +5 -1
  146. package/dist/src/ds/components/Composer/index.d.ts +1 -1
  147. package/dist/src/ds/components/DataList/data-list-cells.d.ts +3 -1
  148. package/dist/src/ds/components/DateRangeTimeline/DateRangeBoundaryPicker.d.ts +10 -0
  149. package/dist/src/ds/components/DateRangeTimeline/DateRangeBoundaryPickers.d.ts +14 -0
  150. package/dist/src/ds/components/DateRangeTimeline/DateRangeHandle.d.ts +15 -0
  151. package/dist/src/ds/components/DateRangeTimeline/DateRangeSelection.d.ts +14 -0
  152. package/dist/src/ds/components/DateRangeTimeline/DateRangeTicks.d.ts +8 -0
  153. package/dist/src/ds/components/DateRangeTimeline/DateRangeTimeline.d.ts +9 -0
  154. package/dist/src/ds/components/DateRangeTimeline/DateRangeTrack.d.ts +13 -0
  155. package/dist/src/ds/components/DateRangeTimeline/DateRangeTrackGrid.d.ts +6 -0
  156. package/dist/src/ds/components/DateRangeTimeline/hooks/use-pointer-drag.d.ts +14 -0
  157. package/dist/src/ds/components/DateRangeTimeline/hooks/useDateRangeBoundaryLayout.d.ts +8 -0
  158. package/dist/src/ds/components/DateRangeTimeline/hooks/useDateRangeKeyboardControls.d.ts +13 -0
  159. package/dist/src/ds/components/DateRangeTimeline/hooks/useDateRangePointerGestures.d.ts +21 -0
  160. package/dist/src/ds/components/DateRangeTimeline/hooks/useDateRangeTimelineState.d.ts +22 -0
  161. package/dist/src/ds/components/DateRangeTimeline/hooks/useDateRangeWheelZoom.d.ts +9 -0
  162. package/dist/src/ds/components/DateRangeTimeline/index.d.ts +3 -0
  163. package/dist/src/ds/components/DateRangeTimeline/lib/date-range-boundary-layout.d.ts +15 -0
  164. package/dist/src/ds/components/DateRangeTimeline/lib/date-range-timeline-grid.d.ts +7 -0
  165. package/dist/src/ds/components/DateRangeTimeline/lib/date-range-timeline-interactions.d.ts +37 -0
  166. package/dist/src/ds/components/DateRangeTimeline/lib/date-range-timeline-view-model.d.ts +37 -0
  167. package/dist/src/ds/components/DateRangeTimeline/lib/date-range-timeline.d.ts +29 -0
  168. package/dist/src/ds/components/DateRangeTimeline/types.d.ts +9 -0
  169. package/dist/src/ds/components/MainSidebar/main-sidebar-nav-item-classes.d.ts +8 -1
  170. package/dist/src/ds/components/MainSidebar/main-sidebar-nav-label.d.ts +3 -3
  171. package/dist/src/ds/components/MainSidebar/main-sidebar-nav-link.d.ts +12 -1
  172. package/dist/src/ds/components/MainSidebar/main-sidebar.d.ts +1 -0
  173. package/dist/src/ds/components/MetricsLineChart/metrics-line-chart.d.ts +6 -1
  174. package/dist/src/ds/components/SankeyChart/index.d.ts +2 -2
  175. package/dist/src/ds/components/SankeyChart/sankey-chart-utils.d.ts +32 -1
  176. package/dist/src/ds/components/SankeyChart/sankey-chart.d.ts +4 -2
  177. package/dist/src/ds/components/SankeyChart/sankey-context.d.ts +6 -1
  178. package/dist/src/ds/components/SankeyChart/use-sankey-chart-measurements.d.ts +14 -0
  179. package/dist/src/ds/components/ScrollArea/scroll-area.d.ts +13 -1
  180. package/dist/src/ds/components/Toaster/index.d.ts +1 -1
  181. package/dist/style.css +154 -2
  182. package/dist/{time-picker-kYaitcDa.cjs → time-picker-Box0pdQU.cjs} +2 -46
  183. package/dist/time-picker-Box0pdQU.cjs.map +1 -0
  184. package/dist/{time-picker-Dg78gQcJ.js → time-picker-ew9aXG86.js} +4 -47
  185. package/dist/time-picker-ew9aXG86.js.map +1 -0
  186. package/dist/{traces-data-list-DKKrObgb.js → traces-data-list-DLmFHLQ6.js} +2 -2
  187. package/dist/{traces-data-list-DKKrObgb.js.map → traces-data-list-DLmFHLQ6.js.map} +1 -1
  188. package/dist/{traces-data-list-Bh6onSDp.cjs → traces-data-list-G69pEglx.cjs} +2 -2
  189. package/dist/{traces-data-list-Bh6onSDp.cjs.map → traces-data-list-G69pEglx.cjs.map} +1 -1
  190. package/package.json +8 -8
  191. package/dist/Card-BOmiCU4T.js +0 -64
  192. package/dist/Card-BOmiCU4T.js.map +0 -1
  193. package/dist/Card-CHeBhO_f.cjs.map +0 -1
  194. package/dist/data-list-top-cell-B5qdzDE4.js.map +0 -1
  195. package/dist/data-list-top-cell-CT5c7UVt.cjs.map +0 -1
  196. package/dist/metrics-line-chart-ByY_VXfb.js.map +0 -1
  197. package/dist/metrics-line-chart-FvsUXcL7.cjs.map +0 -1
  198. package/dist/scroll-area-C3deZ3yj.cjs.map +0 -1
  199. package/dist/scroll-area-DcY6f0GS.js.map +0 -1
  200. package/dist/search-field-block-BFY5jn1F.cjs.map +0 -1
  201. package/dist/search-field-block-DaXYck0w.js.map +0 -1
  202. package/dist/time-picker-Dg78gQcJ.js.map +0 -1
  203. package/dist/time-picker-kYaitcDa.cjs.map +0 -1
@@ -0,0 +1,1011 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
+
5
+ const jsxRuntime = require('react/jsx-runtime');
6
+ const React = require('react');
7
+ const dateFns = require('date-fns');
8
+ const lucideReact = require('lucide-react');
9
+ const Button = require('../Button-B5RnKrGb.cjs');
10
+ const datePicker = require('../date-picker-DrWmUawi.cjs');
11
+ const popover = require('../popover-CO0UtG2f.cjs');
12
+ const Txt = require('../Txt-I5lK8zy7.cjs');
13
+ const utils_cn = require('../cn-vA5sWo9W.cjs');
14
+
15
+ const API_DATE_FORMAT = "yyyy-MM-dd";
16
+ function clamp(value, min, max) {
17
+ return Math.min(max, Math.max(min, value));
18
+ }
19
+ function parseDate(value) {
20
+ const date = dateFns.parseISO(value);
21
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(value) || !dateFns.isValid(date)) return void 0;
22
+ return dateFns.format(date, API_DATE_FORMAT) === value ? date : void 0;
23
+ }
24
+ function toDateRange(state) {
25
+ return {
26
+ from: dateFns.format(dateFns.addDays(state.origin, state.selection.from), API_DATE_FORMAT),
27
+ to: dateFns.format(dateFns.addDays(state.origin, state.selection.to), API_DATE_FORMAT)
28
+ };
29
+ }
30
+ function createTimelineState(value, min, max) {
31
+ const selectedFrom = parseDate(value.from) ?? min;
32
+ const selectedTo = parseDate(value.to) ?? max;
33
+ const absoluteSpan = Math.max(0, dateFns.differenceInCalendarDays(max, min));
34
+ if (absoluteSpan === 0) {
35
+ return {
36
+ origin: min,
37
+ viewport: { from: 0, to: 0 },
38
+ selection: { from: 0, to: 0 }
39
+ };
40
+ }
41
+ const selectedFromIndex = clamp(dateFns.differenceInCalendarDays(selectedFrom, min), 0, absoluteSpan);
42
+ const selectedToIndex = clamp(dateFns.differenceInCalendarDays(selectedTo, min), 0, absoluteSpan);
43
+ return {
44
+ origin: min,
45
+ viewport: { from: 0, to: absoluteSpan },
46
+ selection: {
47
+ from: Math.min(selectedFromIndex, selectedToIndex),
48
+ to: Math.max(selectedFromIndex, selectedToIndex)
49
+ }
50
+ };
51
+ }
52
+ function zoomTimelineViewport(state, maximumIndex, factor, anchor) {
53
+ if (!Number.isFinite(factor) || factor <= 0 || factor === 1) return state;
54
+ const boundedMaximum = Math.max(0, maximumIndex);
55
+ const viewportSpan = state.viewport.to - state.viewport.from;
56
+ const selectionSpan = state.selection.to - state.selection.from;
57
+ const minimumViewportSpan = Math.max(selectionSpan, Math.min(1, boundedMaximum));
58
+ const scaledSpan = clamp(Math.round(viewportSpan * factor), minimumViewportSpan, boundedMaximum);
59
+ const nextSpan = scaledSpan === viewportSpan ? clamp(viewportSpan + (factor > 1 ? 1 : -1), minimumViewportSpan, boundedMaximum) : scaledSpan;
60
+ if (nextSpan === viewportSpan) return state;
61
+ const normalizedAnchor = Number.isFinite(anchor) ? clamp(anchor, 0, 1) : 0.5;
62
+ const anchorIndex = state.viewport.from + viewportSpan * normalizedAnchor;
63
+ const desiredFrom = Math.round(anchorIndex - nextSpan * normalizedAnchor);
64
+ const earliestFrom = Math.max(0, state.selection.to - nextSpan);
65
+ const latestFrom = Math.min(state.selection.from, boundedMaximum - nextSpan);
66
+ const from = clamp(desiredFrom, earliestFrom, latestFrom);
67
+ return {
68
+ ...state,
69
+ viewport: { from, to: from + nextSpan }
70
+ };
71
+ }
72
+ function revealTimelineSelection(viewport, selection, maximumIndex) {
73
+ const boundedMaximum = Math.max(0, maximumIndex);
74
+ const selectionFrom = clamp(Math.min(selection.from, selection.to), 0, boundedMaximum);
75
+ const selectionTo = clamp(Math.max(selection.from, selection.to), selectionFrom, boundedMaximum);
76
+ const viewportSpan = clamp(viewport.to - viewport.from, 0, boundedMaximum);
77
+ const viewportFrom = clamp(viewport.from, 0, boundedMaximum - viewportSpan);
78
+ const normalizedViewport = { from: viewportFrom, to: viewportFrom + viewportSpan };
79
+ const selectionSpan = selectionTo - selectionFrom;
80
+ const nextSpan = Math.max(viewportSpan, selectionSpan);
81
+ if (selectionFrom >= normalizedViewport.from && selectionTo <= normalizedViewport.to) {
82
+ if (normalizedViewport.from === viewport.from && normalizedViewport.to === viewport.to) {
83
+ return viewport;
84
+ }
85
+ return normalizedViewport;
86
+ }
87
+ const desiredFrom = selectionFrom < normalizedViewport.from ? selectionFrom : selectionTo - nextSpan;
88
+ const from = clamp(desiredFrom, 0, boundedMaximum - nextSpan);
89
+ return { from, to: from + nextSpan };
90
+ }
91
+ function getTimelinePosition(index, viewport) {
92
+ const viewportSpan = viewport.to - viewport.from;
93
+ if (viewportSpan === 0) return 50;
94
+ return (index - viewport.from) / viewportSpan * 100;
95
+ }
96
+ function buildTimelineTickOffsets(span) {
97
+ const boundedSpan = Math.max(0, Math.floor(span));
98
+ const tickCount = boundedSpan === 0 ? 1 : Math.min(5, boundedSpan + 1);
99
+ return Array.from(
100
+ { length: tickCount },
101
+ (_, index) => Math.round(boundedSpan * index / Math.max(1, tickCount - 1))
102
+ );
103
+ }
104
+ function buildTimelineTicks(domainFrom, domainTo) {
105
+ const span = Math.max(0, dateFns.differenceInCalendarDays(domainTo, domainFrom));
106
+ const crossesYears = domainFrom.getFullYear() !== domainTo.getFullYear();
107
+ return buildTimelineTickOffsets(span).map((dayOffset) => {
108
+ const date = dateFns.addDays(domainFrom, dayOffset);
109
+ const labelFormat = span > 365 || crossesYears ? "MMM yyyy" : "MMM d";
110
+ return {
111
+ label: dateFns.format(date, labelFormat),
112
+ date: dateFns.format(date, API_DATE_FORMAT),
113
+ position: span === 0 ? 50 : dayOffset / span * 100
114
+ };
115
+ });
116
+ }
117
+ function clampDateRangeToBounds(value, bounds) {
118
+ const min = parseDate(bounds.min);
119
+ const max = parseDate(bounds.max);
120
+ if (!min || !max) return value;
121
+ const from = parseDate(value.from) ?? min;
122
+ const to = parseDate(value.to) ?? max;
123
+ const clampedFrom = new Date(clamp(from.getTime(), min.getTime(), max.getTime()));
124
+ const clampedTo = new Date(clamp(to.getTime(), clampedFrom.getTime(), max.getTime()));
125
+ return {
126
+ from: dateFns.format(clampedFrom, API_DATE_FORMAT),
127
+ to: dateFns.format(clampedTo, API_DATE_FORMAT)
128
+ };
129
+ }
130
+ function getDateRangeBounds(createdAt, today) {
131
+ const max = parseDate(today) ?? /* @__PURE__ */ new Date();
132
+ const created = parseDate(createdAt) ?? max;
133
+ const min = created > max ? max : created;
134
+ return {
135
+ min: dateFns.format(min, API_DATE_FORMAT),
136
+ max: dateFns.format(max, API_DATE_FORMAT)
137
+ };
138
+ }
139
+ function formatDateRangeDuration(value) {
140
+ const from = parseDate(value.from);
141
+ const to = parseDate(value.to);
142
+ if (!from || !to) return "";
143
+ const days = Math.max(1, dateFns.differenceInCalendarDays(to, from) + 1);
144
+ return days === 1 ? "1 day" : `${days} days`;
145
+ }
146
+ function formatDateRangeValueText(value) {
147
+ const from = parseDate(value.from);
148
+ const to = parseDate(value.to);
149
+ if (!from || !to) return `${value.from} through ${value.to}`;
150
+ return `${dateFns.format(from, "MMMM d, yyyy")} through ${dateFns.format(to, "MMMM d, yyyy")}`;
151
+ }
152
+
153
+ function DateRangeBoundaryPicker({ boundary, value, min, max, onSelect }) {
154
+ const [open, setOpen] = React.useState(false);
155
+ const isFrom = boundary === "from";
156
+ const boundaryLabel = isFrom ? "start date" : "end date";
157
+ const selectedDate = parseDate(value);
158
+ const firstDate = parseDate(min) ?? selectedDate;
159
+ const lastDate = parseDate(max) ?? selectedDate;
160
+ const formattedDate = selectedDate ? dateFns.format(selectedDate, "MMM d, yyyy") : value;
161
+ function handleSelect(date) {
162
+ if (!date) return;
163
+ onSelect(boundary, dateFns.format(date, "yyyy-MM-dd"));
164
+ setOpen(false);
165
+ }
166
+ return /* @__PURE__ */ jsxRuntime.jsxs(popover.Popover, { open, onOpenChange: setOpen, children: [
167
+ /* @__PURE__ */ jsxRuntime.jsx(
168
+ popover.PopoverTrigger,
169
+ {
170
+ render: /* @__PURE__ */ jsxRuntime.jsxs(
171
+ Button.Button,
172
+ {
173
+ "aria-label": `Choose ${boundaryLabel}, ${formattedDate}`,
174
+ "aria-expanded": open,
175
+ size: "xs",
176
+ className: "h-11 w-full min-w-0 justify-between gap-2 overflow-hidden px-3 sm:h-8",
177
+ children: [
178
+ /* @__PURE__ */ jsxRuntime.jsx(Txt.Txt, { as: "span", variant: "ui-sm", className: "truncate text-neutral5 tabular-nums", children: formattedDate }),
179
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CalendarDaysIcon, { className: "size-3.5 shrink-0 text-neutral3", "aria-hidden": "true" })
180
+ ]
181
+ }
182
+ )
183
+ }
184
+ ),
185
+ /* @__PURE__ */ jsxRuntime.jsx(popover.PopoverContent, { align: isFrom ? "start" : "end", className: "w-auto p-0", sideOffset: 6, children: /* @__PURE__ */ jsxRuntime.jsx(
186
+ datePicker.DatePicker,
187
+ {
188
+ mode: "single",
189
+ selected: selectedDate,
190
+ defaultMonth: selectedDate,
191
+ fromMonth: firstDate,
192
+ toMonth: lastDate,
193
+ disabled: firstDate && lastDate ? { before: firstDate, after: lastDate } : void 0,
194
+ onSelect: handleSelect
195
+ }
196
+ ) })
197
+ ] });
198
+ }
199
+
200
+ const PREFERRED_PICKER_WIDTH = 160;
201
+ const PREFERRED_PICKER_GAP = 8;
202
+ function resolveDateRangeBoundaryLayout(requestedWidth, positions) {
203
+ const width = Math.max(0, requestedWidth);
204
+ const gap = Math.min(PREFERRED_PICKER_GAP, width);
205
+ const pickerWidth = Math.min(PREFERRED_PICKER_WIDTH, Math.max(0, (width - gap) / 2));
206
+ const maximumLeft = Math.max(0, width - pickerWidth);
207
+ const fromAnchor = clamp(positions.from, 0, 100) / 100 * width;
208
+ const toAnchor = clamp(positions.to, 0, 100) / 100 * width;
209
+ const preferredFromLeft = clamp(fromAnchor - pickerWidth - gap / 2, 0, maximumLeft);
210
+ const preferredToLeft = clamp(toAnchor + gap / 2, 0, maximumLeft);
211
+ const overlap = preferredFromLeft + pickerWidth + gap - preferredToLeft;
212
+ if (overlap <= 0) {
213
+ return {
214
+ from: { left: preferredFromLeft, width: pickerWidth },
215
+ to: { left: preferredToLeft, width: pickerWidth },
216
+ gap
217
+ };
218
+ }
219
+ const pairWidth = pickerWidth * 2 + gap;
220
+ const pairLeft = clamp(preferredFromLeft - overlap / 2, 0, Math.max(0, width - pairWidth));
221
+ return {
222
+ from: { left: pairLeft, width: pickerWidth },
223
+ to: { left: pairLeft + pickerWidth + gap, width: pickerWidth },
224
+ gap
225
+ };
226
+ }
227
+
228
+ function useDateRangeBoundaryLayout(containerRef, positions) {
229
+ const [containerWidth, setContainerWidth] = React.useState();
230
+ React.useLayoutEffect(() => {
231
+ const container = containerRef.current;
232
+ if (!container) return;
233
+ function updateWidth(width) {
234
+ if (!Number.isFinite(width) || width < 0) return;
235
+ setContainerWidth((current) => current === width ? current : width);
236
+ }
237
+ updateWidth(container.getBoundingClientRect().width);
238
+ if (typeof ResizeObserver === "undefined") return;
239
+ const observer = new ResizeObserver((entries) => {
240
+ const entry = entries.find(({ target }) => target === container) ?? entries[0];
241
+ const width = entry?.contentRect.width ?? container.getBoundingClientRect().width;
242
+ updateWidth(width);
243
+ });
244
+ observer.observe(container);
245
+ return () => observer.disconnect();
246
+ }, [containerRef]);
247
+ return containerWidth === void 0 ? void 0 : resolveDateRangeBoundaryLayout(containerWidth, positions);
248
+ }
249
+
250
+ const FALLBACK_PICKER_WIDTH = "clamp(0px, calc(50% - 0.25rem), 10rem)";
251
+ function DateRangeBoundaryPickers({ positions, value, min, max, onSelect }) {
252
+ const containerRef = React.useRef(null);
253
+ const layout = useDateRangeBoundaryLayout(containerRef, positions);
254
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, className: "relative h-16 sm:h-12", children: [
255
+ /* @__PURE__ */ jsxRuntime.jsx(
256
+ "div",
257
+ {
258
+ className: "absolute top-0 min-w-0",
259
+ style: {
260
+ left: layout?.from.left ?? 0,
261
+ width: layout?.from.width ?? FALLBACK_PICKER_WIDTH
262
+ },
263
+ children: /* @__PURE__ */ jsxRuntime.jsx(DateRangeBoundaryPicker, { boundary: "from", value: value.from, min, max: value.to, onSelect })
264
+ }
265
+ ),
266
+ /* @__PURE__ */ jsxRuntime.jsx(
267
+ "div",
268
+ {
269
+ className: "absolute top-0 min-w-0",
270
+ style: {
271
+ left: layout?.to.left ?? "max(calc(50% + 0.25rem), calc(100% - 10rem))",
272
+ width: layout?.to.width ?? FALLBACK_PICKER_WIDTH
273
+ },
274
+ children: /* @__PURE__ */ jsxRuntime.jsx(DateRangeBoundaryPicker, { boundary: "to", value: value.to, min: value.from, max, onSelect })
275
+ }
276
+ ),
277
+ /* @__PURE__ */ jsxRuntime.jsx(
278
+ "div",
279
+ {
280
+ className: "absolute top-11 h-4 w-px -translate-x-1/2 bg-border2 transition-[left] duration-150 ease-out motion-reduce:transition-none sm:top-8",
281
+ style: { left: `${positions.from}%` },
282
+ "aria-hidden": "true"
283
+ }
284
+ ),
285
+ /* @__PURE__ */ jsxRuntime.jsx(
286
+ "div",
287
+ {
288
+ className: "absolute top-11 h-4 w-px -translate-x-1/2 bg-border2 transition-[left] duration-150 ease-out motion-reduce:transition-none sm:top-8",
289
+ style: { left: `${positions.to}%` },
290
+ "aria-hidden": "true"
291
+ }
292
+ )
293
+ ] });
294
+ }
295
+
296
+ function formatBoundaryDate(value) {
297
+ const date = parseDate(value);
298
+ return date ? dateFns.format(date, "MMM d, yyyy") : value;
299
+ }
300
+ function DateRangeTicks({ ticks, min, max }) {
301
+ const firstTick = ticks[0];
302
+ const lastTick = ticks.at(-1);
303
+ if (!firstTick || !lastTick) return null;
304
+ const timelineLabel = `Visible from ${formatBoundaryDate(firstTick.date)} through ${formatBoundaryDate(lastTick.date)}. Available from ${formatBoundaryDate(min)} through ${formatBoundaryDate(max)}`;
305
+ if (firstTick.date === lastTick.date) {
306
+ const date = formatBoundaryDate(firstTick.date);
307
+ const singleLabel = firstTick.date === min && firstTick.date === max ? `Created today · ${date}` : firstTick.date === min ? `Created · ${date}` : firstTick.date === max ? `Today · ${date}` : date;
308
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "group", "aria-label": timelineLabel, className: "flex h-10 items-start justify-center pt-2", children: /* @__PURE__ */ jsxRuntime.jsx(Txt.Txt, { as: "span", variant: "ui-sm", className: "text-neutral3", children: singleLabel }) });
309
+ }
310
+ const startLabel = firstTick.date === min ? `Created · ${formatBoundaryDate(min)}` : firstTick.label;
311
+ const endLabel = lastTick.date === max ? `Today · ${formatBoundaryDate(max)}` : lastTick.label;
312
+ const middleTicks = ticks.slice(1, -1);
313
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "group", className: "relative h-10", "aria-label": timelineLabel, children: [
314
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-0 left-0 flex flex-col items-start", children: [
315
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 h-1.5 w-px bg-border2", "aria-hidden": "true" }),
316
+ /* @__PURE__ */ jsxRuntime.jsx(Txt.Txt, { as: "span", variant: "ui-sm", className: "whitespace-nowrap text-neutral3", children: startLabel })
317
+ ] }),
318
+ middleTicks.map((tick) => /* @__PURE__ */ jsxRuntime.jsxs(
319
+ "div",
320
+ {
321
+ className: "absolute top-0 hidden -translate-x-1/2 flex-col items-center sm:flex",
322
+ style: { left: `${tick.position}%` },
323
+ "aria-hidden": "true",
324
+ children: [
325
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 h-1.5 w-px bg-border2" }),
326
+ /* @__PURE__ */ jsxRuntime.jsx(Txt.Txt, { as: "span", variant: "ui-xs", className: "whitespace-nowrap text-neutral3", children: tick.label })
327
+ ]
328
+ },
329
+ tick.date
330
+ )),
331
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-0 right-0 flex flex-col items-end", children: [
332
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 h-1.5 w-px bg-border2", "aria-hidden": "true" }),
333
+ /* @__PURE__ */ jsxRuntime.jsx(Txt.Txt, { as: "span", variant: "ui-sm", className: "whitespace-nowrap text-neutral3", children: endLabel })
334
+ ] })
335
+ ] });
336
+ }
337
+
338
+ function DateRangeHandle({
339
+ boundary,
340
+ position,
341
+ value,
342
+ valueText,
343
+ min,
344
+ max,
345
+ active,
346
+ onPointerDown,
347
+ onKeyDown
348
+ }) {
349
+ const label = boundary === "from" ? "Start date handle" : "End date handle";
350
+ const date = parseDate(valueText);
351
+ const accessibleValue = date ? dateFns.format(date, "MMMM d, yyyy") : valueText;
352
+ return /* @__PURE__ */ jsxRuntime.jsx(
353
+ "div",
354
+ {
355
+ role: "slider",
356
+ tabIndex: 0,
357
+ "aria-label": label,
358
+ "aria-orientation": "horizontal",
359
+ "aria-valuemin": min,
360
+ "aria-valuemax": max,
361
+ "aria-valuenow": value,
362
+ "aria-valuetext": accessibleValue,
363
+ className: "group absolute inset-y-0 z-30 w-11 -translate-x-1/2 cursor-ew-resize touch-none outline-hidden",
364
+ style: { left: `${position}%` },
365
+ onPointerDown,
366
+ onKeyDown,
367
+ children: /* @__PURE__ */ jsxRuntime.jsx(
368
+ "span",
369
+ {
370
+ className: utils_cn.cn(
371
+ "absolute top-1/2 left-1/2 h-7 w-1.5 -translate-1/2 rounded-xs bg-surface3 ring-1 transition-[width,box-shadow] duration-150 group-hover:w-2 group-focus-visible:w-2 group-focus-visible:ring-2 group-focus-visible:ring-accent3 motion-reduce:transition-none",
372
+ active ? "w-2 ring-2 ring-neutral3/40" : "ring-border2"
373
+ ),
374
+ "aria-hidden": "true"
375
+ }
376
+ )
377
+ }
378
+ );
379
+ }
380
+
381
+ function DateRangeSelection({
382
+ left,
383
+ width,
384
+ duration,
385
+ active,
386
+ value,
387
+ valueText,
388
+ max,
389
+ onPointerDown,
390
+ onKeyDown
391
+ }) {
392
+ const visibleWidth = Math.max(width, 0.6);
393
+ const visibleLeft = Math.min(left, 100 - visibleWidth);
394
+ const showDuration = width >= 12;
395
+ return /* @__PURE__ */ jsxRuntime.jsx(
396
+ "div",
397
+ {
398
+ role: "slider",
399
+ tabIndex: 0,
400
+ "aria-label": "Move selected date range",
401
+ "aria-orientation": "horizontal",
402
+ "aria-valuemin": 0,
403
+ "aria-valuemax": max,
404
+ "aria-valuenow": value,
405
+ "aria-valuetext": valueText,
406
+ className: utils_cn.cn(
407
+ "absolute inset-y-1 z-10 flex cursor-grab touch-none items-center justify-center overflow-hidden rounded-md bg-neutral6/10 text-neutral6 select-none outline-hidden focus-visible:ring-2 focus-visible:ring-accent3 active:cursor-grabbing",
408
+ active ? "transition-none" : "transition-[left,width,background-color] duration-150 ease-out motion-reduce:transition-none"
409
+ ),
410
+ style: { left: `${visibleLeft}%`, width: `${visibleWidth}%` },
411
+ onPointerDown,
412
+ onKeyDown,
413
+ children: showDuration ? (
414
+ // TODO(ds): Txt needs a compact data-label variant.
415
+ /* @__PURE__ */ jsxRuntime.jsx(Txt.Txt, { as: "span", variant: "ui-sm", font: "mono", className: "text-neutral6 tabular-nums", children: duration })
416
+ ) : null
417
+ }
418
+ );
419
+ }
420
+
421
+ function getMarkerHeight(emphasis) {
422
+ if (emphasis === "major") return "h-5";
423
+ if (emphasis === "medium") return "h-3";
424
+ return "h-1.5";
425
+ }
426
+ function DateRangeTrackGrid({ markers }) {
427
+ const interiorMarkers = markers.filter((marker) => marker.position > 0 && marker.position < 100);
428
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-none absolute inset-0", "aria-hidden": "true", children: interiorMarkers.map((marker) => /* @__PURE__ */ jsxRuntime.jsx(
429
+ "span",
430
+ {
431
+ className: utils_cn.cn("absolute top-1/2 w-px -translate-1/2 bg-neutral3/15", getMarkerHeight(marker.emphasis)),
432
+ style: { left: `${marker.position}%` }
433
+ },
434
+ marker.index
435
+ )) });
436
+ }
437
+
438
+ const KEYBOARD_LARGE_STEP_DAYS = 7;
439
+ function getKeyboardDelta(key) {
440
+ if (key === "ArrowLeft" || key === "ArrowDown") return -1;
441
+ if (key === "ArrowRight" || key === "ArrowUp") return 1;
442
+ if (key === "PageDown") return -KEYBOARD_LARGE_STEP_DAYS;
443
+ if (key === "PageUp") return KEYBOARD_LARGE_STEP_DAYS;
444
+ return void 0;
445
+ }
446
+ function getTimelineInteraction(gesture) {
447
+ if (gesture.type === "brush") return { type: "selecting" };
448
+ if (gesture.type === "pan") return { type: "moving" };
449
+ return { type: "resizing", boundary: gesture.boundary };
450
+ }
451
+ function getTimelineIndexAtClientX(clientX, track, viewport) {
452
+ const viewportSpan = viewport.to - viewport.from;
453
+ if (!track || track.width <= 0 || viewportSpan <= 0) return viewport.from;
454
+ const position = clamp((clientX - track.left) / track.width, 0, 1);
455
+ return viewport.from + Math.round(position * viewportSpan);
456
+ }
457
+ function resolveTimelineGestureSelection(gesture, pointerIndex, viewport) {
458
+ const boundedPointerIndex = clamp(pointerIndex, viewport.from, viewport.to);
459
+ if (gesture.type === "brush") {
460
+ return {
461
+ from: Math.min(gesture.startIndex, boundedPointerIndex),
462
+ to: Math.max(gesture.startIndex, boundedPointerIndex)
463
+ };
464
+ }
465
+ if (gesture.type === "pan") {
466
+ const selectionSpan = gesture.originalSelection.to - gesture.originalSelection.from;
467
+ const delta = boundedPointerIndex - gesture.startIndex;
468
+ const from = clamp(gesture.originalSelection.from + delta, viewport.from, viewport.to - selectionSpan);
469
+ return { from, to: from + selectionSpan };
470
+ }
471
+ if (gesture.boundary === "from") {
472
+ return {
473
+ from: clamp(boundedPointerIndex, viewport.from, gesture.originalSelection.to),
474
+ to: gesture.originalSelection.to
475
+ };
476
+ }
477
+ return {
478
+ from: gesture.originalSelection.from,
479
+ to: clamp(boundedPointerIndex, gesture.originalSelection.from, viewport.to)
480
+ };
481
+ }
482
+ function moveTimelineSelectionFromKey(selection, key, maximumIndex) {
483
+ const span = selection.to - selection.from;
484
+ if (key === "Home") return { from: 0, to: span };
485
+ if (key === "End") return { from: maximumIndex - span, to: maximumIndex };
486
+ const delta = getKeyboardDelta(key);
487
+ if (delta === void 0) return void 0;
488
+ const from = clamp(selection.from + delta, 0, maximumIndex - span);
489
+ return { from, to: from + span };
490
+ }
491
+ function resizeTimelineSelectionFromKey(selection, boundary, key, maximumIndex) {
492
+ const { from, to } = selection;
493
+ if (key === "Home") return boundary === "from" ? { from: 0, to } : { from, to: from };
494
+ if (key === "End") {
495
+ return boundary === "from" ? { from: to, to } : { from, to: maximumIndex };
496
+ }
497
+ const delta = getKeyboardDelta(key);
498
+ if (delta === void 0) return void 0;
499
+ return boundary === "from" ? { from: clamp(from + delta, 0, to), to } : { from, to: clamp(to + delta, from, maximumIndex) };
500
+ }
501
+
502
+ function useDateRangeKeyboardControls({ selection, maximumIndex, onCommit }) {
503
+ function handleSelectionKeyDown(event) {
504
+ const nextSelection = moveTimelineSelectionFromKey(selection, event.key, maximumIndex);
505
+ if (!nextSelection) return;
506
+ event.preventDefault();
507
+ onCommit(nextSelection);
508
+ }
509
+ function handleBoundaryKeyDown(boundary, event) {
510
+ const nextSelection = resizeTimelineSelectionFromKey(selection, boundary, event.key, maximumIndex);
511
+ if (!nextSelection) return;
512
+ event.preventDefault();
513
+ onCommit(nextSelection);
514
+ }
515
+ function handleFromKeyDown(event) {
516
+ handleBoundaryKeyDown("from", event);
517
+ }
518
+ function handleToKeyDown(event) {
519
+ handleBoundaryKeyDown("to", event);
520
+ }
521
+ return {
522
+ handleSelectionKeyDown,
523
+ handleFromKeyDown,
524
+ handleToKeyDown
525
+ };
526
+ }
527
+
528
+ function usePointerDrag({ onMove, onEnd, onCancel }) {
529
+ const sessionRef = React.useRef(void 0);
530
+ function startPointerDrag(event, payload) {
531
+ if (sessionRef.current) return false;
532
+ const captureTarget = event.currentTarget;
533
+ captureTarget.setPointerCapture(event.pointerId);
534
+ sessionRef.current = {
535
+ pointerId: event.pointerId,
536
+ captureTarget,
537
+ payload
538
+ };
539
+ return true;
540
+ }
541
+ function getSession(event) {
542
+ const session = sessionRef.current;
543
+ if (!session || session.pointerId !== event.pointerId) return void 0;
544
+ return session;
545
+ }
546
+ function releaseSession(session) {
547
+ sessionRef.current = void 0;
548
+ if (!session.captureTarget.hasPointerCapture(session.pointerId)) return;
549
+ session.captureTarget.releasePointerCapture(session.pointerId);
550
+ }
551
+ function handlePointerMove(event) {
552
+ const session = getSession(event);
553
+ if (!session) return;
554
+ onMove(session.payload, event);
555
+ }
556
+ function handlePointerUp(event) {
557
+ const session = getSession(event);
558
+ if (!session) return;
559
+ releaseSession(session);
560
+ onEnd(session.payload, event);
561
+ }
562
+ function handlePointerCancel(event) {
563
+ const session = getSession(event);
564
+ if (!session) return;
565
+ releaseSession(session);
566
+ onCancel(session.payload, event);
567
+ }
568
+ function handleLostPointerCapture(event) {
569
+ const session = getSession(event);
570
+ if (!session) return;
571
+ sessionRef.current = void 0;
572
+ onCancel(session.payload, event);
573
+ }
574
+ return {
575
+ startPointerDrag,
576
+ handlePointerMove,
577
+ handlePointerUp,
578
+ handlePointerCancel,
579
+ handleLostPointerCapture
580
+ };
581
+ }
582
+
583
+ function useDateRangePointerGestures({
584
+ trackRef,
585
+ timeline,
586
+ onSelectionPreview,
587
+ onSelectionCommit,
588
+ onSelectionCancel
589
+ }) {
590
+ const [interaction, setInteraction] = React.useState();
591
+ function getPointerIndex(clientX) {
592
+ const track = trackRef.current?.getBoundingClientRect();
593
+ return getTimelineIndexAtClientX(clientX, track, timeline.viewport);
594
+ }
595
+ function previewPointerDrag(drag, event) {
596
+ const pointerIndex = getPointerIndex(event.clientX);
597
+ const selection = resolveTimelineGestureSelection(drag.gesture, pointerIndex, timeline.viewport);
598
+ drag.currentSelection = selection;
599
+ onSelectionPreview(selection);
600
+ }
601
+ function commitPointerDrag(drag, event) {
602
+ previewPointerDrag(drag, event);
603
+ setInteraction(void 0);
604
+ onSelectionCommit(drag.currentSelection);
605
+ }
606
+ function cancelPointerDrag() {
607
+ setInteraction(void 0);
608
+ onSelectionCancel();
609
+ }
610
+ const pointerDrag = usePointerDrag({
611
+ onMove: previewPointerDrag,
612
+ onEnd: commitPointerDrag,
613
+ onCancel: cancelPointerDrag
614
+ });
615
+ function beginPointerDrag(event, gesture, currentSelection) {
616
+ const started = pointerDrag.startPointerDrag(event, { gesture, currentSelection });
617
+ if (!started) return false;
618
+ setInteraction(getTimelineInteraction(gesture));
619
+ return true;
620
+ }
621
+ function startBrush(event) {
622
+ if (event.button !== 0) return;
623
+ event.preventDefault();
624
+ const startIndex = getPointerIndex(event.clientX);
625
+ const selection = { from: startIndex, to: startIndex };
626
+ const started = beginPointerDrag(
627
+ event,
628
+ {
629
+ type: "brush",
630
+ startIndex
631
+ },
632
+ selection
633
+ );
634
+ if (!started) return;
635
+ onSelectionPreview(selection);
636
+ }
637
+ function startPan(event) {
638
+ if (event.button !== 0) return;
639
+ event.preventDefault();
640
+ event.stopPropagation();
641
+ beginPointerDrag(
642
+ event,
643
+ {
644
+ type: "pan",
645
+ startIndex: getPointerIndex(event.clientX),
646
+ originalSelection: timeline.selection
647
+ },
648
+ timeline.selection
649
+ );
650
+ }
651
+ function startHandle(boundary, event) {
652
+ if (event.button !== 0) return;
653
+ event.preventDefault();
654
+ event.stopPropagation();
655
+ beginPointerDrag(
656
+ event,
657
+ {
658
+ type: "handle",
659
+ boundary,
660
+ originalSelection: timeline.selection
661
+ },
662
+ timeline.selection
663
+ );
664
+ }
665
+ function handlePointerMove(event) {
666
+ pointerDrag.handlePointerMove(event);
667
+ }
668
+ function handlePointerUp(event) {
669
+ pointerDrag.handlePointerUp(event);
670
+ }
671
+ function handlePointerCancel(event) {
672
+ pointerDrag.handlePointerCancel(event);
673
+ }
674
+ function handleLostPointerCapture(event) {
675
+ pointerDrag.handleLostPointerCapture(event);
676
+ }
677
+ return {
678
+ interaction,
679
+ startBrush,
680
+ startPan,
681
+ startHandle,
682
+ handlePointerMove,
683
+ handlePointerUp,
684
+ handlePointerCancel,
685
+ handleLostPointerCapture
686
+ };
687
+ }
688
+
689
+ const MAX_WHEEL_DELTA = 80;
690
+ const WHEEL_ZOOM_SENSITIVITY = 3e-3;
691
+ function useDateRangeWheelZoom({ rootRef, trackRef, disabled, onZoom }) {
692
+ const handleWheel = React.useEffectEvent((event) => {
693
+ if (!event.ctrlKey) return;
694
+ if (disabled) return;
695
+ event.preventDefault();
696
+ const trackRect = trackRef.current?.getBoundingClientRect();
697
+ const anchor = trackRect && trackRect.width > 0 ? clamp((event.clientX - trackRect.left) / trackRect.width, 0, 1) : 0.5;
698
+ const wheelDelta = clamp(event.deltaY, -MAX_WHEEL_DELTA, MAX_WHEEL_DELTA);
699
+ const factor = Math.exp(wheelDelta * WHEEL_ZOOM_SENSITIVITY);
700
+ onZoom(factor, anchor);
701
+ });
702
+ React.useEffect(() => {
703
+ const root = rootRef.current;
704
+ if (!root) return;
705
+ root.addEventListener("wheel", handleWheel, { passive: false });
706
+ return () => root.removeEventListener("wheel", handleWheel);
707
+ }, [rootRef]);
708
+ }
709
+
710
+ const MAX_GRID_INTERVALS = 24;
711
+ function addVisibleMarker(markerIndices, index, viewport) {
712
+ if (index >= viewport.from && index <= viewport.to) {
713
+ markerIndices.add(index);
714
+ }
715
+ }
716
+ function getMarkerEmphasis(index, majorIndices, mediumIndices) {
717
+ if (majorIndices.has(index)) return "major";
718
+ if (mediumIndices.has(index)) return "medium";
719
+ return "minor";
720
+ }
721
+ function createDateRangeGridMarkers(viewport, selection) {
722
+ const viewportSpan = Math.max(0, viewport.to - viewport.from);
723
+ if (viewportSpan === 0) {
724
+ return [{ index: viewport.from, position: 50, emphasis: "major" }];
725
+ }
726
+ const sampleStep = Math.max(1, Math.ceil(viewportSpan / MAX_GRID_INTERVALS));
727
+ const markerIndices = /* @__PURE__ */ new Set();
728
+ const majorIndices = /* @__PURE__ */ new Set();
729
+ const mediumIndices = /* @__PURE__ */ new Set();
730
+ const tickOffsets = buildTimelineTickOffsets(viewportSpan);
731
+ for (let index = viewport.from; index <= viewport.to; index += sampleStep) {
732
+ markerIndices.add(index);
733
+ }
734
+ for (let index = 0; index < tickOffsets.length; index += 1) {
735
+ const tickOffset = tickOffsets[index];
736
+ if (tickOffset === void 0) continue;
737
+ const majorIndex = viewport.from + tickOffset;
738
+ markerIndices.add(majorIndex);
739
+ majorIndices.add(majorIndex);
740
+ const nextTickOffset = tickOffsets[index + 1];
741
+ if (nextTickOffset === void 0) continue;
742
+ const mediumIndex = viewport.from + Math.round((tickOffset + nextTickOffset) / 2);
743
+ markerIndices.add(mediumIndex);
744
+ mediumIndices.add(mediumIndex);
745
+ }
746
+ addVisibleMarker(markerIndices, selection.from, viewport);
747
+ addVisibleMarker(markerIndices, selection.to, viewport);
748
+ return [...markerIndices].sort((left, right) => left - right).map((index) => ({
749
+ index,
750
+ position: getTimelinePosition(index, viewport),
751
+ emphasis: getMarkerEmphasis(index, majorIndices, mediumIndices)
752
+ }));
753
+ }
754
+
755
+ function createDateRangeBoundaryModel(state) {
756
+ const range = toDateRange(state);
757
+ const fromPosition = getTimelinePosition(state.selection.from, state.viewport);
758
+ const toPosition = getTimelinePosition(state.selection.to, state.viewport);
759
+ return {
760
+ positions: { from: fromPosition, to: toPosition },
761
+ range
762
+ };
763
+ }
764
+ function createDateRangeTrackModel(state, maximumIndex) {
765
+ const range = toDateRange(state);
766
+ const fromPosition = getTimelinePosition(state.selection.from, state.viewport);
767
+ const toPosition = getTimelinePosition(state.selection.to, state.viewport);
768
+ const span = state.selection.to - state.selection.from;
769
+ return {
770
+ handles: {
771
+ from: {
772
+ position: fromPosition,
773
+ value: state.selection.from,
774
+ valueText: range.from,
775
+ min: 0,
776
+ max: state.selection.to
777
+ },
778
+ to: {
779
+ position: toPosition,
780
+ value: state.selection.to,
781
+ valueText: range.to,
782
+ min: state.selection.from,
783
+ max: maximumIndex
784
+ }
785
+ },
786
+ selection: {
787
+ left: fromPosition,
788
+ width: Math.max(0, toPosition - fromPosition),
789
+ duration: formatDateRangeDuration(range),
790
+ value: state.selection.from,
791
+ valueText: formatDateRangeValueText(range),
792
+ max: maximumIndex - span
793
+ }
794
+ };
795
+ }
796
+ function createDateRangeAxisModel(state) {
797
+ const visibleFrom = dateFns.addDays(state.origin, state.viewport.from);
798
+ const visibleTo = dateFns.addDays(state.origin, state.viewport.to);
799
+ return { ticks: buildTimelineTicks(visibleFrom, visibleTo) };
800
+ }
801
+
802
+ function DateRangeTrack({
803
+ wheelTargetRef,
804
+ timeline,
805
+ maximumIndex,
806
+ onSelectionPreview,
807
+ onSelectionCommit,
808
+ onSelectionCancel,
809
+ onZoom
810
+ }) {
811
+ const trackRef = React.useRef(null);
812
+ const pointer = useDateRangePointerGestures({
813
+ trackRef,
814
+ timeline,
815
+ onSelectionPreview,
816
+ onSelectionCommit,
817
+ onSelectionCancel
818
+ });
819
+ const { handleSelectionKeyDown, handleFromKeyDown, handleToKeyDown } = useDateRangeKeyboardControls({
820
+ selection: timeline.selection,
821
+ maximumIndex,
822
+ onCommit: onSelectionCommit
823
+ });
824
+ useDateRangeWheelZoom({
825
+ rootRef: wheelTargetRef,
826
+ trackRef,
827
+ disabled: pointer.interaction !== void 0,
828
+ onZoom
829
+ });
830
+ const model = createDateRangeTrackModel(timeline, maximumIndex);
831
+ const gridMarkers = createDateRangeGridMarkers(timeline.viewport, timeline.selection);
832
+ function startFromHandle(event) {
833
+ pointer.startHandle("from", event);
834
+ }
835
+ function startToHandle(event) {
836
+ pointer.startHandle("to", event);
837
+ }
838
+ return /* @__PURE__ */ jsxRuntime.jsxs(
839
+ "div",
840
+ {
841
+ ref: trackRef,
842
+ role: "group",
843
+ "aria-label": "Date range selection area",
844
+ className: utils_cn.cn(
845
+ "relative h-12 touch-none rounded-lg bg-surface4",
846
+ pointer.interaction?.type === "selecting" ? "cursor-col-resize" : "cursor-crosshair"
847
+ ),
848
+ onPointerDown: pointer.startBrush,
849
+ onPointerMove: pointer.handlePointerMove,
850
+ onPointerUp: pointer.handlePointerUp,
851
+ onPointerCancel: pointer.handlePointerCancel,
852
+ onLostPointerCapture: pointer.handleLostPointerCapture,
853
+ children: [
854
+ /* @__PURE__ */ jsxRuntime.jsx(DateRangeTrackGrid, { markers: gridMarkers }),
855
+ /* @__PURE__ */ jsxRuntime.jsx(
856
+ DateRangeSelection,
857
+ {
858
+ left: model.selection.left,
859
+ width: model.selection.width,
860
+ duration: model.selection.duration,
861
+ active: pointer.interaction !== void 0,
862
+ value: model.selection.value,
863
+ valueText: model.selection.valueText,
864
+ max: model.selection.max,
865
+ onPointerDown: pointer.startPan,
866
+ onKeyDown: handleSelectionKeyDown
867
+ }
868
+ ),
869
+ /* @__PURE__ */ jsxRuntime.jsx(
870
+ DateRangeHandle,
871
+ {
872
+ boundary: "from",
873
+ position: model.handles.from.position,
874
+ value: model.handles.from.value,
875
+ valueText: model.handles.from.valueText,
876
+ min: model.handles.from.min,
877
+ max: model.handles.from.max,
878
+ active: pointer.interaction?.type === "resizing" && pointer.interaction.boundary === "from",
879
+ onPointerDown: startFromHandle,
880
+ onKeyDown: handleFromKeyDown
881
+ }
882
+ ),
883
+ /* @__PURE__ */ jsxRuntime.jsx(
884
+ DateRangeHandle,
885
+ {
886
+ boundary: "to",
887
+ position: model.handles.to.position,
888
+ value: model.handles.to.value,
889
+ valueText: model.handles.to.valueText,
890
+ min: model.handles.to.min,
891
+ max: model.handles.to.max,
892
+ active: pointer.interaction?.type === "resizing" && pointer.interaction.boundary === "to",
893
+ onPointerDown: startToHandle,
894
+ onKeyDown: handleToKeyDown
895
+ }
896
+ )
897
+ ]
898
+ }
899
+ );
900
+ }
901
+
902
+ function resolveTimelineBounds(min, max) {
903
+ const fallbackMax = /* @__PURE__ */ new Date();
904
+ const maxDate = parseDate(max) ?? fallbackMax;
905
+ const requestedMin = parseDate(min) ?? dateFns.addDays(maxDate, -365);
906
+ const minDate = requestedMin > maxDate ? maxDate : requestedMin;
907
+ return { minDate, maxDate };
908
+ }
909
+ function useDateRangeTimelineState({ value, min, max, onCommit }) {
910
+ const { minDate, maxDate } = resolveTimelineBounds(min, max);
911
+ const committedTimeline = createTimelineState(value, minDate, maxDate);
912
+ const maximumIndex = Math.max(0, dateFns.differenceInCalendarDays(maxDate, minDate));
913
+ const [viewport, setViewport] = React.useState(() => committedTimeline.viewport);
914
+ const [draftSelection, setDraftSelection] = React.useState();
915
+ const selection = draftSelection ?? committedTimeline.selection;
916
+ const state = {
917
+ ...committedTimeline,
918
+ viewport: revealTimelineSelection(viewport, selection, maximumIndex),
919
+ selection
920
+ };
921
+ function previewSelection(nextSelection) {
922
+ setDraftSelection(nextSelection);
923
+ }
924
+ function cancelSelection() {
925
+ setDraftSelection(void 0);
926
+ }
927
+ function commitSelection(nextSelection) {
928
+ setDraftSelection(void 0);
929
+ setViewport((current) => revealTimelineSelection(current, nextSelection, maximumIndex));
930
+ onCommit(toDateRange({ ...state, selection: nextSelection }));
931
+ }
932
+ function zoom(factor, anchor) {
933
+ setViewport((current) => {
934
+ const visibleViewport = revealTimelineSelection(current, state.selection, maximumIndex);
935
+ return zoomTimelineViewport({ ...state, viewport: visibleViewport }, maximumIndex, factor, anchor).viewport;
936
+ });
937
+ }
938
+ function selectDate(boundary, nextValue) {
939
+ const date = parseDate(nextValue);
940
+ if (!date) return;
941
+ const dateIndex = clamp(dateFns.differenceInCalendarDays(date, state.origin), 0, maximumIndex);
942
+ const nextSelection = boundary === "from" ? {
943
+ from: clamp(dateIndex, 0, state.selection.to),
944
+ to: state.selection.to
945
+ } : {
946
+ from: state.selection.from,
947
+ to: clamp(dateIndex, state.selection.from, maximumIndex)
948
+ };
949
+ commitSelection(nextSelection);
950
+ }
951
+ return {
952
+ state,
953
+ maximumIndex,
954
+ previewSelection,
955
+ cancelSelection,
956
+ commitSelection,
957
+ zoom,
958
+ selectDate
959
+ };
960
+ }
961
+
962
+ function DateRangeTimeline({ value, min, max, onCommit }) {
963
+ const rootRef = React.useRef(null);
964
+ const timeline = useDateRangeTimelineState({
965
+ value,
966
+ min,
967
+ max,
968
+ onCommit
969
+ });
970
+ const boundaryModel = createDateRangeBoundaryModel(timeline.state);
971
+ const axisModel = createDateRangeAxisModel(timeline.state);
972
+ return /* @__PURE__ */ jsxRuntime.jsxs(
973
+ "div",
974
+ {
975
+ ref: rootRef,
976
+ role: "group",
977
+ "aria-label": "Date range timeline",
978
+ className: "w-full overflow-x-clip select-none [overflow-clip-margin:0.75rem]",
979
+ children: [
980
+ /* @__PURE__ */ jsxRuntime.jsx(
981
+ DateRangeBoundaryPickers,
982
+ {
983
+ positions: boundaryModel.positions,
984
+ value: boundaryModel.range,
985
+ min,
986
+ max,
987
+ onSelect: timeline.selectDate
988
+ }
989
+ ),
990
+ /* @__PURE__ */ jsxRuntime.jsx(
991
+ DateRangeTrack,
992
+ {
993
+ wheelTargetRef: rootRef,
994
+ timeline: timeline.state,
995
+ maximumIndex: timeline.maximumIndex,
996
+ onSelectionPreview: timeline.previewSelection,
997
+ onSelectionCommit: timeline.commitSelection,
998
+ onSelectionCancel: timeline.cancelSelection,
999
+ onZoom: timeline.zoom
1000
+ }
1001
+ ),
1002
+ /* @__PURE__ */ jsxRuntime.jsx(DateRangeTicks, { ticks: axisModel.ticks, min, max })
1003
+ ]
1004
+ }
1005
+ );
1006
+ }
1007
+
1008
+ exports.DateRangeTimeline = DateRangeTimeline;
1009
+ exports.clampDateRangeToBounds = clampDateRangeToBounds;
1010
+ exports.getDateRangeBounds = getDateRangeBounds;
1011
+ //# sourceMappingURL=DateRangeTimeline.cjs.js.map