@homebound/beam 2.118.0 → 2.118.3
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.
- package/dist/components/Table/GridTable.d.ts +1 -0
- package/dist/components/Table/GridTable.js +14 -14
- package/dist/components/Table/RowState.d.ts +2 -2
- package/dist/components/Table/RowState.js +10 -0
- package/dist/components/Table/sortRows.d.ts +3 -3
- package/dist/components/Table/sortRows.js +2 -5
- package/dist/components/Table/useSortState.d.ts +2 -1
- package/dist/components/Table/useSortState.js +11 -5
- package/dist/hooks/useComputed.js +5 -1
- package/package.json +4 -3
|
@@ -149,6 +149,7 @@ export interface GridTableProps<R extends Kinded, S, X> {
|
|
|
149
149
|
/** Whether the header row should be sticky. */
|
|
150
150
|
stickyHeader?: boolean;
|
|
151
151
|
stickyOffset?: string;
|
|
152
|
+
/** Configures sorting via a hash, does not need to be stable. */
|
|
152
153
|
sorting?: GridSortConfig<S>;
|
|
153
154
|
/** Shown in the first row slot, if there are no rows to show, i.e. 'No rows found'. */
|
|
154
155
|
fallbackMessage?: string;
|
|
@@ -85,7 +85,7 @@ exports.setGridTableDefaults = setGridTableDefaults;
|
|
|
85
85
|
*/
|
|
86
86
|
function GridTable(props) {
|
|
87
87
|
var _a, _b, _c, _d;
|
|
88
|
-
const { id = "gridTable", as = "div", columns, rows, style = defaults.style, rowStyles, stickyHeader = defaults.stickyHeader, stickyOffset = "0", xss,
|
|
88
|
+
const { id = "gridTable", as = "div", columns, rows, style = defaults.style, rowStyles, stickyHeader = defaults.stickyHeader, stickyOffset = "0", xss, filter, filterMaxRows, fallbackMessage = "No rows found.", infoMessage, setRowCount, observeRows, persistCollapse, api: callerApi, resizeTarget, activeRowId, } = props;
|
|
89
89
|
// Create a ref that always contains the latest rows, for our effectively-singleton RowState to use
|
|
90
90
|
const rowsRef = (0, react_1.useRef)(rows);
|
|
91
91
|
rowsRef.current = rows;
|
|
@@ -130,14 +130,14 @@ function GridTable(props) {
|
|
|
130
130
|
const columnSizes = (0, columnSizes_1.useSetupColumnSizes)(style, columns, tableRef, resizeTarget);
|
|
131
131
|
// Make a single copy of our current collapsed state, so we'll have a single observer.
|
|
132
132
|
const collapsedIds = (0, hooks_1.useComputed)(() => rowState.collapsedIds, [rowState]);
|
|
133
|
-
const [sortState, setSortKey] = (0, useSortState_1.useSortState)(columns, sorting);
|
|
133
|
+
const [sortState, setSortKey, sortOn] = (0, useSortState_1.useSortState)(columns, props.sorting);
|
|
134
134
|
const maybeSorted = (0, react_1.useMemo)(() => {
|
|
135
|
-
if (
|
|
135
|
+
if (sortOn === "client" && sortState) {
|
|
136
136
|
// If using client-side sort, the sortState use S = number
|
|
137
137
|
return (0, sortRows_1.sortRows)(columns, rows, sortState);
|
|
138
138
|
}
|
|
139
139
|
return rows;
|
|
140
|
-
}, [columns, rows,
|
|
140
|
+
}, [columns, rows, sortOn, sortState]);
|
|
141
141
|
// Filter rows - ensures parent rows remain in the list if any children match the filter.
|
|
142
142
|
const filterRows = (0, react_1.useCallback)((acc, row) => {
|
|
143
143
|
var _a, _b, _c;
|
|
@@ -163,13 +163,13 @@ function GridTable(props) {
|
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
165
|
return acc;
|
|
166
|
-
}, [filter, collapsedIds]);
|
|
166
|
+
}, [filter, collapsedIds, columns]);
|
|
167
167
|
// Flatten + component-ize the sorted rows.
|
|
168
168
|
let [headerRows, filteredRows] = (0, react_1.useMemo)(() => {
|
|
169
169
|
function makeRowComponent(row, level) {
|
|
170
170
|
// We only pass sortState to header rows, b/c non-headers rows shouldn't have to re-render on sorting
|
|
171
171
|
// changes, and so by not passing the sortProps, it means the data rows' React.memo will still cache them.
|
|
172
|
-
const sortProps = row.kind === "header" ? {
|
|
172
|
+
const sortProps = row.kind === "header" ? { sortOn, sortState, setSortKey } : { sortOn };
|
|
173
173
|
const RowComponent = observeRows ? ObservedGridRow : MemoizedGridRow;
|
|
174
174
|
return ((0, jsx_runtime_1.jsx)(RowComponent, Object.assign({}, {
|
|
175
175
|
as,
|
|
@@ -222,10 +222,10 @@ function GridTable(props) {
|
|
|
222
222
|
as,
|
|
223
223
|
maybeSorted,
|
|
224
224
|
columns,
|
|
225
|
-
|
|
225
|
+
filterRows,
|
|
226
226
|
style,
|
|
227
227
|
rowStyles,
|
|
228
|
-
|
|
228
|
+
sortOn,
|
|
229
229
|
setSortKey,
|
|
230
230
|
sortState,
|
|
231
231
|
stickyHeader,
|
|
@@ -240,7 +240,7 @@ function GridTable(props) {
|
|
|
240
240
|
tooManyClientSideRows = true;
|
|
241
241
|
filteredRows = filteredRows.slice(0, filterMaxRows);
|
|
242
242
|
}
|
|
243
|
-
rowState.
|
|
243
|
+
rowState.setVisibleRows(filteredRows.map(([row]) => { var _a; return (_a = row === null || row === void 0 ? void 0 : row.id) !== null && _a !== void 0 ? _a : ""; }));
|
|
244
244
|
// Push back to the caller a way to ask us where a row is.
|
|
245
245
|
const { rowLookup } = props;
|
|
246
246
|
if (rowLookup) {
|
|
@@ -484,7 +484,7 @@ function getFirstOrLastCellCss(style, columnIndex, columns) {
|
|
|
484
484
|
// We extract GridRow to its own mini-component primarily so we can React.memo'ize it.
|
|
485
485
|
function GridRow(props) {
|
|
486
486
|
var _a;
|
|
487
|
-
const { as, columns, row, style, rowStyles, stickyHeader, stickyOffset,
|
|
487
|
+
const { as, columns, row, style, rowStyles, stickyHeader, stickyOffset, sortOn, sortState, setSortKey, openCards, columnSizes, level, getCount, api, ...others } = props;
|
|
488
488
|
const { rowState } = (0, react_1.useContext)(RowState_1.RowStateContext);
|
|
489
489
|
const isActive = (0, hooks_1.useComputed)(() => rowState.activeRowId === `${row.kind}_${row.id}`, [row, rowState]);
|
|
490
490
|
// We treat the "header" kind as special for "good defaults" styling
|
|
@@ -527,12 +527,12 @@ function GridRow(props) {
|
|
|
527
527
|
}
|
|
528
528
|
const maybeContent = applyRowFn(column, row, api);
|
|
529
529
|
currentColspan = isGridCellContent(maybeContent) ? (_a = maybeContent.colspan) !== null && _a !== void 0 ? _a : 1 : 1;
|
|
530
|
-
const canSortColumn = (
|
|
531
|
-
(
|
|
530
|
+
const canSortColumn = (sortOn === "client" && column.clientSideSort !== false) ||
|
|
531
|
+
(sortOn === "server" && !!column.serverSideSortKey);
|
|
532
532
|
const alignment = getAlignment(column, maybeContent);
|
|
533
533
|
const justificationCss = getJustification(column, maybeContent, as, alignment);
|
|
534
|
-
const content = toContent(maybeContent, isHeader, canSortColumn,
|
|
535
|
-
(0, sortRows_1.ensureClientSideSortValueIsSortable)(
|
|
534
|
+
const content = toContent(maybeContent, isHeader, canSortColumn, sortOn === "client", style, as, alignment);
|
|
535
|
+
(0, sortRows_1.ensureClientSideSortValueIsSortable)(sortOn, isHeader, column, columnIndex, maybeContent);
|
|
536
536
|
const maybeNestedCardColumnIndex = columnIndex + (style.nestedCards ? 1 : 0);
|
|
537
537
|
const maybeSticky = (_b = ((isGridCellContent(maybeContent) && maybeContent.sticky) || column.sticky)) !== null && _b !== void 0 ? _b : undefined;
|
|
538
538
|
const maybeStickyColumnStyles = maybeSticky && columnSizes
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ObservableSet } from "mobx";
|
|
2
1
|
import React, { MutableRefObject } from "react";
|
|
3
2
|
import { GridDataRow } from "./GridTable";
|
|
4
3
|
export declare type SelectedState = "checked" | "unchecked" | "partial";
|
|
@@ -22,12 +21,13 @@ export declare class RowState {
|
|
|
22
21
|
private persistCollapse;
|
|
23
22
|
private readonly collapsedRows;
|
|
24
23
|
private readonly selectedRows;
|
|
25
|
-
visibleRows
|
|
24
|
+
private visibleRows;
|
|
26
25
|
activeRowId: string | undefined;
|
|
27
26
|
/**
|
|
28
27
|
* Creates the `RowState` for a given `GridTable`.
|
|
29
28
|
*/
|
|
30
29
|
constructor(rows: MutableRefObject<GridDataRow<any>[]>, persistCollapse: string | undefined, activeRowId: string | undefined);
|
|
30
|
+
setVisibleRows(rowIds: string[]): void;
|
|
31
31
|
get selectedIds(): string[];
|
|
32
32
|
getSelected(id: string): SelectedState;
|
|
33
33
|
selectRow(id: string, selected: boolean): void;
|
|
@@ -45,6 +45,16 @@ class RowState {
|
|
|
45
45
|
this.selectedRows.merge(map);
|
|
46
46
|
}, { equals: mobx_1.comparer.shallow });
|
|
47
47
|
}
|
|
48
|
+
setVisibleRows(rowIds) {
|
|
49
|
+
// ObservableSet doesn't seem to do a `diff` inside `replace` before firing
|
|
50
|
+
// observers/reactions that watch it, which can lead to render loops with the
|
|
51
|
+
// application page is observing `GridTableApi.getSelectedRows`, and merely
|
|
52
|
+
// the act of rendering GridTable (w/o row changes) causes it's `useComputed`
|
|
53
|
+
// to be triggered.
|
|
54
|
+
if (!mobx_1.comparer.shallow(rowIds, [...this.visibleRows.values()])) {
|
|
55
|
+
this.visibleRows.replace(rowIds);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
48
58
|
get selectedIds() {
|
|
49
59
|
// Return only ids that are fully checked, i.e. not partial
|
|
50
60
|
const ids = [...this.selectedRows.entries()]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from "react";
|
|
2
|
-
import { GridCellContent, GridColumn, GridDataRow,
|
|
3
|
-
import { SortState } from "./useSortState";
|
|
2
|
+
import { GridCellContent, GridColumn, GridDataRow, Kinded } from "./GridTable";
|
|
3
|
+
import { SortOn, SortState } from "./useSortState";
|
|
4
4
|
export declare function sortRows<R extends Kinded>(columns: GridColumn<R>[], rows: GridDataRow<R>[], sortState: SortState<number>): GridDataRow<R>[];
|
|
5
|
-
export declare function ensureClientSideSortValueIsSortable(
|
|
5
|
+
export declare function ensureClientSideSortValueIsSortable(sortOn: SortOn, isHeader: boolean, column: GridColumn<any>, idx: number, maybeContent: ReactNode | GridCellContent): void;
|
|
@@ -64,11 +64,8 @@ function sortValue(value) {
|
|
|
64
64
|
}
|
|
65
65
|
return maybeFn;
|
|
66
66
|
}
|
|
67
|
-
function ensureClientSideSortValueIsSortable(
|
|
68
|
-
if (process.env.NODE_ENV !== "production" &&
|
|
69
|
-
!isHeader &&
|
|
70
|
-
(sorting === null || sorting === void 0 ? void 0 : sorting.on) === "client" &&
|
|
71
|
-
column.clientSideSort !== false) {
|
|
67
|
+
function ensureClientSideSortValueIsSortable(sortOn, isHeader, column, idx, maybeContent) {
|
|
68
|
+
if (process.env.NODE_ENV !== "production" && !isHeader && sortOn === "client" && column.clientSideSort !== false) {
|
|
72
69
|
const value = sortValue(maybeContent);
|
|
73
70
|
if (!canClientSideSort(value)) {
|
|
74
71
|
throw new Error(`Column ${idx} passed an unsortable value, use GridCellContent or clientSideSort=false`);
|
|
@@ -8,6 +8,7 @@ import { Direction, GridColumn, GridSortConfig, Kinded } from "./GridTable";
|
|
|
8
8
|
* b) it's index in the `columns` array, if client-side sorting
|
|
9
9
|
*/
|
|
10
10
|
export declare type SortState<S> = readonly [S, Direction];
|
|
11
|
+
export declare type SortOn = "client" | "server" | undefined;
|
|
11
12
|
/** Small custom hook that wraps the "setSortColumn inverts the current sort" logic. */
|
|
12
|
-
export declare function useSortState<R extends Kinded, S>(columns: GridColumn<R, S>[], sorting?: GridSortConfig<S>): [SortState<S> | undefined, (value: S) => void];
|
|
13
|
+
export declare function useSortState<R extends Kinded, S>(columns: GridColumn<R, S>[], sorting?: GridSortConfig<S>): [SortState<S> | undefined, (value: S) => void, SortOn];
|
|
13
14
|
export declare function deriveSortState<S>(currentSortState: SortState<S> | undefined, clickedKey: S, initialSortState: SortState<S> | undefined): SortState<S> | undefined;
|
|
@@ -28,20 +28,26 @@ function useSortState(columns, sorting) {
|
|
|
28
28
|
else {
|
|
29
29
|
return sorting === null || sorting === void 0 ? void 0 : sorting.value;
|
|
30
30
|
}
|
|
31
|
-
},
|
|
31
|
+
},
|
|
32
|
+
// We want to allow the user to not memoize `GridTableProps.sorting` b/c for the
|
|
33
|
+
// initialSortState calc, it's just a bunch of surely hard-coded primitives like
|
|
34
|
+
// sort on client/server, which column is initial.
|
|
35
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
36
|
+
[columns]);
|
|
32
37
|
const [sortState, setSortState] = (0, react_1.useState)(initialSortState);
|
|
33
38
|
// Make a custom setSortKey that is useState-like but contains the ASC->DESC->RESET logic.
|
|
39
|
+
const onSort = (sorting === null || sorting === void 0 ? void 0 : sorting.on) === "server" ? sorting.onSort : undefined;
|
|
34
40
|
const setSortKey = (0, react_1.useCallback)((clickedKey) => {
|
|
35
41
|
const newState = deriveSortState(sortState, clickedKey, initialSortState);
|
|
36
42
|
setSortState(newState);
|
|
37
|
-
if (
|
|
43
|
+
if (onSort) {
|
|
38
44
|
const [newKey, newDirection] = newState !== null && newState !== void 0 ? newState : [undefined, undefined];
|
|
39
|
-
|
|
45
|
+
onSort(newKey, newDirection);
|
|
40
46
|
}
|
|
41
47
|
},
|
|
42
48
|
// Note that sorting.onSort is not listed here, so we bind to whatever the 1st sorting.onSort was
|
|
43
|
-
[sortState,
|
|
44
|
-
return [sortState, setSortKey];
|
|
49
|
+
[initialSortState, sortState, onSort]);
|
|
50
|
+
return [sortState, setSortKey, sorting === null || sorting === void 0 ? void 0 : sorting.on];
|
|
45
51
|
}
|
|
46
52
|
exports.useSortState = useSortState;
|
|
47
53
|
// Exported for testing purposes
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.useComputed = void 0;
|
|
4
7
|
const mobx_1 = require("mobx");
|
|
5
8
|
const react_1 = require("react");
|
|
9
|
+
const fast_deep_equal_1 = __importDefault(require("fast-deep-equal"));
|
|
6
10
|
/** Evaluates a computed function `fn` to a regular value and triggers a re-render whenever it changes. */
|
|
7
11
|
function useComputed(fn, deps) {
|
|
8
12
|
// We always return the useRef value, and use this just to trigger re-renders
|
|
@@ -33,7 +37,7 @@ function useComputed(fn, deps) {
|
|
|
33
37
|
// Only trigger a re-render if this is not the 1st autorun. Note
|
|
34
38
|
// that if deps has changed, we're inherently in a re-render so also
|
|
35
39
|
// don't need to trigger an additional re-render.
|
|
36
|
-
if (oldHasRun && newValue
|
|
40
|
+
if (oldHasRun && !(0, fast_deep_equal_1.default)(newValue, oldValue)) {
|
|
37
41
|
setTick((tick) => tick + 1);
|
|
38
42
|
}
|
|
39
43
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@homebound/beam",
|
|
3
|
-
"version": "2.118.
|
|
3
|
+
"version": "2.118.3",
|
|
4
4
|
"author": "Homebound",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"change-case": "^4.1.2",
|
|
40
40
|
"date-fns": "^2.21.3",
|
|
41
41
|
"dompurify": "^2.3.0",
|
|
42
|
+
"fast-deep-equal": "^3.1.3",
|
|
42
43
|
"framer-motion": "^4.1.11",
|
|
43
44
|
"memoize-one": "^5.2.1",
|
|
44
45
|
"react-aria": "^3.10.0",
|
|
@@ -50,8 +51,8 @@
|
|
|
50
51
|
"react-virtuoso": "^2.4.0",
|
|
51
52
|
"tributejs": "^5.1.3",
|
|
52
53
|
"trix": "^1.3.1",
|
|
53
|
-
"use-
|
|
54
|
-
"use-
|
|
54
|
+
"use-debounce": "^7.0.1",
|
|
55
|
+
"use-query-params": "^1.2.2"
|
|
55
56
|
},
|
|
56
57
|
"peerDependencies": {
|
|
57
58
|
"@emotion/react": ">=11",
|