@rebasepro/ui 0.24.0 → 0.24.1-canary.gcb918fc

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 (241) hide show
  1. package/dist/components/Alert.d.ts +6 -0
  2. package/dist/components/Alert.es.js +82 -0
  3. package/dist/components/Alert.es.js.map +1 -0
  4. package/dist/components/Autocomplete.es.js +53 -0
  5. package/dist/components/Autocomplete.es.js.map +1 -0
  6. package/dist/components/Avatar.d.ts +3 -1
  7. package/dist/components/Avatar.es.js +37 -0
  8. package/dist/components/Avatar.es.js.map +1 -0
  9. package/dist/components/Badge.es.js +27 -0
  10. package/dist/components/Badge.es.js.map +1 -0
  11. package/dist/components/BooleanSwitch.es.js +75 -0
  12. package/dist/components/BooleanSwitch.es.js.map +1 -0
  13. package/dist/components/BooleanSwitchWithLabel.es.js +55 -0
  14. package/dist/components/BooleanSwitchWithLabel.es.js.map +1 -0
  15. package/dist/components/Button.es.js +58 -0
  16. package/dist/components/Button.es.js.map +1 -0
  17. package/dist/components/Card.es.js +26 -0
  18. package/dist/components/Card.es.js.map +1 -0
  19. package/dist/components/CenteredView.es.js +23 -0
  20. package/dist/components/CenteredView.es.js.map +1 -0
  21. package/dist/components/Checkbox.es.js +62 -0
  22. package/dist/components/Checkbox.es.js.map +1 -0
  23. package/dist/components/Chip.es.js +72 -0
  24. package/dist/components/Chip.es.js.map +1 -0
  25. package/dist/components/CircularProgress.es.js +28 -0
  26. package/dist/components/CircularProgress.es.js.map +1 -0
  27. package/dist/components/CircularProgressCenter.es.js +30 -0
  28. package/dist/components/CircularProgressCenter.es.js.map +1 -0
  29. package/dist/components/Collapse.es.js +57 -0
  30. package/dist/components/Collapse.es.js.map +1 -0
  31. package/dist/components/ColorPicker.es.js +89 -0
  32. package/dist/components/ColorPicker.es.js.map +1 -0
  33. package/dist/components/Container.es.js +31 -0
  34. package/dist/components/Container.es.js.map +1 -0
  35. package/dist/components/DateTimeField.d.ts +11 -1
  36. package/dist/components/DateTimeField.es.js +253 -0
  37. package/dist/components/DateTimeField.es.js.map +1 -0
  38. package/dist/components/DebouncedTextField.es.js +93 -0
  39. package/dist/components/DebouncedTextField.es.js.map +1 -0
  40. package/dist/components/Dialog.es.js +98 -0
  41. package/dist/components/Dialog.es.js.map +1 -0
  42. package/dist/components/DialogActions.es.js +15 -0
  43. package/dist/components/DialogActions.es.js.map +1 -0
  44. package/dist/components/DialogContent.es.js +18 -0
  45. package/dist/components/DialogContent.es.js.map +1 -0
  46. package/dist/components/DialogTitle.es.js +26 -0
  47. package/dist/components/DialogTitle.es.js.map +1 -0
  48. package/dist/components/ErrorBoundary.es.js +158 -0
  49. package/dist/components/ErrorBoundary.es.js.map +1 -0
  50. package/dist/components/ExpandablePanel.es.js +91 -0
  51. package/dist/components/ExpandablePanel.es.js.map +1 -0
  52. package/dist/components/FileUpload.es.js +58 -0
  53. package/dist/components/FileUpload.es.js.map +1 -0
  54. package/dist/components/FilterChip.es.js +31 -0
  55. package/dist/components/FilterChip.es.js.map +1 -0
  56. package/dist/components/IconButton.es.js +52 -0
  57. package/dist/components/IconButton.es.js.map +1 -0
  58. package/dist/components/InfoLabel.es.js +18 -0
  59. package/dist/components/InfoLabel.es.js.map +1 -0
  60. package/dist/components/InputLabel.es.js +27 -0
  61. package/dist/components/InputLabel.es.js.map +1 -0
  62. package/dist/components/Label.es.js +18 -0
  63. package/dist/components/Label.es.js.map +1 -0
  64. package/dist/components/LoadingButton.es.js +21 -0
  65. package/dist/components/LoadingButton.es.js.map +1 -0
  66. package/dist/components/Markdown.es.js +88 -0
  67. package/dist/components/Markdown.es.js.map +1 -0
  68. package/dist/components/Menu.es.js +46 -0
  69. package/dist/components/Menu.es.js.map +1 -0
  70. package/dist/components/Menubar.es.js +142 -0
  71. package/dist/components/Menubar.es.js.map +1 -0
  72. package/dist/components/MultiSelect.d.ts +1 -7
  73. package/dist/components/MultiSelect.es.js +271 -0
  74. package/dist/components/MultiSelect.es.js.map +1 -0
  75. package/dist/components/Paper.es.js +16 -0
  76. package/dist/components/Paper.es.js.map +1 -0
  77. package/dist/components/Popover.es.js +113 -0
  78. package/dist/components/Popover.es.js.map +1 -0
  79. package/dist/components/RadioGroup.es.js +30 -0
  80. package/dist/components/RadioGroup.es.js.map +1 -0
  81. package/dist/components/ResizablePanels.d.ts +3 -1
  82. package/dist/components/ResizablePanels.es.js +137 -0
  83. package/dist/components/ResizablePanels.es.js.map +1 -0
  84. package/dist/components/SearchBar.es.js +82 -0
  85. package/dist/components/SearchBar.es.js.map +1 -0
  86. package/dist/components/Select.d.ts +8 -1
  87. package/dist/components/Select.es.js +300 -0
  88. package/dist/components/Select.es.js.map +1 -0
  89. package/dist/components/Separator.es.js +20 -0
  90. package/dist/components/Separator.es.js.map +1 -0
  91. package/dist/components/Sheet.es.js +73 -0
  92. package/dist/components/Sheet.es.js.map +1 -0
  93. package/dist/components/Skeleton.es.js +33 -0
  94. package/dist/components/Skeleton.es.js.map +1 -0
  95. package/dist/components/Slider.es.js +56 -0
  96. package/dist/components/Slider.es.js.map +1 -0
  97. package/dist/components/Table.d.ts +18 -3
  98. package/dist/components/Table.es.js +69 -0
  99. package/dist/components/Table.es.js.map +1 -0
  100. package/dist/components/Tabs.es.js +101 -0
  101. package/dist/components/Tabs.es.js.map +1 -0
  102. package/dist/components/TextField.d.ts +0 -8
  103. package/dist/components/TextField.es.js +111 -0
  104. package/dist/components/TextField.es.js.map +1 -0
  105. package/dist/components/TextareaAutosize.es.js +187 -0
  106. package/dist/components/TextareaAutosize.es.js.map +1 -0
  107. package/dist/components/ToggleButtonGroup.d.ts +6 -1
  108. package/dist/components/ToggleButtonGroup.es.js +35 -0
  109. package/dist/components/ToggleButtonGroup.es.js.map +1 -0
  110. package/dist/components/Tooltip.d.ts +24 -0
  111. package/dist/components/Tooltip.es.js +165 -0
  112. package/dist/components/Tooltip.es.js.map +1 -0
  113. package/dist/components/Typography.es.js +85 -0
  114. package/dist/components/Typography.es.js.map +1 -0
  115. package/dist/components/VirtualTable/VirtualTable.es.js +516 -0
  116. package/dist/components/VirtualTable/VirtualTable.es.js.map +1 -0
  117. package/dist/components/VirtualTable/VirtualTableCell.d.ts +1 -1
  118. package/dist/components/VirtualTable/VirtualTableCell.es.js +40 -0
  119. package/dist/components/VirtualTable/VirtualTableCell.es.js.map +1 -0
  120. package/dist/components/VirtualTable/VirtualTableHeader.es.js +209 -0
  121. package/dist/components/VirtualTable/VirtualTableHeader.es.js.map +1 -0
  122. package/dist/components/VirtualTable/VirtualTableHeaderRow.es.js +166 -0
  123. package/dist/components/VirtualTable/VirtualTableHeaderRow.es.js.map +1 -0
  124. package/dist/components/VirtualTable/VirtualTableProps.d.ts +13 -2
  125. package/dist/components/VirtualTable/VirtualTableRow.d.ts +6 -7
  126. package/dist/components/VirtualTable/VirtualTableRow.es.js +41 -0
  127. package/dist/components/VirtualTable/VirtualTableRow.es.js.map +1 -0
  128. package/dist/components/VirtualTable/fields/VirtualTableDateField.d.ts +2 -1
  129. package/dist/components/VirtualTable/fields/VirtualTableDateField.es.js +71 -0
  130. package/dist/components/VirtualTable/fields/VirtualTableDateField.es.js.map +1 -0
  131. package/dist/components/VirtualTable/fields/VirtualTableInput.es.js +73 -0
  132. package/dist/components/VirtualTable/fields/VirtualTableInput.es.js.map +1 -0
  133. package/dist/components/VirtualTable/fields/VirtualTableNumberInput.es.js +47 -0
  134. package/dist/components/VirtualTable/fields/VirtualTableNumberInput.es.js.map +1 -0
  135. package/dist/components/VirtualTable/fields/VirtualTableSelect.es.js +83 -0
  136. package/dist/components/VirtualTable/fields/VirtualTableSelect.es.js.map +1 -0
  137. package/dist/components/VirtualTable/fields/VirtualTableSwitch.es.js +22 -0
  138. package/dist/components/VirtualTable/fields/VirtualTableSwitch.es.js.map +1 -0
  139. package/dist/components/VirtualTable/selection/SelectionContext.es.js +23 -0
  140. package/dist/components/VirtualTable/selection/SelectionContext.es.js.map +1 -0
  141. package/dist/components/VirtualTable/selection/SelectionStore.es.js +43 -0
  142. package/dist/components/VirtualTable/selection/SelectionStore.es.js.map +1 -0
  143. package/dist/components/VirtualTable/types.d.ts +1 -1
  144. package/dist/components/common/SelectInputLabel.es.js +28 -0
  145. package/dist/components/common/SelectInputLabel.es.js.map +1 -0
  146. package/dist/components/index.es.js +64 -0
  147. package/dist/hooks/PortalContainerContext.es.js +40 -0
  148. package/dist/hooks/PortalContainerContext.es.js.map +1 -0
  149. package/dist/hooks/UiLabelsContext.d.ts +63 -0
  150. package/dist/hooks/UiLabelsContext.es.js +59 -0
  151. package/dist/hooks/UiLabelsContext.es.js.map +1 -0
  152. package/dist/hooks/useDebounceCallback.es.js +15 -0
  153. package/dist/hooks/useDebounceCallback.es.js.map +1 -0
  154. package/dist/hooks/useDebounceValue.es.js +19 -0
  155. package/dist/hooks/useDebounceValue.es.js.map +1 -0
  156. package/dist/hooks/useDebouncedCallback.es.js +36 -0
  157. package/dist/hooks/useDebouncedCallback.es.js.map +1 -0
  158. package/dist/hooks/useInjectStyles.es.js +27 -0
  159. package/dist/hooks/useInjectStyles.es.js.map +1 -0
  160. package/dist/hooks/useIsDarkMode.es.js +61 -0
  161. package/dist/hooks/useIsDarkMode.es.js.map +1 -0
  162. package/dist/hooks/useModalFocus.es.js +81 -0
  163. package/dist/hooks/useModalFocus.es.js.map +1 -0
  164. package/dist/hooks/useOutsideAlerter.es.js +41 -0
  165. package/dist/hooks/useOutsideAlerter.es.js.map +1 -0
  166. package/dist/hooks/useRestoreInterruptedFocus.es.js +61 -0
  167. package/dist/hooks/useRestoreInterruptedFocus.es.js.map +1 -0
  168. package/dist/icons/GitHubIcon.es.js +23 -0
  169. package/dist/icons/GitHubIcon.es.js.map +1 -0
  170. package/dist/icons/HandleIcon.es.js +60 -0
  171. package/dist/icons/HandleIcon.es.js.map +1 -0
  172. package/dist/icons/Icon.es.js +25 -0
  173. package/dist/icons/Icon.es.js.map +1 -0
  174. package/dist/icons/LucideIconByName.d.ts +12 -9
  175. package/dist/icons/LucideIconByName.es.js +83 -0
  176. package/dist/icons/LucideIconByName.es.js.map +1 -0
  177. package/dist/icons/cool_icon_keys.es.js +55 -0
  178. package/dist/icons/cool_icon_keys.es.js.map +1 -0
  179. package/dist/icons/icon_keys.es.js +1941 -0
  180. package/dist/icons/icon_keys.es.js.map +1 -0
  181. package/dist/icons/index.d.ts +4 -3
  182. package/dist/icons/index.es.js +8 -0
  183. package/dist/icons/lucide_icon_map.d.ts +12 -0
  184. package/dist/icons/lucide_icon_map.es.js +2 -0
  185. package/dist/index.d.ts +2 -0
  186. package/dist/index.es.js +87 -9563
  187. package/dist/styles.es.js +158 -0
  188. package/dist/styles.es.js.map +1 -0
  189. package/dist/util/chip_colors.es.js +290 -0
  190. package/dist/util/chip_colors.es.js.map +1 -0
  191. package/dist/util/cls.es.js +10 -0
  192. package/dist/util/cls.es.js.map +1 -0
  193. package/dist/util/debounce.es.js +22 -0
  194. package/dist/util/debounce.es.js.map +1 -0
  195. package/dist/util/hash.es.js +16 -0
  196. package/dist/util/hash.es.js.map +1 -0
  197. package/dist/util/keys.es.js +37 -0
  198. package/dist/util/keys.es.js.map +1 -0
  199. package/dist/util/lazy_chunk.es.js +96 -0
  200. package/dist/util/lazy_chunk.es.js.map +1 -0
  201. package/dist/views/CardView.d.ts +26 -2
  202. package/dist/views/CardView.es.js +435 -0
  203. package/dist/views/CardView.es.js.map +1 -0
  204. package/dist/views/CollectionView/CollectionCardView.es.js +179 -0
  205. package/dist/views/CollectionView/CollectionCardView.es.js.map +1 -0
  206. package/dist/views/CollectionView/CollectionKanbanView.es.js +172 -0
  207. package/dist/views/CollectionView/CollectionKanbanView.es.js.map +1 -0
  208. package/dist/views/CollectionView/CollectionListView.es.js +156 -0
  209. package/dist/views/CollectionView/CollectionListView.es.js.map +1 -0
  210. package/dist/views/CollectionView/CollectionTableView.es.js +149 -0
  211. package/dist/views/CollectionView/CollectionTableView.es.js.map +1 -0
  212. package/dist/views/CollectionView/CollectionView.es.js +197 -0
  213. package/dist/views/CollectionView/CollectionView.es.js.map +1 -0
  214. package/dist/views/CollectionView/CollectionViewToolbar.es.js +155 -0
  215. package/dist/views/CollectionView/CollectionViewToolbar.es.js.map +1 -0
  216. package/dist/views/CollectionView/DefaultCellRenderer.es.js +302 -0
  217. package/dist/views/CollectionView/DefaultCellRenderer.es.js.map +1 -0
  218. package/dist/views/CollectionView/resolveTitleProperty.es.js +56 -0
  219. package/dist/views/CollectionView/resolveTitleProperty.es.js.map +1 -0
  220. package/dist/views/CollectionView/utils.es.js +19 -0
  221. package/dist/views/CollectionView/utils.es.js.map +1 -0
  222. package/dist/views/Kanban/Board.es.js +237 -0
  223. package/dist/views/Kanban/Board.es.js.map +1 -0
  224. package/dist/views/Kanban/BoardColumn.es.js +81 -0
  225. package/dist/views/Kanban/BoardColumn.es.js.map +1 -0
  226. package/dist/views/Kanban/BoardColumnTitle.es.js +27 -0
  227. package/dist/views/Kanban/BoardColumnTitle.es.js.map +1 -0
  228. package/dist/views/Kanban/BoardSortableList.es.js +98 -0
  229. package/dist/views/Kanban/BoardSortableList.es.js.map +1 -0
  230. package/dist/views/Kanban/placement.es.js +27 -0
  231. package/dist/views/Kanban/placement.es.js.map +1 -0
  232. package/dist/views/KanbanView.es.js +11 -0
  233. package/dist/views/KanbanView.es.js.map +1 -0
  234. package/dist/views/ListView.d.ts +15 -2
  235. package/dist/views/ListView.es.js +245 -0
  236. package/dist/views/ListView.es.js.map +1 -0
  237. package/dist/views/useScrollParentRestoration.d.ts +8 -1
  238. package/dist/views/useScrollParentRestoration.es.js +75 -0
  239. package/dist/views/useScrollParentRestoration.es.js.map +1 -0
  240. package/package.json +2 -2
  241. package/dist/index.es.js.map +0 -1
