@monolith-forensics/monolith-ui 1.8.0 → 1.8.1-dev.1

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 (120) hide show
  1. package/dist/Button/Button.js +9 -58
  2. package/dist/Calendar/Calendar.d.ts +3 -1
  3. package/dist/Calendar/Calendar.js +134 -33
  4. package/dist/Calendar/CalendarStyles.d.ts +3 -0
  5. package/dist/Calendar/CalendarStyles.js +92 -14
  6. package/dist/Calendar/calendarHelpers.d.ts +5 -1
  7. package/dist/Calendar/calendarHelpers.js +13 -5
  8. package/dist/Charts/BarChart/BarChart.d.ts +5 -0
  9. package/dist/Charts/BarChart/BarChart.js +549 -0
  10. package/dist/Charts/BarChart/BarChart.lib.d.ts +31 -0
  11. package/dist/Charts/BarChart/BarChart.lib.js +136 -0
  12. package/dist/Charts/BarChart/BarChart.styled.d.ts +51 -0
  13. package/dist/Charts/BarChart/BarChart.styled.js +115 -0
  14. package/dist/Charts/BarChart/BarChart.types.d.ts +171 -0
  15. package/dist/Charts/BarChart/BarChart.types.js +1 -0
  16. package/dist/Charts/BarChart/index.d.ts +3 -0
  17. package/dist/Charts/BarChart/index.js +2 -0
  18. package/dist/Charts/ChartPrimitives/ChartExportControl.d.ts +11 -0
  19. package/dist/Charts/ChartPrimitives/ChartExportControl.js +29 -0
  20. package/dist/Charts/ChartPrimitives/chartActions.styled.d.ts +1 -0
  21. package/dist/Charts/ChartPrimitives/chartActions.styled.js +8 -0
  22. package/dist/Charts/ChartPrimitives/chartLegend.styled.d.ts +12 -0
  23. package/dist/Charts/ChartPrimitives/chartLegend.styled.js +52 -0
  24. package/dist/Charts/ChartPrimitives/chartTooltip.styled.d.ts +19 -0
  25. package/dist/Charts/ChartPrimitives/chartTooltip.styled.js +61 -0
  26. package/dist/Charts/ChartPrimitives/index.d.ts +4 -0
  27. package/dist/Charts/ChartPrimitives/index.js +4 -0
  28. package/dist/Charts/ChartUtils/chartColors.d.ts +8 -0
  29. package/dist/Charts/ChartUtils/chartColors.js +65 -0
  30. package/dist/Charts/ChartUtils/chartExport.d.ts +47 -0
  31. package/dist/Charts/ChartUtils/chartExport.js +311 -0
  32. package/dist/Charts/ChartUtils/chartMath.d.ts +3 -0
  33. package/dist/Charts/ChartUtils/chartMath.js +3 -0
  34. package/dist/Charts/ChartUtils/index.d.ts +3 -0
  35. package/dist/Charts/ChartUtils/index.js +3 -0
  36. package/dist/Charts/HeatMap/HeatMap.d.ts +5 -0
  37. package/dist/Charts/HeatMap/HeatMap.js +212 -0
  38. package/dist/Charts/HeatMap/HeatMap.lib.d.ts +30 -0
  39. package/dist/Charts/HeatMap/HeatMap.lib.js +115 -0
  40. package/dist/Charts/HeatMap/HeatMap.styled.d.ts +37 -0
  41. package/dist/Charts/HeatMap/HeatMap.styled.js +91 -0
  42. package/dist/Charts/HeatMap/HeatMap.types.d.ts +80 -0
  43. package/dist/Charts/HeatMap/HeatMap.types.js +1 -0
  44. package/dist/Charts/HeatMap/index.d.ts +3 -0
  45. package/dist/Charts/HeatMap/index.js +2 -0
  46. package/dist/Charts/LineChart/LineChart.d.ts +5 -0
  47. package/dist/Charts/LineChart/LineChart.js +529 -0
  48. package/dist/Charts/LineChart/LineChart.lib.d.ts +24 -0
  49. package/dist/Charts/LineChart/LineChart.lib.js +132 -0
  50. package/dist/Charts/LineChart/LineChart.styled.d.ts +59 -0
  51. package/dist/Charts/LineChart/LineChart.styled.js +147 -0
  52. package/dist/Charts/LineChart/LineChart.types.d.ts +193 -0
  53. package/dist/Charts/LineChart/LineChart.types.js +1 -0
  54. package/dist/Charts/LineChart/index.d.ts +3 -0
  55. package/dist/Charts/LineChart/index.js +2 -0
  56. package/dist/Charts/PieChart/PieChart.d.ts +4 -0
  57. package/dist/Charts/PieChart/PieChart.js +199 -0
  58. package/dist/Charts/PieChart/PieChart.lib.d.ts +5 -0
  59. package/dist/Charts/PieChart/PieChart.lib.js +19 -0
  60. package/dist/Charts/PieChart/PieChart.styled.d.ts +51 -0
  61. package/dist/Charts/PieChart/PieChart.styled.js +163 -0
  62. package/dist/Charts/PieChart/PieChart.types.d.ts +100 -0
  63. package/dist/Charts/PieChart/PieChart.types.js +1 -0
  64. package/dist/Charts/PieChart/index.d.ts +2 -0
  65. package/dist/Charts/PieChart/index.js +1 -0
  66. package/dist/Charts/index.d.ts +5 -0
  67. package/dist/Charts/index.js +4 -0
  68. package/dist/CheckBox/CheckBox.js +2 -16
  69. package/dist/DateInput/DateInput.js +198 -143
  70. package/dist/DropDownMenu/components/MenuComponent.js +2 -1
  71. package/dist/DropDownMenu/components/MenuItem.js +5 -14
  72. package/dist/DropDownMenu/components/MenuItemList.js +7 -24
  73. package/dist/DropDownMenu/components/StyledFloatContainer.js +1 -1
  74. package/dist/FieldLabel/FieldLabel.js +4 -12
  75. package/dist/FileInputField/FileInputField.js +4 -23
  76. package/dist/FormSection/FormSection.js +5 -25
  77. package/dist/IconButton/IconButton.js +2 -16
  78. package/dist/Input/Input.js +7 -56
  79. package/dist/Pill/Pill.js +8 -79
  80. package/dist/Popover/Popover.context.d.ts +2 -1
  81. package/dist/Popover/Popover.js +5 -2
  82. package/dist/Popover/Popover.styles.d.ts +1 -6
  83. package/dist/Popover/Popover.styles.js +11 -28
  84. package/dist/Popover/Popover.transitions.d.ts +4 -2
  85. package/dist/Popover/Popover.transitions.js +23 -49
  86. package/dist/Popover/PopoverDropdown.js +6 -8
  87. package/dist/Popover/PopoverTarget.js +6 -3
  88. package/dist/SegmentedControl/SegmentedControl.utils.d.ts +2 -2
  89. package/dist/SegmentedControl/SegmentedControl.utils.js +3 -30
  90. package/dist/SelectBox/SelectBox.js +5 -5
  91. package/dist/SelectBox/select-box.styled-components.d.ts +4 -1
  92. package/dist/SelectBox/select-box.styled-components.js +11 -48
  93. package/dist/SelectBox/types.d.ts +1 -0
  94. package/dist/Switch/Switch.d.ts +2 -2
  95. package/dist/Switch/Switch.js +18 -83
  96. package/dist/Table/StateStorage.d.ts +4 -0
  97. package/dist/Table/StateStorage.js +13 -0
  98. package/dist/Table/Table.js +160 -12
  99. package/dist/Table/TableComponents.d.ts +10 -0
  100. package/dist/Table/TableComponents.js +57 -0
  101. package/dist/Table/TableDefaults.d.ts +7 -0
  102. package/dist/Table/TableDefaults.js +7 -0
  103. package/dist/Table/TableProvider.js +263 -71
  104. package/dist/Table/TableRow.js +15 -10
  105. package/dist/Table/types.d.ts +64 -0
  106. package/dist/TagBox/TagBox.js +18 -76
  107. package/dist/TextArea/TextArea.js +4 -23
  108. package/dist/TextInput/TextInput.js +12 -6
  109. package/dist/Utilities/parseTimestamp.js +11 -6
  110. package/dist/core/ArrowButton.d.ts +2 -0
  111. package/dist/core/ArrowButton.js +7 -3
  112. package/dist/core/ClearButton.d.ts +2 -0
  113. package/dist/core/ClearButton.js +7 -3
  114. package/dist/core/controlSizes.d.ts +34 -0
  115. package/dist/core/controlSizes.js +190 -0
  116. package/dist/core/index.d.ts +1 -0
  117. package/dist/core/index.js +1 -0
  118. package/dist/index.d.ts +1 -0
  119. package/dist/index.js +1 -0
  120. package/package.json +5 -1
