@homebound/beam 2.95.0 → 2.96.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.
|
@@ -7,9 +7,14 @@ const components_1 = require("..");
|
|
|
7
7
|
function CollapseToggle(props) {
|
|
8
8
|
const { row } = props;
|
|
9
9
|
const { isCollapsed, toggleCollapsed } = (0, react_1.useContext)(components_1.GridCollapseContext);
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
10
|
+
const [, setTick] = (0, react_1.useState)(0);
|
|
11
|
+
const currentlyCollapsed = isCollapsed(row.id);
|
|
12
|
+
const toggleOnClick = (0, react_1.useCallback)(() => {
|
|
13
|
+
toggleCollapsed(row.id);
|
|
14
|
+
setTick(Date.now());
|
|
15
|
+
}, [row.id, currentlyCollapsed, toggleCollapsed]);
|
|
16
|
+
const iconKey = currentlyCollapsed ? "chevronRight" : "chevronDown";
|
|
17
|
+
const headerIconKey = currentlyCollapsed ? "chevronsRight" : "chevronsDown";
|
|
13
18
|
const isHeader = row.kind === "header";
|
|
14
19
|
if (!isHeader && (!props.row.children || props.row.children.length === 0)) {
|
|
15
20
|
return null;
|
|
@@ -209,7 +209,7 @@ declare type GridRowKind<R extends Kinded, P extends R["kind"]> = DiscriminateUn
|
|
|
209
209
|
* - For server-side sorting, it's the sortKey to pass back to the server to
|
|
210
210
|
* request "sort by this column".
|
|
211
211
|
*
|
|
212
|
-
* - For client-side sorting, it
|
|
212
|
+
* - For client-side sorting, it's type `number`, to represent the current
|
|
213
213
|
* column being sorted, in which case we use the GridCellContent.value.
|
|
214
214
|
*/
|
|
215
215
|
export declare type GridColumn<R extends Kinded, S = {}> = {
|
|
@@ -270,7 +270,8 @@ export declare type GridCellAlignment = "left" | "right" | "center";
|
|
|
270
270
|
* primitive value for filtering and sorting.
|
|
271
271
|
*/
|
|
272
272
|
export declare type GridCellContent = {
|
|
273
|
-
content
|
|
273
|
+
/** The JSX content of the cell. Virtual tables that client-side sort should use a function to avaid perf overhead. */
|
|
274
|
+
content: ReactNode | (() => ReactNode);
|
|
274
275
|
alignment?: GridCellAlignment;
|
|
275
276
|
/** Allow value to be a function in case it's a dynamic value i.e. reading from an inline-edited proxy. */
|
|
276
277
|
value?: MaybeFn<number | string | Date | boolean | null | undefined>;
|
|
@@ -302,13 +303,17 @@ export declare type GridDataRow<R extends Kinded> = {
|
|
|
302
303
|
/** Return the content for a given column def applied to a given row. */
|
|
303
304
|
export declare function applyRowFn<R extends Kinded>(column: GridColumn<R>, row: GridDataRow<R>): ReactNode | GridCellContent;
|
|
304
305
|
/**
|
|
305
|
-
* Provides each row access to
|
|
306
|
+
* Provides each row access to a method to check if it is collapsed and toggle it's collapsed state.
|
|
306
307
|
*
|
|
307
308
|
* Calling `toggleCollapse` will keep the row itself showing, but will hide any
|
|
308
309
|
* children rows (specifically those that have this row's `id` in their `parentIds`
|
|
309
310
|
* prop).
|
|
311
|
+
*
|
|
312
|
+
* headerCollapsed is used to trigger rows at the root level to rerender their chevron when all are
|
|
313
|
+
* collapsed/expanded.
|
|
310
314
|
*/
|
|
311
315
|
declare type GridCollapseContextProps = {
|
|
316
|
+
headerCollapsed: boolean;
|
|
312
317
|
isCollapsed: (id: string) => boolean;
|
|
313
318
|
toggleCollapsed(id: string): void;
|
|
314
319
|
};
|
|
@@ -120,7 +120,7 @@ function GridTable(props) {
|
|
|
120
120
|
stickyOffset,
|
|
121
121
|
openCards: nestedCards ? nestedCards.currentOpenCards() : undefined,
|
|
122
122
|
...sortProps,
|
|
123
|
-
}), `${row.kind}-${row.id}`) }),
|
|
123
|
+
}), `${row.kind}-${row.id}`) }), `${row.kind}-${row.id}`));
|
|
124
124
|
}
|
|
125
125
|
// Split out the header rows from the data rows so that we can put an `infoMessage` in between them (if needed).
|
|
126
126
|
const headerRows = [];
|
|
@@ -256,11 +256,14 @@ function renderTable(style, id, columns, headerRows, filteredRows, firstRowMessa
|
|
|
256
256
|
* [3]: https://github.com/tannerlinsley/react-virtual/issues/108
|
|
257
257
|
*/
|
|
258
258
|
function renderVirtual(style, id, columns, headerRows, filteredRows, firstRowMessage, stickyHeader, firstLastColumnWidth, xss, virtuosoRef) {
|
|
259
|
-
|
|
260
|
-
|
|
259
|
+
const { footerStyle, listStyle } = (0, react_1.useMemo)(() => {
|
|
260
|
+
var _a;
|
|
261
|
+
const { paddingBottom, ...otherRootStyles } = (_a = style.rootCss) !== null && _a !== void 0 ? _a : {};
|
|
262
|
+
return { footerStyle: { paddingBottom }, listStyle: { ...style, rootCss: otherRootStyles } };
|
|
263
|
+
}, [style]);
|
|
261
264
|
return ((0, jsx_runtime_1.jsx)(react_virtuoso_1.Virtuoso, { ref: virtuosoRef, components: {
|
|
262
|
-
List: VirtualRoot(
|
|
263
|
-
Footer: () => (0, jsx_runtime_1.jsx)("div", { css:
|
|
265
|
+
List: VirtualRoot(listStyle, columns, id, firstLastColumnWidth, xss),
|
|
266
|
+
Footer: () => (0, jsx_runtime_1.jsx)("div", { css: footerStyle }, void 0),
|
|
264
267
|
},
|
|
265
268
|
// Pin/sticky both the header row(s) + firstRowMessage to the top
|
|
266
269
|
topItemCount: (stickyHeader ? headerRows.length : 0) + (firstRowMessage ? 1 : 0), itemSize: (el) => {
|
|
@@ -297,7 +300,7 @@ function renderVirtual(style, id, columns, headerRows, filteredRows, firstRowMes
|
|
|
297
300
|
* (solely to capture as params that we can't pass through react-virtuoso's API as props).
|
|
298
301
|
*/
|
|
299
302
|
const VirtualRoot = (0, memoize_one_1.default)((gs, columns, id, firstLastColumnWidth, xss) => {
|
|
300
|
-
return react_1.default.forwardRef(({ style, children }, ref)
|
|
303
|
+
return react_1.default.forwardRef(function VirtualRoot({ style, children }, ref) {
|
|
301
304
|
// This re-renders each time we have new children in the view port
|
|
302
305
|
return ((0, jsx_runtime_1.jsx)("div", Object.assign({ ref: ref, style: style, css: {
|
|
303
306
|
...Css_1.Css.dg.gtc(calcVirtualGridColumns(columns, firstLastColumnWidth)).$,
|
|
@@ -400,7 +403,7 @@ function maybeAddCardColumns(sizes, firstLastColumnWidth) {
|
|
|
400
403
|
}
|
|
401
404
|
function getIndentationCss(style, rowStyle, columnIndex, maybeContent) {
|
|
402
405
|
// Look for cell-specific indent or row-specific indent (row-specific is only one the first column)
|
|
403
|
-
const indent = (
|
|
406
|
+
const indent = (isGridCellContent(maybeContent) && maybeContent.indent) || (columnIndex === 0 && (rowStyle === null || rowStyle === void 0 ? void 0 : rowStyle.indent));
|
|
404
407
|
return indent === 1 ? style.indentOneCss || {} : indent === 2 ? style.indentTwoCss || {} : {};
|
|
405
408
|
}
|
|
406
409
|
function getFirstOrLastCellCss(style, columnIndex, columns) {
|
|
@@ -448,10 +451,10 @@ function GridRow(props) {
|
|
|
448
451
|
return;
|
|
449
452
|
}
|
|
450
453
|
const maybeContent = applyRowFn(column, row);
|
|
451
|
-
currentColspan =
|
|
454
|
+
currentColspan = isGridCellContent(maybeContent) ? (_a = maybeContent.colspan) !== null && _a !== void 0 ? _a : 1 : 1;
|
|
452
455
|
const canSortColumn = ((sorting === null || sorting === void 0 ? void 0 : sorting.on) === "client" && column.clientSideSort !== false) ||
|
|
453
456
|
((sorting === null || sorting === void 0 ? void 0 : sorting.on) === "server" && !!column.serverSideSortKey);
|
|
454
|
-
const content = toContent(maybeContent, isHeader, canSortColumn, style);
|
|
457
|
+
const content = toContent(maybeContent, isHeader, canSortColumn, (sorting === null || sorting === void 0 ? void 0 : sorting.on) === "client", style, as);
|
|
455
458
|
(0, sortRows_1.ensureClientSideSortValueIsSortable)(sorting, isHeader, column, columnIndex, maybeContent);
|
|
456
459
|
// Note that it seems expensive to calc a per-cell class name/CSS-in-JS output,
|
|
457
460
|
// vs. setting global/table-wide CSS like `style.cellCss` on the root grid div with
|
|
@@ -499,26 +502,44 @@ const ObservedGridRow = react_1.default.memo((props) => ((0, jsx_runtime_1.jsx)(
|
|
|
499
502
|
// Invoke this just as a regular function so that Observer sees the proxy access's
|
|
500
503
|
return GridRow(props);
|
|
501
504
|
} }, void 0)));
|
|
505
|
+
/** A heuristic to detect the result of `React.createElement` / i.e. JSX. */
|
|
506
|
+
function isJSX(content) {
|
|
507
|
+
return typeof content === "object" && content && "type" in content && "props" in content;
|
|
508
|
+
}
|
|
502
509
|
/** If a column def return just string text for a given row, apply some default styling. */
|
|
503
|
-
function toContent(content, isHeader, canSortColumn, style) {
|
|
504
|
-
|
|
510
|
+
function toContent(content, isHeader, canSortColumn, isClientSideSorting, style, as) {
|
|
511
|
+
content = isGridCellContent(content) ? content.content : content;
|
|
512
|
+
if (typeof content === "function") {
|
|
513
|
+
// Actually create the JSX by calling `content()` here (which should be as late as
|
|
514
|
+
// possible, i.e. only for visible rows if we're in a virtual table).
|
|
515
|
+
content = content();
|
|
516
|
+
}
|
|
517
|
+
else if (as === "virtual" && canSortColumn && isClientSideSorting && isJSX(content)) {
|
|
518
|
+
// When using client-side sorting, we call `applyRowFn` not only during rendering, but
|
|
519
|
+
// up-front against all rows (for the currently sorted column) to determine their
|
|
520
|
+
// sort values.
|
|
521
|
+
//
|
|
522
|
+
// Pedantically this means that any table using client-side sorting should not
|
|
523
|
+
// build JSX directly in its GridColumn functions, but this overhead is especially
|
|
524
|
+
// noticeable for large/virtualized tables, so we only enforce using functions
|
|
525
|
+
// for those tables.
|
|
526
|
+
throw new Error("GridTables with as=virtual & sortable columns should use functions that return JSX, instead of JSX");
|
|
527
|
+
}
|
|
528
|
+
if (content && typeof content === "string" && isHeader && canSortColumn) {
|
|
505
529
|
return (0, jsx_runtime_1.jsx)(SortHeader_1.SortHeader, { content: content }, void 0);
|
|
506
530
|
}
|
|
507
531
|
else if (style.emptyCell && isContentEmpty(content)) {
|
|
508
532
|
// If the content is empty and the user specified an `emptyCell` node, return that.
|
|
509
533
|
return style.emptyCell;
|
|
510
534
|
}
|
|
511
|
-
else if (isContentAndSettings(content)) {
|
|
512
|
-
return content.content;
|
|
513
|
-
}
|
|
514
535
|
return content;
|
|
515
536
|
}
|
|
516
|
-
function
|
|
537
|
+
function isGridCellContent(content) {
|
|
517
538
|
return typeof content === "object" && !!content && "content" in content;
|
|
518
539
|
}
|
|
519
540
|
const emptyValues = ["", null, undefined];
|
|
520
541
|
function isContentEmpty(content) {
|
|
521
|
-
return emptyValues.includes(
|
|
542
|
+
return emptyValues.includes(content);
|
|
522
543
|
}
|
|
523
544
|
/** Return the content for a given column def applied to a given row. */
|
|
524
545
|
function applyRowFn(column, row) {
|
|
@@ -544,8 +565,9 @@ const defaultRenderFn = (as) => (key, css, content) => {
|
|
|
544
565
|
return ((0, jsx_runtime_1.jsx)(Row, Object.assign({ css: { ...css, ...tableRowStyles(as) } }, { children: content }), key));
|
|
545
566
|
};
|
|
546
567
|
exports.GridCollapseContext = react_1.default.createContext({
|
|
547
|
-
|
|
548
|
-
|
|
568
|
+
headerCollapsed: false,
|
|
569
|
+
isCollapsed: () => false,
|
|
570
|
+
toggleCollapsed: () => { },
|
|
549
571
|
});
|
|
550
572
|
/** Sets up the `GridContext` so that header cells can access the current sort settings. */
|
|
551
573
|
const headerRenderFn = (columns, column, sortState, setSortKey, as) => (key, css, content) => {
|
|
@@ -584,7 +606,7 @@ const alignmentToTextAlign = {
|
|
|
584
606
|
};
|
|
585
607
|
// For alignment, use: 1) cell def, else 2) column def, else 3) left.
|
|
586
608
|
function getJustification(column, maybeContent, as) {
|
|
587
|
-
const alignment = (
|
|
609
|
+
const alignment = (isGridCellContent(maybeContent) && maybeContent.alignment) || column.align || "left";
|
|
588
610
|
// Always apply text alignment.
|
|
589
611
|
const textAlign = Css_1.Css.add("textAlign", alignmentToTextAlign[alignment]).$;
|
|
590
612
|
if (as === "table") {
|
|
@@ -687,7 +709,7 @@ function useToggleIds(rows, persistCollapse) {
|
|
|
687
709
|
// Trigger a re-render
|
|
688
710
|
setTick(collapsedIds.join(","));
|
|
689
711
|
};
|
|
690
|
-
return { isCollapsed, toggleCollapsed: toggleAll };
|
|
712
|
+
return { headerCollapsed: isCollapsed("header"), isCollapsed, toggleCollapsed: toggleAll };
|
|
691
713
|
},
|
|
692
714
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
693
715
|
[rows]);
|
|
@@ -709,10 +731,10 @@ function useToggleIds(rows, persistCollapse) {
|
|
|
709
731
|
// Trigger a re-render
|
|
710
732
|
setTick(collapsedIds.join(","));
|
|
711
733
|
};
|
|
712
|
-
return { isCollapsed, toggleCollapsed: toggleRow };
|
|
734
|
+
return { headerCollapsed: isCollapsed("header"), isCollapsed, toggleCollapsed: toggleRow };
|
|
713
735
|
},
|
|
714
736
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
715
|
-
[]);
|
|
737
|
+
[collapseAllContext.isCollapsed("header")]);
|
|
716
738
|
// Return a copy of the list, b/c we want external useMemos that do explicitly use the
|
|
717
739
|
// entire list as a dep to re-render whenever the list is changed (which they need to
|
|
718
740
|
// see as new list identity).
|
|
@@ -7,7 +7,7 @@ const utils_1 = require("../utils");
|
|
|
7
7
|
function SelectField(props) {
|
|
8
8
|
const { getOptionValue = (o) => o.id, // if unset, assume O implements HasId
|
|
9
9
|
getOptionLabel = (o) => o.name, // if unset, assume O implements HasName
|
|
10
|
-
value, options, onSelect, readOnly = false, errorMsg, onBlur, onFocus, disabled, } = props;
|
|
10
|
+
value, options, onSelect, readOnly = false, errorMsg, onBlur, onFocus, disabled, disabledOptions = [], } = props;
|
|
11
11
|
const tid = (0, utils_1.useTestIds)(props, "select");
|
|
12
12
|
const currentOption = options.find((o) => getOptionValue(o) === value) || options[0];
|
|
13
13
|
return ((0, jsx_runtime_1.jsxs)("select", Object.assign({}, tid, { value:
|
|
@@ -24,7 +24,7 @@ function SelectField(props) {
|
|
|
24
24
|
},
|
|
25
25
|
// Read Only does not apply to `select` fields, instead we'll add in disabled for tests to verify.
|
|
26
26
|
disabled: !!(disabled || readOnly), "data-error": !!errorMsg, "data-errormsg": errorMsg, "data-readonly": readOnly }, { children: [(0, jsx_runtime_1.jsx)("option", { disabled: true, value: "" }, void 0), options.map((option, i) => {
|
|
27
|
-
return ((0, jsx_runtime_1.jsx)("option", Object.assign({ value: `${getOptionValue(option)}
|
|
27
|
+
return ((0, jsx_runtime_1.jsx)("option", Object.assign({ value: `${getOptionValue(option)}`, disabled: disabledOptions.includes(getOptionValue(option).toString()) }, { children: getOptionLabel(option) }), i));
|
|
28
28
|
})] }), void 0));
|
|
29
29
|
}
|
|
30
30
|
exports.SelectField = SelectField;
|