@inkra/react 0.3.0 → 0.4.3

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 (226) hide show
  1. package/README.md +15 -3
  2. package/dist/Button.js +3 -2
  3. package/dist/PencilIcon.d.ts +10 -0
  4. package/dist/PencilIcon.js +83 -39
  5. package/dist/PencilLine.js +9 -1
  6. package/dist/PencilText.js +15 -9
  7. package/dist/accordion/Accordion.js +65 -5
  8. package/dist/alert-dialog/index.js +14 -4
  9. package/dist/audio-player/index.d.ts +2 -0
  10. package/dist/audio-player/index.js +126 -31
  11. package/dist/calendar/index.d.ts +4 -1
  12. package/dist/calendar/index.js +31 -12
  13. package/dist/carousel/index.js +13 -6
  14. package/dist/choice-grid/index.d.ts +6 -1
  15. package/dist/choice-grid/index.js +61 -33
  16. package/dist/color-picker/GradientEditor.d.ts +39 -0
  17. package/dist/color-picker/GradientEditor.js +63 -0
  18. package/dist/color-picker/gradient.d.ts +25 -0
  19. package/dist/color-picker/gradient.js +60 -0
  20. package/dist/color-picker/index.d.ts +27 -7
  21. package/dist/color-picker/index.js +134 -32
  22. package/dist/color-swatch/index.d.ts +10 -0
  23. package/dist/color-swatch/index.js +9 -0
  24. package/dist/command/index.js +27 -4
  25. package/dist/content/index.d.ts +5 -0
  26. package/dist/content/index.js +12 -3
  27. package/dist/context-menu/index.js +1 -1
  28. package/dist/data-table/index.d.ts +7 -1
  29. package/dist/data-table/index.js +64 -16
  30. package/dist/data-table/model.d.ts +2 -0
  31. package/dist/data-table/model.js +7 -0
  32. package/dist/date-picker/index.d.ts +2 -0
  33. package/dist/date-picker/index.js +10 -9
  34. package/dist/day-agenda/index.d.ts +6 -1
  35. package/dist/day-agenda/index.js +46 -21
  36. package/dist/day-agenda/layout.js +65 -9
  37. package/dist/display/ProgressRing.js +7 -3
  38. package/dist/display/index.d.ts +4 -1
  39. package/dist/display/index.js +3 -2
  40. package/dist/dockable-panel/index.js +5 -5
  41. package/dist/drawer/index.js +4 -3
  42. package/dist/editable/index.d.ts +24 -2
  43. package/dist/editable/index.js +142 -37
  44. package/dist/empty/illustrations.d.ts +8 -0
  45. package/dist/empty/illustrations.js +78 -0
  46. package/dist/empty/index.d.ts +6 -1
  47. package/dist/empty/index.js +11 -3
  48. package/dist/flip/index.js +28 -8
  49. package/dist/form/Field.js +11 -10
  50. package/dist/form/Textarea.d.ts +3 -1
  51. package/dist/form/Textarea.js +6 -4
  52. package/dist/hotkey/index.d.ts +1 -0
  53. package/dist/hotkey/index.js +6 -4
  54. package/dist/hover-card/index.js +1 -1
  55. package/dist/image-cropper/index.js +4 -2
  56. package/dist/image-viewer/VideoResource.d.ts +21 -0
  57. package/dist/image-viewer/VideoResource.js +194 -0
  58. package/dist/image-viewer/index.d.ts +14 -0
  59. package/dist/image-viewer/index.js +118 -65
  60. package/dist/image-viewer/lucideGeometry.d.ts +3 -0
  61. package/dist/image-viewer/lucideGeometry.js +19 -0
  62. package/dist/index.d.ts +2 -0
  63. package/dist/index.js +2 -0
  64. package/dist/input-number/index.js +9 -4
  65. package/dist/internal/cache.d.ts +4 -0
  66. package/dist/internal/cache.js +18 -1
  67. package/dist/internal/iconGeometry.d.ts +2 -0
  68. package/dist/internal/iconGeometry.js +32 -0
  69. package/dist/internal/renderTokens.d.ts +4 -0
  70. package/dist/internal/renderTokens.js +4 -0
  71. package/dist/internal/stroke.d.ts +7 -0
  72. package/dist/internal/stroke.js +63 -7
  73. package/dist/internal/surfaceBudget.d.ts +20 -0
  74. package/dist/internal/surfaceBudget.js +69 -0
  75. package/dist/internal/surfaceRenderer.js +87 -28
  76. package/dist/internal/surfaceVisibility.d.ts +3 -0
  77. package/dist/internal/surfaceVisibility.js +60 -0
  78. package/dist/internal/textRenderer.d.ts +2 -0
  79. package/dist/internal/textRenderer.js +3 -3
  80. package/dist/internal/usePencilSurface.js +17 -1
  81. package/dist/internal/wash.js +4 -3
  82. package/dist/journey-map/terrain.js +10 -5
  83. package/dist/layout/index.js +1 -1
  84. package/dist/log/index.js +29 -22
  85. package/dist/markdown/CodeBlock.d.ts +19 -0
  86. package/dist/markdown/CodeBlock.js +86 -0
  87. package/dist/markdown/Deletion.d.ts +2 -0
  88. package/dist/markdown/Deletion.js +69 -0
  89. package/dist/markdown/icons.d.ts +14 -0
  90. package/dist/markdown/icons.js +16 -0
  91. package/dist/markdown/index.d.ts +17 -0
  92. package/dist/markdown/index.js +175 -0
  93. package/dist/node-graph/index.d.ts +2 -0
  94. package/dist/node-graph/index.js +49 -25
  95. package/dist/number-ticker/precision.js +9 -2
  96. package/dist/overflow-list/index.js +5 -2
  97. package/dist/overlay/Overlay.js +1 -1
  98. package/dist/overlay/Popover.js +3 -3
  99. package/dist/overlay/themeBridge.js +14 -2
  100. package/dist/pagination/index.d.ts +10 -1
  101. package/dist/pagination/index.js +6 -4
  102. package/dist/pan-zoom/index.d.ts +2 -0
  103. package/dist/pan-zoom/index.js +7 -6
  104. package/dist/pencil.d.ts +1 -0
  105. package/dist/pencil.js +4 -4
  106. package/dist/radar/index.d.ts +2 -0
  107. package/dist/radar/index.js +40 -20
  108. package/dist/rating/index.d.ts +4 -0
  109. package/dist/rating/index.js +128 -24
  110. package/dist/resizable/index.js +1 -1
  111. package/dist/scroll-area/index.d.ts +1 -1
  112. package/dist/scroll-area/index.js +5 -2
  113. package/dist/select/MultipleSelect.js +5 -4
  114. package/dist/select/SearchableSelect.js +61 -6
  115. package/dist/select/Select.d.ts +1 -0
  116. package/dist/select/Select.js +3 -1
  117. package/dist/select/StepperSelect.d.ts +29 -0
  118. package/dist/select/StepperSelect.js +119 -0
  119. package/dist/select/index.d.ts +2 -0
  120. package/dist/select/index.js +1 -0
  121. package/dist/select/stepperModel.d.ts +7 -0
  122. package/dist/select/stepperModel.js +29 -0
  123. package/dist/selection/Checkbox.js +4 -3
  124. package/dist/selection/RadioGroup.js +29 -13
  125. package/dist/selection/Switch.js +4 -3
  126. package/dist/selection/useNativeChecked.d.ts +2 -1
  127. package/dist/selection/useNativeChecked.js +26 -8
  128. package/dist/slider/index.js +50 -6
  129. package/dist/sortable/index.js +6 -2
  130. package/dist/sparkline/index.js +17 -2
  131. package/dist/sparkline/model.js +11 -4
  132. package/dist/styles/accordion.css +228 -0
  133. package/dist/styles/alert-dialog.css +139 -0
  134. package/dist/styles/audio-player.css +22 -0
  135. package/dist/styles/base.css +638 -0
  136. package/dist/styles/breadcrumb.css +7 -0
  137. package/dist/styles/calendar.css +62 -0
  138. package/dist/styles/carousel.css +173 -0
  139. package/dist/styles/choice-grid.css +226 -0
  140. package/dist/styles/color-picker.css +281 -0
  141. package/dist/styles/color-swatch.css +47 -0
  142. package/dist/styles/command.css +52 -0
  143. package/dist/styles/content.css +79 -0
  144. package/dist/styles/context-menu.css +35 -0
  145. package/dist/styles/data-table.css +31 -0
  146. package/dist/styles/date-picker.css +107 -0
  147. package/dist/styles/day-agenda.css +30 -0
  148. package/dist/styles/display.css +304 -0
  149. package/dist/styles/dockable-panel.css +110 -0
  150. package/dist/styles/drawer.css +133 -0
  151. package/dist/styles/dropdown-menu.css +68 -0
  152. package/dist/styles/editable.css +145 -0
  153. package/dist/styles/empty.css +108 -0
  154. package/dist/styles/flip.css +101 -0
  155. package/dist/styles/form.css +634 -0
  156. package/dist/styles/heatmap.css +256 -0
  157. package/dist/styles/hotkey.css +189 -0
  158. package/dist/styles/hover-card.css +27 -0
  159. package/dist/styles/image-cropper.css +205 -0
  160. package/dist/styles/image-viewer.css +422 -0
  161. package/dist/styles/input-number.css +21 -0
  162. package/dist/styles/journey-map.css +19 -0
  163. package/dist/styles/keypad.css +92 -0
  164. package/dist/styles/layout.css +72 -0
  165. package/dist/styles/leaflet.css +661 -0
  166. package/dist/styles/log.css +203 -0
  167. package/dist/styles/markdown.css +379 -0
  168. package/dist/styles/node-graph.css +142 -0
  169. package/dist/styles/number-ticker.css +83 -0
  170. package/dist/styles/overflow-list.css +51 -0
  171. package/dist/styles/overlay.css +284 -0
  172. package/dist/styles/pagination.css +21 -0
  173. package/dist/styles/pan-zoom.css +136 -0
  174. package/dist/styles/radar.css +1 -0
  175. package/dist/styles/rating.css +121 -0
  176. package/dist/styles/resizable.css +108 -0
  177. package/dist/styles/scroll-area.css +122 -0
  178. package/dist/styles/select.css +557 -0
  179. package/dist/styles/selection.css +264 -0
  180. package/dist/styles/slider.css +145 -0
  181. package/dist/styles/slot.css +109 -0
  182. package/dist/styles/sortable.css +159 -0
  183. package/dist/styles/sparkline.css +24 -0
  184. package/dist/styles/steps.css +27 -0
  185. package/dist/styles/tabs.css +79 -0
  186. package/dist/styles/tag-input.css +213 -0
  187. package/dist/styles/time-picker.css +66 -0
  188. package/dist/styles/timeline.css +24 -0
  189. package/dist/styles/toast.css +160 -0
  190. package/dist/styles/toggle-group.css +107 -0
  191. package/dist/styles/tooltip.css +78 -0
  192. package/dist/styles/transfer.css +148 -0
  193. package/dist/styles/tree-select.css +131 -0
  194. package/dist/styles/tree.css +59 -0
  195. package/dist/styles/typewriter.css +62 -0
  196. package/dist/styles/upload.css +60 -0
  197. package/dist/styles/virtual-list.css +112 -0
  198. package/dist/styles.css +2094 -421
  199. package/dist/tabs/index.d.ts +9 -0
  200. package/dist/tabs/index.js +66 -16
  201. package/dist/tabs/useOverflow.d.ts +20 -0
  202. package/dist/tabs/useOverflow.js +145 -0
  203. package/dist/tag-input/index.d.ts +7 -2
  204. package/dist/tag-input/index.js +70 -26
  205. package/dist/time-picker/index.js +38 -20
  206. package/dist/toast/index.d.ts +19 -11
  207. package/dist/toast/index.js +48 -8
  208. package/dist/toast/manager.d.ts +42 -0
  209. package/dist/toast/manager.js +85 -0
  210. package/dist/toast/useToastNavigation.d.ts +3 -0
  211. package/dist/toast/useToastNavigation.js +75 -0
  212. package/dist/tokens.css +4 -0
  213. package/dist/tooltip/Tooltip.js +3 -3
  214. package/dist/transfer/index.d.ts +5 -0
  215. package/dist/transfer/index.js +93 -29
  216. package/dist/transfer/model.d.ts +4 -0
  217. package/dist/transfer/model.js +37 -11
  218. package/dist/tree/index.d.ts +5 -1
  219. package/dist/tree/index.js +5 -5
  220. package/dist/tree-select/index.js +67 -30
  221. package/dist/tree-select/model.d.ts +11 -5
  222. package/dist/tree-select/model.js +111 -83
  223. package/dist/typewriter/index.js +10 -10
  224. package/dist/upload/index.js +30 -16
  225. package/dist/virtual-list/index.js +7 -5
  226. package/package.json +413 -3