@@ -115,6 +115,55 @@ export const InnerCellContent = styled.div `
115
115
  overflow: hidden;
116
116
  text-overflow: ellipsis;
117
117
  `;
118
+ export const TreeCellWrapper = styled.div `
119
+ display: flex;
120
+ flex-direction: row;
121
+ align-items: center;
122
+ flex: 1 1 auto;
123
+ min-width: 0;
124
+ `;
125
+ export const TreeIndentSpacer = styled.div `
126
+ flex: 0 0 auto;
127
+ width: ${({ $level, $indentPx }) => $level * $indentPx}px;
128
+ `;
129
+ export const TreeChevronButton = styled.button `
130
+ flex: 0 0 auto;
131
+ width: 14px;
132
+ height: 16px;
133
+ padding: 0;
134
+ margin: 0 8px 0 0;
135
+ display: inline-flex;
136
+ align-items: center;
137
+ justify-content: flex-start;
138
+ background: transparent;
139
+ border: none;
140
+ cursor: pointer;
141
+ color: ${({ theme }) => theme.palette.text.secondary};
142
+ transition: transform 120ms ease;
143
+ transform: rotate(${({ $expanded }) => ($expanded ? "90deg" : "0deg")});
144
+
145
+ &:hover {
146
+ color: ${({ theme }) => theme.palette.text.primary};
147
+ }
148
+
149
+ &:focus-visible {
150
+ outline: 1px solid ${({ theme }) => theme.palette.primary.main};
151
+ outline-offset: 1px;
152
+ }
153
+ `;
154
+ export const TreeChevronPlaceholder = styled.div `
155
+ flex: 0 0 auto;
156
+ width: 12px;
157
+ height: 16px;
158
+ margin: 0 10px 0 0;
159
+ `;
160
+ export const TreeCellContent = styled.div `
161
+ flex: 1 1 auto;
162
+ min-width: 0;
163
+ overflow: hidden;
164
+ text-overflow: ellipsis;
165
+ white-space: nowrap;
166
+ `;
118
167
  export const TableViewPort = styled.div `
119
168
  display: flex;
120
169
  flex-direction: column;
@@ -148,6 +197,14 @@ export const StyledTable = styled.div `
148
197
  font-size: ${TableDefaults.td.fontSize.compact}px !important;
