@optimum-intelligence/opti-ui 0.0.0-stage → 0.3.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 (193) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +244 -2
  3. package/dist/components/accordion/Accordion.d.ts +17 -0
  4. package/dist/components/accordion/Accordion.js +204 -0
  5. package/dist/components/accordion/Accordion.js.map +1 -0
  6. package/dist/components/alert/Alert.d.ts +18 -0
  7. package/dist/components/alert/Alert.js +164 -0
  8. package/dist/components/alert/Alert.js.map +1 -0
  9. package/dist/components/app-shell/AppShell.d.ts +97 -0
  10. package/dist/components/app-shell/AppShell.js +689 -0
  11. package/dist/components/app-shell/AppShell.js.map +1 -0
  12. package/dist/components/avatar/Avatar.d.ts +21 -0
  13. package/dist/components/avatar/Avatar.js +190 -0
  14. package/dist/components/avatar/Avatar.js.map +1 -0
  15. package/dist/components/badge/Badge.d.ts +16 -0
  16. package/dist/components/badge/Badge.js +94 -0
  17. package/dist/components/badge/Badge.js.map +1 -0
  18. package/dist/components/breadcrumbs/Breadcrumbs.d.ts +18 -0
  19. package/dist/components/breadcrumbs/Breadcrumbs.js +154 -0
  20. package/dist/components/breadcrumbs/Breadcrumbs.js.map +1 -0
  21. package/dist/components/button/Button.d.ts +23 -0
  22. package/dist/components/button/Button.js +159 -0
  23. package/dist/components/button/Button.js.map +1 -0
  24. package/dist/components/calendar/Calendar.d.ts +59 -0
  25. package/dist/components/calendar/Calendar.js +709 -0
  26. package/dist/components/calendar/Calendar.js.map +1 -0
  27. package/dist/components/calendar/date-utils.d.ts +32 -0
  28. package/dist/components/calendar/date-utils.js +112 -0
  29. package/dist/components/calendar/date-utils.js.map +1 -0
  30. package/dist/components/card/Card.d.ts +42 -0
  31. package/dist/components/card/Card.js +82 -0
  32. package/dist/components/card/Card.js.map +1 -0
  33. package/dist/components/choice/Choice.d.ts +32 -0
  34. package/dist/components/choice/Choice.js +371 -0
  35. package/dist/components/choice/Choice.js.map +1 -0
  36. package/dist/components/collapsible/Collapsible.d.ts +15 -0
  37. package/dist/components/collapsible/Collapsible.js +155 -0
  38. package/dist/components/collapsible/Collapsible.js.map +1 -0
  39. package/dist/components/combobox/Combobox.d.ts +68 -0
  40. package/dist/components/combobox/Combobox.js +752 -0
  41. package/dist/components/combobox/Combobox.js.map +1 -0
  42. package/dist/components/command-palette/CommandPalette.d.ts +52 -0
  43. package/dist/components/command-palette/CommandPalette.js +328 -0
  44. package/dist/components/command-palette/CommandPalette.js.map +1 -0
  45. package/dist/components/data-table/DataTable.d.ts +73 -0
  46. package/dist/components/data-table/DataTable.js +478 -0
  47. package/dist/components/data-table/DataTable.js.map +1 -0
  48. package/dist/components/data-table/DataTableContent.d.ts +35 -0
  49. package/dist/components/data-table/DataTableContent.js +550 -0
  50. package/dist/components/data-table/DataTableContent.js.map +1 -0
  51. package/dist/components/data-table/DataTableFilters.d.ts +15 -0
  52. package/dist/components/data-table/DataTableFilters.js +284 -0
  53. package/dist/components/data-table/DataTableFilters.js.map +1 -0
  54. package/dist/components/data-table/DataTableRow.d.ts +33 -0
  55. package/dist/components/data-table/DataTableRow.js +552 -0
  56. package/dist/components/data-table/DataTableRow.js.map +1 -0
  57. package/dist/components/data-table/context.d.ts +5 -0
  58. package/dist/components/data-table/context.js +14 -0
  59. package/dist/components/data-table/context.js.map +1 -0
  60. package/dist/components/data-table/layout.d.ts +60 -0
  61. package/dist/components/data-table/layout.js +272 -0
  62. package/dist/components/data-table/layout.js.map +1 -0
  63. package/dist/components/data-table/types.d.ts +319 -0
  64. package/dist/components/data-table/useDataTable.d.ts +9 -0
  65. package/dist/components/data-table/useDataTable.js +666 -0
  66. package/dist/components/data-table/useDataTable.js.map +1 -0
  67. package/dist/components/date-picker/DatePicker.d.ts +50 -0
  68. package/dist/components/date-picker/DatePicker.js +485 -0
  69. package/dist/components/date-picker/DatePicker.js.map +1 -0
  70. package/dist/components/date-picker/DateRangePicker.d.ts +39 -0
  71. package/dist/components/date-picker/DateRangePicker.js +339 -0
  72. package/dist/components/date-picker/DateRangePicker.js.map +1 -0
  73. package/dist/components/date-picker/PickerField.d.ts +38 -0
  74. package/dist/components/date-picker/PickerField.js +238 -0
  75. package/dist/components/date-picker/PickerField.js.map +1 -0
  76. package/dist/components/dialog/Dialog.d.ts +70 -0
  77. package/dist/components/dialog/Dialog.js +354 -0
  78. package/dist/components/dialog/Dialog.js.map +1 -0
  79. package/dist/components/drawer/Drawer.d.ts +39 -0
  80. package/dist/components/drawer/Drawer.js +225 -0
  81. package/dist/components/drawer/Drawer.js.map +1 -0
  82. package/dist/components/empty-state/EmptyState.d.ts +12 -0
  83. package/dist/components/empty-state/EmptyState.js +116 -0
  84. package/dist/components/empty-state/EmptyState.js.map +1 -0
  85. package/dist/components/field/Field.d.ts +36 -0
  86. package/dist/components/field/Field.js +337 -0
  87. package/dist/components/field/Field.js.map +1 -0
  88. package/dist/components/file-upload/FileUpload.d.ts +91 -0
  89. package/dist/components/file-upload/FileUpload.js +714 -0
  90. package/dist/components/file-upload/FileUpload.js.map +1 -0
  91. package/dist/components/input/Input.d.ts +49 -0
  92. package/dist/components/input/Input.js +538 -0
  93. package/dist/components/input/Input.js.map +1 -0
  94. package/dist/components/link/Link.d.ts +17 -0
  95. package/dist/components/link/Link.js +91 -0
  96. package/dist/components/link/Link.js.map +1 -0
  97. package/dist/components/menu/Menu.d.ts +79 -0
  98. package/dist/components/menu/Menu.js +615 -0
  99. package/dist/components/menu/Menu.js.map +1 -0
  100. package/dist/components/number-field/NumberField.d.ts +16 -0
  101. package/dist/components/number-field/NumberField.js +188 -0
  102. package/dist/components/number-field/NumberField.js.map +1 -0
  103. package/dist/components/pagination/Pagination.d.ts +34 -0
  104. package/dist/components/pagination/Pagination.js +244 -0
  105. package/dist/components/pagination/Pagination.js.map +1 -0
  106. package/dist/components/popover/Popover.d.ts +22 -0
  107. package/dist/components/popover/Popover.js +186 -0
  108. package/dist/components/popover/Popover.js.map +1 -0
  109. package/dist/components/preview-card/PreviewCard.d.ts +20 -0
  110. package/dist/components/preview-card/PreviewCard.js +114 -0
  111. package/dist/components/preview-card/PreviewCard.js.map +1 -0
  112. package/dist/components/progress/Progress.d.ts +20 -0
  113. package/dist/components/progress/Progress.js +169 -0
  114. package/dist/components/progress/Progress.js.map +1 -0
  115. package/dist/components/scroll-area/ScrollArea.d.ts +8 -0
  116. package/dist/components/scroll-area/ScrollArea.js +112 -0
  117. package/dist/components/scroll-area/ScrollArea.js.map +1 -0
  118. package/dist/components/search-input/SearchInput.d.ts +14 -0
  119. package/dist/components/search-input/SearchInput.js +114 -0
  120. package/dist/components/search-input/SearchInput.js.map +1 -0
  121. package/dist/components/select/Select.d.ts +66 -0
  122. package/dist/components/select/Select.js +601 -0
  123. package/dist/components/select/Select.js.map +1 -0
  124. package/dist/components/separator/Separator.d.ts +4 -0
  125. package/dist/components/separator/Separator.js +46 -0
  126. package/dist/components/separator/Separator.js.map +1 -0
  127. package/dist/components/skeleton/Skeleton.d.ts +16 -0
  128. package/dist/components/skeleton/Skeleton.js +135 -0
  129. package/dist/components/skeleton/Skeleton.js.map +1 -0
  130. package/dist/components/slider/Slider.d.ts +11 -0
  131. package/dist/components/slider/Slider.js +107 -0
  132. package/dist/components/slider/Slider.js.map +1 -0
  133. package/dist/components/spinner/Spinner.d.ts +9 -0
  134. package/dist/components/spinner/Spinner.js +78 -0
  135. package/dist/components/spinner/Spinner.js.map +1 -0
  136. package/dist/components/stack/Stack.d.ts +23 -0
  137. package/dist/components/stack/Stack.js +79 -0
  138. package/dist/components/stack/Stack.js.map +1 -0
  139. package/dist/components/stat-card/StatCard.d.ts +22 -0
  140. package/dist/components/stat-card/StatCard.js +153 -0
  141. package/dist/components/stat-card/StatCard.js.map +1 -0
  142. package/dist/components/stepper/Stepper.d.ts +30 -0
  143. package/dist/components/stepper/Stepper.js +131 -0
  144. package/dist/components/stepper/Stepper.js.map +1 -0
  145. package/dist/components/table/Table.d.ts +53 -0
  146. package/dist/components/table/Table.js +221 -0
  147. package/dist/components/table/Table.js.map +1 -0
  148. package/dist/components/tabs/Tabs.d.ts +19 -0
  149. package/dist/components/tabs/Tabs.js +183 -0
  150. package/dist/components/tabs/Tabs.js.map +1 -0
  151. package/dist/components/tag/Tag.d.ts +22 -0
  152. package/dist/components/tag/Tag.js +121 -0
  153. package/dist/components/tag/Tag.js.map +1 -0
  154. package/dist/components/toast/Toast.d.ts +34 -0
  155. package/dist/components/toast/Toast.js +187 -0
  156. package/dist/components/toast/Toast.js.map +1 -0
  157. package/dist/components/toggle/Toggle.d.ts +31 -0
  158. package/dist/components/toggle/Toggle.js +197 -0
  159. package/dist/components/toggle/Toggle.js.map +1 -0
  160. package/dist/components/toolbar/Toolbar.d.ts +21 -0
  161. package/dist/components/toolbar/Toolbar.js +239 -0
  162. package/dist/components/toolbar/Toolbar.js.map +1 -0
  163. package/dist/components/tooltip/Tooltip.d.ts +33 -0
  164. package/dist/components/tooltip/Tooltip.js +182 -0
  165. package/dist/components/tooltip/Tooltip.js.map +1 -0
  166. package/dist/components/typography/Typography.d.ts +32 -0
  167. package/dist/components/typography/Typography.js +183 -0
  168. package/dist/components/typography/Typography.js.map +1 -0
  169. package/dist/fonts.css +3 -0
  170. package/dist/icons/index.d.ts +133 -0
  171. package/dist/icons/index.js +182 -0
  172. package/dist/icons/index.js.map +1 -0
  173. package/dist/index.d.ts +55 -0
  174. package/dist/index.js +58 -0
  175. package/dist/opti-ui.css +5692 -0
  176. package/dist/theme/ThemeProvider.d.ts +22 -0
  177. package/dist/theme/ThemeProvider.js +115 -0
  178. package/dist/theme/ThemeProvider.js.map +1 -0
  179. package/dist/theme/index.d.ts +2 -0
  180. package/dist/utils/cx.d.ts +10 -0
  181. package/dist/utils/cx.js +18 -0
  182. package/dist/utils/cx.js.map +1 -0
  183. package/dist/utils/index.d.ts +2 -0
  184. package/dist/utils/portal.d.ts +8 -0
  185. package/dist/utils/portal.js +16 -0
  186. package/dist/utils/portal.js.map +1 -0
  187. package/dist/utils/primitive.d.ts +13 -0
  188. package/dist/utils/primitive.js +61 -0
  189. package/dist/utils/primitive.js.map +1 -0
  190. package/dist/utils/search.d.ts +4 -0
  191. package/dist/utils/search.js +15 -0
  192. package/dist/utils/search.js.map +1 -0
  193. package/package.json +71 -4