@@ -1,12 +1,17 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { flexRender, functionalUpdate, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable, } from '@tanstack/react-table';
3
- import { useEffect, useState } from 'react';
3
+ import { useEffect, useMemo, useRef, useState } from 'react';
4
4
  import { Button } from '../Button.js';
5
5
  import { PencilIcon } from '../PencilIcon.js';
6
+ import { PencilSurface } from '../PencilSurface.js';
7
+ import { PencilText } from '../PencilText.js';
6
8
  import { Input } from '../form/Input.js';
7
9
  import { Table } from '../content/index.js';
8
10
  import { Checkbox } from '../selection/Checkbox.js';
11
+ import { normalizeDataTablePagination } from './model.js';
9
12
  const sortIcon = [{ d: 'M 8 10 L 12 6 L 16 10 M 8 14 L 12 18 L 16 14' }];
13
+ const sortAscendingIcon = [{ d: 'M 7 15 L 12 10 L 17 15' }];
14
+ const sortDescendingIcon = [{ d: 'M 7 9 L 12 14 L 17 9' }];
10
15
  const previousIcon = [{ d: 'M 14 7 L 9 12 L 14 17' }];
11
16
  const nextIcon = [{ d: 'M 10 7 L 15 12 L 10 17' }];
12
17
  function classes(...values) {
@@ -15,27 +20,41 @@ function classes(...values) {
15
20
  function resolve(updater, current) {
16
21
  return functionalUpdate(updater, current);
17
22
  }
23
+ function pencilCopy(value, seed) {
24
+ return typeof value === 'string' || typeof value === 'number'
25
+ ? _jsx(PencilText, { seed: seed, children: String(value) })
26
+ : value;
27
+ }
18
28
  function SelectionCheckbox(props) {
19
29
  return _jsx(Checkbox, { className: "inkra-data-table__selection", checked: props.checked, indeterminate: props.indeterminate, disabled: props.disabled, label: _jsx("span", { className: "inkra-visually-hidden", children: props.label }), onCheckedChange: props.onChange });
20
30
  }
21
- export function DataTable({ data, columns, getRowId, caption, density = 'comfortable', layout = 'auto', striped = false, hoverable = true, stickyHeader = false, captionSide = 'top', filterable = false, filterLabel = 'Filter rows', filterPlaceholder = 'Filter rows', globalFilter, defaultGlobalFilter = '', onGlobalFilterChange, sorting, defaultSorting = [], onSortingChange, pagination = false, paginationState, defaultPagination = { pageIndex: 0, pageSize: 10 }, onPaginationChange, selectable = false, rowSelection, defaultRowSelection = {}, onRowSelectionChange, getRowCanSelect, loading = false, loadingMessage = 'Loading rows', emptyMessage = 'No rows', selectAllLabel = 'Select all rows on this page', selectRowLabel = row => `Select row ${row.id}`, previousPageLabel = 'Previous page', nextPageLabel = 'Next page', paginationLabel = 'Table pagination', pageLabel = (page, pageCount) => `Page ${page} of ${pageCount}`, sortStatusLabel = (columnId, direction) => direction === 'asc' ? `${columnId}: sorted ascending` : direction === 'desc' ? `${columnId}: sorted descending` : `${columnId}: not sorted`, className, ...props }) {
31
+ export function DataTable({ data, columns, getRowId, caption, density = 'comfortable', layout = 'auto', striped = false, hoverable = true, stickyHeader = false, captionSide = 'top', filterable = false, filterLabel = 'Filter rows', filterPlaceholder = 'Filter rows', globalFilter, defaultGlobalFilter = '', onGlobalFilterChange, sorting, defaultSorting = [], onSortingChange, pagination = false, manualPagination = false, rowCount, manualSorting = false, manualFiltering = false, paginationState, defaultPagination = { pageIndex: 0, pageSize: 10 }, onPaginationChange, selectable = false, rowSelection, defaultRowSelection = {}, onRowSelectionChange, getRowCanSelect, loading = false, loadingMessage = 'Loading rows', emptyMessage = 'No rows', selectAllLabel = 'Select all rows on this page', selectRowLabel = row => `Select row ${row.id}`, previousPageLabel = 'Previous page', nextPageLabel = 'Next page', paginationLabel = 'Table pagination', pageLabel = (page, pageCount) => `Page ${page} of ${pageCount}`, sortStatusLabel = (columnId, direction) => direction === 'asc' ? `${columnId}: sorted ascending` : direction === 'desc' ? `${columnId}: sorted descending` : `${columnId}: not sorted`, className, ...props }) {
22
32
  const [internalFilter, setInternalFilter] = useState(defaultGlobalFilter);
23
33
  const [internalSorting, setInternalSorting] = useState(defaultSorting);
24
34
  const [internalPagination, setInternalPagination] = useState(defaultPagination);
25
35
  const [internalSelection, setInternalSelection] = useState(defaultRowSelection);
36
+ const paginationCorrection = useRef('');
26
37
  const resolvedFilter = globalFilter ?? internalFilter;
27
38
  const resolvedSorting = sorting ?? internalSorting;
28
39
  const resolvedPagination = paginationState ?? internalPagination;
29
40
  const resolvedSelection = rowSelection ?? internalSelection;
41
+ const paginationRowCount = manualPagination && pagination && rowCount !== undefined
42
+ ? (Number.isFinite(rowCount) ? Math.max(0, Math.floor(rowCount)) : data.length)
43
+ : data.length;
44
+ const initialPagination = useMemo(() => normalizeDataTablePagination(resolvedPagination, paginationRowCount), [resolvedPagination.pageIndex, resolvedPagination.pageSize, paginationRowCount]);
30
45
  const handleSorting = updater => {
31
46
  if (sorting === undefined)
32
47
  setInternalSorting(current => resolve(updater, current));
33
48
  onSortingChange?.(updater);
34
49
  };
35
50
  const handlePagination = updater => {
51
+ const normalizedUpdater = current => {
52
+ const rowCount = manualPagination ? paginationRowCount : table.getPrePaginationRowModel().rows.length;
53
+ return normalizeDataTablePagination(resolve(updater, normalizeDataTablePagination(current, rowCount)), rowCount);
54
+ };
36
55
  if (paginationState === undefined)
37
- setInternalPagination(current => resolve(updater, current));
38
- onPaginationChange?.(updater);
56
+ setInternalPagination(normalizedUpdater);
57
+ onPaginationChange?.(normalizedUpdater);
39
58
  };
40
59
  const handleSelection = updater => {
41
60
  if (rowSelection === undefined)
@@ -53,39 +72,68 @@ export function DataTable({ data, columns, getRowId, caption, density = 'comfort
53
72
  data,
54
73
  columns,
55
74
  getRowId,
75
+ manualPagination: pagination && manualPagination,
76
+ rowCount: pagination && manualPagination ? paginationRowCount : undefined,
77
+ manualSorting,
78
+ manualFiltering,
56
79
  state: {
57
80
  globalFilter: resolvedFilter,
58
81
  sorting: resolvedSorting,
59
- pagination: resolvedPagination,
82
+ pagination: initialPagination,
60
83
  rowSelection: resolvedSelection,
61
84
  },
62
85
  onGlobalFilterChange: updater => handleFilter(resolve(updater, resolvedFilter)),
63
86
  onSortingChange: handleSorting,
64
87
  onPaginationChange: handlePagination,
65
88
  onRowSelectionChange: handleSelection,
89
+ defaultColumn: {
90
+ header: context => pencilCopy(context.column.id, `data-table-header-${context.header.id}`),
91
+ cell: context => pencilCopy(context.renderValue()?.toString?.() ?? null, `data-table-cell-${context.cell.id}`),
92
+ },
66
93
  enableRowSelection: selectable ? getRowCanSelect ?? true : false,
67
94
  getCoreRowModel: getCoreRowModel(),
68
95
  getFilteredRowModel: getFilteredRowModel(),
69
96
  getSortedRowModel: getSortedRowModel(),
70
97
  getPaginationRowModel: pagination ? getPaginationRowModel() : undefined,
71
98
  });
99
+ const filteredRowCount = pagination && manualPagination ? paginationRowCount : table.getPrePaginationRowModel().rows.length;
100
+ const effectivePagination = useMemo(() => normalizeDataTablePagination(initialPagination, filteredRowCount), [initialPagination, filteredRowCount]);
101
+ if (table.getState().pagination.pageIndex !== effectivePagination.pageIndex
102
+ || table.getState().pagination.pageSize !== effectivePagination.pageSize) {
103
+ table.setOptions(previous => ({
104
+ ...previous,
105
+ state: { ...previous.state, pagination: effectivePagination },
106
+ }));
107
+ }
72
108
  const rows = table.getRowModel().rows;
73
- const filteredRowCount = table.getPrePaginationRowModel().rows.length;
74
- const pageCount = Math.max(1, table.getPageCount());
109
+ const pageCount = filteredRowCount === 0 ? 0 : Math.ceil(filteredRowCount / effectivePagination.pageSize);
75
110
  const visibleColumnCount = Math.max(1, table.getVisibleLeafColumns().length + (selectable ? 1 : 0));
76
111
  const hasSelectableRows = rows.some(row => row.getCanSelect());
77
112
  useEffect(() => {
78
113
  if (!pagination)
79
114
  return;
80
- const lastPageIndex = Math.max(0, Math.ceil(filteredRowCount / resolvedPagination.pageSize) - 1);
81
- if (resolvedPagination.pageIndex <= lastPageIndex)
115
+ const matches = resolvedPagination.pageIndex === effectivePagination.pageIndex
116
+ && resolvedPagination.pageSize === effectivePagination.pageSize;
117
+ if (matches) {
118
+ paginationCorrection.current = '';
119
+ return;
120
+ }
121
+ const signature = `${filteredRowCount}:${resolvedPagination.pageIndex}:${resolvedPagination.pageSize}->${effectivePagination.pageIndex}:${effectivePagination.pageSize}`;
122
+ if (paginationCorrection.current === signature)
82
123
  return;
83
- const next = { ...resolvedPagination, pageIndex: lastPageIndex };
124
+ paginationCorrection.current = signature;
84
125
  if (paginationState === undefined)
85
- setInternalPagination(next);
86
- onPaginationChange?.(next);
87
- }, [filteredRowCount, onPaginationChange, pagination, paginationState, resolvedPagination]);
88
- return _jsxs("div", { ...props, className: classes('inkra-data-table', className), "data-loading": loading || undefined, children: [filterable && _jsx("div", { className: "inkra-data-table__toolbar", children: _jsxs("label", { className: "inkra-data-table__filter", children: [_jsx("span", { children: filterLabel }), _jsx(Input, { type: "search", value: resolvedFilter, placeholder: filterPlaceholder, onChange: event => handleFilter(event.currentTarget.value) })] }) }), _jsx(Table.Container, { className: "inkra-data-table__container", children: _jsxs(Table.Root, { density: density, layout: layout, striped: striped, hoverable: hoverable, stickyHeader: stickyHeader, captionSide: captionSide, children: [caption != null && _jsx(Table.Caption, { children: caption }), _jsx(Table.Header, { children: table.getHeaderGroups().map(headerGroup => _jsxs(Table.Row, { children: [selectable && _jsx(Table.Head, { className: "inkra-data-table__select-cell", children: _jsx(SelectionCheckbox, { checked: pagination ? table.getIsAllPageRowsSelected() : table.getIsAllRowsSelected(), indeterminate: pagination ? table.getIsSomePageRowsSelected() : table.getIsSomeRowsSelected(), label: selectAllLabel, disabled: loading || !hasSelectableRows, onChange: checked => pagination ? table.toggleAllPageRowsSelected(checked) : table.toggleAllRowsSelected(checked) }) }), headerGroup.headers.map(header => _jsx(Table.Head, { colSpan: header.colSpan, "aria-sort": header.column.getIsSorted() === 'asc' ? 'ascending' : header.column.getIsSorted() === 'desc' ? 'descending' : header.column.getCanSort() ? 'none' : undefined, children: header.isPlaceholder ? null : header.column.getCanSort() ? _jsxs("button", { type: "button", className: "inkra-data-table__sort", "data-sorted": header.column.getIsSorted() || undefined, onClick: header.column.getToggleSortingHandler(), children: [_jsx("span", { children: flexRender(header.column.columnDef.header, header.getContext()) }), _jsx(PencilIcon, { "aria-hidden": "true", paths: sortIcon, size: 18, sourceSize: 24, active: Boolean(header.column.getIsSorted()) }), _jsx("span", { className: "inkra-visually-hidden", children: sortStatusLabel(header.column.id, header.column.getIsSorted()) })] }) : flexRender(header.column.columnDef.header, header.getContext()) }, header.id))] }, headerGroup.id)) }), _jsx(Table.Body, { children: loading ? _jsx(Table.Row, { children: _jsx(Table.Cell, { colSpan: visibleColumnCount, children: _jsx("div", { className: "inkra-data-table__state", role: "status", children: loadingMessage }) }) })
89
- : rows.length === 0 ? _jsx(Table.Row, { children: _jsx(Table.Cell, { colSpan: visibleColumnCount, children: _jsx("div", { className: "inkra-data-table__state", children: emptyMessage }) }) })
90
- : rows.map(row => _jsxs(Table.Row, { selected: row.getIsSelected(), children: [selectable && _jsx(Table.Cell, { className: "inkra-data-table__select-cell", children: _jsx(SelectionCheckbox, { checked: row.getIsSelected(), label: selectRowLabel(row), disabled: !row.getCanSelect(), onChange: checked => row.toggleSelected(checked) }) }), row.getVisibleCells().map(cell => _jsx(Table.Cell, { children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))] }, row.id)) })] }) }), pagination && _jsxs("nav", { className: "inkra-data-table__pagination", "aria-label": paginationLabel, children: [_jsx(Button, { size: "icon-xs", variant: "ghost", "aria-label": previousPageLabel, disabled: loading || !table.getCanPreviousPage(), onClick: () => table.previousPage(), children: _jsx(PencilIcon, { paths: previousIcon, size: 18, sourceSize: 24 }) }), _jsx("span", { "aria-live": "polite", children: pageLabel(table.getState().pagination.pageIndex + 1, pageCount) }), _jsx(Button, { size: "icon-xs", variant: "ghost", "aria-label": nextPageLabel, disabled: loading || !table.getCanNextPage(), onClick: () => table.nextPage(), children: _jsx(PencilIcon, { paths: nextIcon, size: 18, sourceSize: 24 }) })] })] });
126
+ setInternalPagination(effectivePagination);
127
+ onPaginationChange?.(effectivePagination);
128
+ }, [effectivePagination.pageIndex, effectivePagination.pageSize, filteredRowCount, onPaginationChange, pagination,
129
+ paginationState, resolvedPagination.pageIndex, resolvedPagination.pageSize]);
130
+ return _jsxs("div", { ...props, className: classes('inkra-data-table', className), "data-loading": loading || undefined, children: [filterable && _jsx("div", { className: "inkra-data-table__toolbar", children: _jsxs("label", { className: "inkra-data-table__filter", children: [_jsx("span", { children: pencilCopy(filterLabel, 'data-table-filter-label') }), _jsx(Input, { type: "search", value: resolvedFilter, placeholder: filterPlaceholder, onChange: event => handleFilter(event.currentTarget.value) })] }) }), _jsx(Table.Container, { className: "inkra-data-table__container", children: _jsxs(Table.Root, { density: density, layout: layout, striped: striped, hoverable: hoverable, stickyHeader: stickyHeader, captionSide: captionSide, children: [caption != null && _jsx(Table.Caption, { children: caption }), _jsx(Table.Header, { children: table.getHeaderGroups().map(headerGroup => _jsxs(Table.Row, { children: [selectable && _jsx(Table.Head, { className: "inkra-data-table__select-cell", children: _jsx(SelectionCheckbox, { checked: pagination ? table.getIsAllPageRowsSelected() : table.getIsAllRowsSelected(), indeterminate: pagination ? table.getIsSomePageRowsSelected() : table.getIsSomeRowsSelected(), label: selectAllLabel, disabled: loading || !hasSelectableRows, onChange: checked => pagination ? table.toggleAllPageRowsSelected(checked) : table.toggleAllRowsSelected(checked) }) }), headerGroup.headers.map(header => _jsx(Table.Head, { colSpan: header.colSpan, "aria-sort": header.column.getIsSorted() === 'asc' ? 'ascending' : header.column.getIsSorted() === 'desc' ? 'descending' : header.column.getCanSort() ? 'none' : undefined, children: header.isPlaceholder ? null : (() => {
131
+ const rendered = flexRender(header.column.columnDef.header, header.getContext());
132
+ const direction = header.column.getIsSorted();
133
+ if (!header.column.getCanSort())
134
+ return pencilCopy(rendered, `data-table-header-${header.id}`);
135
+ return _jsxs("button", { type: "button", className: "inkra-data-table__sort", "data-sorted": direction || undefined, onClick: header.column.getToggleSortingHandler(), children: [_jsx(PencilSurface, { as: "span", "aria-hidden": "true", className: "inkra-data-table__sort-focus", variant: "outline", radius: 6, seed: `data-table-sort-${header.id}-focus` }), _jsx("span", { className: "inkra-data-table__sort-label", children: pencilCopy(rendered, `data-table-sort-${header.id}`) }), _jsx(PencilIcon, { "aria-hidden": "true", className: "inkra-data-table__sort-icon", "data-direction": direction || 'none', paths: direction === 'asc' ? sortAscendingIcon : direction === 'desc' ? sortDescendingIcon : sortIcon, size: 18, sourceSize: 24, active: Boolean(direction) }), _jsx("span", { className: "inkra-visually-hidden", children: sortStatusLabel(header.column.id, direction) })] });
136
+ })() }, header.id))] }, headerGroup.id)) }), _jsx(Table.Body, { children: loading ? _jsx(Table.Row, { children: _jsx(Table.Cell, { colSpan: visibleColumnCount, children: _jsx("div", { className: "inkra-data-table__state", role: "status", children: pencilCopy(loadingMessage, 'data-table-loading') }) }) })
137
+ : rows.length === 0 ? _jsx(Table.Row, { children: _jsx(Table.Cell, { colSpan: visibleColumnCount, children: _jsx("div", { className: "inkra-data-table__state", children: pencilCopy(emptyMessage, 'data-table-empty') }) }) })
138
+ : rows.map(row => _jsxs(Table.Row, { selected: row.getIsSelected(), children: [selectable && _jsx(Table.Cell, { className: "inkra-data-table__select-cell", children: _jsx(SelectionCheckbox, { checked: row.getIsSelected(), label: selectRowLabel(row), disabled: !row.getCanSelect(), onChange: checked => row.toggleSelected(checked) }) }), row.getVisibleCells().map(cell => _jsx(Table.Cell, { children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))] }, row.id)) })] }) }), pagination && _jsxs("nav", { className: "inkra-data-table__pagination", "aria-label": paginationLabel, children: [_jsx(Button, { size: "icon-xs", variant: "ghost", "aria-label": previousPageLabel, disabled: loading || pageCount === 0 || effectivePagination.pageIndex <= 0, onClick: () => table.previousPage(), children: _jsx(PencilIcon, { className: "inkra-data-table__page-arrow", paths: previousIcon, size: 18, sourceSize: 24 }) }), _jsx("span", { "aria-live": "polite", "aria-atomic": "true", children: pencilCopy(pageLabel(pageCount === 0 ? 0 : effectivePagination.pageIndex + 1, pageCount), 'data-table-page-label') }), _jsx(Button, { size: "icon-xs", variant: "ghost", "aria-label": nextPageLabel, disabled: loading || pageCount === 0 || effectivePagination.pageIndex >= pageCount - 1, onClick: () => table.nextPage(), children: _jsx(PencilIcon, { className: "inkra-data-table__page-arrow", paths: nextIcon, size: 18, sourceSize: 24 }) })] })] });
91
139
  }
@@ -0,0 +1,2 @@
1
+ import type { PaginationState } from '@tanstack/react-table';
2
+ export declare function normalizeDataTablePagination(state: PaginationState, rowCount: number): PaginationState;
@@ -0,0 +1,7 @@
1
+ export function normalizeDataTablePagination(state, rowCount) {
2
+ const pageSize = Number.isFinite(state.pageSize) ? Math.max(1, Math.floor(state.pageSize)) : 10;
3
+ const count = Number.isFinite(rowCount) ? Math.max(0, Math.floor(rowCount)) : 0;
4
+ const pageCount = count === 0 ? 0 : Math.ceil(count / pageSize);
5
+ const requestedIndex = Number.isFinite(state.pageIndex) ? Math.max(0, Math.floor(state.pageIndex)) : 0;
6
+ return { pageIndex: pageCount === 0 ? 0 : Math.min(requestedIndex, pageCount - 1), pageSize };
7
+ }
@@ -1,6 +1,7 @@
1
1
  import type { AriaAttributes, CSSProperties, FocusEventHandler } from 'react';
2
2
  import type { DateRange } from 'react-day-picker';
3
3
  import type { CalendarProps } from '../calendar/index.js';
4
+ import type { FieldControlSize } from '../form/types.js';
4
5
  export type DatePickerMode = 'single' | 'range';
5
6
  export type DatePickerRange = DateRange;
6
7
  export type DatePickerValue = Date | DatePickerRange | undefined;
@@ -23,6 +24,7 @@ export interface DatePickerProps {
23
24
  onBlur?: FocusEventHandler<HTMLButtonElement>;
24
25
  onFocus?: FocusEventHandler<HTMLButtonElement>;
25
26
  required?: boolean;
27
+ size?: FieldControlSize;
26
28
  style?: CSSProperties;
27
29
  mode?: DatePickerMode;
28
30
  value?: DatePickerValue;
@@ -1,6 +1,6 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { forwardRef, useEffect, useId, useImperativeHandle, useMemo, useRef, useState, } from 'react';
3
- import { Button } from '../Button.js';
3
+ import { Button, ButtonGroup } from '../Button.js';
4
4
  import { Calendar } from '../calendar/index.js';
5
5
  import { mergeAriaIds, useFieldContext } from '../form/context.js';
6
6
  import { useResolvedControlProps } from '../form/controlProps.js';
@@ -52,7 +52,7 @@ function defaultCopy(locale) {
52
52
  export const DatePicker = forwardRef(function DatePicker(allProps, forwardedRef) {
53
53
  const valueControlled = Object.prototype.hasOwnProperty.call(allProps, 'value');
54
54
  const openControlled = Object.prototype.hasOwnProperty.call(allProps, 'open');
55
- const { 'aria-describedby': ariaDescribedBy, 'aria-errormessage': ariaErrorMessage, 'aria-invalid': ariaInvalid, 'aria-label': ariaLabel, autoFocus, calendarLabel, calendarProps = {}, className, clearable = true, clearButtonLabel, defaultOpen = false, defaultValue, dir, disabled = false, endName, form, fullWidth = true, id, invalid = false, locale = 'en-US', mode = 'single', name, onBlur, onFocus, onOpenChange, onValueChange, open, openButtonLabel, placeholder, rangeSeparator = ' – ', readOnly = false, required = false, style, value, } = allProps;
55
+ const { 'aria-describedby': ariaDescribedBy, 'aria-errormessage': ariaErrorMessage, 'aria-invalid': ariaInvalid, 'aria-label': ariaLabel, autoFocus, calendarLabel, calendarProps = {}, className, clearable = true, clearButtonLabel, defaultOpen = false, defaultValue, dir, disabled = false, endName, form, fullWidth = true, id, invalid = false, locale = 'en-US', mode = 'single', name, onBlur, onFocus, onOpenChange, onValueChange, open, openButtonLabel, placeholder, rangeSeparator = ' – ', readOnly = false, required = false, size = 'md', style, value, } = allProps;
56
56
  const generatedId = useId();
57
57
  const field = useFieldContext();
58
58
  const triggerRef = useRef(null);
@@ -85,6 +85,7 @@ export const DatePicker = forwardRef(function DatePicker(allProps, forwardedRef)
85
85
  const captionFormatter = useMemo(() => new Intl.DateTimeFormat(locale, {
86
86
  month: 'long', year: 'numeric',
87
87
  }), [locale]);
88
+ const dayFormatter = useMemo(() => new Intl.NumberFormat(locale), [locale]);
88
89
  const weekdayFormatter = useMemo(() => new Intl.DateTimeFormat(locale, { weekday: 'short' }), [locale]);
89
90
  const displayValue = singleValue
90
91
  ? formatter.format(singleValue)
@@ -128,7 +129,7 @@ export const DatePicker = forwardRef(function DatePicker(allProps, forwardedRef)
128
129
  const { disabled: disabledDays, formatters, labels, defaultMonth, ...restCalendarProps } = calendarProps;
129
130
  const localizedFormatters = {
130
131
  formatCaption: date => captionFormatter.format(date),
131
- formatDay: date => new Intl.NumberFormat(locale).format(date.getDate()),
132
+ formatDay: date => dayFormatter.format(date.getDate()),
132
133
  formatWeekdayName: date => weekdayFormatter.format(date),
133
134
  ...formatters,
134
135
  };
@@ -150,11 +151,11 @@ export const DatePicker = forwardRef(function DatePicker(allProps, forwardedRef)
150
151
  formatters: localizedFormatters,
151
152
  labels: localizedLabels,
152
153
  };
153
- return (_jsxs(Popover.Root, { open: currentOpen, onOpenChange: updateOpen, children: [_jsxs("span", { className: classes('inkra-date-picker', className), "data-disabled": state.disabled || undefined, "data-full-width": fullWidth || undefined, "data-has-value": hasValue || undefined, "data-invalid": state.invalid || constraintInvalid || undefined, "data-mode": mode, "data-readonly": state.readOnly || undefined, dir: dir, style: style, children: [_jsx(Popover.Trigger, { asChild: true, children: _jsx(Button, { "aria-describedby": mergeAriaIds(state.describedBy, accessibleValueId), "aria-errormessage": state.errorMessage, "aria-invalid": constraintInvalid || state.ariaInvalid, "aria-label": ariaLabel ?? (openButtonLabel ? `${openButtonLabel}: ${displayValue}` : undefined), "aria-required": state.required || undefined, autoFocus: autoFocus, className: "inkra-date-picker__trigger", disabled: state.disabled, form: form, fullWidth: fullWidth, id: triggerId, onBlur: onBlur, onFocus: onFocus, ref: triggerRef, startIcon: _jsx(PencilIcon, { paths: calendarPaths, sourceSize: 24, size: 22 }), variant: "outline", children: _jsx("span", { className: "inkra-date-picker__value", "data-placeholder": !hasValue || undefined, children: displayValue }) }) }), accessibleValueId ? _jsx("span", { className: "inkra-date-picker__accessible-value", id: accessibleValueId, children: displayValue }) : null, clearable ? (_jsx(Button, { "aria-hidden": !hasValue || undefined, "aria-label": clearButtonLabel ?? copy.clear, className: "inkra-date-picker__clear", disabled: !hasValue || state.disabled || state.readOnly, onClick: () => {
154
- commit(undefined, 'clear');
155
- updateOpen(false);
156
- triggerRef.current?.focus({ preventScroll: true });
157
- }, size: "icon", tabIndex: hasValue ? 0 : -1, variant: "ghost", children: _jsx(PencilIcon, { paths: clearPaths, sourceSize: 24, size: 22 }) })) : null, name || state.required ? (mode === 'range' ? _jsxs(_Fragment, { children: [_jsx("input", { "aria-hidden": "true", className: "inkra-date-picker__form-field", disabled: state.disabled, form: form, name: name, onChange: () => undefined, onInvalid: event => { event.preventDefault(); setConstraintInvalid(true); triggerRef.current?.focus(); }, readOnly: state.readOnly, required: state.required, tabIndex: -1, type: "text", value: datePickerLocalValue(rangeValue?.from) }), _jsx("input", { "aria-hidden": "true", className: "inkra-date-picker__form-field", disabled: state.disabled, form: form, name: endName ?? (name ? `${name}End` : undefined), onChange: () => undefined, onInvalid: event => { event.preventDefault(); setConstraintInvalid(true); triggerRef.current?.focus(); }, readOnly: state.readOnly, required: state.required, tabIndex: -1, type: "text", value: datePickerLocalValue(rangeValue?.to) })] }) : _jsx("input", { "aria-hidden": "true", className: "inkra-date-picker__form-field", disabled: state.disabled, form: form, name: name, onChange: () => undefined, onInvalid: event => { event.preventDefault(); setConstraintInvalid(true); triggerRef.current?.focus(); }, readOnly: state.readOnly, required: state.required, tabIndex: -1, type: "text", value: datePickerLocalValue(singleValue) })) : null] }), _jsx(Popover.Portal, { children: _jsxs(Popover.Content, { align: "start", "aria-label": calendarLabel ?? copy.calendar, className: "inkra-date-picker__popover", collisionPadding: 12, onOpenAutoFocus: event => {
154
+ return (_jsxs(Popover.Root, { open: currentOpen, onOpenChange: updateOpen, children: [_jsxs("span", { className: classes('inkra-date-picker', className), "data-disabled": state.disabled || undefined, "data-full-width": fullWidth || undefined, "data-has-value": hasValue || undefined, "data-invalid": state.invalid || constraintInvalid || undefined, "data-mode": mode, "data-readonly": state.readOnly || undefined, "data-size": size, dir: dir, style: style, children: [_jsxs(ButtonGroup, { "aria-label": copy.calendar, className: "inkra-date-picker__control", "data-size": size, fullWidth: fullWidth, size: size, variant: "outline", children: [_jsx(Popover.Trigger, { asChild: true, children: _jsx(Button, { "aria-describedby": mergeAriaIds(state.describedBy, accessibleValueId), "aria-errormessage": state.errorMessage, "aria-invalid": constraintInvalid || state.ariaInvalid, "aria-label": ariaLabel ?? (openButtonLabel ? `${openButtonLabel}: ${displayValue}` : undefined), "aria-required": state.required || undefined, autoFocus: autoFocus, className: "inkra-date-picker__trigger", disabled: state.disabled, form: form, fullWidth: fullWidth, id: triggerId, onBlur: onBlur, onFocus: onFocus, ref: triggerRef, startIcon: _jsx(PencilIcon, { paths: calendarPaths, sourceSize: 24, size: 22 }), children: _jsx("span", { className: "inkra-date-picker__value", "data-placeholder": !hasValue || undefined, children: displayValue }) }) }), clearable ? (_jsx(Button, { "aria-hidden": !hasValue || undefined, "aria-label": clearButtonLabel ?? copy.clear, className: "inkra-date-picker__clear", disabled: !hasValue || state.disabled || state.readOnly, onPointerDown: event => event.preventDefault(), onClick: () => {
155
+ commit(undefined, 'clear');
156
+ updateOpen(false);
157
+ triggerRef.current?.focus({ preventScroll: true });
158
+ }, size: size === 'sm' ? 'icon-sm' : size === 'lg' ? 'icon-lg' : 'icon', tabIndex: hasValue ? 0 : -1, variant: "ghost", children: _jsx(PencilIcon, { paths: clearPaths, sourceSize: 24, size: 22 }) })) : null] }), accessibleValueId ? _jsx("span", { className: "inkra-date-picker__accessible-value", id: accessibleValueId, children: displayValue }) : null, name || state.required ? (mode === 'range' ? _jsxs(_Fragment, { children: [_jsx("input", { "aria-hidden": "true", className: "inkra-date-picker__form-field", disabled: state.disabled, form: form, name: name, onChange: () => undefined, onInvalid: event => { event.preventDefault(); setConstraintInvalid(true); triggerRef.current?.focus(); }, readOnly: state.readOnly, required: state.required, tabIndex: -1, type: "text", value: datePickerLocalValue(rangeValue?.from) }), _jsx("input", { "aria-hidden": "true", className: "inkra-date-picker__form-field", disabled: state.disabled, form: form, name: endName ?? (name ? `${name}End` : undefined), onChange: () => undefined, onInvalid: event => { event.preventDefault(); setConstraintInvalid(true); triggerRef.current?.focus(); }, readOnly: state.readOnly, required: state.required, tabIndex: -1, type: "text", value: datePickerLocalValue(rangeValue?.to) })] }) : _jsx("input", { "aria-hidden": "true", className: "inkra-date-picker__form-field", disabled: state.disabled, form: form, name: name, onChange: () => undefined, onInvalid: event => { event.preventDefault(); setConstraintInvalid(true); triggerRef.current?.focus(); }, readOnly: state.readOnly, required: state.required, tabIndex: -1, type: "text", value: datePickerLocalValue(singleValue) })) : null] }), _jsx(Popover.Portal, { children: _jsxs(Popover.Content, { align: "start", "aria-label": calendarLabel ?? copy.calendar, className: "inkra-date-picker__popover", collisionPadding: 12, onOpenAutoFocus: event => {
158
159
  event.preventDefault();
159
160
  requestAnimationFrame(() => {
160
161
  requestAnimationFrame(() => {
@@ -13,6 +13,7 @@ export interface AgendaEvent {
13
13
  }
14
14
  export interface DayAgendaProps extends Omit<HTMLAttributes<HTMLElement>, 'onSelect'> {
15
15
  date: string;
16
+ /** Immutable snapshots: replace the events array and changed event records when editing. */
16
17
  events: readonly AgendaEvent[];
17
18
  selectedEvent?: string;
18
19
  onEventSelect?: (event: AgendaEvent) => void;
@@ -32,6 +33,10 @@ export interface DayAgendaProps extends Omit<HTMLAttributes<HTMLElement>, 'onSel
32
33
  previous: string;
33
34
  next: string;
34
35
  };
36
+ /** Opt-in initial summary. Layout uses every event and all can be revealed. */
37
+ maxVisibleEvents?: number;
38
+ showAllEventsLabel?: (count: number) => string;
39
+ showFewerEventsLabel?: string;
35
40
  }
36
41
  export declare function agendaTime(minute: number, hourCycle?: 'h12' | 'h23', locale?: string): string;
37
42
  export declare function agendaInterval(event: AgendaEvent): {
@@ -40,4 +45,4 @@ export declare function agendaInterval(event: AgendaEvent): {
40
45
  before: boolean;
41
46
  after: boolean;
42
47
  } | null;
43
- export declare function DayAgenda({ date, events, selectedEvent, onEventSelect, locale, timeZoneLabel, emptyLabel, untimedLabel, showOverview, hourCycle, density, showDescriptions, divided, disabled, onPreviousDay, onNextDay, navigationLabels, className, ...props }: DayAgendaProps): import("react").JSX.Element;
48
+ export declare function DayAgenda({ date, events, selectedEvent, onEventSelect, locale, timeZoneLabel, emptyLabel, untimedLabel, showOverview, hourCycle, density, showDescriptions, divided, disabled, onPreviousDay, onNextDay, navigationLabels, maxVisibleEvents, showAllEventsLabel, showFewerEventsLabel, className, ...props }: DayAgendaProps): import("react").JSX.Element;
@@ -1,19 +1,24 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useId } from 'react';
2
+ import { useId, useMemo, useState } from 'react';
3
3
  import { TravelIcon } from '../internal/travelIcons.js';
4
4
  import { Button } from '../Button.js';
5
5
  import { createAgendaScale, layoutAgendaCards } from './layout.js';
6
6
  import { PencilSurface } from '../PencilSurface.js';
7
7
  import { PencilLine } from '../PencilLine.js';
8
8
  import { PencilText } from '../PencilText.js';
9
- export function agendaTime(minute, hourCycle = 'h23', locale = 'en') {
9
+ function formatAgendaMinute(minute, formatter) {
10
10
  const value = Math.max(0, Math.min(1440, Math.floor(minute)));
11
- if (hourCycle === 'h12') {
11
+ if (formatter) {
12
12
  const time = new Date(Date.UTC(2000, 0, 1, 0, value));
13
- return new Intl.DateTimeFormat(locale, { timeZone: 'UTC', hour: 'numeric', minute: '2-digit', hourCycle }).format(time) + (value === 1440 ? ' +1' : '');
13
+ return formatter.format(time) + (value === 1440 ? ' +1' : '');
14
14
  }
15
15
  return value === 1440 ? '00:00 +1' : `${String(Math.floor(value / 60)).padStart(2, '0')}:${String(value % 60).padStart(2, '0')}`;
16
16
  }
17
+ export function agendaTime(minute, hourCycle = 'h23', locale = 'en') {
18
+ return formatAgendaMinute(minute, hourCycle === 'h12'
19
+ ? new Intl.DateTimeFormat(locale, { timeZone: 'UTC', hour: 'numeric', minute: '2-digit', hourCycle })
20
+ : undefined);
21
+ }
17
22
  export function agendaInterval(event) {
18
23
  if (event.startMinute === undefined || !Number.isFinite(event.startMinute))
19
24
  return null;
@@ -24,24 +29,44 @@ export function agendaInterval(event) {
24
29
  return null;
25
30
  return { start: Math.max(0, event.startMinute), end: Math.min(1440, end ?? event.startMinute), before: event.startMinute < 0, after: (end ?? 0) > 1440 };
26
31
  }
27
- export function DayAgenda({ date, events, selectedEvent, onEventSelect, locale = 'en', timeZoneLabel, emptyLabel = 'No events', untimedLabel = 'Time pending', showOverview = true, hourCycle = 'h23', density = 'comfortable', showDescriptions = true, divided = true, disabled = false, onPreviousDay, onNextDay, navigationLabels = { previous: 'Previous day', next: 'Next day' }, className, ...props }) {
32
+ export function DayAgenda({ date, events, selectedEvent, onEventSelect, locale = 'en', timeZoneLabel, emptyLabel = 'No events', untimedLabel = 'Time pending', showOverview = true, hourCycle = 'h23', density = 'comfortable', showDescriptions = true, divided = true, disabled = false, onPreviousDay, onNextDay, navigationLabels = { previous: 'Previous day', next: 'Next day' }, maxVisibleEvents, showAllEventsLabel = count => `Show all ${count} events`, showFewerEventsLabel = 'Show fewer events', className, ...props }) {
28
33
  const id = useId();
29
- const validDate = /^\d{4}-\d{2}-\d{2}$/.test(date) ? new Date(`${date}T12:00:00Z`) : new Date(NaN);
30
- const title = Number.isFinite(validDate.getTime()) ? new Intl.DateTimeFormat(locale, { timeZone: 'UTC', month: 'long', day: 'numeric', weekday: 'short' }).format(validDate) : date;
31
- const ordered = [...events].sort((a, b) => (agendaInterval(a)?.start ?? Infinity) - (agendaInterval(b)?.start ?? Infinity));
32
- const formatTime = (minute) => agendaTime(minute, hourCycle, locale);
33
- const timed = ordered.flatMap(event => {
34
- const interval = agendaInterval(event);
35
- return interval ? [{ ...interval, id: event.id, event }] : [];
36
- });
37
- const intervals = timed.filter(row => row.end > row.start);
38
- const points = timed.filter(row => row.end === row.start);
39
- const scale = createAgendaScale(intervals, points.map(row => row.start), density === 'compact');
40
- const cards = layoutAgendaCards(timed, scale).sort((a, b) => a.start - b.start);
41
- const ticks = Array.from({ length: 25 }, (_, hour) => hour * 60).filter((minute, index, list) => index === 0 || index === 24 || scale.position(minute) - scale.position(list[index - 1]) >= 25);
34
+ const [expandedDate, setExpandedDate] = useState();
35
+ const displayLimit = maxVisibleEvents !== undefined && Number.isFinite(maxVisibleEvents) ? Math.max(0, Math.floor(maxVisibleEvents)) : Infinity;
36
+ const expanded = expandedDate === date;
37
+ const title = useMemo(() => {
38
+ const validDate = /^\d{4}-\d{2}-\d{2}$/.test(date) ? new Date(`${date}T12:00:00Z`) : new Date(NaN);
39
+ return Number.isFinite(validDate.getTime()) ? new Intl.DateTimeFormat(locale, { timeZone: 'UTC', month: 'long', day: 'numeric', weekday: 'short' }).format(validDate) : date;
40
+ }, [date, locale]);
41
+ const timeFormatter = useMemo(() => hourCycle === 'h12'
42
+ ? new Intl.DateTimeFormat(locale, { timeZone: 'UTC', hour: 'numeric', minute: '2-digit', hourCycle })
43
+ : undefined, [hourCycle, locale]);
44
+ const { ordered, pending, scale, cards, ticks } = useMemo(() => {
45
+ const records = events.map(event => ({ event, interval: agendaInterval(event) }))
46
+ .sort((a, b) => (a.interval?.start ?? Infinity) - (b.interval?.start ?? Infinity));
47
+ const ordered = records.map(row => row.event);
48
+ const pending = records.filter(row => !row.interval).map(row => row.event);
49
+ const timed = records.flatMap(({ event, interval }) => interval ? [{ ...interval, id: event.id, event }] : []);
50
+ const intervals = timed.filter(row => row.end > row.start);
51
+ const points = timed.filter(row => row.end === row.start);
52
+ const scale = createAgendaScale(intervals, points.map(row => row.start), density === 'compact');
53
+ const formatTime = (minute) => formatAgendaMinute(minute, timeFormatter);
54
+ const cards = layoutAgendaCards(timed, scale).sort((a, b) => a.start - b.start).map(row => ({
55
+ ...row,
56
+ time: `${row.before ? '\u2190 ' : ''}${formatTime(row.start)}${row.event.endMinute !== undefined ? ` - ${formatTime(row.end)}` : ''}${row.after ? ' \u2192' : ''}`,
57
+ }));
58
+ const ticks = Array.from({ length: 25 }, (_, hour) => hour * 60)
59
+ .filter((minute, index, list) => index === 0 || index === 24 || scale.position(minute) - scale.position(list[index - 1]) >= 25)
60
+ .map(minute => ({ minute, label: formatTime(minute) }));
61
+ return { ordered, pending, scale, cards, ticks };
62
+ }, [density, events, timeFormatter]);
63
+ const shownIds = useMemo(() => expanded || displayLimit >= ordered.length ? null
64
+ : new Set([...ordered.slice(0, displayLimit).map(event => event.id), ...(selectedEvent ? [selectedEvent] : [])]), [expanded, displayLimit, ordered, selectedEvent]);
65
+ const visibleCards = shownIds === null ? cards : cards.filter(row => shownIds.has(row.id));
66
+ const visiblePending = shownIds === null ? pending : pending.filter(event => shownIds.has(event.id));
42
67
  const renderCard = (event, time) => _jsxs(PencilSurface, { variant: "quiet", radius: 6, className: "inkra-agenda__card", "data-selected": selectedEvent === event.id, style: { '--inkra-accent-soft': event.color ?? (event.kind === 'stay' ? '#c8c38f' : event.kind === 'travel' ? '#87bbc0' : '#a7c7ad') }, children: [onEventSelect && _jsx("button", { type: "button", className: "inkra-agenda__hit", disabled: disabled || event.disabled, "aria-label": `${time}, ${event.title}`, "aria-pressed": selectedEvent === event.id, onClick: () => onEventSelect(event) }), _jsxs("div", { className: "inkra-agenda__card-copy", children: [_jsxs("span", { className: "inkra-agenda__card-title", title: event.title, children: [event.icon && _jsx("span", { "aria-hidden": "true", children: event.icon }), _jsx(PencilText, { ellipsis: true, children: event.title })] }), showDescriptions && event.description && _jsx("span", { className: "inkra-agenda__description", children: event.description })] })] });
43
- return _jsxs("section", { ...props, "data-density": density, "data-hour-cycle": hourCycle, className: ['inkra-agenda', className].filter(Boolean).join(' '), "aria-labelledby": props['aria-labelledby'] ?? id, children: [_jsxs("header", { className: "inkra-agenda__heading", children: [_jsx(PencilText, { as: "h3", id: id, children: title }), timeZoneLabel && _jsx(PencilText, { children: timeZoneLabel })] }), _jsx(PencilLine, {}), ordered.length === 0 ? _jsx(PencilText, { as: "p", children: emptyLabel }) : _jsxs(_Fragment, { children: [_jsx("div", { className: "inkra-agenda__timeline", "data-overview": showOverview, children: _jsxs("div", { className: "inkra-agenda__scale", style: { height: scale.height + 24 }, children: [ticks.map(minute => _jsxs("div", { className: "inkra-agenda__tick", style: { top: scale.position(minute) + 12 }, children: [_jsx("span", { children: _jsx(PencilText, { children: formatTime(minute) }) }), divided && _jsx(PencilLine, {})] }, minute)), _jsx("ol", { className: "inkra-agenda__lanes", children: cards.map(row => {
44
- const time = `${row.before ? '\u2190 ' : ''}${formatTime(row.start)}${row.event.endMinute !== undefined ? ` - ${formatTime(row.end)}` : ''}${row.after ? ' \u2192' : ''}`;
68
+ return _jsxs("section", { ...props, "data-density": density, "data-hour-cycle": hourCycle, className: ['inkra-agenda', className].filter(Boolean).join(' '), "aria-labelledby": props['aria-labelledby'] ?? id, children: [_jsxs("header", { className: "inkra-agenda__heading", children: [_jsx(PencilText, { as: "h3", id: id, children: title }), timeZoneLabel && _jsx(PencilText, { children: timeZoneLabel })] }), _jsx(PencilLine, {}), ordered.length === 0 ? _jsx(PencilText, { as: "p", children: emptyLabel }) : _jsxs(_Fragment, { children: [_jsx("div", { className: "inkra-agenda__timeline", "data-overview": showOverview, children: _jsxs("div", { className: "inkra-agenda__scale", style: { height: scale.height + 24 }, children: [ticks.map(({ minute, label }) => _jsxs("div", { className: "inkra-agenda__tick", style: { top: scale.position(minute) + 12 }, children: [_jsx("span", { children: _jsx(PencilText, { children: label }) }), divided && _jsx(PencilLine, {})] }, minute)), _jsx("ol", { className: "inkra-agenda__lanes", children: visibleCards.map(row => {
69
+ const time = row.time;
45
70
  return _jsxs("li", { "data-kind": row.event.kind, style: { top: row.top + 14, height: Math.max(44, row.height - 4), insetInlineStart: `${row.lane / row.lanes * 100}%`, width: `${100 / row.lanes}%` }, children: [renderCard(row.event, time), _jsx("span", { className: "inkra-agenda__accessible-time", children: time })] }, row.id);
46
- }) })] }) }), ordered.some(event => !agendaInterval(event)) && _jsxs("div", { className: "inkra-agenda__pending", children: [_jsx(PencilText, { children: untimedLabel }), ordered.filter(event => !agendaInterval(event)).map(event => _jsx("div", { children: renderCard(event, untimedLabel) }, event.id))] })] }), (onPreviousDay || onNextDay) && _jsxs("footer", { className: "inkra-agenda__navigation", children: [_jsx(PencilLine, {}), _jsx(Button, { variant: "ghost", size: "icon", "aria-label": navigationLabels.previous, title: navigationLabels.previous, disabled: !onPreviousDay, onClick: onPreviousDay, children: _jsx(TravelIcon, { name: "previous" }) }), _jsx(Button, { variant: "ghost", size: "icon", "aria-label": navigationLabels.next, title: navigationLabels.next, disabled: !onNextDay, onClick: onNextDay, children: _jsx(TravelIcon, { name: "next" }) })] })] });
71
+ }) })] }) }), visiblePending.length > 0 && _jsxs("div", { className: "inkra-agenda__pending", children: [_jsx(PencilText, { children: untimedLabel }), visiblePending.map(event => _jsx("div", { children: renderCard(event, untimedLabel) }, event.id))] }), ordered.length > displayLimit && _jsx(Button, { size: "sm", variant: "ghost", "aria-expanded": expanded, onClick: () => setExpandedDate(expanded ? undefined : date), children: expanded ? showFewerEventsLabel : showAllEventsLabel(ordered.length) })] }), (onPreviousDay || onNextDay) && _jsxs("footer", { className: "inkra-agenda__navigation", children: [_jsx(PencilLine, {}), _jsx(Button, { variant: "ghost", size: "icon", "aria-label": navigationLabels.previous, title: navigationLabels.previous, disabled: !onPreviousDay, onClick: onPreviousDay, children: _jsx(TravelIcon, { name: "previous" }) }), _jsx(Button, { variant: "ghost", size: "icon", "aria-label": navigationLabels.next, title: navigationLabels.next, disabled: !onNextDay, onClick: onNextDay, children: _jsx(TravelIcon, { name: "next" }) })] })] });
47
72
  }
@@ -1,16 +1,56 @@
1
+ function heapPush(heap, value, less) {
2
+ let index = heap.length;
3
+ heap.push(value);
4
+ while (index > 0) {
5
+ const parent = (index - 1) >>> 1;
6
+ if (!less(value, heap[parent]))
7
+ break;
8
+ heap[index] = heap[parent];
9
+ index = parent;
10
+ }
11
+ heap[index] = value;
12
+ }
13
+ function heapPop(heap, less) {
14
+ const first = heap[0];
15
+ const last = heap.pop();
16
+ if (heap.length) {
17
+ let index = 0;
18
+ while (index * 2 + 1 < heap.length) {
19
+ let child = index * 2 + 1;
20
+ if (child + 1 < heap.length && less(heap[child + 1], heap[child]))
21
+ child++;
22
+ if (!less(heap[child], last))
23
+ break;
24
+ heap[index] = heap[child];
25
+ index = child;
26
+ }
27
+ heap[index] = last;
28
+ }
29
+ return first;
30
+ }
31
+ const lowerLane = (left, right) => left < right;
32
+ const earlierEnd = (left, right) => left.end < right.end || (left.end === right.end && left.lane < right.lane);
1
33
  export function layoutAgendaIntervals(intervals) {
2
34
  const sorted = [...intervals].sort((a, b) => a.start - b.start || a.id.localeCompare(b.id));
3
35
  const result = [];
4
- let group = [], groupEnd = -Infinity, laneEnds = [];
5
- const flush = () => { for (const item of group)
6
- result.push({ ...item, lanes: laneEnds.length }); group = []; laneEnds = []; };
36
+ let group = [], groupEnd = -Infinity, laneCount = 0;
37
+ let occupied = [], available = [];
38
+ const flush = () => {
39
+ for (const item of group)
40
+ result.push({ ...item, lanes: laneCount });
41
+ group = [];
42
+ occupied = [];
43
+ available = [];
44
+ laneCount = 0;
45
+ };
7
46
  for (const item of sorted) {
8
47
  if (item.start >= groupEnd)
9
48
  flush();
10
- let lane = laneEnds.findIndex(end => end <= item.start);
11
- if (lane < 0)
12
- lane = laneEnds.length;
13
- laneEnds[lane] = item.end;
49
+ while (occupied.length && occupied[0].end <= item.start) {
50
+ heapPush(available, heapPop(occupied, earlierEnd).lane, lowerLane);
51
+ }
52
+ const lane = available.length ? heapPop(available, lowerLane) : laneCount++;
53
+ heapPush(occupied, { end: item.end, lane }, earlierEnd);
14
54
  group.push({ ...item, lane });
15
55
  groupEnd = Math.max(groupEnd, item.end);
16
56
  }
@@ -23,6 +63,10 @@ export function createAgendaScale(intervals, pointTimes, compact) {
23
63
  const boundaries = [...new Set([0, 1440, ...pointTimes, ...intervals.flatMap(row => [row.start, row.end])])].sort((a, b) => a - b);
24
64
  const before = new Map(), after = new Map();
25
65
  const segments = [];
66
+ // Boundaries already contain every endpoint, so the furthest active end is
67
+ // enough to decide occupancy without rescanning every event for every segment.
68
+ const ordered = [...intervals].sort((a, b) => a.start - b.start);
69
+ let intervalIndex = 0, occupiedUntil = -Infinity;
26
70
  let height = 0;
27
71
  for (let index = 0; index < boundaries.length; index++) {
28
72
  const time = boundaries[index];
@@ -33,7 +77,11 @@ export function createAgendaScale(intervals, pointTimes, compact) {
33
77
  const next = boundaries[index + 1];
34
78
  if (next === undefined)
35
79
  continue;
36
- const occupied = intervals.some(row => row.start < next && row.end > time);
80
+ while (intervalIndex < ordered.length && ordered[intervalIndex].start < next) {
81
+ occupiedUntil = Math.max(occupiedUntil, ordered[intervalIndex].end);
82
+ intervalIndex++;
83
+ }
84
+ const occupied = occupiedUntil > time;
37
85
  const size = Math.max(occupied ? rowHeight : 16, (next - time) / 60 * 32);
38
86
  segments.push({ start: time, end: next, top: height, size });
39
87
  height += size;
@@ -43,7 +91,15 @@ export function createAgendaScale(intervals, pointTimes, compact) {
43
91
  const exact = (edge === 'after' ? after : before).get(time);
44
92
  if (exact !== undefined)
45
93
  return exact;
46
- const segment = segments.find(row => time >= row.start && time <= row.end);
94
+ let low = 0, high = segments.length - 1;
95
+ while (low <= high) {
96
+ const middle = (low + high) >>> 1;
97
+ if (segments[middle].start <= time)
98
+ low = middle + 1;
99
+ else
100
+ high = middle - 1;
101
+ }
102
+ const segment = high >= 0 && time <= segments[high].end ? segments[high] : undefined;
47
103
  return segment ? segment.top + (time - segment.start) / (segment.end - segment.start) * segment.size : 0;
48
104
  };
49
105
  return { position, height, rowHeight };
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useMemo, useRef } from 'react';
3
- import { BoundedCache, PENCIL_RENDERER_VERSION, canvasByteCost, canvasIsUsable, effectiveDpr } from '../internal/cache.js';
3
+ import { BoundedCache, PENCIL_RENDERER_VERSION, boundedCanvasSize, canvasByteCost, canvasIsUsable, effectiveDpr } from '../internal/cache.js';
4
4
  import { observeCanvasRecovery } from '../internal/recovery.js';
5
5
  import { seedToNumber } from '../internal/random.js';
6
6
  import { maxStrokeRoughness, pencilRenderDefaults, readRenderToken } from '../internal/renderTokens.js';
@@ -84,6 +84,7 @@ export function ProgressRing({ diameter, thickness, percent, seed }) {
84
84
  const weight = readRenderToken(style, '--inkra-stroke-width', pencilRenderDefaults.strokeWidth, 0.3, 3);
85
85
  const roughness = readRenderToken(style, '--inkra-stroke-amplitude', pencilRenderDefaults.strokeRoughness, 0, maxStrokeRoughness);
86
86
  const dpr = effectiveDpr(2);
87
+ const size = boundedCanvasSize(width, width, dpr);
87
88
  const key = JSON.stringify([PENCIL_RENDERER_VERSION, diameter, thickness, width, dpr, style.color, weight, roughness, seed]);
88
89
  if (key === lastPaint && canvasIsUsable(canvas))
89
90
  return;
@@ -94,7 +95,9 @@ export function ProgressRing({ diameter, thickness, percent, seed }) {
94
95
  }
95
96
  if (!image) {
96
97
  image = document.createElement('canvas');
97
- image.width = image.height = Math.ceil(width * dpr);
98
+ image.width = size.width;
99
+ image.height = size.height;
100
+ image.addEventListener('contextlost', () => ringCache.delete(key), { once: true });
98
101
  const context = image.getContext('2d');
99
102
  if (!context)
100
103
  throw new Error('Canvas unavailable');
@@ -116,6 +119,7 @@ export function ProgressRing({ diameter, thickness, percent, seed }) {
116
119
  lastPaint = key;
117
120
  }
118
121
  catch {
122
+ canvas.width = canvas.height = 0;
119
123
  delete canvas.dataset.ready;
120
124
  lastPaint = '';
121
125
  }
@@ -131,7 +135,7 @@ export function ProgressRing({ diameter, thickness, percent, seed }) {
131
135
  const scheme = matchMedia('(prefers-color-scheme: dark)');
132
136
  scheme.addEventListener('change', paint);
133
137
  window.addEventListener('resize', paint);
134
- const stopRecovery = observeCanvasRecovery(paint);
138
+ const stopRecovery = observeCanvasRecovery(() => { lastPaint = ''; paint(); });
135
139
  paint();
136
140
  return () => {
137
141
  observer.disconnect();
@@ -1,4 +1,4 @@
1
- import type { HTMLAttributes, ReactNode } from 'react';
1
+ import type { CSSProperties, HTMLAttributes, ReactNode } from 'react';
2
2
  import { type ProgressPartialSegment, type ProgressThreshold } from '../segmented-progress/SegmentedProgress.js';
3
3
  export type { ProgressPartialSegment, ProgressThreshold } from '../segmented-progress/SegmentedProgress.js';
4
4
  export type BadgeVariant = 'neutral' | 'accent' | 'success' | 'warning' | 'danger' | 'info';
@@ -22,6 +22,9 @@ export declare const Separator: import("react").ForwardRefExoticComponent<Separa
22
22
  export interface SkeletonProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
23
23
  seed?: string | number;
24
24
  radius?: number;
25
+ width?: CSSProperties['width'];
26
+ height?: CSSProperties['height'];
27
+ animation?: 'none' | 'pulse';
25
28
  }
26
29
  export declare const Skeleton: import("react").ForwardRefExoticComponent<SkeletonProps & import("react").RefAttributes<HTMLDivElement>>;
27
30
  export interface ProgressRenderState {
@@ -30,8 +30,9 @@ export const Separator = forwardRef(function Separator({ className, decorative =
30
30
  : { 'aria-orientation': orientation, role: 'separator' };
31
31
  return (_jsx("div", { ...props, ...semanticProps, className: ['inkra-separator', className].filter(Boolean).join(' '), "data-orientation": orientation, ref: ref, children: _jsx(PencilLine, { className: "inkra-separator__line", orientation: orientation, seed: seed ?? `inkra-separator-${orientation}`, weight: Number.isFinite(weight) ? Math.max(0.3, Math.min(2, weight)) : 0.9 }) }));
32
32
  });
33
- export const Skeleton = forwardRef(function Skeleton({ className, seed = 'inkra-skeleton', radius, ...props }, ref) {
34
- return (_jsx(PencilSurface, { ...props, "aria-hidden": "true", as: "div", className: ['inkra-skeleton', className].filter(Boolean).join(' '), ref: ref, seed: seed, radius: radius, variant: "quiet" }));
33
+ export const Skeleton = forwardRef(function Skeleton({ className, seed = 'inkra-skeleton', radius = 7, width, height, animation = 'none', style, ...props }, ref) {
34
+ const resolvedRadius = Number.isFinite(radius) ? Math.max(0, Math.min(radius, 8)) : 7;
35
+ return (_jsx(PencilSurface, { ...props, "aria-hidden": "true", as: "div", className: ['inkra-skeleton', className].filter(Boolean).join(' '), "data-animation": animation, style: { width, height, borderRadius: resolvedRadius, ...style }, ref: ref, seed: seed, radius: resolvedRadius, variant: "selection" }));
35
36
  });
36
37
  function normalizeMax(max) {
37
38
  return typeof max === 'number' && Number.isFinite(max) && max > 0 ? max : 100;
@@ -43,7 +43,7 @@ export const DockablePanelWorkspace = forwardRef(function DockablePanelWorkspace
43
43
  return () => observer.disconnect();
44
44
  }, []);
45
45
  const activate = useCallback((id) => {
46
- setOrder(current => promoteDockablePanel(current, id));
46
+ setOrder(current => current[current.length - 1] === id ? current : promoteDockablePanel(current, id));
47
47
  if (activeId === undefined)
48
48
  setUncontrolledActiveId(id);
49
49
  if (resolvedActiveId !== id)
@@ -51,7 +51,7 @@ export const DockablePanelWorkspace = forwardRef(function DockablePanelWorkspace
51
51
  }, [activeId, onActiveChange, resolvedActiveId]);
52
52
  useEffect(() => {
53
53
  if (activeId != null)
54
- setOrder(current => promoteDockablePanel(current, activeId));
54
+ setOrder(current => current[current.length - 1] === activeId ? current : promoteDockablePanel(current, activeId));
55
55
  }, [activeId]);
56
56
  const register = useCallback((id) => {
57
57
  setOrder(current => current.includes(id) ? current : [...current, id]);
@@ -72,18 +72,18 @@ export const DockablePanelWorkspace = forwardRef(function DockablePanelWorkspace
72
72
  titleBarVisibility,
73
73
  width: size.width,
74
74
  }), [boundaryPadding, size.height, size.width, titleBarVisibility]);
75
+ const layers = useMemo(() => new Map(order.map((id, index) => [id, index])), [order]);
75
76
  const context = useMemo(() => ({
76
77
  activeId: resolvedActiveId,
77
78
  activate,
78
79
  bounds,
79
80
  commitDrag,
80
81
  layer(id) {
81
- const index = order.indexOf(id);
82
- return baseZIndex + (resolvedActiveId === id ? order.length : Math.max(0, index));
82
+ return baseZIndex + (resolvedActiveId === id ? order.length : (layers.get(id) ?? 0));
83
83
  },
84
84
  register,
85
85
  snapThreshold,
86
- }), [activate, baseZIndex, bounds, commitDrag, order, register, resolvedActiveId, snapThreshold]);
86
+ }), [activate, baseZIndex, bounds, commitDrag, layers, order.length, register, resolvedActiveId, snapThreshold]);
87
87
  function handleDragEnd(event) {
88
88
  const id = String(event.active.id);
89
89
  dragCallbacks.current.get(id)?.({ x: event.delta.x, y: event.delta.y });