149
198
  }
150
199
 
200
+ .mfui-td[data-tree-cell="true"] {
201
+ padding-left: ${TableDefaults.td.padding.normal - 3}px;
202
+ }
203
+
204
+ &[data-compact="true"] .mfui-td[data-tree-cell="true"] {
205
+ padding-left: ${TableDefaults.td.padding.compact - 3}px !important;
206
+ }
207
+
151
208
  .os-scrollbar {
152
209
  pointer-events: auto;
153
210
  visibility: visible;
@@ -21,5 +21,12 @@ declare const TableDefaults: {
21
21
  minWidth: number;
22
22
  maxWidth: number;
23
23
  };
24
+ tree: {
25
+ indentPx: number;
26
+ chevronWidth: number;
27
+ defaultMode: "nested";
28
+ defaultChildrenField: string;
29
+ defaultParentIdField: string;
30
+ };
24
31
  };
25
32
  export default TableDefaults;
@@ -21,5 +21,12 @@ const TableDefaults = {
21
21
  minWidth: 35,
22
22
  maxWidth: 35,
23
23
  },
24
+ tree: {
25
+ indentPx: 16,
26
+ chevronWidth: 16,
27
+ defaultMode: "nested",
28
+ defaultChildrenField: "children",
29
+ defaultParentIdField: "parentId",
30
+ },
24
31
  };
25
32
  export default TableDefaults;
@@ -29,6 +29,27 @@ import { SelectionStatus } from "./enums";
29
29
  import moment from "moment";
30
30
  import { useControlled } from "../Utilities";
31
31
  import { exportTableToExcel } from "./Utils";
