@monolith-forensics/monolith-ui 1.3.114 → 1.3.115-dev.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.
@@ -5,6 +5,6 @@ import useTable from "./useTable";
5
5
  const StaticRows = ({ targetElm, listElm }) => {
6
6
  var _a;
7
7
  const { data, loadingRowIds, keyField } = useTable();
8
- return (_jsx(TBody, { className: "mfui-tbody", children: _jsx(TableViewPort, { className: "mfui-tbody-viewport", ref: targetElm, children: _jsx(TableListElement, { className: "mfui-tbody-list", ref: listElm, children: (_a = data === null || data === void 0 ? void 0 : data.map) === null || _a === void 0 ? void 0 : _a.call(data, (row, index) => (_jsx(TableRow, { rowData: row, loading: !!keyField ? loadingRowIds === null || loadingRowIds === void 0 ? void 0 : loadingRowIds.includes(row[keyField]) : false }, index))) }) }) }));
8
+ return (_jsx(TBody, { className: "mfui-tbody", children: _jsx(TableViewPort, { className: "mfui-tbody-viewport", ref: targetElm, children: _jsx(TableListElement, { className: "mfui-tbody-list", ref: listElm, children: (_a = data === null || data === void 0 ? void 0 : data.map) === null || _a === void 0 ? void 0 : _a.call(data, (row) => (_jsx(TableRow, { rowData: row, loading: !!keyField ? loadingRowIds === null || loadingRowIds === void 0 ? void 0 : loadingRowIds.includes(row[keyField]) : false }, keyField ? row[keyField] : row.__key))) }) }) }));
9
9
  };
10
10
  export default StaticRows;
@@ -9,14 +9,14 @@ var __rest = (this && this.__rest) || function (s, e) {
9
9
  }
10
10
  return t;
11
11
  };
12
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
12
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
14
14
  import { useOverlayScrollbars } from "overlayscrollbars-react";
15
15
  import { StyledTable } from "./TableComponents";
16
16
  import TableHeader from "./TableHeader";
17
17
  import TableProvider from "./TableProvider";
18
18
  import shortUUID from "short-uuid";
19
- import VirtualizedRows from "./VirtualIzedRows";
19
+ import VirtualizedRows from "./VirtualizedRows";
20
20
  import StaticRows from "./StaticRows";
21
21
  import TableMenu from "./TableMenu";
22
22
  import useTable from "./useTable";
@@ -37,7 +37,7 @@ const TableContent = ({ children, }) => {
37
37
  height: tableHeight || "100%",
38
38
  maxHeight: tableMaxHeight,
39
39
  minHeight: tableMinHeight,
40
- }, ref: tableElement, "data-compact": compactState, children: [_jsx(LoadingIndicator, { visible: loading }), _jsx(TableHeader, { headerRowElm: headerRowElm }), visibleColumnCount === 0 && _jsx(_Fragment, {}), virtualized === true ? (_jsx(VirtualizedRows, { tableDimensions: tableDimensions, targetElm: targetElm, listElm: listElm, rowHeight: rowHeight, headerRowHeight: headerRowHeight })) : (_jsx(StaticRows, { targetElm: targetElm, listElm: listElm }))] })] }));
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 }))] })] }));
41
41
  };
42
42
  export const Table = (_a) => {
43
43
  var { data, columnProps, children } = _a, props = __rest(_a, ["data", "columnProps", "children"]) // pass through props straight to table context
@@ -109,6 +109,22 @@ export const Table = (_a) => {
109
109
  resizeObserver.unobserve(element);
110
110
  };
111
111
  }, []);
112
- const __data = useMemo(() => data === null || data === void 0 ? void 0 : data.map((d, i) => (Object.assign(Object.assign({}, d), { __key: shortUUID.generate(), __index: i }))), [data]);
112
+ // Persistent cache mapping each row object to a stable UUID.
113
+ // Uses a WeakMap so entries are automatically garbage collected
114
+ // when their corresponding row objects are no longer referenced.
115
+ const uuidCache = useRef(new WeakMap());
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);
123
+ if (!key) {
124
+ key = shortUUID.uuid();
125
+ uuidCache.current.set(d, key);
126
+ }
127
+ return Object.assign(Object.assign({}, d), { __key: key, __index: i });
128
+ }), [data]);
113
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 }) })));
114
130
  };
@@ -20,7 +20,7 @@ import moment from "moment";
20
20
  export const TableContext = createContext(null);