@@ -0,0 +1,60 @@
1
+ import * as React from 'react';
2
+ import type { DataTableColumn, DataTableEntry } from './types';
3
+ /** Column widths taken from the browser's own layout, then held. */
4
+ export interface ColumnLock {
5
+ key: string;
6
+ /** Width of the wrapper when measured; another one asks for a new measurement. */
7
+ container: number;
8
+ table: number;
9
+ widths: number[];
10
+ }
11
+ /**
12
+ * Lets the browser lay the columns out once, then holds their widths, so
13
+ * paging, sorting, scrolling and editing cannot move them. The widths are
14
+ * measured again when the table is resized or `key` (the column set) changes.
15
+ * `measuring` is true for the render the widths are read from; it is replaced
16
+ * before the browser paints.
17
+ */
18
+ export declare function useColumnLock(anchor: HTMLElement | null, enabled: boolean, key: string, ready: boolean): {
19
+ lock: ColumnLock | null;
20
+ measuring: boolean;
21
+ };
22
+ /**
23
+ * Rows the column widths are measured with, next to the ones on screen: the
24
+ * row with the longest text of each column, and a few spread over the data.
25
+ * Without them the widths would fit the first page only.
26
+ */
27
+ export declare function pickSizerRows<T>(entries: DataTableEntry<T>[], columns: DataTableColumn<T>[]): DataTableEntry<T>[];
28
+ /**
29
+ * Height of the table with a full page of rows, so a shorter last page can keep
30
+ * it and the pagination below stays where the pointer is.
31
+ */
32
+ export declare function usePageHeight(anchor: HTMLElement | null, enabled: boolean, full: boolean, key: string): number | undefined;
33
+ export interface VirtualRowsOptions {
34
+ enabled: boolean;
35
+ /** Number of rows in the list. */
36
+ count: number;
37
+ /** Height every row has. Left out, it is the tallest row seen. */
38
+ rowHeight?: number;
39
+ /** Rows rendered beyond each edge of the view. */
40
+ overscan: number;
41
+ /** Row height assumed before the first row is measured. */
42
+ estimate: number;
43
+ /** Height of the view assumed before it is measured, and on the server. */
44
+ viewport: number;
45
+ /** Changes when the row height may (density): forgets the measured one. */
46
+ key: string;
47
+ }
48
+ export interface VirtualRows {
49
+ /** Rows `start` (included) to `end` (excluded) are rendered. */
50
+ start: number;
51
+ end: number;
52
+ /** Heights standing in for the rows above and below them. */
53
+ before: number;
54
+ after: number;
55
+ /** Height to give every row, once known: equal rows keep the scrollbar steady. */
56
+ rowHeight?: number;
57
+ onScroll: (event: React.UIEvent<HTMLElement>) => void;
58
+ }
59
+ /** Renders only the rows in view of the scrolling wrapper, and stands in for the rest with empty space. */
60
+ export declare function useVirtualRows(anchor: HTMLElement | null, { enabled, count, rowHeight, overscan, estimate, viewport: assumed, key }: VirtualRowsOptions): VirtualRows;
@@ -0,0 +1,272 @@
1
+ "use client";
2
+ import { getColumnValue } from "./useDataTable.js";
3
+ import { c } from "react/compiler-runtime";
4
+ import * as React from "react";
5
+ //#region src/components/data-table/layout.ts
6
+ function tableOf(anchor) {
7
+ return anchor?.closest("table") ?? null;
8
+ }
9
+ function wrapperOf(anchor) {
10
+ return tableOf(anchor)?.parentElement ?? null;
11
+ }
12
+ /**
13
+ * Lets the browser lay the columns out once, then holds their widths, so
14
+ * paging, sorting, scrolling and editing cannot move them. The widths are
15
+ * measured again when the table is resized or `key` (the column set) changes.
16
+ * `measuring` is true for the render the widths are read from; it is replaced
17
+ * before the browser paints.
18
+ */
19
+ function useColumnLock(anchor, enabled, key, ready) {
20
+ const $ = c(16);
21
+ const [lock, setLock] = React.useState(null);
22
+ const [shown, setShown] = React.useState(true);
23
+ const held = enabled && lock?.key === key ? lock : null;
24
+ const measuring = enabled && ready && !held && anchor !== null;
25
+ let t0;
26
+ if ($[0] !== anchor || $[1] !== key || $[2] !== measuring) {
27
+ t0 = () => {
28
+ const table = tableOf(anchor);
29
+ const wrapper = table?.parentElement;
30
+ if (!measuring || !table || !wrapper || !wrapper.clientWidth) return;
31
+ const heads = table.querySelectorAll(":scope > thead > tr > th");
32
+ setLock({
33
+ key,
34
+ container: wrapper.clientWidth,
35
+ table: table.getBoundingClientRect().width,
36
+ widths: Array.from(heads, _temp)
37
+ });
38
+ };
39
+ $[0] = anchor;
40
+ $[1] = key;
41
+ $[2] = measuring;
42
+ $[3] = t0;
43
+ } else t0 = $[3];
44
+ let t1;
45
+ if ($[4] !== anchor || $[5] !== key || $[6] !== measuring || $[7] !== shown) {
46
+ t1 = [
47
+ anchor,
48
+ measuring,
49
+ key,
50
+ shown
51
+ ];
52
+ $[4] = anchor;
53
+ $[5] = key;
54
+ $[6] = measuring;
55
+ $[7] = shown;
56
+ $[8] = t1;
57
+ } else t1 = $[8];
58
+ React.useLayoutEffect(t0, t1);
59
+ let t2;
60
+ let t3;
61
+ if ($[9] !== anchor || $[10] !== enabled) {
62
+ t2 = () => {
63
+ const wrapper_0 = wrapperOf(anchor);
64
+ if (!enabled || !wrapper_0 || typeof ResizeObserver === "undefined") return;
65
+ const observer = new ResizeObserver(() => {
66
+ const width = wrapper_0.clientWidth;
67
+ setShown(width > 0);
68
+ if (width > 0) setLock((current) => current && Math.abs(current.container - width) >= 1 ? null : current);
69
+ });
70
+ observer.observe(wrapper_0);
71
+ return () => observer.disconnect();
72
+ };
73
+ t3 = [anchor, enabled];
74
+ $[9] = anchor;
75
+ $[10] = enabled;
76
+ $[11] = t2;
77
+ $[12] = t3;
78
+ } else {
79
+ t2 = $[11];
80
+ t3 = $[12];
81
+ }
82
+ React.useEffect(t2, t3);
83
+ let t4;
84
+ if ($[13] !== held || $[14] !== measuring) {
85
+ t4 = {
86
+ lock: held,
87
+ measuring
88
+ };
89
+ $[13] = held;
90
+ $[14] = measuring;
91
+ $[15] = t4;
92
+ } else t4 = $[15];
93
+ return t4;
94
+ }
95
+ /**
96
+ * Rows the column widths are measured with, next to the ones on screen: the
97
+ * row with the longest text of each column, and a few spread over the data.
98
+ * Without them the widths would fit the first page only.
99
+ */
100
+ function _temp(head) {
101
+ return head.getBoundingClientRect().width;
102
+ }
103
+ function pickSizerRows(entries, columns) {
104
+ const picked = /* @__PURE__ */ new Set();
105
+ const step = Math.max(1, Math.ceil(entries.length / 12));
106
+ for (let index = 0; index < entries.length; index += step) picked.add(entries[index]);
107
+ for (const column of columns) {
108
+ let longest = null;
109
+ let length = 0;
110
+ for (const entry of entries) {
111
+ const value = getColumnValue(column, entry.row);
112
+ const size = value === null || value === void 0 ? 0 : String(value).length;
113
+ if (size > length) {
114
+ longest = entry;
115
+ length = size;
116
+ }
117
+ }
118
+ if (longest) picked.add(longest);
119
+ }
120
+ return [...picked];
121
+ }
122
+ /**
123
+ * Height of the table with a full page of rows, so a shorter last page can keep
124
+ * it and the pagination below stays where the pointer is.
125
+ */
126
+ function usePageHeight(anchor, enabled, full, key) {
127
+ const $ = c(5);
128
+ const [held, setHeld] = React.useState(null);
129
+ let t0;
130
+ if ($[0] !== anchor || $[1] !== enabled || $[2] !== full || $[3] !== key) {
131
+ t0 = () => {
132
+ const wrapper = wrapperOf(anchor);
133
+ if (!enabled || !full || !wrapper) return;
134
+ const height = wrapper.getBoundingClientRect().height;
135
+ if (height) setHeld((current) => current?.key === key && Math.abs(current.height - height) < 1 ? current : {
136
+ key,
137
+ height
138
+ });
139
+ };
140
+ $[0] = anchor;
141
+ $[1] = enabled;
142
+ $[2] = full;
143
+ $[3] = key;
144
+ $[4] = t0;
145
+ } else t0 = $[4];
146
+ React.useLayoutEffect(t0);
147
+ return enabled && held?.key === key ? held.height : void 0;
148
+ }
149
+ /** Renders only the rows in view of the scrolling wrapper, and stands in for the rest with empty space. */
150
+ function useVirtualRows(anchor, t0) {
151
+ const $ = c(28);
152
+ const { enabled, count, rowHeight, overscan, estimate, viewport: assumed, key } = t0;
153
+ const [first, setFirst] = React.useState(0);
154
+ const [viewport, setViewport] = React.useState(null);
155
+ const [tallest, setTallest] = React.useState(null);
156
+ const measured = tallest?.key === key ? tallest.height : void 0;
157
+ const height = rowHeight ?? measured ?? estimate;
158
+ const visible = Math.ceil((viewport ?? assumed) / height) + 1;
159
+ const top = Math.max(0, Math.min(first, count - 1));
160
+ const from = Math.max(0, top - overscan);
161
+ const start = from - from % 2;
162
+ const end = Math.min(count, top + visible + overscan);
163
+ let t1;
164
+ if ($[0] !== anchor || $[1] !== enabled || $[2] !== key || $[3] !== rowHeight) {
165
+ t1 = () => {
166
+ const table = tableOf(anchor);
167
+ if (!enabled || rowHeight !== void 0 || !table) return;
168
+ let max = 0;
169
+ for (const row of table.querySelectorAll(":scope > tbody > tr[aria-rowindex]")) max = Math.max(max, row.getBoundingClientRect().height);
170
+ if (max) setTallest((current) => current?.key === key && max - current.height < .5 ? current : {
171
+ key,
172
+ height: max
173
+ });
174
+ };
175
+ $[0] = anchor;
176
+ $[1] = enabled;
177
+ $[2] = key;
178
+ $[3] = rowHeight;
179
+ $[4] = t1;
180
+ } else t1 = $[4];
181
+ let t2;
182
+ if ($[5] !== anchor || $[6] !== count || $[7] !== enabled || $[8] !== end || $[9] !== key || $[10] !== rowHeight || $[11] !== start) {
183
+ t2 = [
184
+ anchor,
185
+ enabled,
186
+ rowHeight,
187
+ key,
188
+ start,
189
+ end,
190
+ count
191
+ ];
192
+ $[5] = anchor;
193
+ $[6] = count;
194
+ $[7] = enabled;
195
+ $[8] = end;
196
+ $[9] = key;
197
+ $[10] = rowHeight;
198
+ $[11] = start;
199
+ $[12] = t2;
200
+ } else t2 = $[12];
201
+ React.useLayoutEffect(t1, t2);
202
+ let t3;
203
+ let t4;
204
+ if ($[13] !== anchor || $[14] !== enabled) {
205
+ t3 = () => {
206
+ const wrapper = wrapperOf(anchor);
207
+ if (!enabled || !wrapper) return;
208
+ setViewport(wrapper.clientHeight);
209
+ if (typeof ResizeObserver === "undefined") return;
210
+ const observer = new ResizeObserver(() => setViewport(wrapper.clientHeight));
211
+ observer.observe(wrapper);
212
+ return () => observer.disconnect();
213
+ };
214
+ t4 = [anchor, enabled];
215
+ $[13] = anchor;
216
+ $[14] = enabled;
217
+ $[15] = t3;
218
+ $[16] = t4;
219
+ } else {
220
+ t3 = $[15];
221
+ t4 = $[16];
222
+ }
223
+ React.useEffect(t3, t4);
224
+ if (!enabled) {
225
+ let t5;
226
+ if ($[17] !== count) {
227
+ t5 = {
228
+ start: 0,
229
+ end: count,
230
+ before: 0,
231
+ after: 0,
232
+ onScroll: _temp2
233
+ };
234
+ $[17] = count;
235
+ $[18] = t5;
236
+ } else t5 = $[18];
237
+ return t5;
238
+ }
239
+ const t5 = start * height;
240
+ const t6 = (count - end) * height;
241
+ const t7 = rowHeight ?? measured;
242
+ let t8;
243
+ if ($[19] !== height) {
244
+ t8 = (event) => setFirst(Math.floor(event.currentTarget.scrollTop / height));
245
+ $[19] = height;
246
+ $[20] = t8;
247
+ } else t8 = $[20];
248
+ let t9;
249
+ if ($[21] !== end || $[22] !== start || $[23] !== t5 || $[24] !== t6 || $[25] !== t7 || $[26] !== t8) {
250
+ t9 = {
251
+ start,
252
+ end,
253
+ before: t5,
254
+ after: t6,
255
+ rowHeight: t7,
256
+ onScroll: t8
257
+ };
258
+ $[21] = end;
259
+ $[22] = start;
260
+ $[23] = t5;
261
+ $[24] = t6;
262
+ $[25] = t7;
263
+ $[26] = t8;
264
+ $[27] = t9;
265
+ } else t9 = $[27];
266
+ return t9;
267
+ }
268
+ function _temp2() {}
269
+ //#endregion
270
+ export { pickSizerRows, useColumnLock, usePageHeight, useVirtualRows };
271
+
272
+ //# sourceMappingURL=layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.js","names":["React","getColumnValue","DataTableColumn","DataTableEntry","tableOf","anchor","HTMLElement","closest","wrapperOf","parentElement","ColumnLock","key","container","table","widths","useColumnLock","enabled","ready","$","_c","lock","setLock","useState","shown","setShown","held","measuring","t0","wrapper","clientWidth","heads","querySelectorAll","getBoundingClientRect","width","Array","from","_temp","t1","useLayoutEffect","t2","t3","wrapper_0","ResizeObserver","observer","current","Math","abs","observe","disconnect","useEffect","t4","head","pickSizerRows","entries","T","columns","picked","Set","step","max","ceil","length","index","add","column","longest","entry","value","row","size","undefined","String","usePageHeight","full","setHeld","height","VirtualRowsOptions","count","rowHeight","overscan","estimate","viewport","VirtualRows","start","end","before","after","onScroll","event","UIEvent","useVirtualRows","assumed","first","setFirst","setViewport","tallest","setTallest","measured","visible","top","min","clientHeight","t5","_temp2","t6","t7","t8","floor","currentTarget","scrollTop","t9","_c"],"sources":["../../../src/components/data-table/layout.ts"],"sourcesContent":["import * as React from 'react';\nimport { getColumnValue } from './useDataTable';\nimport type { DataTableColumn, DataTableEntry } from './types';\n\n// The hooks here find the table through an element rendered inside it (its\n// `<colgroup>`), so `tableProps` keeps its own `ref` for the consumer.\nfunction tableOf(anchor: HTMLElement | null) {\n return anchor?.closest('table') ?? null;\n}\n\nfunction wrapperOf(anchor: HTMLElement | null) {\n return tableOf(anchor)?.parentElement ?? null;\n}\n\n// ─── Column widths ───────────────────────────────────────────────────────────\n\n/** Column widths taken from the browser's own layout, then held. */\nexport interface ColumnLock {\n key: string;\n /** Width of the wrapper when measured; another one asks for a new measurement. */\n container: number;\n table: number;\n widths: number[];\n}\n\n/**\n * Lets the browser lay the columns out once, then holds their widths, so\n * paging, sorting, scrolling and editing cannot move them. The widths are\n * measured again when the table is resized or `key` (the column set) changes.\n * `measuring` is true for the render the widths are read from; it is replaced\n * before the browser paints.\n */\nexport function useColumnLock(anchor: HTMLElement | null, enabled: boolean, key: string, ready: boolean) {\n const [lock, setLock] = React.useState<ColumnLock | null>(null);\n const [shown, setShown] = React.useState(true);\n const held = enabled && lock?.key === key ? lock : null;\n const measuring = enabled && ready && !held && anchor !== null;\n\n React.useLayoutEffect(() => {\n const table = tableOf(anchor);\n const wrapper = table?.parentElement;\n // A hidden table (a closed tab) has no widths to read: it is measured when it shows.\n if (!measuring || !table || !wrapper || !wrapper.clientWidth) return;\n const heads = table.querySelectorAll<HTMLElement>(':scope > thead > tr > th');\n setLock({\n key,\n container: wrapper.clientWidth,\n table: table.getBoundingClientRect().width,\n widths: Array.from(heads, (head) => head.getBoundingClientRect().width),\n });\n }, [anchor, measuring, key, shown]);\n\n React.useEffect(() => {\n const wrapper = wrapperOf(anchor);\n if (!enabled || !wrapper || typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(() => {\n const width = wrapper.clientWidth;\n setShown(width > 0);\n if (width > 0) setLock((current) => (current && Math.abs(current.container - width) >= 1 ? null : current));\n });\n observer.observe(wrapper);\n return () => observer.disconnect();\n }, [anchor, enabled]);\n\n return { lock: held, measuring };\n}\n\n/**\n * Rows the column widths are measured with, next to the ones on screen: the\n * row with the longest text of each column, and a few spread over the data.\n * Without them the widths would fit the first page only.\n */\nexport function pickSizerRows<T>(entries: DataTableEntry<T>[], columns: DataTableColumn<T>[]): DataTableEntry<T>[] {\n const picked = new Set<DataTableEntry<T>>();\n const step = Math.max(1, Math.ceil(entries.length / 12));\n for (let index = 0; index < entries.length; index += step) picked.add(entries[index]!);\n for (const column of columns) {\n let longest: DataTableEntry<T> | null = null;\n let length = 0;\n for (const entry of entries) {\n const value = getColumnValue(column, entry.row);\n const size = value === null || value === undefined ? 0 : String(value).length;\n if (size > length) {\n longest = entry;\n length = size;\n }\n }\n if (longest) picked.add(longest);\n }\n return [...picked];\n}\n\n// ─── Page height ─────────────────────────────────────────────────────────────\n\n/**\n * Height of the table with a full page of rows, so a shorter last page can keep\n * it and the pagination below stays where the pointer is.\n */\nexport function usePageHeight(anchor: HTMLElement | null, enabled: boolean, full: boolean, key: string) {\n const [held, setHeld] = React.useState<{ key: string; height: number } | null>(null);\n\n React.useLayoutEffect(() => {\n const wrapper = wrapperOf(anchor);\n if (!enabled || !full || !wrapper) return;\n const height = wrapper.getBoundingClientRect().height;\n if (height) setHeld((current) => (current?.key === key && Math.abs(current.height - height) < 1 ? current : { key, height }));\n });\n\n return enabled && held?.key === key ? held.height : undefined;\n}\n\n// ─── Virtual rows ────────────────────────────────────────────────────────────\n\nexport interface VirtualRowsOptions {\n enabled: boolean;\n /** Number of rows in the list. */\n count: number;\n /** Height every row has. Left out, it is the tallest row seen. */\n rowHeight?: number;\n /** Rows rendered beyond each edge of the view. */\n overscan: number;\n /** Row height assumed before the first row is measured. */\n estimate: number;\n /** Height of the view assumed before it is measured, and on the server. */\n viewport: number;\n /** Changes when the row height may (density): forgets the measured one. */\n key: string;\n}\n\nexport interface VirtualRows {\n /** Rows `start` (included) to `end` (excluded) are rendered. */\n start: number;\n end: number;\n /** Heights standing in for the rows above and below them. */\n before: number;\n after: number;\n /** Height to give every row, once known: equal rows keep the scrollbar steady. */\n rowHeight?: number;\n onScroll: (event: React.UIEvent<HTMLElement>) => void;\n}\n\n/** Renders only the rows in view of the scrolling wrapper, and stands in for the rest with empty space. */\nexport function useVirtualRows(\n anchor: HTMLElement | null,\n { enabled, count, rowHeight, overscan, estimate, viewport: assumed, key }: VirtualRowsOptions,\n): VirtualRows {\n const [first, setFirst] = React.useState(0);\n const [viewport, setViewport] = React.useState<number | null>(null);\n const [tallest, setTallest] = React.useState<{ key: string; height: number } | null>(null);\n\n const measured = tallest?.key === key ? tallest.height : undefined;\n const height = rowHeight ?? measured ?? estimate;\n const visible = Math.ceil((viewport ?? assumed) / height) + 1;\n const top = Math.max(0, Math.min(first, count - 1));\n const from = Math.max(0, top - overscan);\n // An even start keeps the stripes of a striped table on the same rows.\n const start = from - (from % 2);\n const end = Math.min(count, top + visible + overscan);\n\n React.useLayoutEffect(() => {\n const table = tableOf(anchor);\n if (!enabled || rowHeight !== undefined || !table) return;\n let max = 0;\n for (const row of table.querySelectorAll(':scope > tbody > tr[aria-rowindex]')) {\n max = Math.max(max, row.getBoundingClientRect().height);\n }\n // Only ever grows: rows are given this height, so a smaller reading is the height itself.\n if (max) setTallest((current) => (current?.key === key && max - current.height < 0.5 ? current : { key, height: max }));\n }, [anchor, enabled, rowHeight, key, start, end, count]);\n\n React.useEffect(() => {\n const wrapper = wrapperOf(anchor);\n if (!enabled || !wrapper) return;\n setViewport(wrapper.clientHeight);\n if (typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(() => setViewport(wrapper.clientHeight));\n observer.observe(wrapper);\n return () => observer.disconnect();\n }, [anchor, enabled]);\n\n if (!enabled) return { start: 0, end: count, before: 0, after: 0, onScroll: () => undefined };\n return {\n start,\n end,\n before: start * height,\n after: (count - end) * height,\n rowHeight: rowHeight ?? measured,\n onScroll: (event) => setFirst(Math.floor(event.currentTarget.scrollTop / height)),\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,319 @@
1
+ import type * as React from 'react';
2
+ import type { DateRange } from '../calendar/date-utils';
3
+ import type { DateRangePreset } from '../date-picker/DateRangePicker';
4
+ import type { InputProps } from '../input/Input';
5
+ import type { PaginationLabels } from '../pagination/Pagination';
6
+ export interface DataTableFilterOption {
7
+ value: string;
8
+ label: React.ReactNode;
9
+ }
10
+ interface FilterBase {
11
+ /** Key of this filter in `filterValues`. */
12
+ id: string;
13
+ /** Text on the control. */
14
+ label: string;
15
+ }
16
+ /** Dropdown that keeps the rows with one chosen value. */
17
+ export interface DataTableSelectFilter<T> extends FilterBase {
18
+ type: 'select';
19
+ /** Value compared with the chosen option; an array matches on any of its items. Defaults to `row[id]`. */
20
+ value?: (row: T) => unknown;
21
+ /** Options to offer. Left out, they are collected from the rows. */
22
+ options?: DataTableFilterOption[];
23
+ }
24
+ /** On/off chip that keeps the matching rows while it is on. */
25
+ export interface DataTableToggleFilter<T> extends FilterBase {
26
+ type: 'toggle';
27
+ /** Rows kept while the toggle is on. Defaults to rows where `row[id]` is truthy. */
28
+ match?: (row: T) => boolean;
29
+ }
30
+ /** Date range picker that keeps the rows dated inside the range, both days included. */
31
+ export interface DataTableDateRangeFilter<T> extends FilterBase {
32
+ type: 'dateRange';
33
+ /** Date of the row: an ISO string, a `Date` or a timestamp. Defaults to `row[id]`. */
34
+ value?: (row: T) => string | number | Date | null | undefined;
35
+ presets?: DateRangePreset[];
36
+ min?: string;
37
+ max?: string;
38
+ }
39
+ export type DataTableFilter<T> = DataTableSelectFilter<T> | DataTableToggleFilter<T> | DataTableDateRangeFilter<T>;
40
+ type ColumnFilterConfig<F> = F extends FilterBase ? Omit<F, 'id' | 'label'> & {
41
+ label?: string;
42
+ } : never;
43
+ /** A filter declared on a column: its type, or its definition without the `id` (the column's). */
44
+ export type DataTableColumnFilter<T> = DataTableFilter<T>['type'] | ColumnFilterConfig<DataTableFilter<T>>;
45
+ /** A select filter holds the option value, a toggle `true`, a date range `{ start, end }`. */
46
+ export type DataTableFilterValue = string | true | DateRange;
47
+ /** Values of the active filters by filter id. An inactive filter has no key. */
48
+ export type DataTableFilterValues = Record<string, DataTableFilterValue>;
49
+ /** A filter as the table uses it: column shorthand expanded, options collected. */
50
+ export interface DataTableResolvedFilter<T> {
51
+ id: string;
52
+ type: DataTableFilter<T>['type'];
53
+ label: string;
54
+ /** Select filters only; empty for the other types. */
55
+ options: DataTableFilterOption[];
56
+ presets?: DateRangePreset[];
57
+ min?: string;
58
+ max?: string;
59
+ /** Whether `row` passes the filter at `value`. */
60
+ test: (row: T, value: DataTableFilterValue) => boolean;
61
+ }
62
+ export interface DataTableEditorContext<T> {
63
+ row: T;
64
+ /** Current value of the edited field. */
65
+ value: unknown;
66
+ setValue: (value: unknown) => void;
67
+ /** True while the row is being saved. */
68
+ disabled: boolean;
69
+ }
70
+ export interface DataTableColumnEdit<T> {
71
+ /** Built-in editor (default `text`). */
72
+ type?: 'text' | 'checkbox' | 'select';
73
+ /** Row property the editor writes. Defaults to the column id. */
74
+ field?: Extract<keyof T, string>;
75
+ /** Options of a `select` editor. */
76
+ options?: DataTableFilterOption[];
77
+ /** Props for the `text` editor's input (`inputMode`, `placeholder`, `type`…). */
78
+ inputProps?: Omit<InputProps, 'value' | 'defaultValue' | 'onValueChange' | 'size'>;
79
+ /** Replaces the built-in editor. */
80
+ render?: (context: DataTableEditorContext<T>) => React.ReactNode;
81
+ }
82
+ export interface DataTableColumn<T> {
83
+ /** Unique key. Also the property read from the row when there is no `accessor`. */
84
+ id: string;
85
+ header: React.ReactNode;
86
+ /** Plain-text name, used for the column's filter when `header` is not a string. */
87
+ label?: string;
88
+ /** Value used for sorting, searching, filtering and the default cell. Defaults to `row[id]`. */
89
+ accessor?: (row: T) => unknown;
90
+ /** Custom cell content. */
91
+ cell?: (row: T) => React.ReactNode;
92
+ /** Defaults to `true`, or `false` for columns that only define `cell` (actions, icons). */
93
+ sortable?: boolean;
94
+ /** Include this column in the built-in search. Same default as `sortable`. */
95
+ searchable?: boolean;
96
+ /** `contains` (default) matches anywhere in the value; `exact` only whole words, for ids and codes. */
97
+ searchMode?: 'contains' | 'exact';
98
+ /** Custom ascending comparison. */
99
+ compare?: (a: T, b: T) => number;
100
+ /** Adds a filter on this column to the toolbar. */
101
+ filter?: DataTableColumnFilter<T>;
102
+ /** Makes the cell editable once `onRowSave` is set. */
103
+ edit?: NonNullable<DataTableColumnEdit<T>['type']> | DataTableColumnEdit<T>;
104
+ /** Leaves the column out of the grid; it still takes part in search, filters and sorting. */
105
+ hidden?: boolean;
106
+ align?: 'start' | 'center' | 'end';
107
+ /** Right-align with tabular figures. */
108
+ numeric?: boolean;
109
+ /** Monospace text, for ids, codes and timestamps. */
110
+ mono?: boolean;
111
+ /** Quieter text colour, for secondary values. */
112
+ muted?: boolean;
113
+ /** Cuts long text with an ellipsis instead of wrapping it. The column takes its `width`, not the text's. */
114
+ truncate?: boolean;
115
+ /** Preferred width. The table measures its columns once and then holds them, so this is a starting point. */
116
+ width?: number | string;
117
+ /** Floor for the measured width, for columns whose content gets longer on later pages. */
118
+ minWidth?: number | string;
119
+ /** Extra class on the column's body cells. */
120
+ cellClassName?: string | ((row: T) => string | undefined);
121
+ }
122
+ export interface DataTableSort {
123
+ id: string;
124
+ direction: 'asc' | 'desc';
125
+ }
126
+ export interface DataTableRowAction {
127
+ /** Tooltip and accessible name. */
128
+ label: string;
129
+ icon: React.ReactNode;
130
+ onClick: () => void;
131
+ disabled?: boolean;
132
+ /** Colours the button red on hover. */
133
+ danger?: boolean;
134
+ }
135
+ export type DataTableRowTone = 'accent' | 'info' | 'success' | 'warning' | 'danger';
136
+ export interface DataTableEntry<T> {
137
+ id: string;
138
+ row: T;
139
+ }
140
+ export interface DataTableLabels extends PaginationLabels {
141
+ search: string;
142
+ clearSearch: string;
143
+ selectAll: string;
144
+ selectRow: (label: string) => string;
145
+ count: (count: number) => React.ReactNode;
146
+ /** Summary while a search or filter narrows the rows. Defaults to `count` of the total, prefixed with the match count. */
147
+ filteredCount: (count: number, total: number) => React.ReactNode;
148
+ selected: (count: number) => React.ReactNode;
149
+ emptyTitle: React.ReactNode;
150
+ emptyDescription: React.ReactNode;
151
+ noMatchTitle: React.ReactNode;
152
+ noMatchDescription: React.ReactNode;
153
+ /** Shown instead of `noMatchDescription` when filters leave no rows. Defaults to it. */
154
+ noFilterMatchDescription: React.ReactNode;
155
+ loading: string;
156
+ errorTitle: React.ReactNode;
157
+ retry: string;
158
+ /** Accessible name of the filter group. */
159
+ filters: string;
160
+ /** The "no filter" option of a select filter. */
161
+ filterAll: string;
162
+ clearFilters: string;
163
+ /** Accessible name of the row actions column. */
164
+ actions: string;
165
+ edit: string;
166
+ save: string;
167
+ cancel: string;
168
+ delete: string;
169
+ }
170
+ /** Labels with the defaults filled in. Pagination texts stay partial: `Pagination` has its own defaults. */
171
+ export type DataTableResolvedLabels = Omit<DataTableLabels, keyof PaginationLabels> & Partial<PaginationLabels>;
172
+ export interface UseDataTableOptions<T> {
173
+ /** All rows, or in server mode the rows of the current page. */
174
+ data: T[];
175
+ columns: DataTableColumn<T>[];
176
+ /**
177
+ * `server` renders `data` as it is: search, filtering, sorting and pagination are
178
+ * left to the API, driven by the `search` / `filterValues` / `sort` / `page` callbacks.
179
+ */
180
+ mode?: 'client' | 'server';
181
+ /** Server mode: number of rows across all pages. */
182
+ total?: number;
183
+ /** Shows placeholder rows when there is no data yet, and dims the rows during a refetch. */
184
+ loading?: boolean;
185
+ /** Error message shown in place of the rows. */
186
+ error?: React.ReactNode;
187
+ /** Adds a retry button to the error message. */
188
+ onRetry?: () => void;
189
+ /** Stable id per row, needed for selection and editing. Defaults to `row.id`, then the index. */
190
+ getRowId?: (row: T, index: number) => string;
191
+ /** Language whose alphabet orders sorted text and filter options (`"hu"`). Defaults to the browser's. */
192
+ locale?: string;
193
+ /** Show the search box (default `true`). */
194
+ searchable?: boolean;
195
+ search?: string;
196
+ defaultSearch?: string;
197
+ onSearchChange?: (search: string) => void;
198
+ /** Milliseconds of typing pause before the search applies. Defaults to 300 in server mode, 0 otherwise. */
199
+ searchDebounce?: number;
200
+ /** Replaces the built-in search, which matches every term against the searchable columns. */
201
+ filter?: (row: T, search: string) => boolean;
202
+ /** Toolbar filters that are not tied to a column. Columns add theirs with `filter`. */
203
+ filters?: DataTableFilter<T>[];
204
+ filterValues?: DataTableFilterValues;
205
+ defaultFilterValues?: DataTableFilterValues;
206
+ onFilterValuesChange?: (values: DataTableFilterValues) => void;
207
+ sort?: DataTableSort | null;
208
+ defaultSort?: DataTableSort | null;
209
+ onSortChange?: (sort: DataTableSort | null) => void;
210
+ /** Paginate rows (default `true`). */
211
+ paginated?: boolean;
212
+ page?: number;
213
+ onPageChange?: (page: number) => void;
214
+ pageSize?: number;
215
+ defaultPageSize?: number;
216
+ onPageSizeChange?: (pageSize: number) => void;
217
+ /**
218
+ * Selected row ids. Passing `onSelectionChange` adds the checkbox column.
219
+ * Select-all covers every filtered row, or in server mode the current page.
220
+ */
221
+ selection?: string[];
222
+ onSelectionChange?: (selection: string[]) => void;
223
+ /** Makes rows clickable and keyboard-activatable (Enter). */
224
+ onRowClick?: (row: T) => void;
225
+ /** Accessible name of a row, used by row selection and clickable rows. */
226
+ rowLabel?: (row: T) => string;
227
+ /** Trailing cell of each row: icon buttons, or any content (a menu, a link). */
228
+ rowActions?: (row: T) => DataTableRowAction[] | React.ReactNode;
229
+ /** Tints a row to flag its state. */
230
+ rowTone?: (row: T) => DataTableRowTone | null | undefined;
231
+ /** Extra props for a row's `<tr>` (`className`, `style`, `data-*`). */
232
+ rowProps?: (row: T) => React.HTMLAttributes<HTMLTableRowElement> & Record<`data-${string}`, unknown>;
233
+ /**
234
+ * Adds an edit action to each row; columns with `edit` turn into inputs. Called
235
+ * with the edited row. Return a promise to keep the row open while it saves; a
236
+ * rejected one leaves it open for another try.
237
+ */
238
+ onRowSave?: (row: T, original: T) => void | Promise<void>;
239
+ /** Adds a delete action, shown while a row is edited, or on every row without `onRowSave`. */
240
+ onRowDelete?: (row: T) => void;
241
+ /** Override texts for localisation. */
242
+ labels?: Partial<DataTableLabels>;
243
+ }
244
+ /** State and derived rows of a table, returned by `useDataTable` and read by the `DataTable` parts. */
245
+ export interface DataTableInstance<T> {
246
+ mode: 'client' | 'server';
247
+ /** Every column, hidden ones included. */
248
+ columns: DataTableColumn<T>[];
249
+ visibleColumns: DataTableColumn<T>[];
250
+ labels: DataTableResolvedLabels;
251
+ locale?: string;
252
+ /** Every row of `data`, before search and filters. */
253
+ allRows: DataTableEntry<T>[];
254
+ /** Rows after filters, search and sorting, across all pages. In server mode: the current page. */
255
+ rows: DataTableEntry<T>[];
256
+ /** Rows of the current page. */
257
+ pageRows: DataTableEntry<T>[];
258
+ /** Number of matching rows across all pages. */
259
+ count: number;
260
+ /** Number of rows before search and filters; in server mode the same as `count`. */
261
+ totalCount: number;
262
+ /** A search or filter is narrowing the rows. */
263
+ filtered: boolean;
264
+ /** Clears the search and every filter. */
265
+ reset: () => void;
266
+ loading: boolean;
267
+ /** Loading with no rows to show yet. */
268
+ pending: boolean;
269
+ error?: React.ReactNode;
270
+ onRetry?: () => void;
271
+ searchable: boolean;
272
+ search: string;
273
+ setSearch: (search: string) => void;
274
+ searchDebounce: number;
275
+ filters: DataTableResolvedFilter<T>[];
276
+ filterValues: DataTableFilterValues;
277
+ /** Sets one filter; `undefined` clears it. */
278
+ setFilterValue: (id: string, value: DataTableFilterValue | undefined) => void;
279
+ clearFilters: () => void;
280
+ activeFilterCount: number;
281
+ sort: DataTableSort | null;
282
+ setSort: (sort: DataTableSort | null) => void;
283
+ /** Cycles a column through ascending, descending and unsorted. */
284
+ toggleSort: (id: string) => void;
285
+ isSortable: (column: DataTableColumn<T>) => boolean;
286
+ paginated: boolean;
287
+ page: number;
288
+ pageCount: number;
289
+ pageSize: number;
290
+ setPage: (page: number) => void;
291
+ setPageSize: (pageSize: number) => void;
292
+ selectable: boolean;
293
+ /** Ids of the selected rows. */
294
+ selection: string[];
295
+ isSelected: (id: string) => boolean;
296
+ toggleRow: (id: string, selected: boolean) => void;
297
+ toggleAll: (selected: boolean) => void;
298
+ allSelected: boolean;
299
+ someSelected: boolean;
300
+ editable: boolean;
301
+ deletable: boolean;
302
+ /** Id of the row being edited. */
303
+ editingId: string | null;
304
+ /** True while `onRowSave` is pending. */
305
+ saving: boolean;
306
+ startEdit: (id: string) => void;
307
+ cancelEdit: () => void;
308
+ /** Value of a field of the row being edited, with its unsaved changes. */
309
+ editValue: (row: T, field: string) => unknown;
310
+ setEditValue: (field: string, value: unknown) => void;
311
+ saveEdit: () => void;
312
+ deleteRow: (row: T) => void;
313
+ onRowClick?: (row: T) => void;
314
+ rowLabel?: (row: T) => string;
315
+ rowActions?: (row: T) => DataTableRowAction[] | React.ReactNode;
316
+ rowTone?: (row: T) => DataTableRowTone | null | undefined;
317
+ rowProps?: (row: T) => React.HTMLAttributes<HTMLTableRowElement>;
318
+ }
319
+ export {};