@homebound/beam 2.292.0 → 2.294.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.
- package/dist/components/Table/GridTable.d.ts +4 -0
- package/dist/components/Table/GridTable.js +68 -16
- package/dist/components/Table/GridTableApi.d.ts +1 -1
- package/dist/components/Table/GridTableApi.js +3 -11
- package/dist/components/Table/TableStyles.d.ts +4 -0
- package/dist/components/Table/TableStyles.js +2 -0
- package/dist/components/Table/components/CollapseToggle.js +5 -3
- package/dist/components/Table/components/KeptGroupRow.d.ts +10 -0
- package/dist/components/Table/components/KeptGroupRow.js +22 -0
- package/dist/components/Table/components/Row.d.ts +2 -0
- package/dist/components/Table/components/Row.js +14 -10
- package/dist/components/Table/components/cell.d.ts +3 -3
- package/dist/components/Table/components/cell.js +6 -6
- package/dist/components/Table/utils/TableState.d.ts +6 -3
- package/dist/components/Table/utils/TableState.js +127 -38
- package/dist/components/Table/utils/columns.js +4 -2
- package/dist/components/Table/utils/utils.d.ts +2 -1
- package/dist/components/Table/utils/utils.js +8 -3
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useFilter.d.ts +10 -0
- package/dist/hooks/useFilter.js +12 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +7 -1
- package/package.json +15 -15
|
@@ -132,6 +132,10 @@ export interface GridTableProps<R extends Kinded, X> {
|
|
|
132
132
|
* In a "kind" of cute way, headers are not modeled specially, i.e. they are just another
|
|
133
133
|
* row `kind` along with the data rows. (Admittedly, out of pragmatism, we do apply some
|
|
134
134
|
* special styling to the row that uses `kind: "header"`.)
|
|
135
|
+
*
|
|
136
|
+
* For some rationale of our current/historical rendering approaches, see the following doc:
|
|
137
|
+
*
|
|
138
|
+
* https://docs.google.com/document/d/1DFnlkDubK4nG_GLf_hB8yp0flnSNt_3IBh5iOicuaFM/edit#heading=h.9m9cpwgeqfc9
|
|
135
139
|
*/
|
|
136
140
|
export declare function GridTable<R extends Kinded, X extends Only<GridTableXss, X> = any>(props: GridTableProps<R, X>): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
137
141
|
/**
|
|
@@ -80,6 +80,10 @@ exports.setGridTableDefaults = setGridTableDefaults;
|
|
|
80
80
|
* In a "kind" of cute way, headers are not modeled specially, i.e. they are just another
|
|
81
81
|
* row `kind` along with the data rows. (Admittedly, out of pragmatism, we do apply some
|
|
82
82
|
* special styling to the row that uses `kind: "header"`.)
|
|
83
|
+
*
|
|
84
|
+
* For some rationale of our current/historical rendering approaches, see the following doc:
|
|
85
|
+
*
|
|
86
|
+
* https://docs.google.com/document/d/1DFnlkDubK4nG_GLf_hB8yp0flnSNt_3IBh5iOicuaFM/edit#heading=h.9m9cpwgeqfc9
|
|
83
87
|
*/
|
|
84
88
|
function GridTable(props) {
|
|
85
89
|
var _a, _b, _c;
|
|
@@ -136,9 +140,18 @@ function GridTable(props) {
|
|
|
136
140
|
}
|
|
137
141
|
return rows;
|
|
138
142
|
}, [columns, rows, sortOn, sortState, caseSensitive]);
|
|
143
|
+
const keptSelectedDataRows = (0, hooks_1.useComputed)(() => tableState.keptSelectedRows, [tableState]);
|
|
144
|
+
// Sort the `keptSelectedDataRows` separately because the current sorting logic sorts within groups and these "kept" rows are now displayed in a flat list.
|
|
145
|
+
// It could also be the case that some of these rows are no longer in the `props.rows` list, and so wouldn't be sorted by the `maybeSorted` logic above.
|
|
146
|
+
const sortedKeptSelections = (0, react_1.useMemo)(() => {
|
|
147
|
+
if (sortOn === "client" && sortState && keptSelectedDataRows.length > 0) {
|
|
148
|
+
return (0, sortRows_1.sortRows)(columns, keptSelectedDataRows, sortState, caseSensitive);
|
|
149
|
+
}
|
|
150
|
+
return keptSelectedDataRows;
|
|
151
|
+
}, [columns, sortOn, sortState, caseSensitive, keptSelectedDataRows]);
|
|
139
152
|
// Flatten + component-ize the sorted rows.
|
|
140
|
-
let [headerRows, visibleDataRows, totalsRows, expandableHeaderRows, filteredRowIds] = (0, react_1.useMemo)(() => {
|
|
141
|
-
function makeRowComponent(row, level) {
|
|
153
|
+
let [headerRows, visibleDataRows, totalsRows, expandableHeaderRows, keptSelectedRows, filteredRowIds] = (0, react_1.useMemo)(() => {
|
|
154
|
+
function makeRowComponent(row, level, isKeptSelectedRow = false, isLastKeptSelectionRow = false) {
|
|
142
155
|
return ((0, jsx_runtime_1.jsx)(Row_1.Row, { ...{
|
|
143
156
|
as,
|
|
144
157
|
columns,
|
|
@@ -153,6 +166,8 @@ function GridTable(props) {
|
|
|
153
166
|
omitRowHover: "rowHover" in maybeStyle && maybeStyle.rowHover === false,
|
|
154
167
|
sortOn,
|
|
155
168
|
hasExpandableHeader,
|
|
169
|
+
isKeptSelectedRow,
|
|
170
|
+
isLastKeptSelectionRow,
|
|
156
171
|
} }, `${row.kind}-${row.id}`));
|
|
157
172
|
}
|
|
158
173
|
// Split out the header rows from the data rows so that we can put an `infoMessage` in between them (if needed).
|
|
@@ -160,8 +175,8 @@ function GridTable(props) {
|
|
|
160
175
|
const expandableHeaderRows = [];
|
|
161
176
|
const totalsRows = [];
|
|
162
177
|
const visibleDataRows = [];
|
|
178
|
+
const keptSelectedRows = [];
|
|
163
179
|
const filteredRowIds = [];
|
|
164
|
-
const hasTotalsRow = rows.some((row) => row.id === utils_1.TOTALS);
|
|
165
180
|
const hasExpandableHeader = rows.some((row) => row.id === utils_1.EXPANDABLE_HEADER);
|
|
166
181
|
function visit([row, children], level, visible) {
|
|
167
182
|
visible && visibleDataRows.push([row, makeRowComponent(row, level)]);
|
|
@@ -194,14 +209,45 @@ function GridTable(props) {
|
|
|
194
209
|
// Call `visitRows` with our a pre-filtered set list
|
|
195
210
|
const filteredRows = filterRows(api, columns, maybeSorted, filter);
|
|
196
211
|
visitRows(filteredRows, 0, true);
|
|
197
|
-
|
|
198
|
-
|
|
212
|
+
// Check for any selected rows that are not displayed in the table because they don't match the current filter, or are no longer part of the `rows` prop.
|
|
213
|
+
// We persist these selected rows and hoist them to the top of the table.
|
|
214
|
+
if (sortedKeptSelections.length) {
|
|
215
|
+
// The "group row" for selected rows that are hidden by filters and add the children
|
|
216
|
+
const keptSelectionGroupRow = {
|
|
217
|
+
id: utils_1.KEPT_GROUP,
|
|
218
|
+
kind: utils_1.KEPT_GROUP,
|
|
219
|
+
children: sortedKeptSelections,
|
|
220
|
+
initCollapsed: true,
|
|
221
|
+
data: undefined,
|
|
222
|
+
};
|
|
223
|
+
keptSelectedRows.push([keptSelectionGroupRow, makeRowComponent(keptSelectionGroupRow, 1)], ...(collapsedIds.includes(utils_1.KEPT_GROUP)
|
|
224
|
+
? []
|
|
225
|
+
: (sortedKeptSelections.map((row, idx) => [
|
|
226
|
+
row,
|
|
227
|
+
makeRowComponent(row, 1, true, idx === sortedKeptSelections.length - 1),
|
|
228
|
+
]))));
|
|
229
|
+
}
|
|
230
|
+
return [headerRows, visibleDataRows, totalsRows, expandableHeaderRows, keptSelectedRows, filteredRowIds];
|
|
231
|
+
}, [
|
|
232
|
+
as,
|
|
233
|
+
api,
|
|
234
|
+
filter,
|
|
235
|
+
maybeSorted,
|
|
236
|
+
columns,
|
|
237
|
+
style,
|
|
238
|
+
rowStyles,
|
|
239
|
+
sortOn,
|
|
240
|
+
columnSizes,
|
|
241
|
+
collapsedIds,
|
|
242
|
+
getCount,
|
|
243
|
+
sortedKeptSelections,
|
|
244
|
+
]);
|
|
199
245
|
// Once our header rows are created we can organize them in expected order.
|
|
200
246
|
const tableHeadRows = expandableHeaderRows.concat(headerRows).concat(totalsRows);
|
|
201
247
|
let tooManyClientSideRows = false;
|
|
202
248
|
if (filterMaxRows && visibleDataRows.length > filterMaxRows) {
|
|
203
249
|
tooManyClientSideRows = true;
|
|
204
|
-
visibleDataRows = visibleDataRows.slice(0, filterMaxRows);
|
|
250
|
+
visibleDataRows = visibleDataRows.slice(0, filterMaxRows + keptSelectedRows.length);
|
|
205
251
|
}
|
|
206
252
|
tableState.setMatchedRows(filteredRowIds);
|
|
207
253
|
// Push back to the caller a way to ask us where a row is.
|
|
@@ -232,7 +278,7 @@ function GridTable(props) {
|
|
|
232
278
|
// behave semantically the same as `as=div` did for its tests.
|
|
233
279
|
const _as = as === "virtual" && runningInJest ? "div" : as;
|
|
234
280
|
const rowStateContext = (0, react_1.useMemo)(() => ({ tableState: tableState }), [tableState]);
|
|
235
|
-
return ((0, jsx_runtime_1.jsx)(TableState_1.TableStateContext.Provider, { value: rowStateContext, children: (0, jsx_runtime_1.jsxs)(PresentationContext_1.PresentationProvider, { fieldProps: fieldProps, wrap: (_c = style === null || style === void 0 ? void 0 : style.presentationSettings) === null || _c === void 0 ? void 0 : _c.wrap, children: [(0, jsx_runtime_1.jsx)("div", { ref: resizeRef, css: Css_1.Css.w100.if(as === "virtual").w("calc(100% - 20px)").$ }), renders[_as](style, id, columns, visibleDataRows, firstRowMessage, stickyHeader, xss, virtuosoRef, tableHeadRows, stickyOffset, infiniteScroll)] }) }));
|
|
281
|
+
return ((0, jsx_runtime_1.jsx)(TableState_1.TableStateContext.Provider, { value: rowStateContext, children: (0, jsx_runtime_1.jsxs)(PresentationContext_1.PresentationProvider, { fieldProps: fieldProps, wrap: (_c = style === null || style === void 0 ? void 0 : style.presentationSettings) === null || _c === void 0 ? void 0 : _c.wrap, children: [(0, jsx_runtime_1.jsx)("div", { ref: resizeRef, css: Css_1.Css.w100.if(as === "virtual").w("calc(100% - 20px)").$ }), renders[_as](style, id, columns, visibleDataRows, keptSelectedRows, firstRowMessage, stickyHeader, xss, virtuosoRef, tableHeadRows, stickyOffset, infiniteScroll)] }) }));
|
|
236
282
|
}
|
|
237
283
|
exports.GridTable = GridTable;
|
|
238
284
|
// Determine which HTML element to use to build the GridTable
|
|
@@ -242,7 +288,7 @@ const renders = {
|
|
|
242
288
|
virtual: renderVirtual,
|
|
243
289
|
};
|
|
244
290
|
/** Renders table using divs with flexbox rows, which is the default render */
|
|
245
|
-
function renderDiv(style, id, columns, visibleDataRows, firstRowMessage, stickyHeader, xss, _virtuosoRef, tableHeadRows, stickyOffset, _infiniteScroll) {
|
|
291
|
+
function renderDiv(style, id, columns, visibleDataRows, keptSelectedRows, firstRowMessage, stickyHeader, xss, _virtuosoRef, tableHeadRows, stickyOffset, _infiniteScroll) {
|
|
246
292
|
return ((0, jsx_runtime_1.jsxs)("div", { css: {
|
|
247
293
|
// Use `fit-content` to ensure the width of the table takes up the full width of its content.
|
|
248
294
|
// Otherwise, the table's width would be that of its container, which may not be as wide as the table itself.
|
|
@@ -259,10 +305,10 @@ function renderDiv(style, id, columns, visibleDataRows, firstRowMessage, stickyH
|
|
|
259
305
|
...(style.betweenRowsCss ? Css_1.Css.addIn(`& > div > *`, style.betweenRowsCss).$ : {}),
|
|
260
306
|
...(style.firstNonHeaderRowCss ? Css_1.Css.addIn(`& > div:first-of-type > *`, style.firstNonHeaderRowCss).$ : {}),
|
|
261
307
|
...(style.lastRowCss && Css_1.Css.addIn("& > div:last-of-type", style.lastRowCss).$),
|
|
262
|
-
}, children: [firstRowMessage && ((0, jsx_runtime_1.jsx)("div", { css: { ...style.firstRowMessageCss }, "data-gridrow": true, children: firstRowMessage })), visibleDataRows.map(([, node]) => node)] })] }));
|
|
308
|
+
}, children: [keptSelectedRows.map(([, node]) => node), firstRowMessage && ((0, jsx_runtime_1.jsx)("div", { css: { ...style.firstRowMessageCss }, "data-gridrow": true, children: firstRowMessage })), visibleDataRows.map(([, node]) => node)] })] }));
|
|
263
309
|
}
|
|
264
310
|
/** Renders as a table, primarily/solely for good print support. */
|
|
265
|
-
function renderTable(style, id, columns, visibleDataRows, firstRowMessage, stickyHeader, xss, _virtuosoRef, tableHeadRows, stickyOffset, _infiniteScroll) {
|
|
311
|
+
function renderTable(style, id, columns, visibleDataRows, keptSelectedRows, firstRowMessage, stickyHeader, xss, _virtuosoRef, tableHeadRows, stickyOffset, _infiniteScroll) {
|
|
266
312
|
return ((0, jsx_runtime_1.jsxs)("table", { css: {
|
|
267
313
|
...Css_1.Css.w100.add("borderCollapse", "separate").add("borderSpacing", "0").$,
|
|
268
314
|
...Css_1.Css.addIn("& > tbody > tr > * ", style.betweenRowsCss || {})
|
|
@@ -273,7 +319,7 @@ function renderTable(style, id, columns, visibleDataRows, firstRowMessage, stick
|
|
|
273
319
|
...style.rootCss,
|
|
274
320
|
...(style.minWidthPx ? Css_1.Css.mwPx(style.minWidthPx).$ : {}),
|
|
275
321
|
...xss,
|
|
276
|
-
}, "data-testid": id, children: [(0, jsx_runtime_1.jsx)("thead", { css: Css_1.Css.if(stickyHeader).sticky.topPx(stickyOffset).z(utils_1.zIndices.stickyHeader).$, children: tableHeadRows.map(([, node]) => node) }), (0, jsx_runtime_1.jsxs)("tbody", { children: [firstRowMessage && ((0, jsx_runtime_1.jsx)("tr", { children: (0, jsx_runtime_1.jsx)("td", { colSpan: columns.length, css: { ...style.firstRowMessageCss }, children: firstRowMessage }) })), visibleDataRows.map(([, node]) => node)] })] }));
|
|
322
|
+
}, "data-testid": id, children: [(0, jsx_runtime_1.jsx)("thead", { css: Css_1.Css.if(stickyHeader).sticky.topPx(stickyOffset).z(utils_1.zIndices.stickyHeader).$, children: tableHeadRows.map(([, node]) => node) }), (0, jsx_runtime_1.jsxs)("tbody", { children: [keptSelectedRows.map(([, node]) => node), firstRowMessage && ((0, jsx_runtime_1.jsx)("tr", { children: (0, jsx_runtime_1.jsx)("td", { colSpan: columns.length, css: { ...style.firstRowMessageCss }, children: firstRowMessage }) })), visibleDataRows.map(([, node]) => node)] })] }));
|
|
277
323
|
}
|
|
278
324
|
/**
|
|
279
325
|
* Uses react-virtuoso to render rows virtually.
|
|
@@ -295,7 +341,7 @@ function renderTable(style, id, columns, visibleDataRows, firstRowMessage, stick
|
|
|
295
341
|
* [2]: https://github.com/tannerlinsley/react-virtual/issues/85
|
|
296
342
|
* [3]: https://github.com/tannerlinsley/react-virtual/issues/108
|
|
297
343
|
*/
|
|
298
|
-
function renderVirtual(style, id, columns, visibleDataRows, firstRowMessage, stickyHeader, xss, virtuosoRef, tableHeadRows, _stickyOffset, infiniteScroll) {
|
|
344
|
+
function renderVirtual(style, id, columns, visibleDataRows, keptSelectedRows, firstRowMessage, stickyHeader, xss, virtuosoRef, tableHeadRows, _stickyOffset, infiniteScroll) {
|
|
299
345
|
var _a;
|
|
300
346
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
301
347
|
const { footerStyle, listStyle } = (0, react_1.useMemo)(() => {
|
|
@@ -310,13 +356,19 @@ function renderVirtual(style, id, columns, visibleDataRows, firstRowMessage, sti
|
|
|
310
356
|
Footer: () => (0, jsx_runtime_1.jsx)("div", { css: footerStyle }),
|
|
311
357
|
},
|
|
312
358
|
// Pin/sticky both the header row(s) + firstRowMessage to the top
|
|
313
|
-
topItemCount:
|
|
314
|
-
// Since we have
|
|
359
|
+
topItemCount: stickyHeader ? tableHeadRows.length : 0, itemContent: (index) => {
|
|
360
|
+
// Since we have 3 arrays of rows: `tableHeadRows` and `visibleDataRows` and `keptSelectedRows` we must determine which one to render.
|
|
315
361
|
if (index < tableHeadRows.length) {
|
|
316
362
|
return tableHeadRows[index][1];
|
|
317
363
|
}
|
|
318
364
|
// Reset index
|
|
319
365
|
index -= tableHeadRows.length;
|
|
366
|
+
// Show keptSelectedRows if there are any
|
|
367
|
+
if (index < keptSelectedRows.length) {
|
|
368
|
+
return keptSelectedRows[index][1];
|
|
369
|
+
}
|
|
370
|
+
// Reset index
|
|
371
|
+
index -= keptSelectedRows.length;
|
|
320
372
|
// Show firstRowMessage as the first `filteredRow`
|
|
321
373
|
if (firstRowMessage) {
|
|
322
374
|
if (index === 0) {
|
|
@@ -326,9 +378,9 @@ function renderVirtual(style, id, columns, visibleDataRows, firstRowMessage, sti
|
|
|
326
378
|
// first `filteredRow`
|
|
327
379
|
index--;
|
|
328
380
|
}
|
|
329
|
-
// Lastly render
|
|
381
|
+
// Lastly render the table body rows
|
|
330
382
|
return visibleDataRows[index][1];
|
|
331
|
-
}, totalCount: tableHeadRows.length + (firstRowMessage ? 1 : 0) + visibleDataRows.length, ...(infiniteScroll
|
|
383
|
+
}, totalCount: tableHeadRows.length + (firstRowMessage ? 1 : 0) + visibleDataRows.length + keptSelectedRows.length, ...(infiniteScroll
|
|
332
384
|
? {
|
|
333
385
|
increaseViewportBy: {
|
|
334
386
|
bottom: (_a = infiniteScroll.endOffsetPx) !== null && _a !== void 0 ? _a : 500,
|
|
@@ -16,7 +16,7 @@ import { TableState } from "./utils/TableState";
|
|
|
16
16
|
* This is very similar to a `useRef`, except that the parent function has
|
|
17
17
|
* immediate access to `api` and can use it for `useComputed`, instead of
|
|
18
18
|
* having to wait for `ref.current` to be set after the child `GridTable`
|
|
19
|
-
* has
|
|
19
|
+
* has run.
|
|
20
20
|
*/
|
|
21
21
|
export declare function useGridTableApi<R extends Kinded>(): GridTableApi<R>;
|
|
22
22
|
/** Provides an imperative API for an application page to interact with the table. */
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.GridTableApiImpl = exports.useGridTableApi = void 0;
|
|
4
4
|
const react_1 = require("react");
|
|
5
5
|
const TableState_1 = require("./utils/TableState");
|
|
6
|
-
const visitor_1 = require("./utils/visitor");
|
|
7
6
|
/**
|
|
8
7
|
* Creates an `api` handle to drive a `GridTable`.
|
|
9
8
|
*
|
|
@@ -17,7 +16,7 @@ const visitor_1 = require("./utils/visitor");
|
|
|
17
16
|
* This is very similar to a `useRef`, except that the parent function has
|
|
18
17
|
* immediate access to `api` and can use it for `useComputed`, instead of
|
|
19
18
|
* having to wait for `ref.current` to be set after the child `GridTable`
|
|
20
|
-
* has
|
|
19
|
+
* has run.
|
|
21
20
|
*/
|
|
22
21
|
function useGridTableApi() {
|
|
23
22
|
return (0, react_1.useMemo)(() => new GridTableApiImpl(), []);
|
|
@@ -42,16 +41,9 @@ class GridTableApiImpl {
|
|
|
42
41
|
getSelectedRowIds(kind) {
|
|
43
42
|
return this.getSelectedRows(kind).map((row) => row.id);
|
|
44
43
|
}
|
|
45
|
-
// The any is not great, but getting the overload to handle the optional kind is annoying
|
|
44
|
+
// The `any` is not great, but getting the overload to handle the optional kind is annoying
|
|
46
45
|
getSelectedRows(kind) {
|
|
47
|
-
|
|
48
|
-
const selected = [];
|
|
49
|
-
(0, visitor_1.visit)(this.tableState.rows, (row) => {
|
|
50
|
-
if (row.selectable !== false && ids.includes(row.id) && (!kind || row.kind === kind)) {
|
|
51
|
-
selected.push(row);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
return selected;
|
|
46
|
+
return this.tableState.selectedRows.filter((row) => !kind || row.kind === kind);
|
|
55
47
|
}
|
|
56
48
|
clearSelections(id) {
|
|
57
49
|
this.tableState.selectRow("header", false);
|
|
@@ -50,6 +50,10 @@ export interface GridStyle {
|
|
|
50
50
|
}>;
|
|
51
51
|
/** Allows for customization of the background color used to denote an "active" row */
|
|
52
52
|
activeBgColor?: Palette;
|
|
53
|
+
/** Defines styles for the group row which holds the selected rows that have been filtered out */
|
|
54
|
+
keptGroupRowCss?: Properties;
|
|
55
|
+
/** Defines styles for the last row `keptGroup` to provide separation from the rest of the table */
|
|
56
|
+
keptLastRowCss?: Properties;
|
|
53
57
|
}
|
|
54
58
|
export interface GridStyleDef {
|
|
55
59
|
/** Changes the height of the rows when `rowHeight: fixed` to provide more space between rows for input fields. */
|
|
@@ -62,6 +62,8 @@ function memoizedTableStyles() {
|
|
|
62
62
|
presentationSettings: { borderless: true, typeScale: "xs", wrap: rowHeight === "flexible" },
|
|
63
63
|
levels: grouped ? groupedLevels : defaultLevels,
|
|
64
64
|
rowHoverColor: Css_1.Palette.LightBlue100,
|
|
65
|
+
keptGroupRowCss: Css_1.Css.bgYellow100.gray900.xsMd.$,
|
|
66
|
+
keptLastRowCss: Css_1.Css.boxShadow("inset 0px -14px 8px -11px rgba(63,63,63,.18)").$,
|
|
65
67
|
};
|
|
66
68
|
}
|
|
67
69
|
return cache[key];
|
|
@@ -12,9 +12,11 @@ function CollapseToggle(props) {
|
|
|
12
12
|
const isCollapsed = (0, hooks_1.useComputed)(() => tableState.isCollapsed(row.id), [tableState]);
|
|
13
13
|
const iconKey = isCollapsed ? "chevronRight" : "chevronDown";
|
|
14
14
|
const headerIconKey = isCollapsed ? "chevronsRight" : "chevronsDown";
|
|
15
|
-
// If we're not a header, only render a toggle if we have child rows to actually collapse
|
|
16
|
-
const isHeader = row.kind ===
|
|
17
|
-
|
|
15
|
+
// If we're not a header and not the selected group row, only render a toggle if we have child rows to actually collapse
|
|
16
|
+
const isHeader = row.kind === index_1.HEADER;
|
|
17
|
+
const isKeptGroup = row.kind === index_1.KEPT_GROUP;
|
|
18
|
+
const hasChildren = row.children ? row.children.length > 0 : false;
|
|
19
|
+
if (!isHeader && !isKeptGroup && !hasChildren) {
|
|
18
20
|
return null;
|
|
19
21
|
}
|
|
20
22
|
return ((0, jsx_runtime_1.jsx)(index_1.IconButton, { onClick: () => tableState.toggleCollapsed(row.id), icon: isHeader ? headerIconKey : iconKey, compact: compact }));
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { GridDataRow, GridStyle, RenderAs } from "..";
|
|
2
|
+
interface KeptGroupRowProps {
|
|
3
|
+
as: RenderAs;
|
|
4
|
+
columnSizes: string[];
|
|
5
|
+
style: GridStyle;
|
|
6
|
+
row: GridDataRow<any>;
|
|
7
|
+
colSpan: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function KeptGroupRow(props: KeptGroupRowProps): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KeptGroupRow = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const components_1 = require("../..");
|
|
7
|
+
const Table_1 = require("..");
|
|
8
|
+
const Css_1 = require("../../../Css");
|
|
9
|
+
const hooks_1 = require("../../../hooks");
|
|
10
|
+
const utils_1 = require("../../../utils");
|
|
11
|
+
function KeptGroupRow(props) {
|
|
12
|
+
const { as, columnSizes, style, row, colSpan } = props;
|
|
13
|
+
const CellTag = as === "table" ? "td" : "div";
|
|
14
|
+
const { tableState } = (0, react_1.useContext)(Table_1.TableStateContext);
|
|
15
|
+
const numHiddenSelectedRows = (0, hooks_1.useComputed)(() => tableState.keptSelectedRows.length, [tableState]);
|
|
16
|
+
return ((0, jsx_runtime_1.jsx)(CellTag, { css: {
|
|
17
|
+
...style.cellCss,
|
|
18
|
+
...style.keptGroupRowCss,
|
|
19
|
+
...Css_1.Css.pl0.w(`calc(${columnSizes.join(" + ")})`).$,
|
|
20
|
+
}, ...(as === "table" ? { colSpan } : {}), children: (0, jsx_runtime_1.jsxs)("div", { css: Css_1.Css.df.aic.gapPx(12).$, children: [(0, jsx_runtime_1.jsx)("div", { css: Css_1.Css.wPx(38).df.jcc.$, children: (0, jsx_runtime_1.jsx)(Table_1.CollapseToggle, { row: row, compact: true }) }), (0, jsx_runtime_1.jsxs)("div", { css: Css_1.Css.df.aic.gap1.$, children: [(0, jsx_runtime_1.jsx)(components_1.Icon, { icon: "infoCircle", inc: 2 }), `${numHiddenSelectedRows} selected ${(0, utils_1.pluralize)(numHiddenSelectedRows, "row")} hidden due to filters`] })] }) }));
|
|
21
|
+
}
|
|
22
|
+
exports.KeptGroupRow = KeptGroupRow;
|
|
@@ -18,6 +18,8 @@ interface RowProps<R extends Kinded> {
|
|
|
18
18
|
cellHighlight: boolean;
|
|
19
19
|
omitRowHover: boolean;
|
|
20
20
|
hasExpandableHeader: boolean;
|
|
21
|
+
isKeptSelectedRow: boolean;
|
|
22
|
+
isLastKeptSelectionRow: boolean;
|
|
21
23
|
}
|
|
22
24
|
declare function RowImpl<R extends Kinded, S>(props: RowProps<R>): ReactElement;
|
|
23
25
|
/**
|
|
@@ -28,6 +28,7 @@ const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
|
|
|
28
28
|
const mobx_react_1 = require("mobx-react");
|
|
29
29
|
const react_1 = __importStar(require("react"));
|
|
30
30
|
const cell_1 = require("./cell");
|
|
31
|
+
const KeptGroupRow_1 = require("./KeptGroupRow");
|
|
31
32
|
const sortRows_1 = require("../utils/sortRows");
|
|
32
33
|
const TableState_1 = require("../utils/TableState");
|
|
33
34
|
const utils_1 = require("../utils/utils");
|
|
@@ -38,7 +39,7 @@ const shallowEqual_1 = require("../../../utils/shallowEqual");
|
|
|
38
39
|
// We extract Row to its own mini-component primarily so we can React.memo'ize it.
|
|
39
40
|
function RowImpl(props) {
|
|
40
41
|
var _a;
|
|
41
|
-
const { as, columns, row, style, rowStyles, sortOn, columnSizes, level, getCount, api, cellHighlight, omitRowHover, hasExpandableHeader, ...others } = props;
|
|
42
|
+
const { as, columns, row, style, rowStyles, sortOn, columnSizes, level, getCount, api, cellHighlight, omitRowHover, hasExpandableHeader, isKeptSelectedRow, isLastKeptSelectionRow, ...others } = props;
|
|
42
43
|
const { tableState } = (0, react_1.useContext)(TableState_1.TableStateContext);
|
|
43
44
|
const rowId = `${row.kind}_${row.id}`;
|
|
44
45
|
const isActive = (0, hooks_1.useComputed)(() => tableState.activeRowId === rowId, [rowId, tableState]);
|
|
@@ -46,6 +47,7 @@ function RowImpl(props) {
|
|
|
46
47
|
const isHeader = row.kind === utils_1.HEADER;
|
|
47
48
|
const isTotals = row.kind === utils_1.TOTALS;
|
|
48
49
|
const isExpandableHeader = row.kind === utils_1.EXPANDABLE_HEADER;
|
|
50
|
+
const isKeptGroupRow = row.kind === utils_1.KEPT_GROUP;
|
|
49
51
|
const rowStyle = rowStyles === null || rowStyles === void 0 ? void 0 : rowStyles[row.kind];
|
|
50
52
|
const RowTag = as === "table" ? "tr" : "div";
|
|
51
53
|
const revealOnRowHoverClass = "revealOnRowHover";
|
|
@@ -66,14 +68,15 @@ function RowImpl(props) {
|
|
|
66
68
|
[` > .${revealOnRowHoverClass} > *`]: Css_1.Css.invisible.$,
|
|
67
69
|
[`:hover > .${revealOnRowHoverClass} > *`]: Css_1.Css.visible.$,
|
|
68
70
|
},
|
|
71
|
+
...(isLastKeptSelectionRow && Css_1.Css.addIn("&>*", style.keptLastRowCss).$),
|
|
69
72
|
};
|
|
70
73
|
let currentColspan = 1;
|
|
71
74
|
// Keep a running count of how many expanded columns are being shown.
|
|
72
75
|
let currentExpandedColumnCount = 0;
|
|
73
|
-
let
|
|
76
|
+
let foundFirstContentColumn = false;
|
|
74
77
|
let minStickyLeftOffset = 0;
|
|
75
78
|
let expandColumnHidden = false;
|
|
76
|
-
return ((0, jsx_runtime_1.jsx)(RowTag, { css: rowCss, ...others, "data-gridrow": true, ...getCount(row.id), children: columns.map((column, columnIndex) => {
|
|
79
|
+
return ((0, jsx_runtime_1.jsx)(RowTag, { css: rowCss, ...others, "data-gridrow": true, ...getCount(row.id), children: isKeptGroupRow ? ((0, jsx_runtime_1.jsx)(KeptGroupRow_1.KeptGroupRow, { as: as, style: style, columnSizes: columnSizes, row: row, colSpan: columns.length })) : (columns.map((column, columnIndex) => {
|
|
77
80
|
var _a, _b, _c, _d, _e;
|
|
78
81
|
// If the expandable column was hidden, then we need to look at the previous column to format the `expandHeader` and 'header' kinds correctly.
|
|
79
82
|
const maybeExpandedColumn = expandColumnHidden ? columns[columnIndex - 1] : column;
|
|
@@ -94,8 +97,9 @@ function RowImpl(props) {
|
|
|
94
97
|
// If we're rendering the Expandable Header row, then we might need to render the previous column's `expandHeader` property in the case where the column is hidden on expand.
|
|
95
98
|
column = isExpandableHeader ? maybeExpandedColumn : column;
|
|
96
99
|
const { wrapAction = true, isAction = false } = column;
|
|
97
|
-
const
|
|
98
|
-
|
|
100
|
+
const isFirstContentColumn = !isAction && !foundFirstContentColumn;
|
|
101
|
+
const applyFirstContentColumnStyles = !isHeader && isFirstContentColumn;
|
|
102
|
+
foundFirstContentColumn || (foundFirstContentColumn = applyFirstContentColumnStyles);
|
|
99
103
|
if (column.mw) {
|
|
100
104
|
// Validate the column's minWidth definition if set.
|
|
101
105
|
if (!column.mw.endsWith("px") && !column.mw.endsWith("%")) {
|
|
@@ -138,7 +142,7 @@ function RowImpl(props) {
|
|
|
138
142
|
const isExpandable = (0, utils_2.isFunction)(column.expandColumns) ||
|
|
139
143
|
(column.expandColumns && column.expandColumns.length > 0) ||
|
|
140
144
|
column.expandedWidth !== undefined;
|
|
141
|
-
const content = (0, utils_1.toContent)(maybeContent, isHeader, canSortColumn, sortOn === "client", style, as, alignment, column, isExpandableHeader, isExpandable, minStickyLeftOffset);
|
|
145
|
+
const content = (0, utils_1.toContent)(maybeContent, isHeader, canSortColumn, sortOn === "client", style, as, alignment, column, isExpandableHeader, isExpandable, minStickyLeftOffset, isKeptSelectedRow);
|
|
142
146
|
(0, sortRows_1.ensureClientSideSortValueIsSortable)(sortOn, isHeader || isTotals || isExpandableHeader, column, columnIndex, maybeContent);
|
|
143
147
|
const maybeSticky = (_b = (((0, utils_1.isGridCellContent)(maybeContent) && maybeContent.sticky) || column.sticky)) !== null && _b !== void 0 ? _b : undefined;
|
|
144
148
|
const maybeStickyColumnStyles = maybeSticky && columnSizes
|
|
@@ -215,14 +219,14 @@ function RowImpl(props) {
|
|
|
215
219
|
const cellOnClick = applyCellHighlight ? () => api.setActiveCellId(cellId) : undefined;
|
|
216
220
|
const tooltip = (0, utils_1.isGridCellContent)(maybeContent) ? maybeContent.tooltip : undefined;
|
|
217
221
|
const renderFn = ((rowStyle === null || rowStyle === void 0 ? void 0 : rowStyle.renderCell) || (rowStyle === null || rowStyle === void 0 ? void 0 : rowStyle.rowLink)) && wrapAction
|
|
218
|
-
? (0, cell_1.rowLinkRenderFn)(as)
|
|
222
|
+
? (0, cell_1.rowLinkRenderFn)(as, currentColspan)
|
|
219
223
|
: isHeader || isTotals || isExpandableHeader
|
|
220
224
|
? (0, cell_1.headerRenderFn)(column, as, currentColspan)
|
|
221
225
|
: (rowStyle === null || rowStyle === void 0 ? void 0 : rowStyle.onClick) && wrapAction
|
|
222
|
-
? (0, cell_1.rowClickRenderFn)(as, api)
|
|
223
|
-
: (0, cell_1.defaultRenderFn)(as);
|
|
226
|
+
? (0, cell_1.rowClickRenderFn)(as, api, currentColspan)
|
|
227
|
+
: (0, cell_1.defaultRenderFn)(as, currentColspan);
|
|
224
228
|
return renderFn(columnIndex, cellCss, content, row, rowStyle, cellClassNames, cellOnClick, tooltip);
|
|
225
|
-
}) }));
|
|
229
|
+
})) }));
|
|
226
230
|
}
|
|
227
231
|
/**
|
|
228
232
|
* Memoizes Rows so that re-rendering the table doesn't re-render every single row.
|
|
@@ -31,13 +31,13 @@ export type GridCellContent = {
|
|
|
31
31
|
/** Allows rendering a specific cell. */
|
|
32
32
|
export type RenderCellFn<R extends Kinded> = (idx: number, css: Properties, content: ReactNode, row: R, rowStyle: RowStyle<R> | undefined, classNames: string | undefined, onClick: VoidFunction | undefined, tooltip: ReactNode | undefined) => ReactNode;
|
|
33
33
|
/** Renders our default cell element, i.e. if no row links and no custom renderCell are used. */
|
|
34
|
-
export declare const defaultRenderFn: (as: RenderAs) => RenderCellFn<any>;
|
|
34
|
+
export declare const defaultRenderFn: (as: RenderAs, colSpan: number) => RenderCellFn<any>;
|
|
35
35
|
/**
|
|
36
36
|
* Sets up the `GridContext` so that header cells can access the current sort settings.
|
|
37
37
|
* Used for the Header, Totals, and Expanded Header row's cells.
|
|
38
38
|
* */
|
|
39
39
|
export declare const headerRenderFn: (column: GridColumnWithId<any>, as: RenderAs, colSpan: number) => RenderCellFn<any>;
|
|
40
40
|
/** Renders a cell element when a row link is in play. */
|
|
41
|
-
export declare const rowLinkRenderFn: (as: RenderAs) => RenderCellFn<any>;
|
|
41
|
+
export declare const rowLinkRenderFn: (as: RenderAs, colSpan: number) => RenderCellFn<any>;
|
|
42
42
|
/** Renders a cell that will fire the RowStyle.onClick. */
|
|
43
|
-
export declare const rowClickRenderFn: (as: RenderAs, api: GridTableApi<any
|
|
43
|
+
export declare const rowClickRenderFn: (as: RenderAs, api: GridTableApi<any>, colSpan: number) => RenderCellFn<any>;
|
|
@@ -6,9 +6,9 @@ const react_router_dom_1 = require("react-router-dom");
|
|
|
6
6
|
const CssReset_1 = require("../../CssReset");
|
|
7
7
|
const Css_1 = require("../../../Css");
|
|
8
8
|
/** Renders our default cell element, i.e. if no row links and no custom renderCell are used. */
|
|
9
|
-
const defaultRenderFn = (as) => (key, css, content, row, rowStyle, classNames, onClick, tooltip) => {
|
|
9
|
+
const defaultRenderFn = (as, colSpan) => (key, css, content, row, rowStyle, classNames, onClick, tooltip) => {
|
|
10
10
|
const Cell = as === "table" ? "td" : "div";
|
|
11
|
-
return ((0, jsx_runtime_1.jsx)(Cell, { css: { ...css, ...Css_1.Css.cursor("default").$ }, className: classNames, onClick: onClick, children: content }, key));
|
|
11
|
+
return ((0, jsx_runtime_1.jsx)(Cell, { css: { ...css, ...Css_1.Css.cursor("default").$ }, className: classNames, onClick: onClick, ...(as === "table" && { colSpan }), children: content }, key));
|
|
12
12
|
};
|
|
13
13
|
exports.defaultRenderFn = defaultRenderFn;
|
|
14
14
|
/**
|
|
@@ -21,20 +21,20 @@ const headerRenderFn = (column, as, colSpan) => (key, css, content, row, rowStyl
|
|
|
21
21
|
};
|
|
22
22
|
exports.headerRenderFn = headerRenderFn;
|
|
23
23
|
/** Renders a cell element when a row link is in play. */
|
|
24
|
-
const rowLinkRenderFn = (as) => (key, css, content, row, rowStyle, classNames, onClick, tooltip) => {
|
|
24
|
+
const rowLinkRenderFn = (as, colSpan) => (key, css, content, row, rowStyle, classNames, onClick, tooltip) => {
|
|
25
25
|
const to = rowStyle.rowLink(row);
|
|
26
26
|
if (as === "table") {
|
|
27
|
-
return ((0, jsx_runtime_1.jsx)("td", { css: { ...css }, className: classNames, children: (0, jsx_runtime_1.jsx)(react_router_dom_1.Link, { to: to, css: Css_1.Css.noUnderline.color("unset").db.$, className: CssReset_1.navLink, children: content }) }, key));
|
|
27
|
+
return ((0, jsx_runtime_1.jsx)("td", { css: { ...css }, className: classNames, colSpan: colSpan, children: (0, jsx_runtime_1.jsx)(react_router_dom_1.Link, { to: to, css: Css_1.Css.noUnderline.color("unset").db.$, className: CssReset_1.navLink, children: content }) }, key));
|
|
28
28
|
}
|
|
29
29
|
return ((0, jsx_runtime_1.jsx)(react_router_dom_1.Link, { to: to, css: { ...Css_1.Css.noUnderline.color("unset").$, ...css }, className: `${CssReset_1.navLink} ${classNames}`, children: content }, key));
|
|
30
30
|
};
|
|
31
31
|
exports.rowLinkRenderFn = rowLinkRenderFn;
|
|
32
32
|
/** Renders a cell that will fire the RowStyle.onClick. */
|
|
33
|
-
const rowClickRenderFn = (as, api) => (key, css, content, row, rowStyle, classNames, onClick, tooltip) => {
|
|
33
|
+
const rowClickRenderFn = (as, api, colSpan) => (key, css, content, row, rowStyle, classNames, onClick, tooltip) => {
|
|
34
34
|
const Cell = as === "table" ? "td" : "div";
|
|
35
35
|
return ((0, jsx_runtime_1.jsx)(Cell, { ...{ key }, css: { ...css }, className: classNames, onClick: (e) => {
|
|
36
36
|
rowStyle.onClick(row, api);
|
|
37
37
|
onClick && onClick();
|
|
38
|
-
}, children: content }));
|
|
38
|
+
}, ...(as === "table" && { colSpan }), children: content }));
|
|
39
39
|
};
|
|
40
40
|
exports.rowClickRenderFn = rowClickRenderFn;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ObservableSet } from "mobx";
|
|
1
|
+
import { ObservableMap, ObservableSet } from "mobx";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { GridDataRow } from "../components/Row";
|
|
4
4
|
import { GridSortConfig } from "../GridTable";
|
|
@@ -22,8 +22,10 @@ export type SelectedState = "checked" | "unchecked" | "partial";
|
|
|
22
22
|
export declare class TableState {
|
|
23
23
|
private readonly collapsedRows;
|
|
24
24
|
private persistCollapse;
|
|
25
|
-
private readonly
|
|
25
|
+
private readonly rowSelectedState;
|
|
26
|
+
selectedDataRows: ObservableMap<string, GridDataRow<any>>;
|
|
26
27
|
private matchedRows;
|
|
28
|
+
keptSelectedRows: GridDataRow<any>[];
|
|
27
29
|
rows: GridDataRow<any>[];
|
|
28
30
|
activeRowId: string | undefined;
|
|
29
31
|
activeCellId: string | undefined;
|
|
@@ -58,7 +60,8 @@ export declare class TableState {
|
|
|
58
60
|
get expandedColumnIds(): string[];
|
|
59
61
|
toggleExpandedColumn(columnId: string): void;
|
|
60
62
|
setMatchedRows(rowIds: string[]): void;
|
|
61
|
-
|
|
63
|
+
/** Returns either all data rows (non-header, non-totals, etc) that are selected where `row.selectable !== false` */
|
|
64
|
+
get selectedRows(): GridDataRow<any>[];
|
|
62
65
|
getSelected(id: string): SelectedState;
|
|
63
66
|
selectRow(id: string, selected: boolean): void;
|
|
64
67
|
get collapsedIds(): string[];
|
|
@@ -32,10 +32,18 @@ class TableState {
|
|
|
32
32
|
*/
|
|
33
33
|
constructor() {
|
|
34
34
|
// A set of just row ids, i.e. not row.kind+row.id
|
|
35
|
-
this.collapsedRows = new mobx_1.ObservableSet();
|
|
36
|
-
|
|
35
|
+
this.collapsedRows = new mobx_1.ObservableSet([]);
|
|
36
|
+
// This will include rows that are selected, are partially selected, or were at once selected but are now unchecked.
|
|
37
|
+
// Rows within this map may be in `this.rows` or removed from `this.rows`.
|
|
38
|
+
this.rowSelectedState = new mobx_1.ObservableMap();
|
|
39
|
+
// Keep a copy of all the selected GridDataRows - Do not include custom row kinds such as `header`, `totals`, etc.
|
|
40
|
+
// This allows us to keep track of rows that were selected, but may no longer be defined in `GridTable.rows`
|
|
41
|
+
// This will be used to determine which rows are considered "kept" rows when filtering is applied or `GridTableProp.rows` changes
|
|
42
|
+
this.selectedDataRows = new mobx_1.ObservableMap();
|
|
37
43
|
// Set of just row ids. Keeps track of which rows match the filter. Used to filter rows from `selectedIds`
|
|
38
44
|
this.matchedRows = new mobx_1.ObservableSet();
|
|
45
|
+
// All fully selected data rows that do not match the applied filter, if any. Will not include group/parent rows unless they `inferSelectedState: false`
|
|
46
|
+
this.keptSelectedRows = [];
|
|
39
47
|
// The current list of rows, basically a useRef.current. Not reactive.
|
|
40
48
|
this.rows = [];
|
|
41
49
|
// Keeps track of the 'active' row, formatted `${row.kind}_${row.id}`
|
|
@@ -67,10 +75,32 @@ class TableState {
|
|
|
67
75
|
});
|
|
68
76
|
// Whenever our `matchedRows` change (i.e. via filtering) then we need to re-derive header and parent rows' selected state.
|
|
69
77
|
(0, mobx_1.reaction)(() => [...this.matchedRows.values()].sort(), () => {
|
|
78
|
+
const newlyKeptRows = [...this.selectedDataRows.values()].filter((row) => keptSelectionsFilter(row, this.matchedRows));
|
|
79
|
+
// If the kept rows went from empty to not empty, then introduce the SELECTED_GROUP row as collapsed
|
|
80
|
+
if (newlyKeptRows.length > 0 && this.keptSelectedRows.length === 0) {
|
|
81
|
+
this.collapsedRows.add(utils_1.KEPT_GROUP);
|
|
82
|
+
}
|
|
83
|
+
// When filters are applied, we need to determine if there are any selected rows that are no longer matched.
|
|
84
|
+
if (!mobx_1.comparer.shallow(newlyKeptRows, this.keptSelectedRows)) {
|
|
85
|
+
this.keptSelectedRows = newlyKeptRows;
|
|
86
|
+
}
|
|
87
|
+
// Re-derive the selected state for both the header and parent rows
|
|
70
88
|
const map = new Map();
|
|
71
|
-
map.set("header", deriveParentSelected(this.rows.flatMap((row) => this.setNestedSelectedStates(row, map))));
|
|
89
|
+
map.set("header", deriveParentSelected([...this.rows, ...this.keptSelectedRows].flatMap((row) => this.setNestedSelectedStates(row, map))));
|
|
72
90
|
// Merge the changes back into the selected rows state
|
|
73
|
-
this.
|
|
91
|
+
this.rowSelectedState.merge(map);
|
|
92
|
+
// Update the selectedDataRows to include those that are fully selected based on the filter changes
|
|
93
|
+
[...this.rowSelectedState.entries()].forEach(([id, state]) => {
|
|
94
|
+
if (this.selectedDataRows.has(id) && state !== "checked") {
|
|
95
|
+
this.selectedDataRows.delete(id);
|
|
96
|
+
}
|
|
97
|
+
else if (!this.selectedDataRows.has(id) && state === "checked") {
|
|
98
|
+
const row = this.rows.find((row) => row.id === id);
|
|
99
|
+
if (row) {
|
|
100
|
+
this.selectedDataRows.set(id, row);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
74
104
|
}, { equals: mobx_1.comparer.shallow });
|
|
75
105
|
}
|
|
76
106
|
loadCollapse(persistCollapse, rows) {
|
|
@@ -88,14 +118,22 @@ class TableState {
|
|
|
88
118
|
}
|
|
89
119
|
}
|
|
90
120
|
loadSelected(rows) {
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const
|
|
121
|
+
const selectedRows = [];
|
|
122
|
+
(0, visitor_1.visit)(rows, (row) => row.initSelected && selectedRows.push(row));
|
|
123
|
+
const selectedStateMap = new Map();
|
|
124
|
+
const selectedRowMap = new Map();
|
|
95
125
|
selectedRows.forEach((row) => {
|
|
96
|
-
|
|
126
|
+
selectedStateMap.set(row.id, "checked");
|
|
127
|
+
selectedRowMap.set(row.id, row);
|
|
97
128
|
});
|
|
98
|
-
this.
|
|
129
|
+
this.rowSelectedState.merge(selectedStateMap);
|
|
130
|
+
this.selectedDataRows.merge(selectedRowMap);
|
|
131
|
+
// Determine if we need to initially display the kept selected group
|
|
132
|
+
const newlyKeptRows = selectedRows.filter((row) => keptSelectionsFilter(row, this.matchedRows));
|
|
133
|
+
if (!mobx_1.comparer.shallow(newlyKeptRows, this.keptSelectedRows)) {
|
|
134
|
+
this.collapsedRows.add(utils_1.KEPT_GROUP);
|
|
135
|
+
this.keptSelectedRows = newlyKeptRows;
|
|
136
|
+
}
|
|
99
137
|
}
|
|
100
138
|
initSortState(sortConfig, columns) {
|
|
101
139
|
var _a, _b;
|
|
@@ -180,6 +218,10 @@ class TableState {
|
|
|
180
218
|
}
|
|
181
219
|
// Finally replace our existing list of rows
|
|
182
220
|
this.rows = rows;
|
|
221
|
+
// Update the selected rows map to include the rows' updated data
|
|
222
|
+
const selectedRows = new Map();
|
|
223
|
+
(0, visitor_1.visit)(this.rows, (row) => this.selectedDataRows.has(row.id) && selectedRows.set(row.id, row));
|
|
224
|
+
this.selectedDataRows.merge(selectedRows);
|
|
183
225
|
}
|
|
184
226
|
setColumns(columns, visibleColumnsStorageKey) {
|
|
185
227
|
const isInitial = !this.columns || this.columns.length === 0;
|
|
@@ -299,59 +341,95 @@ class TableState {
|
|
|
299
341
|
this.matchedRows.replace(rowIds);
|
|
300
342
|
}
|
|
301
343
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
.filter(([id, v]) => this.matchedRows.has(id) && v === "checked")
|
|
306
|
-
.map(([k]) => k);
|
|
307
|
-
// Hide our header marker
|
|
308
|
-
const headerIndex = ids.indexOf("header");
|
|
309
|
-
if (headerIndex > -1) {
|
|
310
|
-
ids.splice(headerIndex, 1);
|
|
311
|
-
}
|
|
312
|
-
return ids;
|
|
344
|
+
/** Returns either all data rows (non-header, non-totals, etc) that are selected where `row.selectable !== false` */
|
|
345
|
+
get selectedRows() {
|
|
346
|
+
return [...this.selectedDataRows.values()].filter((row) => row.selectable !== false && !utils_1.reservedRowKinds.includes(row.kind));
|
|
313
347
|
}
|
|
314
348
|
// Should be called in an Observer/useComputed to trigger re-renders
|
|
315
349
|
getSelected(id) {
|
|
316
|
-
|
|
317
|
-
|
|
350
|
+
var _a;
|
|
351
|
+
// Return the row's selected state if it's been explicitly set. If it is in the selectedDataRows set, then it's selected. Otherwise, it's unchecked.
|
|
352
|
+
return (_a = this.rowSelectedState.get(id)) !== null && _a !== void 0 ? _a : (this.selectedDataRows.has(id) ? "checked" : "unchecked");
|
|
318
353
|
}
|
|
319
354
|
selectRow(id, selected) {
|
|
355
|
+
var _a;
|
|
320
356
|
if (id === "header") {
|
|
321
357
|
// Select/unselect all has special behavior
|
|
322
358
|
if (selected) {
|
|
323
359
|
// Just mash the header + all rows + children as selected
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
this.
|
|
360
|
+
const selectedStateMap = new Map();
|
|
361
|
+
const selectedRowMap = new Map();
|
|
362
|
+
selectedStateMap.set("header", "checked");
|
|
363
|
+
(0, visitor_1.visit)(this.rows, (row) => {
|
|
364
|
+
if (!utils_1.reservedRowKinds.includes(row.kind) && this.matchedRows.has(row.id)) {
|
|
365
|
+
selectedStateMap.set(row.id, "checked");
|
|
366
|
+
selectedRowMap.set(row.id, row);
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
this.rowSelectedState.replace(selectedStateMap);
|
|
370
|
+
// Use `merge` to ensure we don't lose any row that were selected, but no longer in `this.rows` (i.e. due to server-side filtering)
|
|
371
|
+
this.selectedDataRows.merge(selectedRowMap);
|
|
328
372
|
}
|
|
329
373
|
else {
|
|
330
374
|
// Similarly "unmash" all rows + children.
|
|
331
|
-
this.
|
|
375
|
+
this.rowSelectedState.clear();
|
|
376
|
+
this.selectedDataRows.clear();
|
|
377
|
+
this.keptSelectedRows = [];
|
|
332
378
|
}
|
|
333
379
|
}
|
|
334
380
|
else {
|
|
335
381
|
// This is the regular/non-header behavior to just add/remove the individual row id,
|
|
336
382
|
// plus percolate the change down-to-child + up-to-parents.
|
|
337
383
|
// Find the clicked on row
|
|
338
|
-
const curr = findRow(this.rows, id);
|
|
384
|
+
const curr = (_a = findRow(this.rows, id)) !== null && _a !== void 0 ? _a : (this.selectedDataRows.has(id) ? { row: this.selectedDataRows.get(id), parents: [] } : undefined);
|
|
339
385
|
if (!curr) {
|
|
340
386
|
return;
|
|
341
387
|
}
|
|
342
388
|
// Everything here & down is deterministically on/off
|
|
343
|
-
const
|
|
344
|
-
(0, visitor_1.visit)([curr.row], (row) =>
|
|
389
|
+
const selectedStateMap = new Map();
|
|
390
|
+
(0, visitor_1.visit)([curr.row], (row) => {
|
|
391
|
+
// The `visit` method walks through the selected row and all of its children, if any.
|
|
392
|
+
// Depending on whether we are determining the clicked row's state or its children, then we handle updating the selection differently.
|
|
393
|
+
// We can tell if we are determining a child row's selected state by checking against the row selected `id` and the row we're currently evaluating `row.id`.
|
|
394
|
+
const isClickedRow = row.id === id;
|
|
395
|
+
// Only update the selected states if we're updating the clicked row, or if we are checking a child row, then the row must match the filter.
|
|
396
|
+
// Meaning, rows that are filtered out and displayed in the "selectedGroup" can only change their selection state by interacting with them directly.
|
|
397
|
+
if (isClickedRow || this.matchedRows.has(row.id)) {
|
|
398
|
+
selectedStateMap.set(row.id, selected ? "checked" : "unchecked");
|
|
399
|
+
if (selected) {
|
|
400
|
+
this.selectedDataRows.set(row.id, row);
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
this.selectedDataRows.delete(row.id);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
});
|
|
345
407
|
// Now walk up the parents and see if they are now-all-checked/now-all-unchecked/some-of-each
|
|
346
408
|
for (const parent of [...curr.parents].reverse()) {
|
|
347
409
|
// Only derive selected state of the parent row if `inferSelectedState` is not `false`
|
|
348
410
|
if (parent.children && parent.inferSelectedState !== false) {
|
|
349
|
-
|
|
411
|
+
const selectedState = deriveParentSelected(this.getMatchedChildrenStates(parent.children, selectedStateMap));
|
|
412
|
+
if (selectedState === "checked") {
|
|
413
|
+
this.selectedDataRows.set(parent.id, parent);
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
this.selectedDataRows.delete(parent.id);
|
|
417
|
+
}
|
|
418
|
+
selectedStateMap.set(parent.id, selectedState);
|
|
350
419
|
}
|
|
351
420
|
}
|
|
352
421
|
// And do the header + top-level "children" as a final one-off
|
|
353
|
-
|
|
354
|
-
|
|
422
|
+
selectedStateMap.set("header", deriveParentSelected(this.getMatchedChildrenStates([...this.rows, ...this.keptSelectedRows], selectedStateMap)));
|
|
423
|
+
// And merge the new selected state map into the existing one
|
|
424
|
+
this.rowSelectedState.merge(selectedStateMap);
|
|
425
|
+
// Lastly, we need to update the `keptSelectedRows` if the row was deselected.
|
|
426
|
+
// (If selected === true, then it's not possible for the row to be in `keptSelectedRows` as you can only select rows that match the filter)
|
|
427
|
+
if (!selected) {
|
|
428
|
+
const newlyKeptRows = [...this.selectedDataRows.values()].filter((row) => keptSelectionsFilter(row, this.matchedRows));
|
|
429
|
+
if (!mobx_1.comparer.shallow(newlyKeptRows, this.keptSelectedRows)) {
|
|
430
|
+
this.keptSelectedRows = newlyKeptRows;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
355
433
|
}
|
|
356
434
|
}
|
|
357
435
|
get collapsedIds() {
|
|
@@ -371,8 +449,8 @@ class TableState {
|
|
|
371
449
|
collapsedIds.splice(0, collapsedIds.length);
|
|
372
450
|
}
|
|
373
451
|
else {
|
|
374
|
-
// Otherwise push `header` to the list as a hint that we're in the collapsed-all state
|
|
375
|
-
collapsedIds.push(
|
|
452
|
+
// Otherwise push `header` and `selectedGroup` to the list as a hint that we're in the collapsed-all state
|
|
453
|
+
collapsedIds.push(utils_1.HEADER, utils_1.KEPT_GROUP);
|
|
376
454
|
// Find all non-leaf rows so that toggling "all collapsed" -> "all not collapsed" opens
|
|
377
455
|
// the parent rows of any level.
|
|
378
456
|
const parentIds = new Set();
|
|
@@ -397,6 +475,7 @@ class TableState {
|
|
|
397
475
|
else {
|
|
398
476
|
collapsedIds.splice(i, 1);
|
|
399
477
|
}
|
|
478
|
+
// TODO: Need to handle the kept selected group row.
|
|
400
479
|
// If all rows have been expanded individually, but the 'header' was collapsed, then remove the header from the collapsedIds so it reverts to the expanded state
|
|
401
480
|
if (collapsedIds.length === 1 && collapsedIds[0] === "header") {
|
|
402
481
|
collapsedIds.splice(0, 1);
|
|
@@ -415,13 +494,15 @@ class TableState {
|
|
|
415
494
|
}
|
|
416
495
|
getMatchedChildrenStates(children, map) {
|
|
417
496
|
const respectedChildren = children.flatMap(getChildrenForDerivingSelectState);
|
|
497
|
+
// When determining the children selected states to base the parent's state from, then only base this off of rows that match the filter or are in the "hidden selected" group (via the `filter` below)
|
|
418
498
|
return respectedChildren
|
|
419
|
-
.filter((row) => row.id !== "header" && this.matchedRows.has(row.id))
|
|
499
|
+
.filter((row) => row.id !== "header" && (this.matchedRows.has(row.id) || this.selectedDataRows.has(row.id)))
|
|
420
500
|
.map((row) => map.get(row.id) || this.getSelected(row.id));
|
|
421
501
|
}
|
|
422
502
|
// Recursively traverse through rows to determine selected state of parent rows based on children
|
|
503
|
+
// Returns the selected states for the immediately children (if any) of the row passed in
|
|
423
504
|
setNestedSelectedStates(row, map) {
|
|
424
|
-
if (this.matchedRows.has(row.id)) {
|
|
505
|
+
if (this.matchedRows.has(row.id) || this.selectedDataRows.has(row.id)) {
|
|
425
506
|
// do not derive selected state if there are no children, or if `inferSelectedState` is set to false
|
|
426
507
|
if (!row.children || row.inferSelectedState === false) {
|
|
427
508
|
return [this.getSelected(row.id)];
|
|
@@ -437,6 +518,7 @@ class TableState {
|
|
|
437
518
|
exports.TableState = TableState;
|
|
438
519
|
/** Returns the child rows needed for deriving the selected state of a parent/group row */
|
|
439
520
|
function getChildrenForDerivingSelectState(row) {
|
|
521
|
+
// Only look deeper if the parent row does not infer its selected state
|
|
440
522
|
if (row.children && row.inferSelectedState === false) {
|
|
441
523
|
return [row, ...row.children.flatMap(getChildrenForDerivingSelectState)];
|
|
442
524
|
}
|
|
@@ -445,6 +527,7 @@ function getChildrenForDerivingSelectState(row) {
|
|
|
445
527
|
/** Provides a context for rows to access their table's `TableState`. */
|
|
446
528
|
exports.TableStateContext = react_1.default.createContext({
|
|
447
529
|
get tableState() {
|
|
530
|
+
console.log("wtf?");
|
|
448
531
|
throw new Error("No TableStateContext provider");
|
|
449
532
|
},
|
|
450
533
|
});
|
|
@@ -534,3 +617,9 @@ function deriveSortState(currentSortState, clickedKey, initialSortState) {
|
|
|
534
617
|
return initialSortState;
|
|
535
618
|
}
|
|
536
619
|
exports.deriveSortState = deriveSortState;
|
|
620
|
+
function keptSelectionsFilter(row, matchedRows) {
|
|
621
|
+
return (!matchedRows.has(row.id) &&
|
|
622
|
+
row.selectable !== false &&
|
|
623
|
+
!utils_1.reservedRowKinds.includes(row.kind) &&
|
|
624
|
+
(!row.children || row.inferSelectedState === false));
|
|
625
|
+
}
|
|
@@ -41,8 +41,8 @@ function selectColumn(columnDef) {
|
|
|
41
41
|
id: "beamSelectColumn",
|
|
42
42
|
clientSideSort: false,
|
|
43
43
|
align: "center",
|
|
44
|
-
// Defining `w:
|
|
45
|
-
w: "
|
|
44
|
+
// Defining `w: 40px` to accommodate for the `16px` wide checkbox and `12px` of padding on either side.
|
|
45
|
+
w: "40px",
|
|
46
46
|
wrapAction: false,
|
|
47
47
|
isAction: true,
|
|
48
48
|
expandColumns: undefined,
|
|
@@ -52,6 +52,7 @@ function selectColumn(columnDef) {
|
|
|
52
52
|
// Use any of the user's per-row kind methods if they have them.
|
|
53
53
|
...columnDef,
|
|
54
54
|
};
|
|
55
|
+
// Use newMethodMissingProxy so the user can use whatever kinds they want, i.e. `myRowKind: () => ...Toggle... `
|
|
55
56
|
return (0, utils_2.newMethodMissingProxy)(base, (key) => {
|
|
56
57
|
return (data, { row }) => ({
|
|
57
58
|
content: (0, jsx_runtime_1.jsx)(SelectToggle_1.SelectToggle, { id: row.id, disabled: row.selectable === false }),
|
|
@@ -82,6 +83,7 @@ function collapseColumn(columnDef) {
|
|
|
82
83
|
totals: utils_1.emptyCell,
|
|
83
84
|
...columnDef,
|
|
84
85
|
};
|
|
86
|
+
// Use newMethodMissingProxy so the user can use whatever kinds they want, i.e. `myRowKind: () => ...Collapse... `
|
|
85
87
|
return (0, utils_2.newMethodMissingProxy)(base, (key) => {
|
|
86
88
|
return (data, { row, level }) => ({
|
|
87
89
|
content: (0, jsx_runtime_1.jsx)(CollapseToggle_1.CollapseToggle, { row: row, compact: level > 0 }),
|
|
@@ -6,7 +6,7 @@ import { GridStyle } from "../TableStyles";
|
|
|
6
6
|
import { GridCellAlignment, GridColumnWithId, Kinded, RenderAs } from "../types";
|
|
7
7
|
import { Properties } from "../../../Css";
|
|
8
8
|
/** If a column def return just string text for a given row, apply some default styling. */
|
|
9
|
-
export declare function toContent(maybeContent: ReactNode | GridCellContent, isHeader: boolean, canSortColumn: boolean, isClientSideSorting: boolean, style: GridStyle, as: RenderAs, alignment: GridCellAlignment, column: GridColumnWithId<any>, isExpandableHeader: boolean, isExpandable: boolean, minStickyLeftOffset: number): ReactNode;
|
|
9
|
+
export declare function toContent(maybeContent: ReactNode | GridCellContent, isHeader: boolean, canSortColumn: boolean, isClientSideSorting: boolean, style: GridStyle, as: RenderAs, alignment: GridCellAlignment, column: GridColumnWithId<any>, isExpandableHeader: boolean, isExpandable: boolean, minStickyLeftOffset: number, isKeptSelectedRow: boolean): ReactNode;
|
|
10
10
|
export declare function isGridCellContent(content: ReactNode | GridCellContent): content is GridCellContent;
|
|
11
11
|
/** Return the content for a given column def applied to a given row. */
|
|
12
12
|
export declare function applyRowFn<R extends Kinded>(column: GridColumnWithId<R>, row: GridDataRow<R>, api: GridTableApi<R>, level: number, expanded: boolean): ReactNode | GridCellContent;
|
|
@@ -23,6 +23,7 @@ export declare function matchesFilter(maybeContent: ReactNode | GridCellContent,
|
|
|
23
23
|
export declare const HEADER = "header";
|
|
24
24
|
export declare const TOTALS = "totals";
|
|
25
25
|
export declare const EXPANDABLE_HEADER = "expandableHeader";
|
|
26
|
+
export declare const KEPT_GROUP = "keptGroup";
|
|
26
27
|
export declare const reservedRowKinds: string[];
|
|
27
28
|
export declare const zIndices: {
|
|
28
29
|
stickyHeader: number;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.zIndices = exports.reservedRowKinds = exports.EXPANDABLE_HEADER = exports.TOTALS = exports.HEADER = exports.matchesFilter = exports.maybeApplyFunction = exports.getJustification = exports.getAlignment = exports.getFirstOrLastCellCss = exports.emptyCell = exports.DESC = exports.ASC = exports.applyRowFn = exports.isGridCellContent = exports.toContent = void 0;
|
|
3
|
+
exports.zIndices = exports.reservedRowKinds = exports.KEPT_GROUP = exports.EXPANDABLE_HEADER = exports.TOTALS = exports.HEADER = exports.matchesFilter = exports.maybeApplyFunction = exports.getJustification = exports.getAlignment = exports.getFirstOrLastCellCss = exports.emptyCell = exports.DESC = exports.ASC = exports.applyRowFn = exports.isGridCellContent = exports.toContent = void 0;
|
|
4
4
|
const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
|
|
5
5
|
const Icon_1 = require("../../Icon");
|
|
6
6
|
const ExpandableHeader_1 = require("../components/ExpandableHeader");
|
|
@@ -8,8 +8,12 @@ const SortHeader_1 = require("../components/SortHeader");
|
|
|
8
8
|
const Css_1 = require("../../../Css");
|
|
9
9
|
const getInteractiveElement_1 = require("../../../utils/getInteractiveElement");
|
|
10
10
|
/** If a column def return just string text for a given row, apply some default styling. */
|
|
11
|
-
function toContent(maybeContent, isHeader, canSortColumn, isClientSideSorting, style, as, alignment, column, isExpandableHeader, isExpandable, minStickyLeftOffset) {
|
|
11
|
+
function toContent(maybeContent, isHeader, canSortColumn, isClientSideSorting, style, as, alignment, column, isExpandableHeader, isExpandable, minStickyLeftOffset, isKeptSelectedRow) {
|
|
12
12
|
var _a, _b, _c;
|
|
13
|
+
// Rows within the kept selection group cannot be collapsed
|
|
14
|
+
if (isKeptSelectedRow && column.id === "beamCollapseColumn") {
|
|
15
|
+
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, {});
|
|
16
|
+
}
|
|
13
17
|
let content = isGridCellContent(maybeContent) ? maybeContent.content : maybeContent;
|
|
14
18
|
if (typeof content === "function") {
|
|
15
19
|
// Actually create the JSX by calling `content()` here (which should be as late as
|
|
@@ -153,7 +157,8 @@ exports.matchesFilter = matchesFilter;
|
|
|
153
157
|
exports.HEADER = "header";
|
|
154
158
|
exports.TOTALS = "totals";
|
|
155
159
|
exports.EXPANDABLE_HEADER = "expandableHeader";
|
|
156
|
-
exports.
|
|
160
|
+
exports.KEPT_GROUP = "keptGroup";
|
|
161
|
+
exports.reservedRowKinds = [exports.HEADER, exports.TOTALS, exports.EXPANDABLE_HEADER, exports.KEPT_GROUP];
|
|
157
162
|
exports.zIndices = {
|
|
158
163
|
stickyHeader: 4,
|
|
159
164
|
stickyColumns: 3,
|
package/dist/hooks/index.d.ts
CHANGED
package/dist/hooks/index.js
CHANGED
|
@@ -16,6 +16,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./useBreakpoint"), exports);
|
|
18
18
|
__exportStar(require("./useComputed"), exports);
|
|
19
|
+
__exportStar(require("./useFilter"), exports);
|
|
19
20
|
__exportStar(require("./useGroupBy"), exports);
|
|
20
21
|
__exportStar(require("./useHover"), exports);
|
|
21
22
|
__exportStar(require("./usePersistedFilter"), exports);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FilterDefs } from "..";
|
|
2
|
+
interface UsePersistedFilterProps<F> {
|
|
3
|
+
filterDefs: FilterDefs<F>;
|
|
4
|
+
}
|
|
5
|
+
interface FilterHook<F> {
|
|
6
|
+
filter: F;
|
|
7
|
+
setFilter: (filter: F) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function useFilter<F>({ filterDefs }: UsePersistedFilterProps<F>): FilterHook<F>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useFilter = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
function useFilter({ filterDefs }) {
|
|
7
|
+
const [filter, setFilter] = (0, react_1.useState)(Object.fromEntries((0, utils_1.safeEntries)(filterDefs)
|
|
8
|
+
.filter(([key, def]) => def(key).defaultValue !== undefined)
|
|
9
|
+
.map(([key, def]) => [key, def(key).defaultValue])));
|
|
10
|
+
return { setFilter, filter };
|
|
11
|
+
}
|
|
12
|
+
exports.useFilter = useFilter;
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -31,3 +31,4 @@ export declare function areArraysEqual(a: any[], b: any[]): boolean;
|
|
|
31
31
|
export declare function isPromise(obj: any | Promise<any>): obj is Promise<any>;
|
|
32
32
|
export declare function isFunction(f: any): f is Function;
|
|
33
33
|
export declare function isDefined<T extends any>(param: T | undefined | null): param is T;
|
|
34
|
+
export declare function pluralize(count: number | unknown[], noun: string, pluralNoun?: string): string;
|
package/dist/utils/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.isDefined = exports.isFunction = exports.isPromise = exports.areArraysEqual = exports.isAbsoluteUrl = exports.EmptyRef = exports.safeEntries = exports.noop = exports.omitKey = exports.safeKeys = exports.maybeCall = exports.toGroupState = exports.toToggleState = void 0;
|
|
17
|
+
exports.pluralize = exports.isDefined = exports.isFunction = exports.isPromise = exports.areArraysEqual = exports.isAbsoluteUrl = exports.EmptyRef = exports.safeEntries = exports.noop = exports.omitKey = exports.safeKeys = exports.maybeCall = exports.toGroupState = exports.toToggleState = void 0;
|
|
18
18
|
/** Adapts our state to what useToggleState returns in a stateless manner. */
|
|
19
19
|
function toToggleState(isSelected, onChange) {
|
|
20
20
|
return {
|
|
@@ -97,3 +97,9 @@ function isDefined(param) {
|
|
|
97
97
|
return param !== null && param !== undefined;
|
|
98
98
|
}
|
|
99
99
|
exports.isDefined = isDefined;
|
|
100
|
+
function pluralize(count, noun, pluralNoun) {
|
|
101
|
+
if ((Array.isArray(count) ? count.length : count) === 1)
|
|
102
|
+
return noun;
|
|
103
|
+
return pluralNoun || `${noun}s`;
|
|
104
|
+
}
|
|
105
|
+
exports.pluralize = pluralize;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@homebound/beam",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.294.0",
|
|
4
4
|
"author": "Homebound",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -36,19 +36,19 @@
|
|
|
36
36
|
"@homebound/form-state": "^2.17.0",
|
|
37
37
|
"@internationalized/number": "^3.0.3",
|
|
38
38
|
"@popperjs/core": "^2.11.6",
|
|
39
|
-
"@react-aria/utils": "^3.
|
|
39
|
+
"@react-aria/utils": "^3.18.0",
|
|
40
40
|
"change-case": "^4.1.2",
|
|
41
41
|
"date-fns": "^2.28.0",
|
|
42
42
|
"dompurify": "^2.3.0",
|
|
43
43
|
"fast-deep-equal": "^3.1.3",
|
|
44
44
|
"framer-motion": "^9.0.4",
|
|
45
45
|
"memoize-one": "^5.2.1",
|
|
46
|
-
"react-aria": "^3.
|
|
46
|
+
"react-aria": "^3.26.0",
|
|
47
47
|
"react-day-picker": "8.0.7",
|
|
48
48
|
"react-popper": "^2.3.0",
|
|
49
49
|
"react-router": "^5.3.4",
|
|
50
50
|
"react-router-dom": "^5.3.4",
|
|
51
|
-
"react-stately": "^3.
|
|
51
|
+
"react-stately": "^3.24.0",
|
|
52
52
|
"react-virtuoso": "^4.2.2",
|
|
53
53
|
"tributejs": "^5.1.3",
|
|
54
54
|
"trix": "^1.3.1",
|
|
@@ -81,16 +81,17 @@
|
|
|
81
81
|
"@homebound/tsconfig": "^1.0.3",
|
|
82
82
|
"@semantic-release/exec": "^6.0.3",
|
|
83
83
|
"@semantic-release/git": "^10.0.1",
|
|
84
|
-
"@storybook/addon-docs": "^7.0.
|
|
85
|
-
"@storybook/addon-essentials": "^7.0.
|
|
86
|
-
"@storybook/addon-interactions": "^7.0.
|
|
87
|
-
"@storybook/addon-links": "^7.0.
|
|
88
|
-
"@storybook/
|
|
89
|
-
"@storybook/
|
|
84
|
+
"@storybook/addon-docs": "^7.0.26",
|
|
85
|
+
"@storybook/addon-essentials": "^7.0.26",
|
|
86
|
+
"@storybook/addon-interactions": "^7.0.26",
|
|
87
|
+
"@storybook/addon-links": "^7.0.26",
|
|
88
|
+
"@storybook/addon-mdx-gfm": "^7.0.26",
|
|
89
|
+
"@storybook/addons": "^7.0.26",
|
|
90
|
+
"@storybook/manager-api": "^7.0.26",
|
|
90
91
|
"@storybook/mdx2-csf": "1.1.0",
|
|
91
|
-
"@storybook/react": "^7.0.
|
|
92
|
-
"@storybook/react-vite": "^7.0.
|
|
93
|
-
"@storybook/testing-library": "^0.
|
|
92
|
+
"@storybook/react": "^7.0.26",
|
|
93
|
+
"@storybook/react-vite": "^7.0.26",
|
|
94
|
+
"@storybook/testing-library": "^0.2.0",
|
|
94
95
|
"@testing-library/jest-dom": "^5.16.5",
|
|
95
96
|
"@tsconfig/recommended": "^1.0.2",
|
|
96
97
|
"@types/dompurify": "^2.2.3",
|
|
@@ -118,10 +119,9 @@
|
|
|
118
119
|
"prettier": "^2.8.4",
|
|
119
120
|
"prettier-plugin-organize-imports": "^3.2.2",
|
|
120
121
|
"react": "^18.2.0",
|
|
121
|
-
"react-beautiful-dnd": "^13.1.1",
|
|
122
122
|
"react-dom": "^18.2.0",
|
|
123
123
|
"semantic-release": "^20.1.0",
|
|
124
|
-
"storybook": "^7.0.
|
|
124
|
+
"storybook": "^7.0.26",
|
|
125
125
|
"storybook-addon-designs": "beta",
|
|
126
126
|
"ts-jest": "^29.0.5",
|
|
127
127
|
"ts-node": "^10.9.1",
|