21
21
  const TableProvider = (_a) => {
22
22
  var { children, columns, tableInstanceRef, stateStorage, height, maxHeight, minHeight, focusedRowId } = _a, props = __rest(_a, ["children", "columns", "tableInstanceRef", "stateStorage", "height", "maxHeight", "minHeight", "focusedRowId"]);
23
- const _columns = columns
23
+ const _columns = useMemo(() => columns
24
24
  .map((child, index) => {
25
25
  if (child.dataField === "__key") {
26
26
  throw new Error("dataField cannot be __key");
@@ -36,7 +36,7 @@ const TableProvider = (_a) => {
36
36
  : TableDefaults.td.minWidth
37
37
  : TableDefaults.td.minWidth, columnId: shortUUID.generate(), enableResize: resolveColumnResize(child, props.enableColumnResize) });
38
38
  })
39
- .sort((a, b) => a.orderValue - b.orderValue);
39
+ .sort((a, b) => a.orderValue - b.orderValue), [columns, props.enableColumnResize]);
40
40
  const savedTableState = useMemo(() => {
41
41
  return (stateStorage === null || stateStorage === void 0 ? void 0 : stateStorage.enabled)
42
42
  ? StateStorage.getTableState(stateStorage.key)
@@ -221,157 +221,114 @@ const TableProvider = (_a) => {
221
221
  });
222
222
  });
223
223
  };