32
+ const RESERVED_FIELDS = [
33
+ "__key",
34
+ "__index",
35
+ "__level",
36
+ "__parentKey",
37
+ "__hasChildren",
38
+ "__childKeys",
39
+ ];
40
+ const EMPTY_TREE_META = {
41
+ parentKeyMap: new Map(),
42
+ childrenKeyMap: new Map(),
43
+ expandableKeys: [],
44
+ };
45
+ const DEFAULT_TREE_OPTIONS = {
46
+ enabled: false,
47
+ mode: "nested",
48
+ childrenField: "children",
49
+ parentIdField: "parentId",
50
+ indentPx: 16,
51
+ autoExpandOnMatch: true,
52
+ };
32
53
  const calculateSelectionTotal = (selectionState, totalRecords, dataLength = 0) => {
33
54
  if (!selectionState) {
34
55
  return 0;
@@ -48,11 +69,21 @@ const calculateSelectionTotal = (selectionState, totalRecords, dataLength = 0) =
48
69
  };
49
70
  export const TableContext = createContext(null);
50
71
  const TableProvider = (_a) => {
51
- var { children, columns, data, keyField, tableInstanceRef, stateStorage, tableMenuOptions, manualSorting, manualFiltering, manualSearch, manualExport, height, maxHeight, minHeight, focusedRowId, enableColumnResize, enableSorting, compact, totalRecords, onColumnStateChange, onColumnReorder, onColumnHeaderClick, onSort, onRowUpdated, tableElement, headerRowElm, tableDimensions, targetElm, listElm, defaultSelectionState, selectionState, onSelectionChange, defaultFilterState, filterState, onFilterChange } = _a, props = __rest(_a, ["children", "columns", "data", "keyField", "tableInstanceRef", "stateStorage", "tableMenuOptions", "manualSorting", "manualFiltering", "manualSearch", "manualExport", "height", "maxHeight", "minHeight", "focusedRowId", "enableColumnResize", "enableSorting", "compact", "totalRecords", "onColumnStateChange", "onColumnReorder", "onColumnHeaderClick", "onSort", "onRowUpdated", "tableElement", "headerRowElm", "tableDimensions", "targetElm", "listElm", "defaultSelectionState", "selectionState", "onSelectionChange", "defaultFilterState", "filterState", "onFilterChange"]);
72
+ var { children, columns, data, keyField, tableInstanceRef, stateStorage, tableMenuOptions, manualSorting, manualFiltering, manualSearch, manualExport, height, maxHeight, minHeight, focusedRowId, enableColumnResize, enableSorting, compact, totalRecords, onColumnStateChange, onColumnReorder, onColumnHeaderClick, onSort, onRowUpdated, tableElement, headerRowElm, tableDimensions, targetElm, listElm, defaultSelectionState, selectionState, onSelectionChange, defaultFilterState, filterState, onFilterChange, treeOptions, treeMeta, defaultExpandedKeys, expandedKeys, onExpandedChange } = _a, props = __rest(_a, ["children", "columns", "data", "keyField", "tableInstanceRef", "stateStorage", "tableMenuOptions", "manualSorting", "manualFiltering", "manualSearch", "manualExport", "height", "maxHeight", "minHeight", "focusedRowId", "enableColumnResize", "enableSorting", "compact", "totalRecords", "onColumnStateChange", "onColumnReorder", "onColumnHeaderClick", "onSort", "onRowUpdated", "tableElement", "headerRowElm", "tableDimensions", "targetElm", "listElm", "defaultSelectionState", "selectionState", "onSelectionChange", "defaultFilterState", "filterState", "onFilterChange", "treeOptions", "treeMeta", "defaultExpandedKeys", "expandedKeys", "onExpandedChange"]);
73
+ const _treeOptions = treeOptions || DEFAULT_TREE_OPTIONS;
74
+ const _treeMeta = treeMeta || EMPTY_TREE_META;
75
+ if (_treeOptions.enabled) {
76
+ if (RESERVED_FIELDS.includes(_treeOptions.childrenField)) {
77
+ throw new Error(`treeOptions.childrenField cannot be a reserved internal field: "${_treeOptions.childrenField}".`);
78
+ }
79
+ if (RESERVED_FIELDS.includes(_treeOptions.parentIdField)) {
80
+ throw new Error(`treeOptions.parentIdField cannot be a reserved internal field: "${_treeOptions.parentIdField}".`);
81
+ }
82
+ }
52
83
  const _columns = useMemo(() => columns
53
84
  .map((child, index) => {
54
- if (child.dataField === "__key") {
55
- throw new Error("dataField cannot be __key");
85
+ if (RESERVED_FIELDS.includes(child.dataField)) {
86
+ throw new Error(`dataField cannot be a reserved internal field: "${child.dataField}". Reserved: ${RESERVED_FIELDS.join(", ")}`);
56
87
  }
57
88
  // check for duplicate dataFields
58
89
  const dataFieldCount = columns.filter((col) => col.dataField === child.dataField).length;
@@ -71,7 +102,7 @@ const TableProvider = (_a) => {
71
102
  ? StateStorage.getTableState(stateStorage.key)
72
103
  : undefined;
73
104
  }, [stateStorage === null || stateStorage === void 0 ? void 0 : stateStorage.key]);
74
- const { columnState: savedColumnState, selectionState: savedSelectionState, sortState: savedSortState, searchState: savedSearchState, filterState: savedFilterState, } = savedTableState || {};
105
+ const { columnState: savedColumnState, selectionState: savedSelectionState, sortState: savedSortState, searchState: savedSearchState, filterState: savedFilterState, expandedKeys: savedExpandedKeys, } = savedTableState || {};
75
106
  const [compactState, setCompactState] = useState(compact || false);
76
107
  const [columnState, _setColumnState] = useState(syncColumnState(_columns, savedColumnState));
77
108
  const columnStateRef = useRef(columnState);
@@ -85,6 +116,8 @@ const TableProvider = (_a) => {
85
116
  rules: [],
86
117
  });
87
118
  const [sortState, setSortState] = useState(savedSortState);
119
+ const [_expandedKeys, _setExpandedKeys] = useControlled(expandedKeys, defaultExpandedKeys || savedExpandedKeys || []);
120
+ const expandedKeysSet = useMemo(() => new Set(_expandedKeys), [_expandedKeys]);
88
121
  const [_selectionState, _setSelectionState] = useControlled(selectionState, defaultSelectionState || {
89
122
  selectedRowKeys: (savedSelectionState === null || savedSelectionState === void 0 ? void 0 : savedSelectionState.selectedRowKeys) || [],
90
123
  excludedRowKeys: (savedSelectionState === null || savedSelectionState === void 0 ? void 0 : savedSelectionState.excludedRowKeys) || [],
@@ -238,58 +271,99 @@ const TableProvider = (_a) => {
238
271
  }
239
272
  }
240
273
  };
241
- const sortData = (sortState) => {
242
- // sort data
243
- return data.sort((a, b) => {
244
- if (sortState) {
245
- const aValue = a[sortState.dataField];
246
- const bValue = b[sortState.dataField];
247
- const aIsEmpty = aValue === null || aValue === undefined;
248
- const bIsEmpty = bValue === null || bValue === undefined;
249
- // Treat empty values as the smallest values.
250
- if (aIsEmpty && bIsEmpty) {
251
- return 0;
252
- }
253
- if (aIsEmpty || bIsEmpty) {
254
- if (sortState.dir === "asc") {
255
- return aIsEmpty ? -1 : 1;
256
- }
257
- if (sortState.dir === "desc") {
258
- return aIsEmpty ? 1 : -1;
259
- }
260
- }
274
+ const compareRows = (a, b, sortState) => {
275
+ if (sortState) {
276
+ const aValue = a[sortState.dataField];
277
+ const bValue = b[sortState.dataField];
278
+ const aIsEmpty = aValue === null || aValue === undefined;
279
+ const bIsEmpty = bValue === null || bValue === undefined;
280
+ // Treat empty values as the smallest values.
281
+ if (aIsEmpty && bIsEmpty) {
282
+ return 0;
283
+ }
284
+ if (aIsEmpty || bIsEmpty) {
261
285
  if (sortState.dir === "asc") {
262
- if (aValue < bValue) {
263
- return -1;
264
- }
265
- if (aValue > bValue) {
266
- return 1;
267
- }
268
- return 0;
286
+ return aIsEmpty ? -1 : 1;
269
287
  }
270
- else if (sortState.dir === "desc") {
271
- if (aValue > bValue) {
272
- return -1;
273
- }
274
- if (aValue < bValue) {
275
- return 1;
276
- }
277
- return 0;
288
+ if (sortState.dir === "desc") {
289
+ return aIsEmpty ? 1 : -1;
278
290
  }
279
291
  }
280
- // sort by __index
281
- if (a.__index < b.__index) {
282
- return -1;
292
+ if (sortState.dir === "asc") {
293
+ if (aValue < bValue)
294
+ return -1;
295
+ if (aValue > bValue)
296
+ return 1;
297
+ return 0;
283
298
  }
284
- if (a.__index > b.__index) {
285
- return 1;
299
+ else if (sortState.dir === "desc") {
300
+ if (aValue > bValue)
301
+ return -1;
302
+ if (aValue < bValue)
303
+ return 1;
304
+ return 0;
286
305
  }
287
- return 0;
288
- });
306
+ }
307
+ // sort by __index
308
+ if (a.__index < b.__index)
309
+ return -1;
310
+ if (a.__index > b.__index)
311
+ return 1;
312
+ return 0;
313
+ };
314
+ const sortData = (sortState) => {
315
+ if (!_treeOptions.enabled) {
316
+ // Flat data — sort in-place (preserves existing behavior).
317
+ return data.sort((a, b) => compareRows(a, b, sortState));
318
+ }
319
+ // Tree-aware sort: sort siblings only, then re-DFS to rebuild the
320
+ // flat order. Cross-parent sorting is never allowed in tree mode.
321
+ const byKey = new Map();
322
+ const childrenByParent = new Map();
323
+ for (const row of data) {
324
+ byKey.set(row.__key, row);
325
+ const pk = row.__parentKey;
326
+ if (!childrenByParent.has(pk))
327
+ childrenByParent.set(pk, []);
328
+ childrenByParent.get(pk).push(row);
329
+ }
330
+ for (const [, siblings] of childrenByParent) {
331
+ siblings.sort((a, b) => compareRows(a, b, sortState));
332
+ }
333
+ const result = [];
334
+ const walk = (parentKey) => {
335
+ const siblings = childrenByParent.get(parentKey);
336
+ if (!siblings)
337
+ return;
338
+ for (const row of siblings) {
339
+ result.push(row);
340
+ if (row.__hasChildren)
341
+ walk(row.__key);
342
+ }
343
+ };
344
+ walk(undefined);
345
+ return result;
346
+ };
347
+ const collectAncestors = (rows) => {
348
+ const ancestorSet = new Set();
349
+ const byKey = new Map();
350
+ for (const r of data)
351
+ byKey.set(r.__key, r);
352
+ for (const row of rows) {
353
+ let parentKey = row.__parentKey;
354
+ while (parentKey) {
355
+ if (ancestorSet.has(parentKey))
356
+ break;
357
+ ancestorSet.add(parentKey);
358
+ const parent = byKey.get(parentKey);
359
+ parentKey = parent === null || parent === void 0 ? void 0 : parent.__parentKey;
360
+ }
361
+ }
362
+ return ancestorSet;
289
363
  };
290
- const searchData = (searchText) => {
364
+ const searchData = (rows, searchText) => {
291
365
  const columnKeys = columnState.map((col) => col.dataField);
292
- return data.filter((row) => {
366
+ const matched = rows.filter((row) => {
293
367
  return columnKeys.some((key) => {
294
368
  if (typeof row[key] === "string") {
295
369
  return row[key].toLowerCase().includes(searchText.toLowerCase());
@@ -297,6 +371,16 @@ const TableProvider = (_a) => {
297
371
  return false;
298
372
  });
299
373
  });
374
+ if (!_treeOptions.enabled) {
375
+ return { rows: matched, ancestorKeys: new Set() };
376
+ }
377
+ const ancestorKeys = collectAncestors(matched);
378
+ const visibleSet = new Set(matched.map((r) => r.__key));
379
+ ancestorKeys.forEach((k) => visibleSet.add(k));
380
+ return {
381
+ rows: rows.filter((r) => visibleSet.has(r.__key)),
382
+ ancestorKeys,
383
+ };
300
384
  };
301
385
  const rowMatchesRule = (row, rule) => {
302
386
  var _a, _b, _c, _d;
@@ -396,16 +480,26 @@ const TableProvider = (_a) => {
396
480
  return true;
397
481
  }
398
482
  };
399
- const filterData = (filter) => {
400
- if (!data)
401
- return [];
483
+ const filterData = (rows, filter) => {
484
+ if (!rows)
485
+ return { rows: [], ancestorKeys: new Set() };
402
486
  const { combinator, rules } = filter;
403
- if (!combinator || !rules)
404
- return data;
405
- if (combinator === "or") {
406
- return data.filter((row) => rules.some((rule) => rowMatchesRule(row, rule)));
487
+ if (!combinator || !rules || rules.length === 0) {
488
+ return { rows, ancestorKeys: new Set() };
407
489
  }
408
- return data.filter((row) => rules.every((rule) => rowMatchesRule(row, rule)));
490
+ const matched = combinator === "or"
491
+ ? rows.filter((row) => rules.some((rule) => rowMatchesRule(row, rule)))
492
+ : rows.filter((row) => rules.every((rule) => rowMatchesRule(row, rule)));
493
+ if (!_treeOptions.enabled) {
494
+ return { rows: matched, ancestorKeys: new Set() };
495
+ }
496
+ const ancestorKeys = collectAncestors(matched);
497
+ const visibleSet = new Set(matched.map((r) => r.__key));
498
+ ancestorKeys.forEach((k) => visibleSet.add(k));
499
+ return {
500
+ rows: rows.filter((r) => visibleSet.has(r.__key)),
501
+ ancestorKeys,
502
+ };
409
503
  };
410
504
  const toggleColumnVisibility = (dataField) => {
411
505
  const newColumnState = columnState.map((col) => {
@@ -434,6 +528,47 @@ const TableProvider = (_a) => {
434
528
  const key = !!keyField ? row[keyField] : row.__key;
435
529
  return String(key);
436
530
  };
531
+ const firstVisibleDataField = useMemo(() => { var _a; return (_a = columnState.find((c) => c.visible !== false)) === null || _a === void 0 ? void 0 : _a.dataField; }, [columnState]);
532
+ const persistExpandedKeys = (next) => {
533
+ if ((stateStorage === null || stateStorage === void 0 ? void 0 : stateStorage.enabled) && (stateStorage === null || stateStorage === void 0 ? void 0 : stateStorage.type) === "localStorage") {
534
+ StateStorage.setExpandedKeys(stateStorage.key, next);
535
+ }
536
+ };
537
+ const updateExpandedKeys = (next) => {
538
+ _setExpandedKeys(next);
539
+ onExpandedChange === null || onExpandedChange === void 0 ? void 0 : onExpandedChange(next);
540
+ persistExpandedKeys(next);
541
+ };
542
+ const expandRow = (row) => {
543
+ var _a;
544
+ const key = (_a = row === null || row === void 0 ? void 0 : row.__key) !== null && _a !== void 0 ? _a : getRowKey(row);
545
+ if (expandedKeysSet.has(key))
546
+ return;
547
+ updateExpandedKeys([..._expandedKeys, key]);
548
+ };
549
+ const collapseRow = (row) => {
550
+ var _a;
551
+ const key = (_a = row === null || row === void 0 ? void 0 : row.__key) !== null && _a !== void 0 ? _a : getRowKey(row);
552
+ if (!expandedKeysSet.has(key))
553
+ return;
554
+ updateExpandedKeys(_expandedKeys.filter((k) => k !== key));
555
+ };
556
+ const toggleRowExpanded = (row) => {
557
+ var _a;
558
+ const key = (_a = row === null || row === void 0 ? void 0 : row.__key) !== null && _a !== void 0 ? _a : getRowKey(row);
559
+ if (expandedKeysSet.has(key)) {
560
+ updateExpandedKeys(_expandedKeys.filter((k) => k !== key));
561
+ }
562
+ else {
563
+ updateExpandedKeys([..._expandedKeys, key]);
564
+ }
565
+ };
566
+ const expandAllRows = () => {
567
+ updateExpandedKeys([..._treeMeta.expandableKeys]);
568
+ };
569
+ const collapseAllRows = () => {
570
+ updateExpandedKeys([]);
571
+ };
437
572
  const selectRow = (row) => {
438
573
  const key = getRowKey(row);
439
574
  const newSelectionState = {
@@ -571,6 +706,61 @@ const TableProvider = (_a) => {
571
706
  const key = getRowKey(row);
572
707
  return focusedRowId === key;
573
708
  };
709
+ const { _data, effectiveExpandedKeys } = useMemo(() => {
710
+ let processedData = data || [];
711
+ if (manualSorting !== true) {
712
+ processedData = sortData(sortState);
713
+ }
714
+ const autoExpandedSet = new Set();
715
+ if (manualFiltering !== true && _filterState) {
716
+ const result = filterData(processedData, _filterState);
717
+ processedData = result.rows;
718
+ if (_treeOptions.enabled && _treeOptions.autoExpandOnMatch) {
719
+ result.ancestorKeys.forEach((k) => autoExpandedSet.add(k));
720
+ }
721
+ }
722
+ if (manualSearch !== true && search) {
723
+ const result = searchData(processedData, search);
724
+ processedData = result.rows;
725
+ if (_treeOptions.enabled && _treeOptions.autoExpandOnMatch) {
726
+ result.ancestorKeys.forEach((k) => autoExpandedSet.add(k));
727
+ }
728
+ }
729
+ // Compute the effective expanded set: user expansion ∪ auto-expanded ancestors.
730
+ // Never mutates user's _expandedKeys.
731
+ const effective = new Set(expandedKeysSet);
732
+ autoExpandedSet.forEach((k) => effective.add(k));
733
+ // Final pass: drop rows whose ancestors are not in the effective set.
734
+ if (_treeOptions.enabled) {
735
+ const byKey = new Map();
736
+ for (const r of processedData)
737
+ byKey.set(r.__key, r);
738
+ processedData = processedData.filter((row) => {
739
+ let pk = row.__parentKey;
740
+ while (pk) {
741
+ if (!effective.has(pk))
742
+ return false;
743
+ const parent = byKey.get(pk);
744
+ pk = parent === null || parent === void 0 ? void 0 : parent.__parentKey;
745
+ }
746
+ return true;
747
+ });
748
+ }
749
+ return { _data: processedData, effectiveExpandedKeys: effective };
750
+ }, [
751
+ data,
752
+ columnState,
753
+ search,
754
+ sortState,
755
+ _filterState,
756
+ expandedKeysSet,
757
+ _treeOptions,
758
+ ]);
759
+ const isRowExpanded = (row) => {
760
+ var _a;
761
+ const key = (_a = row === null || row === void 0 ? void 0 : row.__key) !== null && _a !== void 0 ? _a : getRowKey(row);
762
+ return effectiveExpandedKeys.has(key);
763
+ };
574
764
  if (tableInstanceRef) {
575
765
  tableInstanceRef.current = {
576
766
  columnState,
@@ -587,6 +777,13 @@ const TableProvider = (_a) => {
587
777
  clearSelections,
588
778
  runSearch,
589
779
  clearSearch,
780
+ expandRow,
781
+ collapseRow,
782
+ toggleRowExpanded,
783
+ isRowExpanded,
784
+ expandAllRows,
785
+ collapseAllRows,
786
+ getExpandedRowKeys: () => [..._expandedKeys],
590
787
  getTableState: () => {
591
788
  return {
592
789
  columnState,
@@ -598,24 +795,12 @@ const TableProvider = (_a) => {
598
795
  },
599
796
  sortState,
600
797
  searchState: search,
601
- _filterState,
798
+ filterState: _filterState,
799
+ expandedKeys: [..._expandedKeys],
602
800
  };
603
801
  },
604
802
  };
605
803
  }
606
- const _data = useMemo(() => {
607
- let processedData = data; // create a new array to avoid mutating the original data
608
- if (manualSorting !== true) {
609
- processedData = sortData(sortState);
610
- }
611
- if (manualFiltering !== true && _filterState) {
612
- processedData = filterData(_filterState);
613
- }
614
- if (manualSearch !== true && search) {
615
- processedData = searchData(search);
616
- }
617
- return processedData;
618
- }, [data, columnState, search, sortState, _filterState]);
619
804
  return (_jsx(TableContext.Provider, { value: Object.assign({ columnState, setColumnState: updateColumnState, sortState, searchState: search, totalRecords,
620
805
  keyField,
621
806
  handleColumnReorder,
@@ -640,6 +825,13 @@ const TableProvider = (_a) => {
640
825
  stateStorage, tableHeight: height, tableMaxHeight: maxHeight, tableMinHeight: minHeight, compact, tableElement: tableElement, headerRowElm: headerRowElm, tableDimensions: tableDimensions, targetElm: targetElm, listElm: listElm, enableColumnResize,
641
826
  onSelectionChange,
642
827
  onColumnStateChange,
643
- onColumnReorder, onRowUpdated: onRowUpdated || (() => { }), tableMenuOptions, data: _data }, props), children: children }));
828
+ onColumnReorder, onRowUpdated: onRowUpdated || (() => { }), tableMenuOptions, data: _data, treeOptions: _treeOptions, firstVisibleDataField, expandedKeys: _expandedKeys, effectiveExpandedKeys,
829
+ isRowExpanded,
830
+ toggleRowExpanded,
831
+ expandRow,
832
+ collapseRow,
833
+ expandAllRows,
834
+ collapseAllRows,
835
+ onExpandedChange }, props), children: children }));
644
836
  };
645
837
  export default TableProvider;
@@ -1,20 +1,21 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Maximize2Icon } from "lucide-react";
2
+ import { ChevronRightIcon, Maximize2Icon } from "lucide-react";
3
3
  import ColumnResizer from "./ColumnResizer";
4
- import { InnerCellContent, TD, TR } from "./TableComponents";
4
+ import { InnerCellContent, TD, TR, TreeCellContent, TreeCellWrapper, TreeChevronButton, TreeChevronPlaceholder, TreeIndentSpacer, } from "./TableComponents";
5
5
  import useTable from "./useTable";
6
6
  import ActionCell from "./ActionCell";
7
7
  import ActionButton from "./ActionButton";
8
8
  import CheckBox from "../CheckBox";
9
9
  import { LoadingCellIndicator } from "./LoadingCellIndicator";
10
10
  const TableRow = ({ rowData, loading, rowStyle }) => {
11
- const { columnState, enableActionButton, onActionButtonClick, actionButtonIcon: Icon, enableSelection, selectRow, deselectRow, isRowSelected, isRowFocused, onRowUpdated, } = useTable();
11
+ const { columnState, enableActionButton, onActionButtonClick, actionButtonIcon: Icon, enableSelection, selectRow, deselectRow, isRowSelected, isRowFocused, onRowUpdated, treeOptions, firstVisibleDataField, isRowExpanded, toggleRowExpanded, } = useTable();
12
12
  const selected = isRowSelected(rowData);
13
13
  const focused = isRowFocused(rowData);
14
14
  const handleSelectionChange = (e) => {
15
15
  e === true ? selectRow(rowData) : deselectRow(rowData);
16
16
  };
17
17
  return (_jsxs(TR, { className: "mfui-tr", style: rowStyle, "data-key": rowData.__key, "data-selected": selected, "data-focused": focused, children: [enableSelection && (_jsx(ActionCell, { className: `mfui-td column-select`, children: _jsx(InnerCellContent, { className: "mfui inner-cell-content row-action row-selection-action", children: _jsx(CheckBox, { className: `mfui-checkbox`, value: selected, onChange: (e) => handleSelectionChange(e) }) }) })), enableActionButton && (_jsx(ActionCell, { className: `mfui-td column-action`, children: _jsx(InnerCellContent, { className: "mfui inner-cell-content row-action", children: _jsx(ActionButton, { variant: "subtle", onClick: () => onActionButtonClick === null || onActionButtonClick === void 0 ? void 0 : onActionButtonClick(rowData), children: Icon ? _jsx(Icon, { size: 14 }) : _jsx(Maximize2Icon, { size: 14 }) }) }) })), columnState.map((column, index) => {
18
+ var _a;
18
19
  if (column.visible === false)
19
20
  return null;
20
21
  if (loading) {
@@ -24,16 +25,20 @@ const TableRow = ({ rowData, loading, rowStyle }) => {
24
25
  flex: column.width ? "0 0 auto" : "1",
25
26
  }, children: _jsx(LoadingCellIndicator, {}) }, index));
26
27
  }
27
- return (_jsxs(TD, { className: `mfui-td column-${column.columnId}`, "data-field": column.dataField, style: {
28
+ const cellBody = column.render
29
+ ? column.render({ rowData, onRowUpdated })
30
+ : rowData[column.dataField];
31
+ const isTreeColumn = (treeOptions === null || treeOptions === void 0 ? void 0 : treeOptions.enabled) === true &&
32
+ column.dataField === firstVisibleDataField;
33
+ const expanded = isTreeColumn ? isRowExpanded(rowData) : false;
34
+ return (_jsxs(TD, { className: `mfui-td column-${column.columnId}`, "data-field": column.dataField, "data-tree-cell": isTreeColumn ? "true" : undefined, style: {
28
35
  width: column.width,
29
36
  minWidth: column.minWidth,
30
37
  flex: column.width ? "0 0 auto" : "1",
31
- }, children: [(column === null || column === void 0 ? void 0 : column.enableResize) === true && _jsx(ColumnResizer, { column: column }), _jsx(InnerCellContent, { className: "mfui inner-cell-content", children: column.render
32
- ? column.render({
33
- rowData,
34
- onRowUpdated,
35
- })
36
- : rowData[column.dataField] })] }, index));
38
+ }, children: [(column === null || column === void 0 ? void 0 : column.enableResize) === true && _jsx(ColumnResizer, { column: column }), _jsx(InnerCellContent, { className: "mfui inner-cell-content", children: isTreeColumn ? (_jsxs(TreeCellWrapper, { children: [_jsx(TreeIndentSpacer, { "$level": (_a = rowData.__level) !== null && _a !== void 0 ? _a : 0, "$indentPx": treeOptions.indentPx }), rowData.__hasChildren ? (_jsx(TreeChevronButton, { type: "button", "$expanded": expanded, onClick: (e) => {
39
+ e.stopPropagation();
40
+ toggleRowExpanded(rowData);
41
+ }, "aria-label": expanded ? "Collapse row" : "Expand row", "aria-expanded": expanded, children: _jsx(ChevronRightIcon, { size: 14 }) })) : (_jsx(TreeChevronPlaceholder, {})), _jsx(TreeCellContent, { children: cellBody })] })) : (cellBody) })] }, index));
37
42
  })] }));
38
43
  };
39
44
  export default TableRow;