@monolith-forensics/monolith-ui 1.5.2-dev.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,6 +28,10 @@ export interface MonolithDefaultTheme {
28
28
  main: string;
29
29
  contrastText: string;
30
30
  };
31
+ success: {
32
+ main: string;
33
+ contrastText: string;
34
+ };
31
35
  background: {
32
36
  default: string;
33
37
  paper: string;
@@ -11,8 +11,6 @@ type GetSearchStateFn = (key: string) => string;
11
11
  type SetSearchStateFn = (key: string, value: string) => void;
12
12
  type GetFilterStateFn = (key: string) => Query | undefined;
13
13
  type SetFilterStateFn = (key: string, value?: Query | null) => void;
14
- type GetExpandedKeysFn = (key: string) => string[] | undefined;
15
- type SetExpandedKeysFn = (key: string, value: string[]) => void;
16
14
  declare const StateStorage: {
17
15
  getTableState: GetTableStateFn;
18
16
  getColumnState: GetColumnStateFn;
@@ -25,7 +23,5 @@ declare const StateStorage: {
25
23
  setSearchState: SetSearchStateFn;
26
24
  getFilterState: GetFilterStateFn;
27
25
  setFilterState: SetFilterStateFn;
28
- getExpandedKeys: GetExpandedKeysFn;
29
- setExpandedKeys: SetExpandedKeysFn;
30
26
  };
31
27
  export default StateStorage;
@@ -82,17 +82,6 @@ const setFilterState = (key, value) => {
82
82
  const newState = Object.assign(Object.assign({}, previousState), { filterState: value });
83
83
  set(key, newState);
84
84
  };
85
- const getExpandedKeys = (key) => {
86
- const data = getTableState(key);
87
- return data.expandedKeys;
88
- };
89
- const setExpandedKeys = (key, value) => {
90
- // get previous state
91
- const previousState = getTableState(key);
92
- // merge previous state with new state
93
- const newState = Object.assign(Object.assign({}, previousState), { expandedKeys: value });
94
- set(key, newState);
95
- };
96
85
  const StateStorage = {
97
86
  getTableState,
98
87
  getColumnState,
@@ -105,7 +94,5 @@ const StateStorage = {
105
94
  setSearchState,
106
95
  getFilterState,
107
96
  setFilterState,
108
- getExpandedKeys,
109
- setExpandedKeys,
110
97
  };
111
98
  export default StateStorage;
@@ -22,7 +22,6 @@ import TableMenu from "./TableMenu";
22
22
  import useTable from "./useTable";
23
23
  import LoadingIndicator from "./LoadingIndicator";
24
24
  import styled from "styled-components";
25
- import TableDefaults from "./TableDefaults";
26
25
  const StyledTableContainer = styled.div `
27
26
  display: flex;
28
27
  flex-direction: column;
@@ -41,20 +40,8 @@ const TableContent = ({ children, }) => {
41
40
  }, ref: tableElement, "data-compact": compactState, children: [_jsx(LoadingIndicator, { visible: loading }), _jsx(TableHeader, { headerRowElm: headerRowElm }), visibleColumnCount === 0 && _jsx("div", { children: "No columns visible" }), virtualized === true ? (_jsx(VirtualizedRows, { tableDimensions: tableDimensions, targetElm: targetElm, listElm: listElm, rowHeight: rowHeight, headerRowHeight: headerRowHeight })) : (_jsx(StaticRows, { targetElm: targetElm, listElm: listElm }))] })] }));
42
41
  };
43
42
  export const Table = (_a) => {
44
- var { data, columnProps, children, treeOptions } = _a, props = __rest(_a, ["data", "columnProps", "children", "treeOptions"]) // pass through props straight to table context
43
+ var { data, columnProps, children } = _a, props = __rest(_a, ["data", "columnProps", "children"]) // pass through props straight to table context
45
44
  ;
46
- const resolvedTreeOptions = useMemo(() => {
47
- var _a, _b, _c, _d, _e, _f;
48
- return ({
49
- enabled: (_a = treeOptions === null || treeOptions === void 0 ? void 0 : treeOptions.enabled) !== null && _a !== void 0 ? _a : false,
50
- mode: (_b = treeOptions === null || treeOptions === void 0 ? void 0 : treeOptions.mode) !== null && _b !== void 0 ? _b : TableDefaults.tree.defaultMode,
51
- childrenField: (_c = treeOptions === null || treeOptions === void 0 ? void 0 : treeOptions.childrenField) !== null && _c !== void 0 ? _c : TableDefaults.tree.defaultChildrenField,
52
- parentIdField: (_d = treeOptions === null || treeOptions === void 0 ? void 0 : treeOptions.parentIdField) !== null && _d !== void 0 ? _d : TableDefaults.tree.defaultParentIdField,
53
- idField: treeOptions === null || treeOptions === void 0 ? void 0 : treeOptions.idField,
54
- indentPx: (_e = treeOptions === null || treeOptions === void 0 ? void 0 : treeOptions.indentPx) !== null && _e !== void 0 ? _e : TableDefaults.tree.indentPx,
55
- autoExpandOnMatch: (_f = treeOptions === null || treeOptions === void 0 ? void 0 : treeOptions.autoExpandOnMatch) !== null && _f !== void 0 ? _f : true,
56
- });
57
- }, [treeOptions]);
58
45
  const tableElement = useRef(null);
59
46
  const targetElm = useRef(null);
60
47
  const listElm = useRef(null);
@@ -126,153 +113,18 @@ export const Table = (_a) => {
126
113
  // Uses a WeakMap so entries are automatically garbage collected
127
114
  // when their corresponding row objects are no longer referenced.
128
115
  const uuidCache = useRef(new WeakMap());
129
- const getOrAssignKey = (row) => {
130
- let key = uuidCache.current.get(row);
116
+ // Augment each row with a stable __key (UUID) and __index.
117
+ // The WeakMap ensures that the same row object always receives
118
+ // the same UUID across re-renders, preventing unnecessary DOM
119
+ // reconciliation. useMemo preserves referential stability of
120
+ // the output array when the data reference hasn't changed.
121
+ const __data = useMemo(() => data === null || data === void 0 ? void 0 : data.map((d, i) => {
122
+ let key = uuidCache.current.get(d);
131
123
  if (!key) {
132
124
  key = shortUUID.uuid();
133
- uuidCache.current.set(row, key);
134
- }
135
- return key;
136
- };
137
- // Augment each row with stable metadata. When tree mode is disabled,
138
- // produces a flat 1:1 mapping with __key and __index. When enabled,
139
- // walks the input (nested or flat) into a flat depth-first array
140
- // and adds tree metadata (__level, __parentKey, __hasChildren, __childKeys).
141
- const { rows: __data, treeMeta } = useMemo(() => {
142
- const parentKeyMap = new Map();
143
- const childrenKeyMap = new Map();
144
- const expandableKeys = [];
145
- if (!data) {
146
- return {
147
- rows: [],
148
- treeMeta: { parentKeyMap, childrenKeyMap, expandableKeys },
149
- };
150
- }
151
- if (!resolvedTreeOptions.enabled) {
152
- const rows = data.map((d, i) => (Object.assign(Object.assign({}, d), { __key: getOrAssignKey(d), __index: i })));
153
- return {
154
- rows,
155
- treeMeta: { parentKeyMap, childrenKeyMap, expandableKeys },
156
- };
125
+ uuidCache.current.set(d, key);
157
126
  }
158
- const rows = [];
159
- let counter = 0;
160
- if (resolvedTreeOptions.mode === "nested") {
161
- const childrenField = resolvedTreeOptions.childrenField;
162
- const walk = (node, level, parentKey) => {
163
- const key = getOrAssignKey(node);
164
- const rawChildren = node === null || node === void 0 ? void 0 : node[childrenField];
165
- const children = Array.isArray(rawChildren) ? rawChildren : [];
166
- const childKeys = children.map((c) => getOrAssignKey(c));
167
- // Strip the nested children field from the augmented copy to avoid
168
- // memory bloat and prevent accidental iteration of the raw tree.
169
- const _a = node, _b = childrenField, _stripped = _a[_b], rest = __rest(_a, [typeof _b === "symbol" ? _b : _b + ""]);
170
- const augmented = Object.assign(Object.assign({}, rest), { __key: key, __index: counter++, __level: level, __parentKey: parentKey, __hasChildren: children.length > 0, __childKeys: childKeys });
171
- rows.push(augmented);
172
- parentKeyMap.set(key, parentKey);
173
- if (children.length > 0) {
174
- childrenKeyMap.set(key, childKeys);
175
- expandableKeys.push(key);
176
- }
177
- for (const child of children) {
178
- walk(child, level + 1, key);
179
- }
180
- };
181
- const rootKeys = [];
182
- for (const root of data) {
183
- rootKeys.push(getOrAssignKey(root));
184
- walk(root, 0, undefined);
185
- }
186
- childrenKeyMap.set(undefined, rootKeys);
187
- }
188
- else {
189
- // flat mode
190
- const idField = resolvedTreeOptions.idField || props.keyField;
191
- if (!idField) {
192
- throw new Error('Table treeOptions.mode="flat" requires either treeOptions.idField or keyField to be set so the table can resolve parent-child relationships against a stable id.');
193
- }
194
- const parentIdField = resolvedTreeOptions.parentIdField;
195
- // Pass 1: assign __key, build idToKey
196
- const idToKey = new Map();
197
- const idToRow = new Map();
198
- for (const d of data) {
199
- const id = d === null || d === void 0 ? void 0 : d[idField];
200
- if (id === undefined || id === null)
201
- continue;
202
- idToKey.set(id, getOrAssignKey(d));
203
- idToRow.set(id, d);
204
- }
205
- // Pass 2: build parent->children id map. Roots = parentId null/undefined
206
- // OR parentId points at a non-existent row.
207
- const parentToChildIds = new Map();
208
- const rootIds = [];
209
- let warnedOrphan = false;
210
- for (const d of data) {
211
- const id = d === null || d === void 0 ? void 0 : d[idField];
212
- if (id === undefined || id === null)
213
- continue;
214
- const parentId = d === null || d === void 0 ? void 0 : d[parentIdField];
215
- if (parentId === undefined || parentId === null) {
216
- rootIds.push(id);
217
- continue;
218
- }
219
- if (!idToRow.has(parentId)) {
220
- if (!warnedOrphan) {
221
- // eslint-disable-next-line no-console
222
- console.warn(`Table tree (flat mode): row id "${id}" references missing parentId "${parentId}"; treating as root.`);
223
- warnedOrphan = true;
224
- }
225
- rootIds.push(id);
226
- continue;
227
- }
228
- if (!parentToChildIds.has(parentId))
229
- parentToChildIds.set(parentId, []);
230
- parentToChildIds.get(parentId).push(id);
231
- }
232
- // Pass 3: DFS from roots with cycle detection
233
- const visiting = new Set();
234
- let warnedCycle = false;
235
- const walkFlat = (id, level, parentKey) => {
236
- if (visiting.has(id)) {
237
- if (!warnedCycle) {
238
- // eslint-disable-next-line no-console
239
- console.warn(`Table tree (flat mode): cycle detected at row id "${id}"; truncating subtree.`);
240
- warnedCycle = true;
241
- }
242
- return;
243
- }
244
- visiting.add(id);
245
- const node = idToRow.get(id);
246
- const key = idToKey.get(id);
247
- const childIds = parentToChildIds.get(id) || [];
248
- const childKeys = childIds
249
- .map((cid) => idToKey.get(cid))
250
- .filter((k) => !!k);
251
- const augmented = Object.assign(Object.assign({}, node), { __key: key, __index: counter++, __level: level, __parentKey: parentKey, __hasChildren: childIds.length > 0, __childKeys: childKeys });
252
- rows.push(augmented);
253
- parentKeyMap.set(key, parentKey);
254
- if (childIds.length > 0) {
255
- childrenKeyMap.set(key, childKeys);
256
- expandableKeys.push(key);
257
- }
258
- for (const cid of childIds) {
259
- walkFlat(cid, level + 1, key);
260
- }
261
- visiting.delete(id);
262
- };
263
- const rootKeys = [];
264
- for (const id of rootIds) {
265
- const k = idToKey.get(id);
266
- if (k)
267
- rootKeys.push(k);
268
- walkFlat(id, 0, undefined);
269
- }
270
- childrenKeyMap.set(undefined, rootKeys);
271
- }
272
- return {
273
- rows,
274
- treeMeta: { parentKeyMap, childrenKeyMap, expandableKeys },
275
- };
276
- }, [data, resolvedTreeOptions, props.keyField]);
277
- return (_jsx(TableProvider, Object.assign({ columns: columnProps, data: __data, tableElement: tableElement, headerRowElm: headerRowElm, tableDimensions: tableDimensions, targetElm: targetElm, listElm: listElm, treeOptions: resolvedTreeOptions, treeMeta: treeMeta }, props, { children: _jsx(TableContent, { children: children }) })));
127
+ return Object.assign(Object.assign({}, d), { __key: key, __index: i });
128
+ }), [data]);
129
+ return (_jsx(TableProvider, Object.assign({ columns: columnProps, data: __data, tableElement: tableElement, headerRowElm: headerRowElm, tableDimensions: tableDimensions, targetElm: targetElm, listElm: listElm }, props, { children: _jsx(TableContent, { children: children }) })));
278
130
  };
@@ -6,16 +6,6 @@ export declare const THR: import("styled-components/dist/types").IStyledComponen
6
6
  export declare const TD: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, TDProps>> & string;
7
7
  export declare const TH: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof TDProps> & TDProps, never>> & string;
8
8
  export declare const InnerCellContent: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
9
- export declare const TreeCellWrapper: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
10
- export declare const TreeIndentSpacer: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
11
- $level: number;
12
- $indentPx: number;
13
- }>> & string;
14
- export declare const TreeChevronButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, {
15
- $expanded: boolean;
16
- }>> & string;
17
- export declare const TreeChevronPlaceholder: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
18
- export declare const TreeCellContent: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
19
9
  export declare const TableViewPort: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
20
10
  export declare const TableListElement: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
21
11
  export declare const TableWrapper: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
@@ -115,55 +115,6 @@ 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
- gap: 4px;
125
- `;
126
- export const TreeIndentSpacer = styled.div `
127
- flex: 0 0 auto;
128
- width: ${({ $level, $indentPx }) => $level * $indentPx}px;
129
- `;
130
- export const TreeChevronButton = styled.button `
131
- flex: 0 0 auto;
132
- width: 16px;
133
- height: 16px;
134
- padding: 0;
135
- margin: 0;
136
- display: inline-flex;
137
- align-items: center;
138
- justify-content: center;
139
- background: transparent;
140
- border: none;
141
- cursor: pointer;
142
- color: ${({ theme }) => theme.palette.text.secondary};
143
- transition: transform 120ms ease;
144
- transform: rotate(${({ $expanded }) => ($expanded ? "90deg" : "0deg")});
145
-
146
- &:hover {
147
- color: ${({ theme }) => theme.palette.text.primary};
148
- }
149
-
150
- &:focus-visible {
151
- outline: 1px solid ${({ theme }) => theme.palette.primary.main};
152
- outline-offset: 1px;
153
- }
154
- `;
155
- export const TreeChevronPlaceholder = styled.div `
156
- flex: 0 0 auto;
157
- width: 16px;
158
- height: 16px;
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
- `;
167
118
  export const TableViewPort = styled.div `
168
119
  display: flex;
169
120
  flex-direction: column;
@@ -21,12 +21,5 @@ 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
- };
31
24
  };
32
25
  export default TableDefaults;
@@ -21,12 +21,5 @@ 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
- },
31
24
  };
32
25
  export default TableDefaults;
@@ -16,23 +16,24 @@ const StyledCaption = styled.span `
16
16
  `;
17
17
  const TableHeader = ({ headerRowElm }) => {
18
18
  var _a;
19
- const { columnState, sortState, setColumnState, onColumnResize, handleColumnReorder, enableColumnReorder, handleColumnHeaderClick, enableColumnResize, headerRowHeight, enableActionButton, enableSelection, selectionState, disableSelectAll, selectAllRows, clearSelections, compactState, } = useTable();
19
+ const { columnState, sortState, setColumnState: updateColumnState, onColumnResize, handleColumnReorder, enableColumnReorder, handleColumnHeaderClick, enableColumnResize, headerRowHeight, enableActionButton, enableSelection, selectionState, disableSelectAll, selectAllRows, clearSelections, compactState, } = useTable();
20
20
  const [dragColumn, setDragColumn] = useState(null);
21
21
  const [dropColumn, setDropColumn] = useState(null);
22
22
  const handleResize = (e) => {
23
23
  const columnId = e.column.columnId;
24
- setColumnState((prevColumnState) => {
25
- const newState = prevColumnState.map((col) => {
24
+ const newWidth = Number(e.newWidth);
25
+ updateColumnState((prevColumnState) => {
26
+ const nextColumnState = prevColumnState.map((col) => {
26
27
  if (col.columnId === columnId) {
27
- return Object.assign(Object.assign({}, col), { width: Number(e.newWidth) });
28
+ return Object.assign(Object.assign({}, col), { width: newWidth });
28
29
  }
29
30
  return col;
30
31
  });
31
32
  onColumnResize === null || onColumnResize === void 0 ? void 0 : onColumnResize({
32
- column: Object.assign(Object.assign({}, e.column), { width: Number(e.newWidth) }),
33
- columnState: newState,
33
+ column: Object.assign(Object.assign({}, e.column), { width: newWidth }),
34
+ columnState: nextColumnState,
34
35
  });
35
- return newState;
36
+ return nextColumnState;
36
37
  });
37
38
  };
38
39
  const handleChangeSelection = (e) => {
@@ -19,36 +19,16 @@ var __rest = (this && this.__rest) || function (s, e) {
19
19
  return t;
20
20
  };
21
21
  import { jsx as _jsx } from "react/jsx-runtime";
22
- import { createContext, useMemo, useState } from "react";
22
+ import { createContext, useEffect, useMemo, useRef, useState } from "react";
23
23
  import StateStorage from "./StateStorage";
24
24
  import { resolveColumnResize, syncColumnState } from "./Utils";
25
+ import { notifyAndPersistColumnState, resolveColumnStateUpdate, } from "./Utils/columnStateUpdate";
25
26
  import TableDefaults from "./TableDefaults";
26
27
  import shortUUID from "short-uuid";
27
28
  import { SelectionStatus } from "./enums";
28
29
  import moment from "moment";
29
30
  import { useControlled } from "../Utilities";
30
31
  import { exportTableToExcel } from "./Utils";
31
- const RESERVED_FIELDS = [
32
- "__key",
33
- "__index",
34
- "__level",
35
- "__parentKey",
36
- "__hasChildren",
37
- "__childKeys",
38
- ];
39
- const EMPTY_TREE_META = {
40
- parentKeyMap: new Map(),
41
- childrenKeyMap: new Map(),
42
- expandableKeys: [],
43
- };
44
- const DEFAULT_TREE_OPTIONS = {
45
- enabled: false,
46
- mode: "nested",
47
- childrenField: "children",
48
- parentIdField: "parentId",
49
- indentPx: 16,
50
- autoExpandOnMatch: true,
51
- };
52
32
  const calculateSelectionTotal = (selectionState, totalRecords, dataLength = 0) => {
53
33
  if (!selectionState) {
54
34
  return 0;
@@ -68,21 +48,11 @@ const calculateSelectionTotal = (selectionState, totalRecords, dataLength = 0) =
68
48
  };
69
49
  export const TableContext = createContext(null);
70
50
  const TableProvider = (_a) => {
71
- 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"]);
72
- const _treeOptions = treeOptions || DEFAULT_TREE_OPTIONS;
73
- const _treeMeta = treeMeta || EMPTY_TREE_META;
74
- if (_treeOptions.enabled) {
75
- if (RESERVED_FIELDS.includes(_treeOptions.childrenField)) {
76
- throw new Error(`treeOptions.childrenField cannot be a reserved internal field: "${_treeOptions.childrenField}".`);
77
- }
78
- if (RESERVED_FIELDS.includes(_treeOptions.parentIdField)) {
79
- throw new Error(`treeOptions.parentIdField cannot be a reserved internal field: "${_treeOptions.parentIdField}".`);
80
- }
81
- }
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"]);
82
52
  const _columns = useMemo(() => columns
83
53
  .map((child, index) => {
84
- if (RESERVED_FIELDS.includes(child.dataField)) {
85
- throw new Error(`dataField cannot be a reserved internal field: "${child.dataField}". Reserved: ${RESERVED_FIELDS.join(", ")}`);
54
+ if (child.dataField === "__key") {
55
+ throw new Error("dataField cannot be __key");
86
56
  }
87
57
  // check for duplicate dataFields
88
58
  const dataFieldCount = columns.filter((col) => col.dataField === child.dataField).length;
@@ -101,9 +71,13 @@ const TableProvider = (_a) => {
101
71
  ? StateStorage.getTableState(stateStorage.key)
102
72
  : undefined;
103
73
  }, [stateStorage === null || stateStorage === void 0 ? void 0 : stateStorage.key]);
104
- const { columnState: savedColumnState, selectionState: savedSelectionState, sortState: savedSortState, searchState: savedSearchState, filterState: savedFilterState, expandedKeys: savedExpandedKeys, } = savedTableState || {};
74
+ const { columnState: savedColumnState, selectionState: savedSelectionState, sortState: savedSortState, searchState: savedSearchState, filterState: savedFilterState, } = savedTableState || {};
105
75
  const [compactState, setCompactState] = useState(compact || false);
106
- const [columnState, setColumnState] = useState(syncColumnState(_columns, savedColumnState));
76
+ const [columnState, _setColumnState] = useState(syncColumnState(_columns, savedColumnState));
77
+ const columnStateRef = useRef(columnState);
78
+ useEffect(() => {
79
+ columnStateRef.current = columnState;
80
+ }, [columnState]);
107
81
  const [search, setSearch] = useState(savedSearchState || "");
108
82
  const [_filterState, _setFilterState] = useControlled(filterState, defaultFilterState ||
109
83
  savedFilterState || {
@@ -111,8 +85,6 @@ const TableProvider = (_a) => {
111
85
  rules: [],
112
86
  });
113
87
  const [sortState, setSortState] = useState(savedSortState);
114
- const [_expandedKeys, _setExpandedKeys] = useControlled(expandedKeys, defaultExpandedKeys || savedExpandedKeys || []);
115
- const expandedKeysSet = useMemo(() => new Set(_expandedKeys), [_expandedKeys]);
116
88
  const [_selectionState, _setSelectionState] = useControlled(selectionState, defaultSelectionState || {
117
89
  selectedRowKeys: (savedSelectionState === null || savedSelectionState === void 0 ? void 0 : savedSelectionState.selectedRowKeys) || [],
118
90
  excludedRowKeys: (savedSelectionState === null || savedSelectionState === void 0 ? void 0 : savedSelectionState.excludedRowKeys) || [],
@@ -148,14 +120,20 @@ const TableProvider = (_a) => {
148
120
  const getColumnState = (dataField) => {
149
121
  return columnState.find((col) => col.dataField === dataField);
150
122
  };
151
- const updateColumnState = (columnState) => {
152
- setColumnState(columnState);
153
- onColumnStateChange === null || onColumnStateChange === void 0 ? void 0 : onColumnStateChange(columnState);
154
- if (stateStorage === null || stateStorage === void 0 ? void 0 : stateStorage.enabled) {
155
- if ((stateStorage === null || stateStorage === void 0 ? void 0 : stateStorage.type) === "localStorage") {
156
- StateStorage.setColumnState(stateStorage.key, columnState);
157
- }
158
- }
123
+ const updateColumnState = (columnStateUpdate) => {
124
+ const previousColumnState = columnStateRef.current;
125
+ const nextColumnState = resolveColumnStateUpdate(columnStateUpdate, previousColumnState);
126
+ columnStateRef.current = nextColumnState;
127
+ _setColumnState(nextColumnState);
128
+ notifyAndPersistColumnState({
129
+ nextColumnState,
130
+ onColumnStateChange,
131
+ persistColumnState: (stateStorage === null || stateStorage === void 0 ? void 0 : stateStorage.enabled) && (stateStorage === null || stateStorage === void 0 ? void 0 : stateStorage.type) === "localStorage"
132
+ ? (columnState) => {
133
+ StateStorage.setColumnState(stateStorage.key, columnState);
134
+ }
135
+ : undefined,
136
+ });
159
137
  };
160
138
  const handleColumnReorder = (dragColumn, dropColumn) => {
161
139
  var _a, _b;
@@ -260,99 +238,58 @@ const TableProvider = (_a) => {
260
238
  }
261
239
  }
262
240
  };
263
- const compareRows = (a, b, sortState) => {
264
- if (sortState) {
265
- const aValue = a[sortState.dataField];
266
- const bValue = b[sortState.dataField];
267
- const aIsEmpty = aValue === null || aValue === undefined;
268
- const bIsEmpty = bValue === null || bValue === undefined;
269
- // Treat empty values as the smallest values.
270
- if (aIsEmpty && bIsEmpty) {
271
- return 0;
272
- }
273
- if (aIsEmpty || bIsEmpty) {
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
261
  if (sortState.dir === "asc") {
275
- return aIsEmpty ? -1 : 1;
262
+ if (aValue < bValue) {
263
+ return -1;
264
+ }
265
+ if (aValue > bValue) {
266
+ return 1;
267
+ }
268
+ return 0;
276
269
  }
277
- if (sortState.dir === "desc") {
278
- return aIsEmpty ? 1 : -1;
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;
279
278
  }
280
279
  }
281
- if (sortState.dir === "asc") {
282
- if (aValue < bValue)
283
- return -1;
284
- if (aValue > bValue)
285
- return 1;
286
- return 0;
287
- }
288
- else if (sortState.dir === "desc") {
289
- if (aValue > bValue)
290
- return -1;
291
- if (aValue < bValue)
292
- return 1;
293
- return 0;
294
- }
295
- }
296
- // sort by __index
297
- if (a.__index < b.__index)
298
- return -1;
299
- if (a.__index > b.__index)
300
- return 1;
301
- return 0;
302
- };
303
- const sortData = (sortState) => {
304
- if (!_treeOptions.enabled) {
305
- // Flat data — sort in-place (preserves existing behavior).
306
- return data.sort((a, b) => compareRows(a, b, sortState));
307
- }
308
- // Tree-aware sort: sort siblings only, then re-DFS to rebuild the
309
- // flat order. Cross-parent sorting is never allowed in tree mode.
310
- const byKey = new Map();
311
- const childrenByParent = new Map();
312
- for (const row of data) {
313
- byKey.set(row.__key, row);
314
- const pk = row.__parentKey;
315
- if (!childrenByParent.has(pk))
316
- childrenByParent.set(pk, []);
317
- childrenByParent.get(pk).push(row);
318
- }
319
- for (const [, siblings] of childrenByParent) {
320
- siblings.sort((a, b) => compareRows(a, b, sortState));
321
- }
322
- const result = [];
323
- const walk = (parentKey) => {
324
- const siblings = childrenByParent.get(parentKey);
325
- if (!siblings)
326
- return;
327
- for (const row of siblings) {
328
- result.push(row);
329
- if (row.__hasChildren)
330
- walk(row.__key);
280
+ // sort by __index
281
+ if (a.__index < b.__index) {
282
+ return -1;
331
283
  }
332
- };
333
- walk(undefined);
334
- return result;
335
- };
336
- const collectAncestors = (rows) => {
337
- const ancestorSet = new Set();
338
- const byKey = new Map();
339
- for (const r of data)
340
- byKey.set(r.__key, r);
341
- for (const row of rows) {
342
- let parentKey = row.__parentKey;
343
- while (parentKey) {
344
- if (ancestorSet.has(parentKey))
345
- break;
346
- ancestorSet.add(parentKey);
347
- const parent = byKey.get(parentKey);
348
- parentKey = parent === null || parent === void 0 ? void 0 : parent.__parentKey;
284
+ if (a.__index > b.__index) {
285
+ return 1;
349
286
  }
350
- }
351
- return ancestorSet;
287
+ return 0;
288
+ });
352
289
  };
353
- const searchData = (rows, searchText) => {
290
+ const searchData = (searchText) => {
354
291
  const columnKeys = columnState.map((col) => col.dataField);
355
- const matched = rows.filter((row) => {
292
+ return data.filter((row) => {
356
293
  return columnKeys.some((key) => {
357
294
  if (typeof row[key] === "string") {
358
295
  return row[key].toLowerCase().includes(searchText.toLowerCase());
@@ -360,16 +297,6 @@ const TableProvider = (_a) => {
360
297
  return false;
361
298
  });
362
299
  });
363
- if (!_treeOptions.enabled) {
364
- return { rows: matched, ancestorKeys: new Set() };
365
- }
366
- const ancestorKeys = collectAncestors(matched);
367
- const visibleSet = new Set(matched.map((r) => r.__key));
368
- ancestorKeys.forEach((k) => visibleSet.add(k));
369
- return {
370
- rows: rows.filter((r) => visibleSet.has(r.__key)),
371
- ancestorKeys,
372
- };
373
300
  };
374
301
  const rowMatchesRule = (row, rule) => {
375
302
  var _a, _b, _c, _d;
@@ -469,26 +396,16 @@ const TableProvider = (_a) => {
469
396
  return true;
470
397
  }
471
398
  };
472
- const filterData = (rows, filter) => {
473
- if (!rows)
474
- return { rows: [], ancestorKeys: new Set() };
399
+ const filterData = (filter) => {
400
+ if (!data)
401
+ return [];
475
402
  const { combinator, rules } = filter;
476
- if (!combinator || !rules || rules.length === 0) {
477
- return { rows, ancestorKeys: new Set() };
403
+ if (!combinator || !rules)
404
+ return data;
405
+ if (combinator === "or") {
406
+ return data.filter((row) => rules.some((rule) => rowMatchesRule(row, rule)));
478
407
  }
479
- const matched = combinator === "or"
480
- ? rows.filter((row) => rules.some((rule) => rowMatchesRule(row, rule)))
481
- : rows.filter((row) => rules.every((rule) => rowMatchesRule(row, rule)));
482
- if (!_treeOptions.enabled) {
483
- return { rows: matched, ancestorKeys: new Set() };
484
- }
485
- const ancestorKeys = collectAncestors(matched);
486
- const visibleSet = new Set(matched.map((r) => r.__key));
487
- ancestorKeys.forEach((k) => visibleSet.add(k));
488
- return {
489
- rows: rows.filter((r) => visibleSet.has(r.__key)),
490
- ancestorKeys,
491
- };
408
+ return data.filter((row) => rules.every((rule) => rowMatchesRule(row, rule)));
492
409
  };
493
410
  const toggleColumnVisibility = (dataField) => {
494
411
  const newColumnState = columnState.map((col) => {
@@ -517,47 +434,6 @@ const TableProvider = (_a) => {
517
434
  const key = !!keyField ? row[keyField] : row.__key;
518
435
  return String(key);
519
436
  };
520
- const firstVisibleDataField = useMemo(() => { var _a; return (_a = columnState.find((c) => c.visible !== false)) === null || _a === void 0 ? void 0 : _a.dataField; }, [columnState]);
521
- const persistExpandedKeys = (next) => {
522
- if ((stateStorage === null || stateStorage === void 0 ? void 0 : stateStorage.enabled) && (stateStorage === null || stateStorage === void 0 ? void 0 : stateStorage.type) === "localStorage") {
523
- StateStorage.setExpandedKeys(stateStorage.key, next);
524
- }
525
- };
526
- const updateExpandedKeys = (next) => {
527
- _setExpandedKeys(next);
528
- onExpandedChange === null || onExpandedChange === void 0 ? void 0 : onExpandedChange(next);
529
- persistExpandedKeys(next);
530
- };
531
- const expandRow = (row) => {
532
- var _a;
533
- const key = (_a = row === null || row === void 0 ? void 0 : row.__key) !== null && _a !== void 0 ? _a : getRowKey(row);
534
- if (expandedKeysSet.has(key))
535
- return;
536
- updateExpandedKeys([..._expandedKeys, key]);
537
- };
538
- const collapseRow = (row) => {
539
- var _a;
540
- const key = (_a = row === null || row === void 0 ? void 0 : row.__key) !== null && _a !== void 0 ? _a : getRowKey(row);
541
- if (!expandedKeysSet.has(key))
542
- return;
543
- updateExpandedKeys(_expandedKeys.filter((k) => k !== key));
544
- };
545
- const toggleRowExpanded = (row) => {
546
- var _a;
547
- const key = (_a = row === null || row === void 0 ? void 0 : row.__key) !== null && _a !== void 0 ? _a : getRowKey(row);
548
- if (expandedKeysSet.has(key)) {
549
- updateExpandedKeys(_expandedKeys.filter((k) => k !== key));
550
- }
551
- else {
552
- updateExpandedKeys([..._expandedKeys, key]);
553
- }
554
- };
555
- const expandAllRows = () => {
556
- updateExpandedKeys([..._treeMeta.expandableKeys]);
557
- };
558
- const collapseAllRows = () => {
559
- updateExpandedKeys([]);
560
- };
561
437
  const selectRow = (row) => {
562
438
  const key = getRowKey(row);
563
439
  const newSelectionState = {
@@ -695,61 +571,6 @@ const TableProvider = (_a) => {
695
571
  const key = getRowKey(row);
696
572
  return focusedRowId === key;
697
573
  };
698
- const { _data, effectiveExpandedKeys } = useMemo(() => {
699
- let processedData = data || [];
700
- if (manualSorting !== true) {
701
- processedData = sortData(sortState);
702
- }
703
- const autoExpandedSet = new Set();
704
- if (manualFiltering !== true && _filterState) {
705
- const result = filterData(processedData, _filterState);
706
- processedData = result.rows;
707
- if (_treeOptions.enabled && _treeOptions.autoExpandOnMatch) {
708
- result.ancestorKeys.forEach((k) => autoExpandedSet.add(k));
709
- }
710
- }
711
- if (manualSearch !== true && search) {
712
- const result = searchData(processedData, search);
713
- processedData = result.rows;
714
- if (_treeOptions.enabled && _treeOptions.autoExpandOnMatch) {
715
- result.ancestorKeys.forEach((k) => autoExpandedSet.add(k));
716
- }
717
- }
718
- // Compute the effective expanded set: user expansion ∪ auto-expanded ancestors.
719
- // Never mutates user's _expandedKeys.
720
- const effective = new Set(expandedKeysSet);
721
- autoExpandedSet.forEach((k) => effective.add(k));
722
- // Final pass: drop rows whose ancestors are not in the effective set.
723
- if (_treeOptions.enabled) {
724
- const byKey = new Map();
725
- for (const r of processedData)
726
- byKey.set(r.__key, r);
727
- processedData = processedData.filter((row) => {
728
- let pk = row.__parentKey;
729
- while (pk) {
730
- if (!effective.has(pk))
731
- return false;
732
- const parent = byKey.get(pk);
733
- pk = parent === null || parent === void 0 ? void 0 : parent.__parentKey;
734
- }
735
- return true;
736
- });
737
- }
738
- return { _data: processedData, effectiveExpandedKeys: effective };
739
- }, [
740
- data,
741
- columnState,
742
- search,
743
- sortState,
744
- _filterState,
745
- expandedKeysSet,
746
- _treeOptions,
747
- ]);
748
- const isRowExpanded = (row) => {
749
- var _a;
750
- const key = (_a = row === null || row === void 0 ? void 0 : row.__key) !== null && _a !== void 0 ? _a : getRowKey(row);
751
- return effectiveExpandedKeys.has(key);
752
- };
753
574
  if (tableInstanceRef) {
754
575
  tableInstanceRef.current = {
755
576
  columnState,
@@ -766,13 +587,6 @@ const TableProvider = (_a) => {
766
587
  clearSelections,
767
588
  runSearch,
768
589
  clearSearch,
769
- expandRow,
770
- collapseRow,
771
- toggleRowExpanded,
772
- isRowExpanded,
773
- expandAllRows,
774
- collapseAllRows,
775
- getExpandedRowKeys: () => [..._expandedKeys],
776
590
  getTableState: () => {
777
591
  return {
778
592
  columnState,
@@ -785,14 +599,24 @@ const TableProvider = (_a) => {
785
599
  sortState,
786
600
  searchState: search,
787
601
  filterState: _filterState,
788
- expandedKeys: [..._expandedKeys],
789
602
  };
790
603
  },
791
604
  };
792
605
  }
793
- return (_jsx(TableContext.Provider, { value: Object.assign({ columnState,
794
- setColumnState,
795
- sortState, searchState: search, totalRecords,
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
+ return (_jsx(TableContext.Provider, { value: Object.assign({ columnState, setColumnState: updateColumnState, sortState, searchState: search, totalRecords,
796
620
  keyField,
797
621
  handleColumnReorder,
798
622
  handleColumnHeaderClick, selectionState: _selectionState, selectRow,
@@ -816,13 +640,6 @@ const TableProvider = (_a) => {
816
640
  stateStorage, tableHeight: height, tableMaxHeight: maxHeight, tableMinHeight: minHeight, compact, tableElement: tableElement, headerRowElm: headerRowElm, tableDimensions: tableDimensions, targetElm: targetElm, listElm: listElm, enableColumnResize,
817
641
  onSelectionChange,
818
642
  onColumnStateChange,
819
- onColumnReorder, onRowUpdated: onRowUpdated || (() => { }), tableMenuOptions, data: _data, treeOptions: _treeOptions, firstVisibleDataField, expandedKeys: _expandedKeys, effectiveExpandedKeys,
820
- isRowExpanded,
821
- toggleRowExpanded,
822
- expandRow,
823
- collapseRow,
824
- expandAllRows,
825
- collapseAllRows,
826
- onExpandedChange }, props), children: children }));
643
+ onColumnReorder, onRowUpdated: onRowUpdated || (() => { }), tableMenuOptions, data: _data }, props), children: children }));
827
644
  };
828
645
  export default TableProvider;
@@ -1,21 +1,20 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { ChevronRightIcon, Maximize2Icon } from "lucide-react";
2
+ import { Maximize2Icon } from "lucide-react";
3
3
  import ColumnResizer from "./ColumnResizer";
4
- import { InnerCellContent, TD, TR, TreeCellContent, TreeCellWrapper, TreeChevronButton, TreeChevronPlaceholder, TreeIndentSpacer, } from "./TableComponents";
4
+ import { InnerCellContent, TD, TR } 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, treeOptions, firstVisibleDataField, isRowExpanded, toggleRowExpanded, } = useTable();
11
+ const { columnState, enableActionButton, onActionButtonClick, actionButtonIcon: Icon, enableSelection, selectRow, deselectRow, isRowSelected, isRowFocused, onRowUpdated, } = 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
- 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", 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;
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) => {
19
18
  if (column.visible === false)
20
19
  return null;
21
20
  if (loading) {
@@ -25,20 +24,16 @@ const TableRow = ({ rowData, loading, rowStyle }) => {
25
24
  flex: column.width ? "0 0 auto" : "1",
26
25
  }, children: _jsx(LoadingCellIndicator, {}) }, index));
27
26
  }
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
27
  return (_jsxs(TD, { className: `mfui-td column-${column.columnId}`, "data-field": column.dataField, style: {
35
28
  width: column.width,
36
29
  minWidth: column.minWidth,
37
30
  flex: column.width ? "0 0 auto" : "1",
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: 12 }) })) : (_jsx(TreeChevronPlaceholder, {})), _jsx(TreeCellContent, { children: cellBody })] })) : (cellBody) })] }, index));
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));
42
37
  })] }));
43
38
  };
44
39
  export default TableRow;
@@ -0,0 +1,7 @@
1
+ import type { ColumnState, ColumnStateUpdate } from "../types";
2
+ export declare const resolveColumnStateUpdate: (columnStateUpdate: ColumnStateUpdate, previousColumnState: ColumnState[]) => ColumnState[];
3
+ export declare const notifyAndPersistColumnState: ({ nextColumnState, onColumnStateChange, persistColumnState, }: {
4
+ nextColumnState: ColumnState[];
5
+ onColumnStateChange?: (columnState: ColumnState[]) => void;
6
+ persistColumnState?: (columnState: ColumnState[]) => void;
7
+ }) => void;
@@ -0,0 +1,9 @@
1
+ export const resolveColumnStateUpdate = (columnStateUpdate, previousColumnState) => {
2
+ return typeof columnStateUpdate === "function"
3
+ ? columnStateUpdate(previousColumnState)
4
+ : columnStateUpdate;
5
+ };
6
+ export const notifyAndPersistColumnState = ({ nextColumnState, onColumnStateChange, persistColumnState, }) => {
7
+ onColumnStateChange === null || onColumnStateChange === void 0 ? void 0 : onColumnStateChange(nextColumnState);
8
+ persistColumnState === null || persistColumnState === void 0 ? void 0 : persistColumnState(nextColumnState);
9
+ };
@@ -10,42 +10,6 @@ export type StateStorage = {
10
10
  type: "localStorage";
11
11
  key: string;
12
12
  };
13
- export type TreeMode = "nested" | "flat";
14
- export type TreeOptions = {
15
- /** Enable tree-row behavior. Default: false */
16
- enabled?: boolean;
17
- /** Input shape. Default: "nested" */
18
- mode?: TreeMode;
19
- /** For mode="nested": field name that holds child array. Default: "children" */
20
- childrenField?: string;
21
- /** For mode="flat": field name that holds parent id. Default: "parentId" */
22
- parentIdField?: string;
23
- /** For mode="flat": field name that holds the row's stable id. Falls back to keyField. */
24
- idField?: string;
25
- /** Pixel width of one indentation level. Default: 16 */
26
- indentPx?: number;
27
- /** If a descendant matches search/filter, auto-expand its ancestors for the effective view. Default: true */
28
- autoExpandOnMatch?: boolean;
29
- };
30
- export type ResolvedTreeOptions = {
31
- enabled: boolean;
32
- mode: TreeMode;
33
- childrenField: string;
34
- parentIdField: string;
35
- idField?: string;
36
- indentPx: number;
37
- autoExpandOnMatch: boolean;
38
- };
39
- export type ExpandedKeysState = string[];
40
- export type OnExpandedChangeFn = (keys: string[]) => void;
41
- export type TreeMeta = {
42
- /** Map of row key -> parent row key (undefined for roots) */
43
- parentKeyMap: Map<string, string | undefined>;
44
- /** Map of parent key -> ordered list of direct child keys */
45
- childrenKeyMap: Map<string | undefined, string[]>;
46
- /** All row keys that have children (eligible for expansion) */
47
- expandableKeys: string[];
48
- };
49
13
  export type RenderOptions = {
50
14
  rowData: any;
51
15
  onRowUpdated?: (context: {
@@ -91,7 +55,6 @@ export type TableState = {
91
55
  sortState?: SortState;
92
56
  filterState?: Query;
93
57
  searchState?: string;
94
- expandedKeys?: ExpandedKeysState;
95
58
  };
96
59
  export interface onResizeFinishedProps {
97
60
  column: ColumnState;
@@ -105,6 +68,7 @@ export interface onResizeProps {
105
68
  width: number;
106
69
  }[];
107
70
  }
71
+ export type ColumnStateUpdate = ColumnState[] | ((prevColumnState: ColumnState[]) => ColumnState[]);
108
72
  export interface ResizeHandlerProps {
109
73
  event: React.MouseEvent<HTMLDivElement>;
110
74
  columnId?: string;
@@ -132,24 +96,13 @@ export type TableDimensions = {
132
96
  };
133
97
  export type TableContextType = {
134
98
  columnState: ColumnState[];
135
- setColumnState: React.Dispatch<React.SetStateAction<ColumnState[]>>;
99
+ setColumnState: (columnStateUpdate: ColumnStateUpdate) => void;
136
100
  sortState?: SortState | null;
137
101
  filterState?: Query | null;
138
102
  searchState?: string | null;
139
103
  data: any[];
140
104
  totalRecords?: number;
141
105
  keyField?: string;
142
- treeOptions: ResolvedTreeOptions;
143
- firstVisibleDataField?: string;
144
- expandedKeys: string[];
145
- effectiveExpandedKeys: Set<string>;
146
- isRowExpanded: (row: any) => boolean;
147
- toggleRowExpanded: (row: any) => void;
148
- expandRow: (row: any) => void;
149
- collapseRow: (row: any) => void;
150
- expandAllRows: () => void;
151
- collapseAllRows: () => void;
152
- onExpandedChange?: OnExpandedChangeFn;
153
106
  tableHeight?: number;
154
107
  tableMaxHeight?: number;
155
108
  tableMinHeight?: number;
@@ -261,11 +214,6 @@ export interface TableProviderProps {
261
214
  defaultFilterState?: Query;
262
215
  filterState?: Query;
263
216
  onFilterChange?: (e: Query) => void;
264
- treeOptions?: ResolvedTreeOptions;
265
- defaultExpandedKeys?: ExpandedKeysState;
266
- expandedKeys?: ExpandedKeysState;
267
- onExpandedChange?: OnExpandedChangeFn;
268
- treeMeta?: TreeMeta;
269
217
  }
270
218
  export interface TableRowProps {
271
219
  rowData: {
@@ -387,14 +335,10 @@ export interface TableProps {
387
335
  defaultFilterState?: Query;
388
336
  filterState?: Query;
389
337
  onFilterChange?: (e: Query) => void;
390
- treeOptions?: TreeOptions;
391
- defaultExpandedKeys?: ExpandedKeysState;
392
- expandedKeys?: ExpandedKeysState;
393
- onExpandedChange?: OnExpandedChangeFn;
394
338
  }
395
339
  export type TableInstance = {
396
340
  columnState: ColumnState[];
397
- setColumnState: (e: ColumnState[]) => void;
341
+ setColumnState: (columnStateUpdate: ColumnStateUpdate) => void;
398
342
  getColumnState: (dataField: string) => ColumnState | undefined;
399
343
  onColumnResize?: (e: OnColumnChangeProps) => void;
400
344
  onColumnReorder?: (e: OnColumnChangeProps) => void;
@@ -411,11 +355,4 @@ export type TableInstance = {
411
355
  clearSearch: () => void;
412
356
  clearSelections: () => void;
413
357
  getTableState: () => TableState;
414
- expandRow: (row: any) => void;
415
- collapseRow: (row: any) => void;
416
- toggleRowExpanded: (row: any) => void;
417
- isRowExpanded: (row: any) => boolean;
418
- expandAllRows: () => void;
419
- collapseAllRows: () => void;
420
- getExpandedRowKeys: () => string[];
421
358
  };
@@ -69,6 +69,10 @@ const lightVariant = {
69
69
  main: "#ff4500",
70
70
  contrastText: "#FFF",
71
71
  },
72
+ success: {
73
+ main: green[700],
74
+ contrastText: "#FFF",
75
+ },
72
76
  background: {
73
77
  default: "#FFF",
74
78
  paper: "#FFF",
@@ -228,6 +232,10 @@ const darkVariant = merge(lightVariant, {
228
232
  main: "#3f3f3f",
229
233
  contrastText: "#FFF",
230
234
  },
235
+ success: {
236
+ main: green[500],
237
+ contrastText: "#FFF",
238
+ },
231
239
  background: {
232
240
  default: "#222222",
233
241
  paper: "#333333",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monolith-forensics/monolith-ui",
3
- "version": "1.5.2-dev.0",
3
+ "version": "1.7.0",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "author": "Matt Danner (Monolith Forensics LLC)",