224
+ const rowMatchesRule = (row, rule) => {
225
+ var _a, _b, _c, _d;
226
+ const column = columnState.find((col) => col.dataField === rule.dataField);
227
+ const { value, operator, dataField } = rule;
228
+ if (operator.value === "isEmpty") {
229
+ return !row[dataField];
230
+ }
231
+ if (operator.value === "isNotEmpty") {
232
+ return !!row[dataField];
233
+ }
234
+ if (!value || value.length === 0) {
235
+ return true;
236
+ }
237
+ switch (operator.value) {
238
+ case "=":
239
+ if ((column === null || column === void 0 ? void 0 : column.dataType) === "date") {
240
+ return moment(row[dataField]).isSame(moment(value[0]), "day");
241
+ }
242
+ else if ((column === null || column === void 0 ? void 0 : column.dataType) === "datetime") {
243
+ return moment(row[dataField]).isSame(moment(value[0]));
244
+ }
245
+ return row[dataField] == value[0];
246
+ case "!=":
247
+ if ((column === null || column === void 0 ? void 0 : column.dataType) === "date") {
248
+ return !moment(row[dataField]).isSame(moment(value[0]), "day");
249
+ }
250
+ else if ((column === null || column === void 0 ? void 0 : column.dataType) === "datetime") {
251
+ return !moment(row[dataField]).isSame(moment(value[0]));
252
+ }
253
+ return row[dataField] != value[0];
254
+ case ">":
255
+ if ((column === null || column === void 0 ? void 0 : column.dataType) === "date") {
256
+ return moment(row[dataField]).isAfter(moment(value[0]), "day");
257
+ }
258
+ else if ((column === null || column === void 0 ? void 0 : column.dataType) === "datetime") {
259
+ return moment(row[dataField]).isAfter(moment(value[0]));
260
+ }
261
+ return row[dataField] > value[0];
262
+ case "<":
263
+ if ((column === null || column === void 0 ? void 0 : column.dataType) === "date") {
264
+ return moment(row[dataField]).isBefore(moment(value[0]), "day");
265
+ }
266
+ else if ((column === null || column === void 0 ? void 0 : column.dataType) === "datetime") {
267
+ return moment(row[dataField]).isBefore(moment(value[0]));
268
+ }
269
+ return row[dataField] < value[0];
270
+ case ">=":
271
+ if ((column === null || column === void 0 ? void 0 : column.dataType) === "date") {
272
+ return moment(row[dataField]).isSameOrAfter(moment(value[0]), "day");
273
+ }
274
+ else if ((column === null || column === void 0 ? void 0 : column.dataType) === "datetime") {
275
+ return moment(row[dataField]).isSameOrAfter(moment(value[0]));
276
+ }
277
+ return row[dataField] >= value[0];
278
+ case "<=":
279
+ if ((column === null || column === void 0 ? void 0 : column.dataType) === "date") {
280
+ return moment(row[dataField]).isSameOrBefore(moment(value[0]), "day");
281
+ }
282
+ else if ((column === null || column === void 0 ? void 0 : column.dataType) === "datetime") {
283
+ return moment(row[dataField]).isSameOrBefore(moment(value[0]));
284
+ }
285
+ return row[dataField] <= value[0];
286
+ case "between":
287
+ if ((column === null || column === void 0 ? void 0 : column.dataType) === "date") {
288
+ return moment(row[dataField]).isBetween(moment(value === null || value === void 0 ? void 0 : value[0]), moment(value === null || value === void 0 ? void 0 : value[1]), "day", "[]");
289
+ }
290
+ else if ((column === null || column === void 0 ? void 0 : column.dataType) === "datetime") {
291
+ return moment(row[dataField]).isBetween(moment(value === null || value === void 0 ? void 0 : value[0]), moment(value === null || value === void 0 ? void 0 : value[1]), undefined, "[]");
292
+ }
293
+ return (row[dataField] >= value[0] &&
294
+ row[dataField] <= value[1]);
295
+ case "notBetween":
296
+ if ((column === null || column === void 0 ? void 0 : column.dataType) === "date") {
297
+ return !moment(row[dataField]).isBetween(moment(value === null || value === void 0 ? void 0 : value[0]), moment(value === null || value === void 0 ? void 0 : value[1]), "day", "[]");
298
+ }
299
+ else if ((column === null || column === void 0 ? void 0 : column.dataType) === "datetime") {
300
+ return !moment(row[dataField]).isBetween(moment(value === null || value === void 0 ? void 0 : value[0]), moment(value === null || value === void 0 ? void 0 : value[1]), undefined, "[]");
301
+ }
302
+ return (row[dataField] < value[0] ||
303
+ row[dataField] > value[1]);
304
+ case "contains":
305
+ return (_a = row[dataField]) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes((_b = value[0]) === null || _b === void 0 ? void 0 : _b.toString().toLowerCase());
306
+ case "doesNotContain":
307
+ return !((_c = row[dataField]) === null || _c === void 0 ? void 0 : _c.toLowerCase().includes((_d = value[0]) === null || _d === void 0 ? void 0 : _d.toString().toLowerCase()));
308
+ case "in":
309
+ if (!Array.isArray(value) || value.length === 0) {
310
+ return true;
311
+ }
312
+ return !!value.find((v) => v == row[dataField]);
313
+ case "nin":
314
+ if (!Array.isArray(value) || value.length === 0) {
315
+ return true;
316
+ }
317
+ return !value.find((v) => v == row[dataField]);
318
+ default:
319
+ return true;
320
+ }
321
+ };
224
322
  const filterData = (filter) => {
225
323
  if (!props.data)
226
324
  return [];
227
- // create copy of data
228
- let processedData = props.data.map((row) => row);
229
325
  const { combinator, rules } = filter;
230
326
  if (!combinator || !rules)
231
327
  return props.data;
232
- for (const rule of rules) {
233
- const column = columnState.find((col) => col.dataField === rule.dataField);
234
- const { value, operator, dataField } = rule;
235
- if (operator.value === "isEmpty" || operator.value === "isNotEmpty") {
236
- processedData = processedData.filter((row) => {
237
- return operator.value === "isEmpty"
238
- ? !row[dataField]
239
- : row[dataField];
240
- });
241
- }
242
- if (!value || value.length === 0) {
243
- continue;
244
- }
245
- switch (operator.value) {
246
- case "=":
247
- processedData = processedData.filter((row) => {
248
- if ((column === null || column === void 0 ? void 0 : column.dataType) === "date") {
249
- return moment(row[dataField]).isSame(moment(value[0]), "day");
250
- }
251
- else if ((column === null || column === void 0 ? void 0 : column.dataType) === "datetime") {
252
- return moment(row[dataField]).isSame(moment(value[0]));
253
- }
254
- return row[dataField] == value[0];
255
- });
256
- break;
257
- case "!=":
258
- processedData = processedData.filter((row) => {
259
- if ((column === null || column === void 0 ? void 0 : column.dataType) === "date") {
260
- return !moment(row[dataField]).isSame(moment(value[0]), "day");
261
- }
262
- else if ((column === null || column === void 0 ? void 0 : column.dataType) === "datetime") {
263
- return !moment(row[dataField]).isSame(moment(value[0]));
264
- }
265
- return row[dataField] != value[0];
266
- });
267
- break;
268
- case ">":
269
- processedData = processedData.filter((row) => {
270
- if ((column === null || column === void 0 ? void 0 : column.dataType) === "date") {
271
- return moment(row[dataField]).isAfter(moment(value[0]), "day");
272
- }
273
- else if ((column === null || column === void 0 ? void 0 : column.dataType) === "datetime") {
274
- return moment(row[dataField]).isAfter(moment(value[0]));
275
- }
276
- return row[dataField] > value[0];
277
- });
278
- break;
279
- case "<":
280
- processedData = processedData.filter((row) => {
281
- if ((column === null || column === void 0 ? void 0 : column.dataType) === "date") {
282
- return moment(row[dataField]).isBefore(moment(value[0]), "day");
283
- }
284
- else if ((column === null || column === void 0 ? void 0 : column.dataType) === "datetime") {
285
- return moment(row[dataField]).isBefore(moment(value[0]));
286
- }
287
- return row[dataField] < value[0];
288
- });
289
- break;
290
- case ">=":
291
- processedData = processedData.filter((row) => {
292
- if ((column === null || column === void 0 ? void 0 : column.dataType) === "date") {
293
- return moment(row[dataField]).isSameOrAfter(moment(value[0]), "day");
294
- }
295
- else if ((column === null || column === void 0 ? void 0 : column.dataType) === "datetime") {
296
- return moment(row[dataField]).isSameOrAfter(moment(value[0]));
297
- }
298
- return row[dataField] >= value[0];
299
- });
300
- break;
301
- case "<=":
302
- processedData = processedData.filter((row) => {
303
- if ((column === null || column === void 0 ? void 0 : column.dataType) === "date") {
304
- return moment(row[dataField]).isSameOrBefore(moment(value[0]), "day");
305
- }
306
- else if ((column === null || column === void 0 ? void 0 : column.dataType) === "datetime") {
307
- return moment(row[dataField]).isSameOrBefore(moment(value[0]));
308
- }
309
- return row[dataField] <= value[0];
310
- });
311
- break;
312
- case "between":
313
- processedData = processedData.filter((row) => {
314
- if ((column === null || column === void 0 ? void 0 : column.dataType) === "date") {
315
- return moment(row[dataField]).isBetween(moment(value === null || value === void 0 ? void 0 : value[0]), moment(value === null || value === void 0 ? void 0 : value[1]), "day", "[]");
316
- }
317
- else if ((column === null || column === void 0 ? void 0 : column.dataType) === "datetime") {
318
- return moment(row[dataField]).isBetween(moment(value === null || value === void 0 ? void 0 : value[0]), moment(value === null || value === void 0 ? void 0 : value[1]), undefined, "[]");
319
- }
320
- return (row[dataField] >= value[0] &&
321
- row[dataField] <= value[1]);
322
- });
323
- break;
324
- case "notBetween":
325
- processedData = processedData.filter((row) => {
326
- if ((column === null || column === void 0 ? void 0 : column.dataType) === "date") {
327
- return !moment(row[dataField]).isBetween(moment(value === null || value === void 0 ? void 0 : value[0]), moment(value === null || value === void 0 ? void 0 : value[1]), "day", "[]");
328
- }
329
- else if ((column === null || column === void 0 ? void 0 : column.dataType) === "datetime") {
330
- return !moment(row[dataField]).isBetween(moment(value === null || value === void 0 ? void 0 : value[0]), moment(value === null || value === void 0 ? void 0 : value[1]), undefined, "[]");
331
- }
332
- return (row[dataField] < value[0] ||
333
- row[dataField] > value[1]);
334
- });
335
- break;
336
- case "contains":
337
- processedData = processedData.filter((row) => {
338
- var _a, _b;
339
- return (_a = row[dataField]) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes((_b = value[0]) === null || _b === void 0 ? void 0 : _b.toString().toLowerCase());
340
- });
341
- break;
342
- case "doesNotContain":
343
- processedData = processedData.filter((row) => {
344
- var _a, _b;
345
- return !((_a = row[dataField]) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes((_b = value[0]) === null || _b === void 0 ? void 0 : _b.toString().toLowerCase()));
346
- });
347
- break;
348
- case "in":
349
- if (!Array.isArray(value)) {
350
- break;
351
- }
352
- else if (value.length === 0) {
353
- break;
354
- }
355
- processedData = processedData.filter((row) => {
356
- return value.find((v) => v == row[dataField]);
357
- });
358
- break;
359
- case "nin":
360
- if (!Array.isArray(value)) {
361
- break;
362
- }
363
- else if (value.length === 0) {
364
- break;
365
- }
366
- processedData = processedData.filter((row) => {
367
- return !value.find((v) => v == row[dataField]);
368
- });
369
- break;
370
- default:
371
- break;
372
- }
328
+ if (combinator === "or") {
329
+ return props.data.filter((row) => rules.some((rule) => rowMatchesRule(row, rule)));
373
330
  }
374
- return processedData;
331
+ return props.data.filter((row) => rules.every((rule) => rowMatchesRule(row, rule)));
375
332
  };
376
333
  const toggleColumnVisibility = (dataField) => {
377
334
  const newColumnState = columnState.map((col) => {
@@ -386,8 +343,8 @@ const TableProvider = (_a) => {
386
343
  setCompactState((prev) => !prev);
387
344
  };
388
345
  const getColumnVisibility = (dataField) => {
389
- var _a;
390
- return (((_a = columnState.find((col) => col.dataField === dataField)) === null || _a === void 0 ? void 0 : _a.visible) || true);
346
+ var _a, _b;
347
+ return ((_b = (_a = columnState.find((col) => col.dataField === dataField)) === null || _a === void 0 ? void 0 : _a.visible) !== null && _b !== void 0 ? _b : true);
391
348
  };
392
349
  const saveSelectionState = (selectionState) => {
393
350
  if (stateStorage === null || stateStorage === void 0 ? void 0 : stateStorage.enabled) {
@@ -427,7 +384,8 @@ const TableProvider = (_a) => {
427
384
  };
428
385
  const deselectRow = (row) => {
429
386
  var _a;
430
- const key = !!props.keyField ? props.keyField : "__key";
387
+ const key = !!props.keyField ? row[props.keyField] : row.__key;
388
+ const keyField = !!props.keyField ? props.keyField : "__key";
431
389
  const newSelectionState = {
432
390
  selectedRowKeys,
433
391
  excludedRowKeys,
@@ -439,14 +397,14 @@ const TableProvider = (_a) => {
439
397
  newSelectionState.selectionStatus = SelectionStatus.SomeExcluded;
440
398
  newSelectionState.excludedRowKeys = [
441
399
  ...newSelectionState.excludedRowKeys,
442
- row[key],
400
+ key,
443
401
  ];
444
402
  }
445
403
  if (selectedRowKeys.length === 1) {
446
404
  newSelectionState.selectionStatus = SelectionStatus.None;
447
405
  }
448
406
  newSelectionState.selectedRowKeys =
449
- newSelectionState.selectedRowKeys.filter((k) => k !== row[key]);
407
+ newSelectionState.selectedRowKeys.filter((k) => k !== key);
450
408
  newSelectionState.totalSelected = calculateSelectionTotal(newSelectionState, props === null || props === void 0 ? void 0 : props.totalRecords, props.data.length);
451
409
  setSelectionStatus(newSelectionState.selectionStatus);
452
410
  setSelectedRowKeys(newSelectionState.selectedRowKeys);
@@ -552,7 +510,7 @@ const TableProvider = (_a) => {
552
510
  };
553
511
  }
554
512
  const _data = useMemo(() => {
555
- let processedData = props.data;
513
+ let processedData = props.data; // create a new array to avoid mutating the original data
556
514
  if (props.manualSorting !== true) {
557
515
  processedData = sortData(sortState);
558
516
  }
@@ -22,7 +22,7 @@ const VirtualizedRows = ({ tableDimensions, targetElm, listElm, rowHeight, heade
22
22
  }, children: ({ data, index, style }) => {
23
23
  var _a;
24
24
  const row = ((_a = data === null || data === void 0 ? void 0 : data.data) === null || _a === void 0 ? void 0 : _a[index]) || {};
25
- return (_jsx(TableRow, { rowData: row, loading: !!keyField ? loadingRowIds === null || loadingRowIds === void 0 ? void 0 : loadingRowIds.includes(row[keyField]) : false, rowStyle: Object.assign(Object.assign({}, style), { height: rowHeight }) }, index));
25
+ return (_jsx(TableRow, { rowData: row, loading: !!keyField ? loadingRowIds === null || loadingRowIds === void 0 ? void 0 : loadingRowIds.includes(row[keyField]) : false, rowStyle: Object.assign(Object.assign({}, style), { height: rowHeight }) }, keyField ? row[keyField] : row.__key));
26
26
  } }) }));
27
27
  };
28
28
  export default VirtualizedRows;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monolith-forensics/monolith-ui",
3
- "version": "1.3.114",
3
+ "version": "1.3.115-dev.0",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "author": "Matt Danner (Monolith Forensics LLC)",