@@ -0,0 +1,149 @@
1
+ "use client";
2
+ import { cls } from "../../util/cls.es.js";
3
+ import { VirtualTable } from "../../components/VirtualTable/VirtualTable.es.js";
4
+ import { DefaultCellRenderer } from "./DefaultCellRenderer.es.js";
5
+ import { getValueInPath } from "./utils.es.js";
6
+ import { useCallback, useMemo } from "react";
7
+ import { jsx } from "react/jsx-runtime";
8
+ //#region src/views/CollectionView/CollectionTableView.tsx
9
+ var DEFAULT_COLUMN_WIDTHS = {
10
+ string: 200,
11
+ number: 140,
12
+ date: 160,
13
+ boolean: 120,
14
+ array: 200,
15
+ map: 200,
16
+ reference: 200,
17
+ relation: 200,
18
+ geopoint: 180
19
+ };
20
+ var FALLBACK_COLUMN_WIDTH = 200;
21
+ var SIZE_TO_ROW_HEIGHT = {
22
+ xs: 36,
23
+ s: 44,
24
+ m: 52,
25
+ l: 64,
26
+ xl: 80
27
+ };
28
+ var SORTABLE_TYPES = /* @__PURE__ */ new Set([
29
+ "string",
30
+ "number",
31
+ "date"
32
+ ]);
33
+ var VIEW_SIZE_TO_RENDERER_SIZE = {
34
+ xs: "tiny",
35
+ s: "small",
36
+ m: "medium",
37
+ l: "large",
38
+ xl: "large"
39
+ };
40
+ /**
41
+ * Headless table view that wraps {@link VirtualTable}.
42
+ *
43
+ * Converts property configurations into virtual table columns and wires up
44
+ * sorting, pagination, and cell rendering without coupling to any data layer.
45
+ *
46
+ * @group Collection View
47
+ */
48
+ function CollectionTableView({ dataController, properties, propertiesOrder, displayedColumnIds, onRowClick, cellRenderer, size = "m", emptyComponent, hoverRow = true, className, onColumnResize, onColumnsOrderChange }) {
49
+ const columns = useMemo(() => {
50
+ const order = propertiesOrder ?? Object.keys(properties);
51
+ return (displayedColumnIds ? order.filter((key) => displayedColumnIds.includes(key)) : order.filter((key) => {
52
+ const prop = properties[key];
53
+ return prop && !prop.hideFromCollection;
54
+ })).map((key) => {
55
+ const prop = properties[key];
56
+ if (!prop) return null;
57
+ return {
58
+ key,
59
+ width: prop.columnWidth || DEFAULT_COLUMN_WIDTHS[prop.type] || FALLBACK_COLUMN_WIDTH,
60
+ title: prop.name,
61
+ sortable: SORTABLE_TYPES.has(prop.type),
62
+ resizable: true
63
+ };
64
+ }).filter((col) => col !== null);
65
+ }, [
66
+ properties,
67
+ propertiesOrder,
68
+ displayedColumnIds
69
+ ]);
70
+ const rowHeight = SIZE_TO_ROW_HEIGHT[size];
71
+ const rendererSize = VIEW_SIZE_TO_RENDERER_SIZE[size];
72
+ const tableCellRenderer = useCallback((params) => {
73
+ if (!params.rowData) return null;
74
+ const propertyConfig = properties[params.column.key];
75
+ if (!propertyConfig) return null;
76
+ const value = getValueInPath(params.rowData, params.column.key);
77
+ const cellProps = {
78
+ row: params.rowData,
79
+ propertyKey: params.column.key,
80
+ property: propertyConfig,
81
+ value,
82
+ size: rendererSize,
83
+ width: params.width
84
+ };
85
+ if (cellRenderer) {
86
+ const custom = cellRenderer(cellProps);
87
+ if (custom !== void 0) return /* @__PURE__ */ jsx("div", {
88
+ className: "flex items-center h-full px-1 overflow-hidden",
89
+ children: custom
90
+ });
91
+ }
92
+ return /* @__PURE__ */ jsx("div", {
93
+ className: "flex items-center h-full px-1 overflow-hidden",
94
+ children: /* @__PURE__ */ jsx(DefaultCellRenderer, { ...cellProps })
95
+ });
96
+ }, [
97
+ properties,
98
+ cellRenderer,
99
+ rendererSize
100
+ ]);
101
+ const handleRowClick = useCallback(({ rowData }) => {
102
+ onRowClick?.(rowData);
103
+ }, [onRowClick]);
104
+ const handleEndReached = useCallback(() => {
105
+ if (dataController.paginationEnabled && !dataController.noMoreToLoad) {
106
+ const currentCount = dataController.itemCount ?? dataController.pageSize ?? 50;
107
+ const pageSize = dataController.pageSize ?? 50;
108
+ dataController.setItemCount?.(currentCount + pageSize);
109
+ }
110
+ }, [
111
+ dataController.paginationEnabled,
112
+ dataController.noMoreToLoad,
113
+ dataController.itemCount,
114
+ dataController.pageSize,
115
+ dataController.setItemCount
116
+ ]);
117
+ const handleColumnResize = useCallback((params) => {
118
+ onColumnResize?.({
119
+ key: params.key,
120
+ width: params.width
121
+ });
122
+ }, [onColumnResize]);
123
+ const handleColumnsOrderChange = useCallback((newColumns) => {
124
+ onColumnsOrderChange?.(newColumns.map((col) => col.key));
125
+ }, [onColumnsOrderChange]);
126
+ return /* @__PURE__ */ jsx(VirtualTable, {
127
+ data: dataController.data,
128
+ columns,
129
+ cellRenderer: tableCellRenderer,
130
+ rowHeight,
131
+ sortBy: dataController.sortBy,
132
+ onSortByUpdate: dataController.setSortBy,
133
+ onRowClick: handleRowClick,
134
+ onEndReached: handleEndReached,
135
+ onColumnResize: handleColumnResize,
136
+ onColumnsOrderChange: handleColumnsOrderChange,
137
+ loading: dataController.loading,
138
+ emptyComponent,
139
+ error: dataController.error,
140
+ hoverRow,
141
+ className: cls("h-full", className),
142
+ onScroll: dataController.onScroll,
143
+ initialScroll: dataController.initialScroll
144
+ });
145
+ }
146
+ //#endregion
147
+ export { CollectionTableView };
148
+
149
+ //# sourceMappingURL=CollectionTableView.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CollectionTableView.es.js","names":[],"sources":["../../../src/views/CollectionView/CollectionTableView.tsx"],"sourcesContent":["\"use client\";\nimport React, { useCallback, useMemo } from \"react\";\nimport { VirtualTable } from \"../../components/VirtualTable/VirtualTable\";\nimport type { VirtualTableColumn, CellRendererParams, OnVirtualTableColumnResizeParams } from \"../../components/VirtualTable/VirtualTableProps\";\nimport { DefaultCellRenderer } from \"./DefaultCellRenderer\";\nimport { cls } from \"../../util\";\nimport type {\n CollectionPropertyConfig,\n CollectionDataController,\n CellRendererOverride,\n CollectionViewSize,\n CollectionSelectionController,\n CellRendererProps,\n} from \"./CollectionViewTypes\";\n\nimport { getValueInPath } from \"./utils\";\n\nconst DEFAULT_COLUMN_WIDTHS: Record<string, number> = {\n string: 200,\n number: 140,\n date: 160,\n boolean: 120,\n array: 200,\n map: 200,\n reference: 200,\n relation: 200,\n geopoint: 180,\n};\nconst FALLBACK_COLUMN_WIDTH = 200;\n\nconst SIZE_TO_ROW_HEIGHT: Record<CollectionViewSize, number> = {\n xs: 36,\n s: 44,\n m: 52,\n l: 64,\n xl: 80,\n};\n\nconst SORTABLE_TYPES = new Set([\"string\", \"number\", \"date\"]);\n\ntype RendererSize = \"tiny\" | \"small\" | \"medium\" | \"large\";\n\nconst VIEW_SIZE_TO_RENDERER_SIZE: Record<CollectionViewSize, RendererSize> = {\n xs: \"tiny\",\n s: \"small\",\n m: \"medium\",\n l: \"large\",\n xl: \"large\",\n};\n\n/**\n * Props for the {@link CollectionTableView} component.\n * @group Collection View\n */\nexport type CollectionTableViewProps<T = Record<string, unknown>> = {\n dataController: CollectionDataController<T>;\n properties: Record<string, CollectionPropertyConfig>;\n propertiesOrder?: string[];\n displayedColumnIds?: string[];\n idProperty?: string;\n titleProperty?: string;\n onRowClick?: (row: T) => void;\n cellRenderer?: CellRendererOverride<T>;\n size?: CollectionViewSize;\n selectionEnabled?: boolean;\n selectionController?: CollectionSelectionController<T>;\n highlightedItems?: T[];\n emptyComponent?: React.ReactNode;\n hoverRow?: boolean;\n className?: string;\n onColumnResize?: (params: { key: string; width: number }) => void;\n onColumnsOrderChange?: (keys: string[]) => void;\n};\n\n/**\n * Headless table view that wraps {@link VirtualTable}.\n *\n * Converts property configurations into virtual table columns and wires up\n * sorting, pagination, and cell rendering without coupling to any data layer.\n *\n * @group Collection View\n */\nexport function CollectionTableView<T extends Record<string, unknown> = Record<string, unknown>>({\n dataController,\n properties,\n propertiesOrder,\n displayedColumnIds,\n onRowClick,\n cellRenderer,\n size = \"m\",\n emptyComponent,\n hoverRow = true,\n className,\n onColumnResize,\n onColumnsOrderChange,\n}: CollectionTableViewProps<T>) {\n // ── Build columns ────────────────────────────────────────────────\n const columns = useMemo<VirtualTableColumn[]>(() => {\n const order = propertiesOrder ?? Object.keys(properties);\n const filteredKeys = displayedColumnIds\n ? order.filter((key) => displayedColumnIds.includes(key))\n : order.filter((key) => {\n const prop = properties[key];\n return prop && !prop.hideFromCollection;\n });\n\n return filteredKeys\n .map((key): VirtualTableColumn | null => {\n const prop = properties[key];\n if (!prop) return null;\n return {\n key,\n width: prop.columnWidth || DEFAULT_COLUMN_WIDTHS[prop.type] || FALLBACK_COLUMN_WIDTH,\n title: prop.name,\n sortable: SORTABLE_TYPES.has(prop.type),\n resizable: true,\n };\n })\n .filter((col): col is VirtualTableColumn => col !== null);\n }, [properties, propertiesOrder, displayedColumnIds]);\n\n // ── Row height ───────────────────────────────────────────────────\n const rowHeight = SIZE_TO_ROW_HEIGHT[size];\n const rendererSize = VIEW_SIZE_TO_RENDERER_SIZE[size];\n\n // ── Cell renderer ────────────────────────────────────────────────\n const tableCellRenderer = useCallback(\n (params: CellRendererParams<Record<string, unknown>>): React.ReactNode => {\n if (!params.rowData) return null;\n\n const propertyConfig = properties[params.column.key];\n if (!propertyConfig) return null;\n\n const value = getValueInPath(\n params.rowData,\n params.column.key\n );\n\n const cellProps: CellRendererProps<T> = {\n row: params.rowData as T,\n propertyKey: params.column.key,\n property: propertyConfig,\n value,\n size: rendererSize,\n width: params.width,\n };\n\n // Try custom renderer first\n if (cellRenderer) {\n const custom = cellRenderer(cellProps);\n if (custom !== undefined) {\n return (\n <div className=\"flex items-center h-full px-1 overflow-hidden\">\n {custom}\n </div>\n );\n }\n }\n\n return (\n <div className=\"flex items-center h-full px-1 overflow-hidden\">\n <DefaultCellRenderer {...cellProps} />\n </div>\n );\n },\n [properties, cellRenderer, rendererSize]\n );\n\n // ── Row click ────────────────────────────────────────────────────\n const handleRowClick = useCallback(\n ({ rowData }: { rowData: Record<string, unknown> }) => {\n onRowClick?.(rowData as T);\n },\n [onRowClick]\n );\n\n // ── Pagination ──────────────────────────────────────────────────\n const handleEndReached = useCallback(() => {\n if (dataController.paginationEnabled && !dataController.noMoreToLoad) {\n const currentCount = dataController.itemCount ?? dataController.pageSize ?? 50;\n const pageSize = dataController.pageSize ?? 50;\n dataController.setItemCount?.(currentCount + pageSize);\n }\n }, [\n dataController.paginationEnabled,\n dataController.noMoreToLoad,\n dataController.itemCount,\n dataController.pageSize,\n dataController.setItemCount,\n ]);\n\n // ── Column resize ───────────────────────────────────────────────\n const handleColumnResize = useCallback(\n (params: OnVirtualTableColumnResizeParams) => {\n onColumnResize?.({ key: params.key, width: params.width });\n },\n [onColumnResize]\n );\n\n // ── Column reorder ──────────────────────────────────────────────\n const handleColumnsOrderChange = useCallback(\n (newColumns: VirtualTableColumn[]) => {\n onColumnsOrderChange?.(newColumns.map((col) => col.key));\n },\n [onColumnsOrderChange]\n );\n\n return (\n <VirtualTable\n data={dataController.data}\n columns={columns}\n cellRenderer={tableCellRenderer}\n rowHeight={rowHeight}\n sortBy={dataController.sortBy}\n onSortByUpdate={dataController.setSortBy}\n onRowClick={handleRowClick}\n onEndReached={handleEndReached}\n onColumnResize={handleColumnResize}\n onColumnsOrderChange={handleColumnsOrderChange}\n loading={dataController.loading}\n emptyComponent={emptyComponent}\n error={dataController.error}\n hoverRow={hoverRow}\n className={cls(\"h-full\", className)}\n onScroll={dataController.onScroll}\n initialScroll={dataController.initialScroll}\n />\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,197 @@
1
+ import { cls } from "../../util/cls.es.js";
2
+ import { Typography } from "../../components/Typography.es.js";
3
+ import { CollectionViewToolbar } from "./CollectionViewToolbar.es.js";
4
+ import { CollectionTableView } from "./CollectionTableView.es.js";
5
+ import { CollectionCardView } from "./CollectionCardView.es.js";
6
+ import { CollectionListView } from "./CollectionListView.es.js";
7
+ import { CollectionKanbanView } from "./CollectionKanbanView.es.js";
8
+ import { useCallback, useMemo, useState } from "react";
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
+ //#region src/views/CollectionView/CollectionView.tsx
11
+ var DEFAULT_ENABLED_VIEWS = [
12
+ "list",
13
+ "table",
14
+ "cards",
15
+ "kanban"
16
+ ];
17
+ /**
18
+ * A data-agnostic collection view component that orchestrates table, card,
19
+ * list, and kanban views — driven entirely by props.
20
+ *
21
+ * Zero imports from any entity or data layer. This component lives in
22
+ * `@rebasepro/ui` and can be used with any data source.
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * import { CollectionView } from "@rebasepro/ui";
27
+ *
28
+ * <CollectionView
29
+ * dataController={{ data: myRows, loading: false, noMoreToLoad: true }}
30
+ * properties={{
31
+ * name: { type: "string", name: "Name" },
32
+ * email: { type: "string", name: "Email" },
33
+ * }}
34
+ * title="Contacts"
35
+ * onRowClick={(row) => openDetail(row.id)}
36
+ * />
37
+ * ```
38
+ *
39
+ * @group Collection View
40
+ */
41
+ function CollectionView({ dataController, properties, propertiesOrder, displayedColumnIds, idProperty = "id", titleProperty, title, viewMode: viewModeProp, defaultViewMode = "list", enabledViews = DEFAULT_ENABLED_VIEWS, onViewModeChange: onViewModeChangeProp, size: sizeProp, defaultSize = "m", onSizeChange: onSizeChangeProp, kanbanProperty: kanbanPropertyProp, kanbanPropertyOptions, onKanbanPropertyChange: onKanbanPropertyChangeProp, onRowClick, onRowCreate, canCreate = true, selectionEnabled, selectionController, highlightedItems, onMultipleDelete, cellRenderer, emptyComponent, customViews, toolbarActionsStart, toolbarActionsEnd, hideToolbar, hoverRow = true, onColumnResize, onColumnsOrderChange, className }) {
42
+ const [internalViewMode, setInternalViewMode] = useState(defaultViewMode);
43
+ const viewMode = viewModeProp ?? internalViewMode;
44
+ const onViewModeChange = useCallback((mode) => {
45
+ setInternalViewMode(mode);
46
+ onViewModeChangeProp?.(mode);
47
+ }, [onViewModeChangeProp]);
48
+ const [internalSize, setInternalSize] = useState(defaultSize);
49
+ const size = sizeProp ?? internalSize;
50
+ const onSizeChange = useCallback((s) => {
51
+ setInternalSize(s);
52
+ onSizeChangeProp?.(s);
53
+ }, [onSizeChangeProp]);
54
+ const [internalKanbanProperty, setInternalKanbanProperty] = useState(kanbanPropertyProp);
55
+ const kanbanProperty = kanbanPropertyProp ?? internalKanbanProperty;
56
+ const onKanbanPropertyChange = useCallback((property) => {
57
+ setInternalKanbanProperty(property);
58
+ onKanbanPropertyChangeProp?.(property);
59
+ }, [onKanbanPropertyChangeProp]);
60
+ const resolvedKanbanProperty = useMemo(() => {
61
+ if (kanbanProperty) return kanbanProperty;
62
+ const order = propertiesOrder ?? Object.keys(properties);
63
+ for (const key of order) {
64
+ const prop = properties[key];
65
+ if (prop && prop.type === "string" && prop.enum) return key;
66
+ }
67
+ }, [
68
+ kanbanProperty,
69
+ properties,
70
+ propertiesOrder
71
+ ]);
72
+ const resolvedKanbanPropertyOptions = useMemo(() => {
73
+ if (kanbanPropertyOptions) return kanbanPropertyOptions;
74
+ const options = [];
75
+ const order = propertiesOrder ?? Object.keys(properties);
76
+ for (const key of order) {
77
+ const prop = properties[key];
78
+ if (prop && prop.type === "string" && prop.enum) options.push({
79
+ key,
80
+ label: prop.name
81
+ });
82
+ }
83
+ return options;
84
+ }, [
85
+ kanbanPropertyOptions,
86
+ properties,
87
+ propertiesOrder
88
+ ]);
89
+ const sharedProps = {
90
+ dataController,
91
+ properties,
92
+ propertiesOrder,
93
+ idProperty,
94
+ titleProperty,
95
+ onRowClick,
96
+ cellRenderer,
97
+ selectionEnabled,
98
+ selectionController,
99
+ highlightedItems,
100
+ emptyComponent
101
+ };
102
+ const customViewEntries = useMemo(() => {
103
+ if (!customViews) return [];
104
+ return Object.entries(customViews).filter(([key]) => !DEFAULT_ENABLED_VIEWS.includes(key)).map(([key, entry]) => {
105
+ const isBare = typeof entry === "function";
106
+ return {
107
+ key,
108
+ name: isBare ? key : entry.name ?? key,
109
+ icon: isBare ? void 0 : entry.icon,
110
+ Component: isBare ? entry : entry.Component
111
+ };
112
+ });
113
+ }, [customViews]);
114
+ function renderView() {
115
+ const custom = customViewEntries.find((entry) => entry.key === viewMode);
116
+ if (custom) {
117
+ const { Component } = custom;
118
+ return /* @__PURE__ */ jsx(Component, {
119
+ ...sharedProps,
120
+ onRowCreate: canCreate ? onRowCreate : void 0,
121
+ canCreate,
122
+ size
123
+ });
124
+ }
125
+ switch (viewMode) {
126
+ case "table": return /* @__PURE__ */ jsx(CollectionTableView, {
127
+ ...sharedProps,
128
+ displayedColumnIds,
129
+ size,
130
+ hoverRow,
131
+ onColumnResize,
132
+ onColumnsOrderChange
133
+ });
134
+ case "cards": return /* @__PURE__ */ jsx(CollectionCardView, {
135
+ ...sharedProps,
136
+ size
137
+ });
138
+ case "kanban": return resolvedKanbanProperty ? /* @__PURE__ */ jsx(CollectionKanbanView, {
139
+ ...sharedProps,
140
+ kanbanProperty: resolvedKanbanProperty,
141
+ onRowCreate: canCreate ? onRowCreate : void 0
142
+ }) : /* @__PURE__ */ jsx("div", {
143
+ className: "flex items-center justify-center p-8",
144
+ children: /* @__PURE__ */ jsx(Typography, {
145
+ variant: "body2",
146
+ color: "secondary",
147
+ children: "No enum property available for kanban grouping."
148
+ })
149
+ });
150
+ default: return /* @__PURE__ */ jsx(CollectionListView, {
151
+ ...sharedProps,
152
+ size
153
+ });
154
+ }
155
+ }
156
+ return /* @__PURE__ */ jsxs("div", {
157
+ className: cls("overflow-hidden h-full w-full rounded-md flex flex-col", "bg-surface-sheet", className),
158
+ children: [
159
+ dataController.error && dataController.data.length > 0 && /* @__PURE__ */ jsx("div", {
160
+ className: "flex items-center gap-4 px-4 py-2 bg-red-50 dark:bg-red-900/20 border-b border-red-200 dark:border-red-800 flex-shrink-0",
161
+ children: /* @__PURE__ */ jsxs(Typography, {
162
+ variant: "body2",
163
+ className: "text-red-700 dark:text-red-300 flex-1",
164
+ children: [
165
+ /* @__PURE__ */ jsx("strong", { children: "Warning:" }),
166
+ " ",
167
+ dataController.error.message || "Failed to update data."
168
+ ]
169
+ })
170
+ }),
171
+ !hideToolbar && /* @__PURE__ */ jsx(CollectionViewToolbar, {
172
+ viewMode,
173
+ onViewModeChange: enabledViews.length > 1 ? onViewModeChange : void 0,
174
+ enabledViews,
175
+ customViews: customViewEntries,
176
+ size,
177
+ onSizeChange,
178
+ searchString: dataController.searchString,
179
+ onSearchChange: dataController.setSearchString,
180
+ loading: dataController.loading,
181
+ actionsStart: toolbarActionsStart,
182
+ actionsEnd: toolbarActionsEnd,
183
+ kanbanPropertyOptions: viewMode === "kanban" ? resolvedKanbanPropertyOptions : void 0,
184
+ selectedKanbanProperty: resolvedKanbanProperty,
185
+ onKanbanPropertyChange
186
+ }),
187
+ /* @__PURE__ */ jsx("div", {
188
+ className: "flex-1 overflow-hidden",
189
+ children: renderView()
190
+ })
191
+ ]
192
+ });
193
+ }
194
+ //#endregion
195
+ export { CollectionView };
196
+
197
+ //# sourceMappingURL=CollectionView.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CollectionView.es.js","names":[],"sources":["../../../src/views/CollectionView/CollectionView.tsx"],"sourcesContent":["import React, { useCallback, useMemo, useState } from \"react\";\nimport { cls } from \"../../util\";\nimport { Typography } from \"../../components/Typography\";\nimport { CollectionViewToolbar } from \"./CollectionViewToolbar\";\nimport { CollectionTableView } from \"./CollectionTableView\";\nimport { CollectionCardView } from \"./CollectionCardView\";\nimport { CollectionListView } from \"./CollectionListView\";\nimport { CollectionKanbanView } from \"./CollectionKanbanView\";\nimport type {\n CollectionViewMode,\n CollectionViewSize,\n CollectionPropertyConfig,\n CollectionDataController,\n CellRendererOverride,\n CollectionCustomViewEntry,\n CollectionSelectionController,\n KanbanPropertyOption\n} from \"./CollectionViewTypes\";\n\n/**\n * Props for the headless {@link CollectionView} component.\n *\n * This component is data-agnostic — it renders collection data based on\n * property configurations and callbacks, with ZERO coupling to any entity\n * or data layer.\n *\n * @group Collection View\n */\nexport interface CollectionViewProps<T = Record<string, unknown>> {\n // ── Data ──────────────────────────────────────────────────────────\n\n /**\n * Data controller providing rows, loading state, pagination,\n * filter/sort/search state and setters.\n */\n dataController: CollectionDataController<T>;\n\n /**\n * Property definitions keyed by property key.\n * Drives column rendering, cell formatting, and filter UI.\n */\n properties: Record<string, CollectionPropertyConfig>;\n\n /**\n * Display order of properties. If omitted, uses Object.keys(properties).\n */\n propertiesOrder?: string[];\n\n /**\n * Subset of property keys to actually display as columns.\n * If omitted, all non-hidden properties are shown.\n */\n displayedColumnIds?: string[];\n\n // ── Identity ──────────────────────────────────────────────────────\n\n /** Property key used as row identifier. Default: \"id\" */\n idProperty?: string;\n\n /** Property key used as the display title in card/list views */\n titleProperty?: string;\n\n // ── View configuration ────────────────────────────────────────────\n\n /** Title displayed above the collection. `false` to hide. */\n title?: string | false;\n\n /** Controlled view mode. If omitted, internal state is used. */\n viewMode?: CollectionViewMode;\n\n /** Default view mode when uncontrolled. Default: \"list\" */\n defaultViewMode?: CollectionViewMode;\n\n /** Which view modes are available in the toggle */\n enabledViews?: CollectionViewMode[];\n\n /** Callback when view mode changes */\n onViewModeChange?: (mode: CollectionViewMode) => void;\n\n // ── Size ──────────────────────────────────────────────────────────\n\n /** Controlled size. If omitted, internal state is used. */\n size?: CollectionViewSize;\n\n /** Default size. Default: \"m\" */\n defaultSize?: CollectionViewSize;\n\n /** Callback when size changes */\n onSizeChange?: (size: CollectionViewSize) => void;\n\n // ── Kanban ─────────────────────────────────────────────────────────\n\n /** Property key for kanban column grouping */\n kanbanProperty?: string;\n\n /** Available kanban property options (shown in view toggle) */\n kanbanPropertyOptions?: KanbanPropertyOption[];\n\n /** Callback when kanban column property changes */\n onKanbanPropertyChange?: (property: string) => void;\n\n // ── Interactions ──────────────────────────────────────────────────\n\n /** Called when a row is clicked */\n onRowClick?: (row: T) => void;\n\n /** Called when the \"create\" button is clicked */\n onRowCreate?: (defaults?: Record<string, unknown>) => void;\n\n /** Whether creation is allowed (shows the + button) */\n canCreate?: boolean;\n\n // ── Selection ─────────────────────────────────────────────────────\n\n /** Enable row selection checkboxes */\n selectionEnabled?: boolean;\n\n /** Selection controller */\n selectionController?: CollectionSelectionController<T>;\n\n /** Highlighted items (e.g. recently clicked) */\n highlightedItems?: T[];\n\n /** Callback for bulk delete */\n onMultipleDelete?: (rows: T[]) => void;\n\n // ── Custom rendering ──────────────────────────────────────────────\n\n /**\n * Custom cell renderer override. Return undefined to use default.\n * This is how the connected wrapper injects PropertyPreview.\n */\n cellRenderer?: CellRendererOverride<T>;\n\n /** Custom empty state component */\n emptyComponent?: React.ReactNode;\n\n /**\n * Additional ways to render these rows, keyed by view mode. A key here can\n * be named by `viewMode`, `defaultViewMode` and `enabledViews`, and gets\n * its own entry in the view switcher.\n *\n * A custom view is another rendering of the *same* `dataController`, so it\n * inherits the toolbar's search, the loading state and selection. A\n * component that fetches its own data does not want to be a view mode —\n * the toolbar above it would be describing a query it does not render.\n *\n * @example\n * ```tsx\n * <CollectionView\n * customViews={{ map: { name: \"Map\", Component: MapView } }}\n * enabledViews={[\"table\", \"map\"]}\n * defaultViewMode=\"map\"\n * />\n * ```\n */\n customViews?: Record<string, CollectionCustomViewEntry<T>>;\n\n // ── Toolbar ───────────────────────────────────────────────────────\n\n /** Actions rendered at the start (left) of the toolbar */\n toolbarActionsStart?: React.ReactNode;\n\n /** Actions rendered at the end (right) of the toolbar */\n toolbarActionsEnd?: React.ReactNode;\n\n /** Hide the built-in toolbar entirely */\n hideToolbar?: boolean;\n\n // ── Table-specific ────────────────────────────────────────────────\n\n /** Enable row hover highlight in table view */\n hoverRow?: boolean;\n\n /** Callback when table columns are resized */\n onColumnResize?: (params: { key: string; width: number }) => void;\n\n /** Callback when table columns are reordered */\n onColumnsOrderChange?: (keys: string[]) => void;\n\n // ── Style ─────────────────────────────────────────────────────────\n\n /** CSS class for the outer container */\n className?: string;\n}\n\nconst DEFAULT_ENABLED_VIEWS: CollectionViewMode[] = [\"list\", \"table\", \"cards\", \"kanban\"];\n\n/**\n * A data-agnostic collection view component that orchestrates table, card,\n * list, and kanban views — driven entirely by props.\n *\n * Zero imports from any entity or data layer. This component lives in\n * `@rebasepro/ui` and can be used with any data source.\n *\n * @example\n * ```tsx\n * import { CollectionView } from \"@rebasepro/ui\";\n *\n * <CollectionView\n * dataController={{ data: myRows, loading: false, noMoreToLoad: true }}\n * properties={{\n * name: { type: \"string\", name: \"Name\" },\n * email: { type: \"string\", name: \"Email\" },\n * }}\n * title=\"Contacts\"\n * onRowClick={(row) => openDetail(row.id)}\n * />\n * ```\n *\n * @group Collection View\n */\nexport function CollectionView<T extends Record<string, unknown> = Record<string, unknown>>({\n dataController,\n properties,\n propertiesOrder,\n displayedColumnIds,\n idProperty = \"id\",\n titleProperty,\n title,\n viewMode: viewModeProp,\n defaultViewMode = \"list\",\n enabledViews = DEFAULT_ENABLED_VIEWS,\n onViewModeChange: onViewModeChangeProp,\n size: sizeProp,\n defaultSize = \"m\",\n onSizeChange: onSizeChangeProp,\n kanbanProperty: kanbanPropertyProp,\n kanbanPropertyOptions,\n onKanbanPropertyChange: onKanbanPropertyChangeProp,\n onRowClick,\n onRowCreate,\n canCreate = true,\n selectionEnabled,\n selectionController,\n highlightedItems,\n onMultipleDelete,\n cellRenderer,\n emptyComponent,\n customViews,\n toolbarActionsStart,\n toolbarActionsEnd,\n hideToolbar,\n hoverRow = true,\n onColumnResize,\n onColumnsOrderChange,\n className\n}: CollectionViewProps<T>) {\n\n // ── View mode state (uncontrolled with controlled override) ───────\n\n const [internalViewMode, setInternalViewMode] = useState<CollectionViewMode>(defaultViewMode);\n const viewMode = viewModeProp ?? internalViewMode;\n\n const onViewModeChange = useCallback((mode: CollectionViewMode) => {\n setInternalViewMode(mode);\n onViewModeChangeProp?.(mode);\n }, [onViewModeChangeProp]);\n\n // ── Size state (uncontrolled with controlled override) ────────────\n\n const [internalSize, setInternalSize] = useState<CollectionViewSize>(defaultSize);\n const size = sizeProp ?? internalSize;\n\n const onSizeChange = useCallback((s: CollectionViewSize) => {\n setInternalSize(s);\n onSizeChangeProp?.(s);\n }, [onSizeChangeProp]);\n\n // ── Kanban property state ─────────────────────────────────────────\n\n const [internalKanbanProperty, setInternalKanbanProperty] = useState<string | undefined>(kanbanPropertyProp);\n const kanbanProperty = kanbanPropertyProp ?? internalKanbanProperty;\n\n const onKanbanPropertyChange = useCallback((property: string) => {\n setInternalKanbanProperty(property);\n onKanbanPropertyChangeProp?.(property);\n }, [onKanbanPropertyChangeProp]);\n\n // Auto-detect kanban property if not provided\n const resolvedKanbanProperty = useMemo(() => {\n if (kanbanProperty) return kanbanProperty;\n // Find first enum property\n const order = propertiesOrder ?? Object.keys(properties);\n for (const key of order) {\n const prop = properties[key];\n if (prop && prop.type === \"string\" && prop.enum) {\n return key;\n }\n }\n return undefined;\n }, [kanbanProperty, properties, propertiesOrder]);\n\n // Build kanban property options from properties if not provided\n const resolvedKanbanPropertyOptions = useMemo((): KanbanPropertyOption[] => {\n if (kanbanPropertyOptions) return kanbanPropertyOptions;\n const options: KanbanPropertyOption[] = [];\n const order = propertiesOrder ?? Object.keys(properties);\n for (const key of order) {\n const prop = properties[key];\n if (prop && prop.type === \"string\" && prop.enum) {\n options.push({ key, label: prop.name });\n }\n }\n return options;\n }, [kanbanPropertyOptions, properties, propertiesOrder]);\n\n // ── Render ────────────────────────────────────────────────────────\n\n const sharedProps = {\n dataController,\n properties,\n propertiesOrder,\n idProperty,\n titleProperty,\n onRowClick,\n cellRenderer,\n selectionEnabled,\n selectionController,\n highlightedItems,\n emptyComponent,\n };\n\n // Resolved once so both the switcher and the render path agree. A custom\n // view keyed as a built-in is ignored rather than allowed to shadow it —\n // the switch below checks the built-ins first, so honouring such a key\n // here would put the switcher and the canvas out of step.\n const customViewEntries = useMemo(() => {\n if (!customViews) return [];\n return Object.entries(customViews)\n .filter(([key]) => !DEFAULT_ENABLED_VIEWS.includes(key))\n .map(([key, entry]) => {\n const isBare = typeof entry === \"function\";\n return {\n key,\n name: isBare ? key : (entry.name ?? key),\n icon: isBare ? undefined : entry.icon,\n Component: isBare ? entry : entry.Component\n };\n });\n }, [customViews]);\n\n function renderView() {\n const custom = customViewEntries.find((entry) => entry.key === viewMode);\n if (custom) {\n const { Component } = custom;\n return (\n <Component\n {...sharedProps}\n onRowCreate={canCreate ? onRowCreate : undefined}\n canCreate={canCreate}\n size={size}\n />\n );\n }\n\n switch (viewMode) {\n case \"table\":\n return (\n <CollectionTableView<T>\n {...sharedProps}\n displayedColumnIds={displayedColumnIds}\n size={size}\n hoverRow={hoverRow}\n onColumnResize={onColumnResize}\n onColumnsOrderChange={onColumnsOrderChange}\n />\n );\n case \"cards\":\n return (\n <CollectionCardView<T>\n {...sharedProps}\n size={size}\n />\n );\n case \"kanban\":\n return resolvedKanbanProperty ? (\n <CollectionKanbanView<T>\n {...sharedProps}\n kanbanProperty={resolvedKanbanProperty}\n // `canCreate` is documented as \"whether creation is\n // allowed (shows the + button)\" and was destructured\n // and read nowhere, so the button showed regardless.\n // The kanban view draws its add affordance exactly when\n // it has a callback, so withholding the callback is\n // what the prop means.\n onRowCreate={canCreate ? onRowCreate : undefined}\n />\n ) : (\n <div className=\"flex items-center justify-center p-8\">\n <Typography variant=\"body2\" color=\"secondary\">\n No enum property available for kanban grouping.\n </Typography>\n </div>\n );\n case \"list\":\n default:\n return (\n <CollectionListView<T>\n {...sharedProps}\n size={size}\n />\n );\n }\n }\n\n return (\n <div\n className={cls(\n \"overflow-hidden h-full w-full rounded-md flex flex-col\",\n \"bg-surface-sheet\",\n className\n )}\n >\n {/* Error banner */}\n {dataController.error && dataController.data.length > 0 && (\n <div className=\"flex items-center gap-4 px-4 py-2 bg-red-50 dark:bg-red-900/20 border-b border-red-200 dark:border-red-800 flex-shrink-0\">\n <Typography variant=\"body2\" className=\"text-red-700 dark:text-red-300 flex-1\">\n <strong>Warning:</strong> {dataController.error.message || \"Failed to update data.\"}\n </Typography>\n </div>\n )}\n\n {/* Toolbar */}\n {!hideToolbar && (\n <CollectionViewToolbar\n viewMode={viewMode}\n onViewModeChange={enabledViews.length > 1 ? onViewModeChange : undefined}\n enabledViews={enabledViews}\n customViews={customViewEntries}\n size={size}\n onSizeChange={onSizeChange}\n searchString={dataController.searchString}\n onSearchChange={dataController.setSearchString}\n loading={dataController.loading}\n actionsStart={toolbarActionsStart}\n actionsEnd={toolbarActionsEnd}\n kanbanPropertyOptions={viewMode === \"kanban\" ? resolvedKanbanPropertyOptions : undefined}\n selectedKanbanProperty={resolvedKanbanProperty}\n onKanbanPropertyChange={onKanbanPropertyChange}\n />\n )}\n\n {/* View */}\n <div className=\"flex-1 overflow-hidden\">\n {renderView()}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,155 @@
1
+ "use client";
2
+ import { cls } from "../../util/cls.es.js";
3
+ import { iconSize } from "../../icons/Icon.es.js";
4
+ import { Typography } from "../../components/Typography.es.js";
5
+ import { IconButton } from "../../components/IconButton.es.js";
6
+ import { SearchBar } from "../../components/SearchBar.es.js";
7
+ import { Select, SelectItem } from "../../components/Select.es.js";
8
+ import { Popover } from "../../components/Popover.es.js";
9
+ import { ToggleButtonGroup } from "../../components/ToggleButtonGroup.es.js";
10
+ import { useCallback, useId, useState } from "react";
11
+ import { jsx, jsxs } from "react/jsx-runtime";
12
+ import { Kanban, LayoutGrid, LayoutList, Settings2, Table2 } from "lucide-react";
13
+ //#region src/views/CollectionView/CollectionViewToolbar.tsx
14
+ var VIEW_MODE_ICONS = {
15
+ list: /* @__PURE__ */ jsx(LayoutList, { size: iconSize.small }),
16
+ table: /* @__PURE__ */ jsx(Table2, { size: iconSize.small }),
17
+ cards: /* @__PURE__ */ jsx(LayoutGrid, { size: iconSize.small }),
18
+ kanban: /* @__PURE__ */ jsx(Kanban, { size: iconSize.small })
19
+ };
20
+ var VIEW_MODE_LABELS = {
21
+ list: "List",
22
+ table: "Table",
23
+ cards: "Cards",
24
+ kanban: "Board"
25
+ };
26
+ var SIZE_LABELS = {
27
+ xs: "Extra Small",
28
+ s: "Small",
29
+ m: "Medium",
30
+ l: "Large",
31
+ xl: "Extra Large"
32
+ };
33
+ /**
34
+ * Toolbar for the headless CollectionView.
35
+ *
36
+ * Renders a search bar, view mode toggle (popover with toggle button group),
37
+ * optional size selector, optional kanban property selector, and action slots.
38
+ *
39
+ * @group Collection View
40
+ */
41
+ function CollectionViewToolbar({ viewMode, onViewModeChange, enabledViews = [
42
+ "list",
43
+ "table",
44
+ "cards",
45
+ "kanban"
46
+ ], customViews, size = "m", onSizeChange, searchString, onSearchChange, loading, actionsStart, actionsEnd, compact, kanbanPropertyOptions, selectedKanbanProperty, onKanbanPropertyChange }) {
47
+ const [popoverOpen, setPopoverOpen] = useState(false);
48
+ const viewLabelId = useId();
49
+ const viewOptions = enabledViews.map((mode) => {
50
+ const custom = customViews?.find((entry) => entry.key === mode);
51
+ return {
52
+ value: mode,
53
+ label: VIEW_MODE_LABELS[mode] ?? custom?.name ?? mode,
54
+ icon: VIEW_MODE_ICONS[mode] ?? custom?.icon ?? /* @__PURE__ */ jsx(LayoutList, { size: iconSize.small })
55
+ };
56
+ });
57
+ const handleViewModeChange = useCallback((mode) => {
58
+ onViewModeChange?.(mode);
59
+ }, [onViewModeChange]);
60
+ const handleSizeChange = useCallback((value) => {
61
+ onSizeChange?.(value);
62
+ }, [onSizeChange]);
63
+ const handleKanbanPropertyChange = useCallback((value) => {
64
+ onKanbanPropertyChange?.(value);
65
+ }, [onKanbanPropertyChange]);
66
+ const showViewToggle = onViewModeChange && enabledViews.length > 1;
67
+ const showSizeSelector = onSizeChange && viewMode !== "kanban";
68
+ const showKanbanSelector = kanbanPropertyOptions && kanbanPropertyOptions.length > 0 && viewMode === "kanban" && onKanbanPropertyChange;
69
+ const showPopover = showViewToggle || showSizeSelector || showKanbanSelector;
70
+ return /* @__PURE__ */ jsxs("div", {
71
+ className: cls("flex items-center gap-2 px-2 flex-shrink-0", "border-b border-hairline", compact ? "py-0.5 gap-1" : "py-1"),
72
+ children: [
73
+ actionsStart,
74
+ onSearchChange && /* @__PURE__ */ jsx(SearchBar, {
75
+ onTextSearch: onSearchChange,
76
+ size: "small",
77
+ initialValue: searchString,
78
+ loading,
79
+ expandable: true
80
+ }),
81
+ /* @__PURE__ */ jsx("div", { className: "flex-1" }),
82
+ actionsEnd,
83
+ showPopover && /* @__PURE__ */ jsx(Popover, {
84
+ open: popoverOpen,
85
+ onOpenChange: setPopoverOpen,
86
+ side: "bottom",
87
+ align: "end",
88
+ trigger: /* @__PURE__ */ jsx(IconButton, {
89
+ "aria-label": "View settings",
90
+ size: "small",
91
+ onClick: () => setPopoverOpen((prev) => !prev),
92
+ children: /* @__PURE__ */ jsx(Settings2, { size: iconSize.small })
93
+ }),
94
+ children: /* @__PURE__ */ jsxs("div", {
95
+ className: "p-3 min-w-[200px] flex flex-col gap-3",
96
+ children: [
97
+ showViewToggle && /* @__PURE__ */ jsxs("div", {
98
+ className: "flex flex-col gap-1",
99
+ children: [/* @__PURE__ */ jsx(Typography, {
100
+ id: viewLabelId,
101
+ variant: "caption",
102
+ color: "secondary",
103
+ className: "font-medium uppercase tracking-wider",
104
+ children: "View"
105
+ }), /* @__PURE__ */ jsx(ToggleButtonGroup, {
106
+ "aria-labelledby": viewLabelId,
107
+ value: viewMode,
108
+ onValueChange: handleViewModeChange,
109
+ options: viewOptions
110
+ })]
111
+ }),
112
+ showSizeSelector && /* @__PURE__ */ jsxs("div", {
113
+ className: "flex flex-col gap-1",
114
+ children: [/* @__PURE__ */ jsx(Typography, {
115
+ variant: "caption",
116
+ color: "secondary",
117
+ className: "font-medium uppercase tracking-wider",
118
+ children: "Size"
119
+ }), /* @__PURE__ */ jsx(Select, {
120
+ value: size,
121
+ onValueChange: handleSizeChange,
122
+ size: "small",
123
+ children: Object.keys(SIZE_LABELS).map((sizeKey) => /* @__PURE__ */ jsx(SelectItem, {
124
+ value: sizeKey,
125
+ children: SIZE_LABELS[sizeKey]
126
+ }, sizeKey))
127
+ })]
128
+ }),
129
+ showKanbanSelector && /* @__PURE__ */ jsxs("div", {
130
+ className: "flex flex-col gap-1",
131
+ children: [/* @__PURE__ */ jsx(Typography, {
132
+ variant: "caption",
133
+ color: "secondary",
134
+ className: "font-medium uppercase tracking-wider",
135
+ children: "Group by"
136
+ }), /* @__PURE__ */ jsx(Select, {
137
+ value: selectedKanbanProperty,
138
+ onValueChange: handleKanbanPropertyChange,
139
+ size: "small",
140
+ children: kanbanPropertyOptions.map((option) => /* @__PURE__ */ jsx(SelectItem, {
141
+ value: option.key,
142
+ children: option.label
143
+ }, option.key))
144
+ })]
145
+ })
146
+ ]
147
+ })
148
+ })
149
+ ]
150
+ });
151
+ }
152
+ //#endregion
153
+ export { CollectionViewToolbar };
154
+
155
+ //# sourceMappingURL=CollectionViewToolbar.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CollectionViewToolbar.es.js","names":[],"sources":["../../../src/views/CollectionView/CollectionViewToolbar.tsx"],"sourcesContent":["\"use client\";\nimport React, { useCallback, useId, useState } from \"react\";\nimport { LayoutList, Table2, LayoutGrid, Kanban, Settings2 } from \"lucide-react\";\nimport { iconSize } from \"../../icons/Icon\";\nimport { SearchBar } from \"../../components/SearchBar\";\nimport { Popover } from \"../../components/Popover\";\nimport { ToggleButtonGroup } from \"../../components/ToggleButtonGroup\";\nimport type { ToggleButtonOption } from \"../../components/ToggleButtonGroup\";\nimport { Typography } from \"../../components/Typography\";\nimport { Select, SelectItem } from \"../../components/Select\";\nimport { IconButton } from \"../../components/IconButton\";\nimport { cls } from \"../../util\";\nimport type { CollectionViewMode, CollectionViewSize, KanbanPropertyOption } from \"./CollectionViewTypes\";\n\n/**\n * Props for the {@link CollectionViewToolbar} component.\n * @group Collection View\n */\nexport type CollectionViewToolbarProps = {\n viewMode: CollectionViewMode;\n onViewModeChange?: (mode: CollectionViewMode) => void;\n enabledViews?: CollectionViewMode[];\n /**\n * Custom view modes, supplying the label and icon for their switcher\n * entries. Without these an `enabledViews` key outside the four built-ins\n * has no label and no icon to draw.\n */\n customViews?: { key: string, name: string, icon?: React.ReactNode }[];\n size?: CollectionViewSize;\n onSizeChange?: (size: CollectionViewSize) => void;\n searchString?: string;\n onSearchChange?: (search?: string) => void;\n loading?: boolean;\n actionsStart?: React.ReactNode;\n actionsEnd?: React.ReactNode;\n compact?: boolean;\n kanbanPropertyOptions?: KanbanPropertyOption[];\n selectedKanbanProperty?: string;\n onKanbanPropertyChange?: (property: string) => void;\n};\n\nconst VIEW_MODE_ICONS: Record<string, React.ReactNode> = {\n list: <LayoutList size={iconSize.small} />,\n table: <Table2 size={iconSize.small} />,\n cards: <LayoutGrid size={iconSize.small} />,\n kanban: <Kanban size={iconSize.small} />,\n};\n\nconst VIEW_MODE_LABELS: Record<string, string> = {\n list: \"List\",\n table: \"Table\",\n cards: \"Cards\",\n kanban: \"Board\",\n};\n\nconst SIZE_LABELS: Record<CollectionViewSize, string> = {\n xs: \"Extra Small\",\n s: \"Small\",\n m: \"Medium\",\n l: \"Large\",\n xl: \"Extra Large\",\n};\n\n/**\n * Toolbar for the headless CollectionView.\n *\n * Renders a search bar, view mode toggle (popover with toggle button group),\n * optional size selector, optional kanban property selector, and action slots.\n *\n * @group Collection View\n */\nexport function CollectionViewToolbar({\n viewMode,\n onViewModeChange,\n enabledViews = [\"list\", \"table\", \"cards\", \"kanban\"],\n customViews,\n size = \"m\",\n onSizeChange,\n searchString,\n onSearchChange,\n loading,\n actionsStart,\n actionsEnd,\n compact,\n kanbanPropertyOptions,\n selectedKanbanProperty,\n onKanbanPropertyChange,\n}: CollectionViewToolbarProps) {\n const [popoverOpen, setPopoverOpen] = useState(false);\n const viewLabelId = useId();\n\n // A key outside the four built-ins takes its label and icon from the\n // custom view that declared it; without one it falls back to the key\n // itself and the list glyph, so an entry is never blank.\n const viewOptions: ToggleButtonOption<CollectionViewMode>[] = enabledViews.map((mode) => {\n const custom = customViews?.find((entry) => entry.key === mode);\n return {\n value: mode,\n label: VIEW_MODE_LABELS[mode] ?? custom?.name ?? mode,\n icon: VIEW_MODE_ICONS[mode] ?? custom?.icon ?? <LayoutList size={iconSize.small} />,\n };\n });\n\n const handleViewModeChange = useCallback(\n (mode: CollectionViewMode) => {\n onViewModeChange?.(mode);\n },\n [onViewModeChange]\n );\n\n const handleSizeChange = useCallback(\n (value: string) => {\n onSizeChange?.(value as CollectionViewSize);\n },\n [onSizeChange]\n );\n\n const handleKanbanPropertyChange = useCallback(\n (value: string) => {\n onKanbanPropertyChange?.(value);\n },\n [onKanbanPropertyChange]\n );\n\n const showViewToggle = onViewModeChange && enabledViews.length > 1;\n const showSizeSelector = onSizeChange && viewMode !== \"kanban\";\n const showKanbanSelector =\n kanbanPropertyOptions &&\n kanbanPropertyOptions.length > 0 &&\n viewMode === \"kanban\" &&\n onKanbanPropertyChange;\n const showPopover = showViewToggle || showSizeSelector || showKanbanSelector;\n\n return (\n <div\n className={cls(\n \"flex items-center gap-2 px-2 flex-shrink-0\",\n \"border-b border-hairline\",\n compact ? \"py-0.5 gap-1\" : \"py-1\"\n )}\n >\n {actionsStart}\n\n {onSearchChange && (\n <SearchBar\n onTextSearch={onSearchChange}\n size=\"small\"\n initialValue={searchString}\n loading={loading}\n expandable\n />\n )}\n\n <div className=\"flex-1\" />\n\n {actionsEnd}\n\n {showPopover && (\n <Popover\n open={popoverOpen}\n onOpenChange={setPopoverOpen}\n side=\"bottom\"\n align=\"end\"\n trigger={\n <IconButton\n aria-label=\"View settings\"\n size=\"small\"\n onClick={() => setPopoverOpen((prev) => !prev)}\n >\n <Settings2 size={iconSize.small} />\n </IconButton>\n }\n >\n <div className=\"p-3 min-w-[200px] flex flex-col gap-3\">\n {showViewToggle && (\n <div className=\"flex flex-col gap-1\">\n <Typography\n id={viewLabelId}\n variant=\"caption\"\n color=\"secondary\"\n className=\"font-medium uppercase tracking-wider\"\n >\n View\n </Typography>\n <ToggleButtonGroup<CollectionViewMode>\n aria-labelledby={viewLabelId}\n value={viewMode}\n onValueChange={handleViewModeChange}\n options={viewOptions}\n />\n </div>\n )}\n\n {showSizeSelector && (\n <div className=\"flex flex-col gap-1\">\n <Typography\n variant=\"caption\"\n color=\"secondary\"\n className=\"font-medium uppercase tracking-wider\"\n >\n Size\n </Typography>\n <Select\n value={size}\n onValueChange={handleSizeChange}\n size=\"small\"\n >\n {(Object.keys(SIZE_LABELS) as CollectionViewSize[]).map(\n (sizeKey) => (\n <SelectItem key={sizeKey} value={sizeKey}>\n {SIZE_LABELS[sizeKey]}\n </SelectItem>\n )\n )}\n </Select>\n </div>\n )}\n\n {showKanbanSelector && (\n <div className=\"flex flex-col gap-1\">\n <Typography\n variant=\"caption\"\n color=\"secondary\"\n className=\"font-medium uppercase tracking-wider\"\n >\n Group by\n </Typography>\n <Select\n value={selectedKanbanProperty}\n onValueChange={handleKanbanPropertyChange}\n size=\"small\"\n >\n {kanbanPropertyOptions.map((option) => (\n <SelectItem key={option.key} value={option.key}>\n {option.label}\n </SelectItem>\n ))}\n </Select>\n </div>\n )}\n </div>\n </Popover>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAyCA,IAAM,kBAAmD;CACrD,MAAM,oBAAC,YAAD,EAAY,MAAM,SAAS,MAAQ,CAAA;CACzC,OAAO,oBAAC,QAAD,EAAQ,MAAM,SAAS,MAAQ,CAAA;CACtC,OAAO,oBAAC,YAAD,EAAY,MAAM,SAAS,MAAQ,CAAA;CAC1C,QAAQ,oBAAC,QAAD,EAAQ,MAAM,SAAS,MAAQ,CAAA;AAC3C;AAEA,IAAM,mBAA2C;CAC7C,MAAM;CACN,OAAO;CACP,OAAO;CACP,QAAQ;AACZ;AAEA,IAAM,cAAkD;CACpD,IAAI;CACJ,GAAG;CACH,GAAG;CACH,GAAG;CACH,IAAI;AACR;;;;;;;;;AAUA,SAAgB,sBAAsB,EAClC,UACA,kBACA,eAAe;CAAC;CAAQ;CAAS;CAAS;AAAQ,GAClD,aACA,OAAO,KACP,cACA,cACA,gBACA,SACA,cACA,YACA,SACA,uBACA,wBACA,0BAC2B;CAC3B,MAAM,CAAC,aAAa,kBAAkB,SAAS,KAAK;CACpD,MAAM,cAAc,MAAM;CAK1B,MAAM,cAAwD,aAAa,KAAK,SAAS;EACrF,MAAM,SAAS,aAAa,MAAM,UAAU,MAAM,QAAQ,IAAI;EAC9D,OAAO;GACH,OAAO;GACP,OAAO,iBAAiB,SAAS,QAAQ,QAAQ;GACjD,MAAM,gBAAgB,SAAS,QAAQ,QAAQ,oBAAC,YAAD,EAAY,MAAM,SAAS,MAAQ,CAAA;EACtF;CACJ,CAAC;CAED,MAAM,uBAAuB,aACxB,SAA6B;EAC1B,mBAAmB,IAAI;CAC3B,GACA,CAAC,gBAAgB,CACrB;CAEA,MAAM,mBAAmB,aACpB,UAAkB;EACf,eAAe,KAA2B;CAC9C,GACA,CAAC,YAAY,CACjB;CAEA,MAAM,6BAA6B,aAC9B,UAAkB;EACf,yBAAyB,KAAK;CAClC,GACA,CAAC,sBAAsB,CAC3B;CAEA,MAAM,iBAAiB,oBAAoB,aAAa,SAAS;CACjE,MAAM,mBAAmB,gBAAgB,aAAa;CACtD,MAAM,qBACF,yBACA,sBAAsB,SAAS,KAC/B,aAAa,YACb;CACJ,MAAM,cAAc,kBAAkB,oBAAoB;CAE1D,OACI,qBAAC,OAAD;EACI,WAAW,IACP,8CACA,4BACA,UAAU,iBAAiB,MAC/B;YALJ;GAOK;GAEA,kBACG,oBAAC,WAAD;IACI,cAAc;IACd,MAAK;IACL,cAAc;IACL;IACT,YAAA;GACH,CAAA;GAGL,oBAAC,OAAD,EAAK,WAAU,SAAU,CAAA;GAExB;GAEA,eACG,oBAAC,SAAD;IACI,MAAM;IACN,cAAc;IACd,MAAK;IACL,OAAM;IACN,SACI,oBAAC,YAAD;KACI,cAAW;KACX,MAAK;KACL,eAAe,gBAAgB,SAAS,CAAC,IAAI;eAE7C,oBAAC,WAAD,EAAW,MAAM,SAAS,MAAQ,CAAA;IAC1B,CAAA;cAGhB,qBAAC,OAAD;KAAK,WAAU;eAAf;MACK,kBACG,qBAAC,OAAD;OAAK,WAAU;iBAAf,CACI,oBAAC,YAAD;QACI,IAAI;QACJ,SAAQ;QACR,OAAM;QACN,WAAU;kBACb;OAEW,CAAA,GACZ,oBAAC,mBAAD;QACI,mBAAiB;QACjB,OAAO;QACP,eAAe;QACf,SAAS;OACZ,CAAA,CACA;;MAGR,oBACG,qBAAC,OAAD;OAAK,WAAU;iBAAf,CACI,oBAAC,YAAD;QACI,SAAQ;QACR,OAAM;QACN,WAAU;kBACb;OAEW,CAAA,GACZ,oBAAC,QAAD;QACI,OAAO;QACP,eAAe;QACf,MAAK;kBAEH,OAAO,KAAK,WAAW,CAAC,CAA0B,KAC/C,YACG,oBAAC,YAAD;SAA0B,OAAO;mBAC5B,YAAY;QACL,GAFK,OAEL,CAEpB;OACI,CAAA,CACP;;MAGR,sBACG,qBAAC,OAAD;OAAK,WAAU;iBAAf,CACI,oBAAC,YAAD;QACI,SAAQ;QACR,OAAM;QACN,WAAU;kBACb;OAEW,CAAA,GACZ,oBAAC,QAAD;QACI,OAAO;QACP,eAAe;QACf,MAAK;kBAEJ,sBAAsB,KAAK,WACxB,oBAAC,YAAD;SAA6B,OAAO,OAAO;mBACtC,OAAO;QACA,GAFK,OAAO,GAEZ,CACf;OACG,CAAA,CACP;;KAER;;GACA,CAAA;EAEZ;;AAEb"}