@homebound/beam 2.303.0 → 2.304.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.
@@ -99,17 +99,21 @@ function GridTable(props) {
99
99
  api.init(persistCollapse, virtuosoRef, rows);
100
100
  api.setActiveRowId(activeRowId);
101
101
  api.setActiveCellId(activeCellId);
102
+ // Push the initial columns directly into tableState, b/c that is what
103
+ // makes the tests pass, but then further updates we'll do through useEffect
104
+ // to avoid "Cannot update component during render" errors.
105
+ api.tableState.setColumns(columnsWithIds, visibleColumnsStorageKey);
102
106
  return api;
103
107
  }, [props.api]);
104
108
  const style = (0, TableStyles_1.resolveStyles)(maybeStyle);
105
109
  const { tableState } = api;
106
110
  tableState.setRows(rows);
107
- tableState.setColumns(columnsWithIds, visibleColumnsStorageKey);
108
- const columns = (0, hooks_1.useComputed)(() => tableState.columns
109
- .filter((c) => tableState.visibleColumnIds.includes(c.id))
110
- .flatMap((c) => c.expandColumns && tableState.expandedColumnIds.includes(c.id)
111
- ? [c, ...tableState.getExpandedColumns(c)]
112
- : [c]), [tableState]);
111
+ (0, react_1.useEffect)(() => {
112
+ tableState.setColumns(columnsWithIds, visibleColumnsStorageKey);
113
+ }, [tableState, columnsWithIds, visibleColumnsStorageKey]);
114
+ const columns = (0, hooks_1.useComputed)(() => {
115
+ return tableState.visibleColumns;
116
+ }, [tableState]);
113
117
  // Initialize the sort state. This will only happen on the first render.
114
118
  // Once the `TableState.sort` is defined, it will not re-initialize.
115
119
  tableState.initSortState(props.sorting, columns);
@@ -128,6 +132,7 @@ function GridTable(props) {
128
132
  // (or us) is resetting component state more than necessary, so we track render counts from
129
133
  // here instead.
130
134
  const { getCount } = (0, useRenderCount_1.useRenderCount)();
135
+ // Our column sizes use either `w` or `expandedWidth`, so see which columns are currently expanded
131
136
  const expandedColumnIds = (0, hooks_1.useComputed)(() => tableState.expandedColumnIds, [tableState]);
132
137
  const columnSizes = (0, useSetupColumnSizes_1.useSetupColumnSizes)(style, columns, resizeTarget !== null && resizeTarget !== void 0 ? resizeTarget : resizeRef, expandedColumnIds);
133
138
  // Make a single copy of our current collapsed state, so we'll have a single observer.
@@ -22,28 +22,30 @@ export declare function useGridTableApi<R extends Kinded>(): GridTableApi<R>;
22
22
  /** Provides an imperative API for an application page to interact with the table. */
23
23
  export type GridTableApi<R extends Kinded> = {
24
24
  /** Scrolls row `index` into view; only supported with `as=virtual` and after a `useEffect`. */
25
- scrollToIndex: (index: number) => void;
25
+ scrollToIndex(index: number): void;
26
26
  /** Returns the ids of currently-selected rows. */
27
27
  getSelectedRowIds(): string[];
28
28
  getSelectedRowIds<K extends R["kind"]>(kind: K): string[];
29
29
  /** Returns the currently-selected rows. */
30
30
  getSelectedRows(): GridDataRow<R>[];
31
+ /** Returns the currently-selected rows of the given `kind`. */
31
32
  getSelectedRows<K extends R["kind"]>(kind: K): GridDataRow<DiscriminateUnion<R, "kind", K>>[];
32
- /** Deselects all rows */
33
+ /** Set selected state of a row by id. */
34
+ selectRow(id: string, selected?: boolean): void;
35
+ /** De-selects all selected rows. */
33
36
  clearSelections(): void;
37
+ /** Whether a row is currently collapsed. */
38
+ isCollapsedRow(id: string): boolean;
39
+ /** Toggle collapse state of a row by id. */
40
+ toggleCollapsedRow(id: string): void;
34
41
  /** Sets the internal state of 'activeRowId' */
35
- setActiveRowId: (id: string | undefined) => void;
42
+ setActiveRowId(id: string | undefined): void;
36
43
  /** Sets the internal state of 'activeCellId' */
37
- setActiveCellId: (id: string | undefined) => void;
38
- /** Set selected state of a row by id */
39
- selectRow: (id: string, selected?: boolean) => void;
40
- /** Deletes a row from the table */
41
- deleteRows: (ids: string[]) => void;
42
- /** Toggle collapse state of a row by id */
43
- toggleCollapsedRow: (id: string) => void;
44
- isCollapsedRow: (id: string) => boolean;
45
- setVisibleColumns: (ids: string[]) => void;
46
- getVisibleColumnIds: () => string[];
44
+ setActiveCellId(id: string | undefined): void;
45
+ /** Deletes a row from the table, i.e. so it's not detected as kept. */
46
+ deleteRows(ids: string[]): void;
47
+ getVisibleColumnIds(): string[];
48
+ setVisibleColumns(ids: string[]): void;
47
49
  };
48
50
  export declare class GridTableApiImpl<R extends Kinded> implements GridTableApi<R> {
49
51
  readonly tableState: TableState;
@@ -31,6 +31,7 @@ class GridTableApiImpl {
31
31
  }
32
32
  /** Called once by the GridTable when it takes ownership of this api instance. */
33
33
  init(persistCollapse, virtuosoRef, rows) {
34
+ // Technically this drives both row-collapse and column-expanded
34
35
  if (persistCollapse)
35
36
  this.tableState.loadCollapse(persistCollapse);
36
37
  this.virtuosoRef = virtuosoRef;
@@ -24,22 +24,23 @@ function EditColumnsButton(props) {
24
24
  : (0, OverlayTrigger_1.isIconButton)(trigger)
25
25
  ? trigger.icon
26
26
  : trigger.name);
27
- const { options } = (0, react_1.useMemo)(() => {
28
- return columns.reduce((acc, column) => {
29
- // Only include options that can be hidden and have the `name` property defined.
30
- if (!column.canHide)
31
- return acc;
32
- if (!column.name || column.name.length === 0 || !column.id || column.id.length === 0) {
33
- console.warn("Column is missing 'name' and/or 'id' property required by the Edit Columns button", column);
34
- return acc;
35
- }
36
- // Add current column as an option
37
- return { ...acc, options: acc.options.concat({ label: column.name, value: column.id }) };
38
- }, { options: [] });
39
- }, [columns]);
27
+ const options = (0, react_1.useMemo)(() => columns
28
+ // Only include options that can be hidden
29
+ .filter((column) => column.canHide)
30
+ // And have the `name` property defined
31
+ .filter((column) => {
32
+ if (!column.name || column.name.length === 0 || !column.id || column.id.length === 0) {
33
+ console.warn("Column is missing 'name' and/or 'id' property required by the Edit Columns button", column);
34
+ return false;
35
+ }
36
+ return true;
37
+ })
38
+ .map((column) => ({ label: column.name, value: column.id })), [columns]);
40
39
  const selectedValues = (0, hooks_1.useComputed)(() => api.getVisibleColumnIds(), [api]);
41
- const setSelectedValues = (0, react_1.useCallback)((values) => {
42
- api.setVisibleColumns(columns.filter((column) => (column.canHide ? values.includes(column.id) : true)).map((c) => c.id));
40
+ const setSelectedValues = (0, react_1.useCallback)((ids) => {
41
+ // Doesn't `options` already filter us to non-hidden/valid-id columns? I.e. could we just do:
42
+ // api.setVisibleColumns(ids);
43
+ api.setVisibleColumns(columns.filter((column) => (column.canHide ? ids.includes(column.id) : true)).map((c) => c.id));
43
44
  }, [columns, api]);
44
45
  return ((0, jsx_runtime_1.jsx)(OverlayTrigger_1.OverlayTrigger, { ...props, menuTriggerProps: menuTriggerProps, state: state, buttonRef: buttonRef, ...tid, children: (0, jsx_runtime_1.jsxs)("div", { css: {
45
46
  ...Css_1.Css.bgWhite.py5.px3.maxwPx(380).bshBasic.$,
@@ -22,7 +22,7 @@ function ExpandableHeader(props) {
22
22
  return ((0, jsx_runtime_1.jsxs)("button", { ...hoverProps, css: Css_1.Css.df.xsMd.aic.jcsb.gap2.px1.hPx(32).mxPx(-8).w("calc(100% + 16px)").br4.lightBlue700.if(isHovered).bgGray100.$, onClick: async () => {
23
23
  if ((0, utils_2.isFunction)(column.expandColumns)) {
24
24
  setIsLoading(true);
25
- await tableState.loadExpandedColumns(column);
25
+ await tableState.loadExpandedColumns(column.id);
26
26
  setIsLoading(false);
27
27
  }
28
28
  // manually calling this as loadExpandedColumns does not toggle
@@ -78,11 +78,11 @@ function RowImpl(props) {
78
78
  let minStickyLeftOffset = 0;
79
79
  let expandColumnHidden = false;
80
80
  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) => {
81
- var _a, _b, _c, _d, _e;
81
+ var _a, _b, _c, _d;
82
82
  // If the expandable column was hidden, then we need to look at the previous column to format the `expandHeader` and 'header' kinds correctly.
83
83
  const maybeExpandedColumn = expandColumnHidden ? columns[columnIndex - 1] : column;
84
84
  // Figure out if this column should be considered 'expanded' or not. If the column is hidden on expand, then we need to look at the previous column to see if it's expanded.
85
- const isExpanded = tableState.expandedColumnIds.includes(maybeExpandedColumn.id);
85
+ const isExpanded = tableState.isExpandedColumn(maybeExpandedColumn.id);
86
86
  // If the column is hidden on expand, we don't want to render it. We'll flag that it was hidden, so on the next column we can render this column's "expandHeader" property.
87
87
  if (column.hideOnExpand && isExpanded) {
88
88
  expandColumnHidden = true;
@@ -90,9 +90,9 @@ function RowImpl(props) {
90
90
  }
91
91
  // Need to keep track of the expanded columns so we can add borders as expected for the header rows
92
92
  const numExpandedColumns = isExpanded
93
- ? ((_a = tableState.getExpandedColumns(maybeExpandedColumn)) === null || _a === void 0 ? void 0 : _a.length)
93
+ ? tableState.numberOfExpandedChildren(maybeExpandedColumn.id)
94
94
  ? // Subtract 1 if the column is hidden on expand, since we're not rendering it.
95
- tableState.getExpandedColumns(maybeExpandedColumn).length - (maybeExpandedColumn.hideOnExpand ? 1 : 0)
95
+ tableState.numberOfExpandedChildren(maybeExpandedColumn.id) - (maybeExpandedColumn.hideOnExpand ? 1 : 0)
96
96
  : 0
97
97
  : 0;
98
98
  // 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.
@@ -145,7 +145,7 @@ function RowImpl(props) {
145
145
  column.expandedWidth !== undefined;
146
146
  const content = (0, utils_1.toContent)(maybeContent, isHeader, canSortColumn, sortOn === "client", style, as, alignment, column, isExpandableHeader, isExpandable, minStickyLeftOffset, isKeptSelectedRow);
147
147
  (0, sortRows_1.ensureClientSideSortValueIsSortable)(sortOn, isHeader || isTotals || isExpandableHeader, column, columnIndex, maybeContent);
148
- const maybeSticky = (_b = (((0, utils_1.isGridCellContent)(maybeContent) && maybeContent.sticky) || column.sticky)) !== null && _b !== void 0 ? _b : undefined;
148
+ const maybeSticky = (_a = (((0, utils_1.isGridCellContent)(maybeContent) && maybeContent.sticky) || column.sticky)) !== null && _a !== void 0 ? _a : undefined;
149
149
  const maybeStickyColumnStyles = maybeSticky && columnSizes
150
150
  ? {
151
151
  ...Css_1.Css.sticky.z(utils_1.zIndices.stickyColumns).bgWhite.$,
@@ -199,13 +199,13 @@ function RowImpl(props) {
199
199
  currentExpandedColumnCount === 0 &&
200
200
  Css_1.Css.boxShadow(`inset -1px -1px 0 ${Css_1.Palette.Gray200}`).$),
201
201
  // Or level-specific styling
202
- ...(!isHeader && !isTotals && !isExpandableHeader && !!style.levels && ((_c = style.levels[level]) === null || _c === void 0 ? void 0 : _c.cellCss)),
202
+ ...(!isHeader && !isTotals && !isExpandableHeader && !!style.levels && ((_b = style.levels[level]) === null || _b === void 0 ? void 0 : _b.cellCss)),
203
203
  // Level specific styling for the first content column
204
- ...(applyFirstContentColumnStyles && !!style.levels && ((_d = style.levels[level]) === null || _d === void 0 ? void 0 : _d.firstContentColumn)),
204
+ ...(applyFirstContentColumnStyles && !!style.levels && ((_c = style.levels[level]) === null || _c === void 0 ? void 0 : _c.firstContentColumn)),
205
205
  // The specific cell's css (if any from GridCellContent)
206
206
  ...rowStyleCellCss,
207
207
  // Apply active row styling for non-nested card styles.
208
- ...(isActive ? Css_1.Css.bgColor((_e = style.activeBgColor) !== null && _e !== void 0 ? _e : Css_1.Palette.LightBlue50).$ : {}),
208
+ ...(isActive ? Css_1.Css.bgColor((_d = style.activeBgColor) !== null && _d !== void 0 ? _d : Css_1.Palette.LightBlue50).$ : {}),
209
209
  // Add any cell specific style overrides
210
210
  ...((0, utils_1.isGridCellContent)(maybeContent) && maybeContent.typeScale ? Css_1.Css[maybeContent.typeScale].$ : {}),
211
211
  // And any cell specific css
@@ -85,6 +85,12 @@ export type GridColumn<R extends Kinded> = {
85
85
  /** Determines whether this column should be hidden when expanded (only the 'expandColumns' would show) */
86
86
  hideOnExpand?: boolean;
87
87
  };
88
+ /**
89
+ * Adds an `id` to `GridColumn`, for use in storage/APIs.
90
+ *
91
+ * Ideally we'd require this on `GridColumn` itself, but that would be
92
+ * a large breaking change for a lot of tables that don't need column ids.
93
+ */
88
94
  export type GridColumnWithId<R extends Kinded> = GridColumn<R> & {
89
95
  id: string;
90
96
  expandColumns?: GridColumnWithId<R>[] | (() => Promise<GridColumn<R>[]>);
@@ -102,6 +108,6 @@ export type IfAny<T, Y, N> = 0 extends 1 & T ? Y : N;
102
108
  export type InfiniteScroll = {
103
109
  /** will be called when the user scrolls to the end of the list with the last item index as an argument. */
104
110
  onEndReached: (index: number) => void;
105
- /** The number of pixels from the bottom of the list to eagerly trigger `onEndReached`. The default is is 500px. */
111
+ /** The number of pixels from the bottom of the list to eagerly trigger `onEndReached`. The default is 500px. */
106
112
  endOffsetPx?: number;
107
113
  };
@@ -0,0 +1,24 @@
1
+ import { GridColumnWithId } from "../../..";
2
+ import { ColumnStates } from "./ColumnStates";
3
+ import { ColumnStorage } from "./ColumnStorage";
4
+ /**
5
+ * A reactive/observable wrapper around each GridColumn.
6
+ *
7
+ * This is primarily for tracking visible/expanded columns for tables
8
+ * that use the expandable columns feature.
9
+ */
10
+ export declare class ColumnState {
11
+ private states;
12
+ column: GridColumnWithId<any>;
13
+ children: ColumnState[] | undefined;
14
+ private visible;
15
+ private expanded;
16
+ constructor(states: ColumnStates, storage: ColumnStorage, column: GridColumnWithId<any>);
17
+ setVisible(visible: boolean): void;
18
+ get isExpanded(): boolean;
19
+ toggleExpanded(): void;
20
+ /** Calls the `column.expandColumns` function, if set, and adds the resulting columns. */
21
+ doExpand(force?: boolean): Promise<void>;
22
+ /** Returns this column, if visible, and its children, if expanded. */
23
+ get maybeSelfAndChildren(): ColumnState[];
24
+ }
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ColumnState = void 0;
4
+ const mobx_1 = require("mobx");
5
+ const src_1 = require("../../..");
6
+ const index_1 = require("../../../utils/index");
7
+ /**
8
+ * A reactive/observable wrapper around each GridColumn.
9
+ *
10
+ * This is primarily for tracking visible/expanded columns for tables
11
+ * that use the expandable columns feature.
12
+ */
13
+ class ColumnState {
14
+ constructor(states, storage, column) {
15
+ var _a, _b, _c;
16
+ this.states = states;
17
+ this.children = undefined;
18
+ this.visible = true;
19
+ this.expanded = false;
20
+ this.column = column;
21
+ // If the user sets `canHide: true`, we default to hidden unless they set `initVisible: true`
22
+ this.visible = (_a = storage.wasVisible(column.id)) !== null && _a !== void 0 ? _a : (column.canHide ? (_b = column.initVisible) !== null && _b !== void 0 ? _b : false : true);
23
+ if (this.visible && ((_c = storage.wasExpanded(column.id)) !== null && _c !== void 0 ? _c : column.initExpanded)) {
24
+ this.expanded = true;
25
+ this.doExpand();
26
+ }
27
+ (0, mobx_1.makeAutoObservable)(this, { column: mobx_1.observable.ref });
28
+ }
29
+ setVisible(visible) {
30
+ const wasVisible = this.visible;
31
+ this.visible = visible;
32
+ // If an expandable header is becoming visible for the 1st time, expand it
33
+ if (!wasVisible && visible && this.column.initExpanded && this.children === undefined) {
34
+ this.expanded = true;
35
+ this.doExpand();
36
+ }
37
+ }
38
+ get isExpanded() {
39
+ return this.expanded;
40
+ }
41
+ toggleExpanded() {
42
+ const wasExpanded = this.expanded;
43
+ this.expanded = !this.expanded;
44
+ // The first time we expand, fetch our children. Note that ExpandableHeader
45
+ // technically pre-loads our children, so it can show a spinner while loading,
46
+ // and only after loading is complete, tell our column to expand.
47
+ if (!wasExpanded)
48
+ this.doExpand();
49
+ }
50
+ /** Calls the `column.expandColumns` function, if set, and adds the resulting columns. */
51
+ async doExpand(force = false) {
52
+ const { expandColumns } = this.column;
53
+ // If we've already got the children, don't re-expand unless forced (i.e. props.columns changed)
54
+ if (this.children !== undefined && !force)
55
+ return;
56
+ if ((0, index_1.isFunction)(expandColumns)) {
57
+ const ecs = await expandColumns();
58
+ this.children = (0, src_1.assignDefaultColumnIds)(ecs).map((ec) => this.states.addColumn(ec));
59
+ }
60
+ else if (expandColumns) {
61
+ this.children = expandColumns.map((ec) => this.states.addColumn(ec));
62
+ }
63
+ }
64
+ /** Returns this column, if visible, and its children, if expanded. */
65
+ get maybeSelfAndChildren() {
66
+ if (!this.visible) {
67
+ return [];
68
+ }
69
+ else if (this.expanded && this.children) {
70
+ // Maybe do the `hideOnExpand` thing here? Seems cute, but the Row rendering still
71
+ // needs to do the "look back to the prior column for the expandableHeader cell" logic.
72
+ // if (this.column.hideOnExpand) {
73
+ // return this.children.flatMap((c) => c.selfAndMaybeChildren);
74
+ // }
75
+ return [this, ...this.children.flatMap((c) => c.maybeSelfAndChildren)];
76
+ }
77
+ else {
78
+ return [this];
79
+ }
80
+ }
81
+ }
82
+ exports.ColumnState = ColumnState;
@@ -0,0 +1,29 @@
1
+ import { GridColumnWithId } from "../../..";
2
+ import { ColumnState } from "./ColumnState";
3
+ /** A reactive/observable wrapper around our columns. */
4
+ export declare class ColumnStates {
5
+ private columns;
6
+ private map;
7
+ private storage;
8
+ constructor();
9
+ /**
10
+ * Updates our internal columns states when `props.columns` changes.
11
+ *
12
+ * We handle sessionStorage here b/c we allow the user to either provide their own
13
+ * storage key, or calc the storage key based on the currently-visible columns.
14
+ * So like you expand a column, and new columns show up, but we'll remember they
15
+ * were hidden last time you looked at this specific expansion of columns.
16
+ */
17
+ setColumns(columns: GridColumnWithId<any>[], visibleColumnsStorageKey: string | undefined): void;
18
+ /** Adds a column to our state, i.e. maybe a dynamically loaded column. */
19
+ addColumn(column: GridColumnWithId<any>): ColumnState;
20
+ /** Returns the `ColumnState` for the given `id`. */
21
+ get(id: string): ColumnState;
22
+ /** Returns all currently-expanded columns. */
23
+ get expandedColumns(): ColumnState[];
24
+ /** Returns a flat list of all visible columns. */
25
+ get allVisibleColumns(): ColumnState[];
26
+ setVisibleColumns(ids: string[]): void;
27
+ loadExpanded(storageKey: string): void;
28
+ loadVisible(storageKey: string): void;
29
+ }
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ColumnStates = void 0;
4
+ const change_case_1 = require("change-case");
5
+ const mobx_1 = require("mobx");
6
+ const ColumnState_1 = require("./ColumnState");
7
+ const ColumnStorage_1 = require("./ColumnStorage");
8
+ /** A reactive/observable wrapper around our columns. */
9
+ class ColumnStates {
10
+ constructor() {
11
+ // The top-level list of columns
12
+ this.columns = [];
13
+ this.map = new Map();
14
+ this.storage = new ColumnStorage_1.ColumnStorage(this);
15
+ (0, mobx_1.makeAutoObservable)(this);
16
+ }
17
+ /**
18
+ * Updates our internal columns states when `props.columns` changes.
19
+ *
20
+ * We handle sessionStorage here b/c we allow the user to either provide their own
21
+ * storage key, or calc the storage key based on the currently-visible columns.
22
+ * So like you expand a column, and new columns show up, but we'll remember they
23
+ * were hidden last time you looked at this specific expansion of columns.
24
+ */
25
+ setColumns(columns, visibleColumnsStorageKey) {
26
+ if (columns.some((c) => c.canHide)) {
27
+ // We optionally auto-calc visible columns based on the currently-_potentially_-visible columns
28
+ visibleColumnsStorageKey !== null && visibleColumnsStorageKey !== void 0 ? visibleColumnsStorageKey : (visibleColumnsStorageKey = (0, change_case_1.camelCase)(columns.map((c) => c.id).join()));
29
+ this.loadVisible(visibleColumnsStorageKey);
30
+ }
31
+ this.columns = columns.map((c) => this.addColumn(c));
32
+ // After the very first non-zero `setColumns`, we disconnect from sessionStorage
33
+ if (columns.length > 0)
34
+ this.storage.done();
35
+ }
36
+ /** Adds a column to our state, i.e. maybe a dynamically loaded column. */
37
+ addColumn(column) {
38
+ const existing = this.map.get(column.id);
39
+ if (!existing) {
40
+ const cs = new ColumnState_1.ColumnState(this, this.storage, column);
41
+ this.map.set(column.id, cs);
42
+ return cs;
43
+ }
44
+ else {
45
+ existing.column = column;
46
+ // Any time a column is re-added (i.e. props.columns changed), re-expand it
47
+ if (existing.isExpanded)
48
+ existing.doExpand(true);
49
+ return existing;
50
+ }
51
+ }
52
+ /** Returns the `ColumnState` for the given `id`. */
53
+ get(id) {
54
+ const cs = this.map.get(id);
55
+ if (!cs)
56
+ throw new Error(`No ColumnState for ${id}`);
57
+ return cs;
58
+ }
59
+ /** Returns all currently-expanded columns. */
60
+ get expandedColumns() {
61
+ return this.columns.filter((cs) => cs.isExpanded);
62
+ }
63
+ /** Returns a flat list of all visible columns. */
64
+ get allVisibleColumns() {
65
+ return this.columns.flatMap((cs) => cs.maybeSelfAndChildren);
66
+ }
67
+ setVisibleColumns(ids) {
68
+ for (const cs of this.map.values()) {
69
+ cs.setVisible(ids.includes(cs.column.id));
70
+ }
71
+ }
72
+ loadExpanded(storageKey) {
73
+ this.storage.loadExpanded(storageKey);
74
+ }
75
+ loadVisible(storageKey) {
76
+ this.storage.loadVisible(storageKey);
77
+ }
78
+ }
79
+ exports.ColumnStates = ColumnStates;
@@ -0,0 +1,13 @@
1
+ import { ColumnStates } from "./ColumnStates";
2
+ /** Loads/saves the column state from sessionStorage. */
3
+ export declare class ColumnStorage {
4
+ private states;
5
+ private expandedIds;
6
+ private visibleIds;
7
+ constructor(states: ColumnStates);
8
+ loadExpanded(persistCollapse: string): void;
9
+ loadVisible(storageKey: string): void;
10
+ wasExpanded(id: string): boolean | undefined;
11
+ wasVisible(id: string): boolean | undefined;
12
+ done(): void;
13
+ }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ColumnStorage = void 0;
4
+ const mobx_1 = require("mobx");
5
+ const utils_1 = require("./utils");
6
+ /** Loads/saves the column state from sessionStorage. */
7
+ class ColumnStorage {
8
+ constructor(states) {
9
+ this.states = states;
10
+ }
11
+ loadExpanded(persistCollapse) {
12
+ const key = `expandedColumn_${persistCollapse}`;
13
+ this.expandedIds = (0, utils_1.loadArrayOrUndefined)(key);
14
+ (0, mobx_1.reaction)(() => this.states.expandedColumns.map((cs) => cs.column.id), (columnIds) => sessionStorage.setItem(key, JSON.stringify(columnIds)));
15
+ }
16
+ loadVisible(storageKey) {
17
+ this.visibleIds = (0, utils_1.loadArrayOrUndefined)(storageKey);
18
+ // Unlike the others, where we only store the value on change, we immediately
19
+ // store this value (but I'm not sure why...), hence using `autorun`.
20
+ (0, mobx_1.autorun)(() => {
21
+ const columnIds = this.states.allVisibleColumns.map((cs) => cs.column.id);
22
+ sessionStorage.setItem(storageKey, JSON.stringify(columnIds));
23
+ });
24
+ }
25
+ wasExpanded(id) {
26
+ var _a;
27
+ return (_a = this.expandedIds) === null || _a === void 0 ? void 0 : _a.includes(id);
28
+ }
29
+ wasVisible(id) {
30
+ var _a;
31
+ return (_a = this.visibleIds) === null || _a === void 0 ? void 0 : _a.includes(id);
32
+ }
33
+ done() {
34
+ this.expandedIds = undefined;
35
+ }
36
+ }
37
+ exports.ColumnStorage = ColumnStorage;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RowStorage = void 0;
4
4
  const mobx_1 = require("mobx");
5
+ const src_1 = require("../../..");
5
6
  /**
6
7
  * Manages loading/saving our currently-collapsed rows to session storage.
7
8
  *
@@ -19,12 +20,9 @@ class RowStorage {
19
20
  }
20
21
  load(persistCollapse) {
21
22
  // Load what our previously collapsed rows were
22
- const ids = sessionStorage.getItem(persistCollapse);
23
- if (ids) {
24
- this.historicalIds = JSON.parse(ids);
25
- }
23
+ this.historicalIds = (0, src_1.loadArrayOrUndefined)(persistCollapse);
26
24
  // And store new collapsed rows going forward
27
- (0, mobx_1.reaction)(() => [...this.states.collapsedRows.map((rs) => rs.row.id)], (rowIds) => sessionStorage.setItem(persistCollapse, JSON.stringify(rowIds)));
25
+ (0, mobx_1.reaction)(() => this.states.collapsedRows.map((rs) => rs.row.id), (rowIds) => sessionStorage.setItem(persistCollapse, JSON.stringify(rowIds)));
28
26
  }
29
27
  /** Once the first real-data load is done, we ignore historical ids so that we prefer any new data's `initCollapsed`. */
30
28
  done() {
@@ -1,4 +1,3 @@
1
- import { ObservableSet } from "mobx";
2
1
  import React from "react";
3
2
  import { GridDataRow } from "../components/Row";
4
3
  import { GridSortConfig } from "../GridTable";
@@ -22,18 +21,15 @@ export type SelectedState = "checked" | "unchecked" | "partial";
22
21
  export declare class TableState {
23
22
  private persistCollapse;
24
23
  private rows;
24
+ columns: GridColumnWithId<any>[];
25
25
  private readonly rowStates;
26
+ private readonly columnStates;
26
27
  activeRowId: string | undefined;
27
28
  activeCellId: string | undefined;
28
29
  sortConfig: GridSortConfig | undefined;
29
30
  sort: SortState;
30
31
  private initialSortState;
31
32
  private onSort;
32
- columns: GridColumnWithId<any>[];
33
- private expandedColumns;
34
- visibleColumns: ObservableSet<string>;
35
- private visibleColumnsStorageKey;
36
- private loadedColumns;
37
33
  /**
38
34
  * Creates the `RowState` for a given `GridTable`.
39
35
  */
@@ -44,16 +40,16 @@ export declare class TableState {
44
40
  get sortState(): SortState | undefined;
45
41
  setRows(rows: GridDataRow<any>[]): void;
46
42
  setColumns(columns: GridColumnWithId<any>[], visibleColumnsStorageKey: string | undefined): void;
47
- /** Determines which columns to expand immediately vs async */
48
- parseAndUpdateExpandedColumns(columnsToExpand: GridColumnWithId<any>[]): Promise<void>;
49
- /** Updates the state of which columns are expanded */
50
- updateExpandedColumns(newColumns: GridColumnWithId<any>[]): void;
51
- loadExpandedColumns(column: GridColumnWithId<any>): Promise<void>;
52
- getExpandedColumns(column: GridColumnWithId<any>): GridColumnWithId<any>[];
53
- setVisibleColumns(ids: string[]): void;
43
+ /** Returns visible columns, i.e. those that are visible + any expanded children. */
44
+ get visibleColumns(): GridColumnWithId<any>[];
45
+ /** Implements GridTableApi.visibleColumnIds. */
54
46
  get visibleColumnIds(): string[];
47
+ setVisibleColumns(ids: string[]): void;
55
48
  get expandedColumnIds(): string[];
49
+ isExpandedColumn(columnId: string): boolean;
56
50
  toggleExpandedColumn(columnId: string): void;
51
+ numberOfExpandedChildren(columnId: string): number;
52
+ loadExpandedColumns(columnId: string): Promise<void>;
57
53
  /** Called when GridTable has re-calced the rows that pass the client-side filter, or all rows. */
58
54
  setMatchedRows(rowIds: string[]): void;
59
55
  /** Returns selected data rows (non-header, non-totals, etc.), ignoring rows that have `row.selectable !== false`. */
@@ -4,13 +4,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.deriveSortState = exports.TableStateContext = exports.TableState = void 0;
7
- const change_case_1 = require("change-case");
8
7
  const mobx_1 = require("mobx");
9
8
  const react_1 = __importDefault(require("react"));
9
+ const ColumnStates_1 = require("./ColumnStates");
10
10
  const RowStates_1 = require("./RowStates");
11
11
  const utils_1 = require("./utils");
12
- const utils_2 = require("../../../utils");
13
- const columns_1 = require("./columns");
14
12
  /**
15
13
  * Stores the collapsed & selected state of rows.
16
14
  *
@@ -33,7 +31,10 @@ class TableState {
33
31
  constructor() {
34
32
  // The current list of rows, basically a useRef.current. Only shallow reactive.
35
33
  this.rows = [];
34
+ // The current list of columns, basically a useRef.current. Only ref reactive.
35
+ this.columns = [];
36
36
  this.rowStates = new RowStates_1.RowStates();
37
+ this.columnStates = new ColumnStates_1.ColumnStates();
37
38
  // Keeps track of the 'active' row, formatted `${row.kind}_${row.id}`
38
39
  this.activeRowId = undefined;
39
40
  // Keeps track of the 'active' cell, formatted `${row.kind}_${row.id}_${column.name}`
@@ -41,15 +42,6 @@ class TableState {
41
42
  // Tracks the active sort column(s), so GridTable or SortHeaders can reactively
42
43
  // re-render (for GridTable, only if client-side sorting)
43
44
  this.sort = {};
44
- // Non-reactive list of our columns
45
- this.columns = [];
46
- // An observable set of column ids to keep track of which columns are currently expanded
47
- this.expandedColumns = new mobx_1.ObservableSet();
48
- // An observable set of column ids to keep track of which columns are visible
49
- this.visibleColumns = new mobx_1.ObservableSet();
50
- this.visibleColumnsStorageKey = "";
51
- // Cache for already loaded expandable columns
52
- this.loadedColumns = new Map();
53
45
  // Make ourselves an observable so that mobx will do caching of .collapseIds so
54
46
  // that it'll be a stable identity for GridTable to useMemo against.
55
47
  (0, mobx_1.makeAutoObservable)(this, {
@@ -59,8 +51,7 @@ class TableState {
59
51
  // updated _contents_ of a given row, even if our other selected/matched row states don't change.
60
52
  // (as any b/c rows is private, so the mapped type doesn't see it)
61
53
  rows: mobx_1.observable.shallow,
62
- // Do not observe columns, expect this to be a non-reactive value for us to base our reactive values off of.
63
- columns: false,
54
+ columns: mobx_1.observable.ref,
64
55
  });
65
56
  // If the kept rows went from empty to not empty, then introduce the SELECTED_GROUP row as collapsed
66
57
  (0, mobx_1.reaction)(() => [...this.keptRows.values()], (curr, prev) => {
@@ -72,6 +63,7 @@ class TableState {
72
63
  loadCollapse(persistCollapse) {
73
64
  this.persistCollapse = persistCollapse;
74
65
  this.rowStates.storage.load(persistCollapse);
66
+ this.columnStates.loadExpanded(persistCollapse);
75
67
  }
76
68
  initSortState(sortConfig, columns) {
77
69
  var _a, _b;
@@ -127,118 +119,48 @@ class TableState {
127
119
  }
128
120
  // Updates the list of rows and regenerates the collapsedRows property if needed.
129
121
  setRows(rows) {
122
+ // Note that because of using `rows: observable.shallow` above, this is always
123
+ // false, and this logic runs on every render. We can eventually fix this, but it
124
+ // is convenient b/c it puts no-longer-kept rows back into the right spot in their
125
+ // parents.
130
126
  if (rows !== this.rows) {
131
127
  this.rowStates.setRows(rows);
132
128
  this.rows = rows;
133
129
  }
134
130
  }
135
131
  setColumns(columns, visibleColumnsStorageKey) {
136
- const isInitial = !this.columns || this.columns.length === 0;
137
132
  if (columns !== this.columns) {
138
- this.visibleColumnsStorageKey = visibleColumnsStorageKey !== null && visibleColumnsStorageKey !== void 0 ? visibleColumnsStorageKey : (0, change_case_1.camelCase)(columns.map((c) => c.id).join());
139
- this.visibleColumns.replace(readOrSetLocalVisibleColumnState(columns, this.visibleColumnsStorageKey));
140
- // Figure out which columns need to be expanded when rendering a new set of columns
141
- const localStorageColumns = this.persistCollapse ? readExpandedColumnsStorage(this.persistCollapse) : [];
142
- // On initial render the columns we start with is whatever is in local storage.
143
- // On subsequent renders we want to keep the columns that were previously expanded.
144
- const columnIdsToExpand = isInitial ? localStorageColumns : this.expandedColumnIds;
145
- // Create a list of all existing column ids. (We ignore the `initExpanded` property for existing columns)
146
- const existingColumnIds = this.columns.map((c) => c.id);
147
- // Add any columns to our array that are new columns that should be initially expanded.
148
- columnIdsToExpand.push(...columns.filter((c) => !existingColumnIds.includes(c.id) && c.initExpanded).map((c) => c.id));
149
- // Clear the cache to force to re-fetch the expanded columns.
150
- this.loadedColumns.clear();
151
- // Send the new array of columns along to be parsed and expanded.
152
- this.parseAndUpdateExpandedColumns(columns.filter((c) => columnIdsToExpand.includes(c.id)));
133
+ this.columnStates.setColumns(columns, visibleColumnsStorageKey);
153
134
  this.columns = columns;
154
135
  }
155
136
  }
156
- /** Determines which columns to expand immediately vs async */
157
- async parseAndUpdateExpandedColumns(columnsToExpand) {
158
- // Separate out which columns need to be loaded async vs which can be loaded immediately.
159
- const [localColumnsToExpand, asyncColumnsToExpand] = columnsToExpand.reduce((acc, c) => {
160
- if ((0, utils_2.isFunction)(c.expandColumns)) {
161
- return [acc[0], acc[1].concat(c)];
162
- }
163
- return [acc[0].concat(c), acc[1]];
164
- }, [[], []]);
165
- // Handle all async expanding columns using a Promise.all.
166
- // This will allow the table to render immediately, then cause a rerender with the new columns
167
- if (asyncColumnsToExpand.length > 0) {
168
- // Note: Not using a Promise.all because there seems to be a bug in Apollo with applying TypePolicies when using Promise.all.
169
- // TODO: Update comment with Apollo issue link.
170
- // Promise.all(asyncColumnsToExpand.map(async (c) => await this.loadExpandedColumns(c))).then(() =>
171
- // this.updateExpandedColumns(asyncColumnsToExpand),
172
- // );
173
- // Instead, doing each async request in sequence for now.
174
- for await (const column of asyncColumnsToExpand) {
175
- await this.loadExpandedColumns(column);
176
- }
177
- this.updateExpandedColumns(asyncColumnsToExpand);
178
- }
179
- // For local columns, we skip the Promise in order to have the correct state on the initial load.
180
- if (localColumnsToExpand.length > 0) {
181
- this.updateExpandedColumns(localColumnsToExpand);
182
- }
183
- }
184
- /** Updates the state of which columns are expanded */
185
- updateExpandedColumns(newColumns) {
186
- const newColumnIds = newColumns.map((c) => c.id);
187
- // If there is a difference between list of current expanded columns vs list we just created, then replace
188
- const isDifferent = newColumnIds.length !== this.expandedColumnIds.length ||
189
- !this.expandedColumnIds.every((c) => newColumnIds.includes(c));
190
- if (isDifferent) {
191
- // Note: `this.expandedColumns` is a Set, so it will take care of dedupe-ing.
192
- this.expandedColumns.replace([...this.expandedColumnIds, ...newColumnIds]);
193
- // Update session storage if necessary.
194
- if (this.persistCollapse) {
195
- sessionStorage.setItem(getColumnStorageKey(this.persistCollapse), JSON.stringify([...this.expandedColumns]));
196
- }
197
- }
198
- }
199
- // load and trigger column to be expanded
200
- async loadExpandedColumns(column) {
201
- // if we don't have anything in our cache and our expanded columns are a function
202
- if (!this.loadedColumns.has(column.id) && (0, utils_2.isFunction)(column.expandColumns)) {
203
- // set our result to the function call of expandColumns
204
- const result = await column.expandColumns();
205
- // once we have the loaded columns, add result to local cache
206
- this.loadedColumns.set(column.id, (0, columns_1.assignDefaultColumnIds)(result));
207
- }
137
+ /** Returns visible columns, i.e. those that are visible + any expanded children. */
138
+ get visibleColumns() {
139
+ return this.columnStates.allVisibleColumns.map((cs) => cs.column);
208
140
  }
209
- // if there is a promise, then grab the already loaded expandable columns from the cache, if not then return the expandedColumns property
210
- getExpandedColumns(column) {
211
- var _a, _b;
212
- return (0, utils_2.isFunction)(column.expandColumns) ? (_a = this.loadedColumns.get(column.id)) !== null && _a !== void 0 ? _a : [] : (_b = column.expandColumns) !== null && _b !== void 0 ? _b : [];
141
+ /** Implements GridTableApi.visibleColumnIds. */
142
+ get visibleColumnIds() {
143
+ return this.visibleColumns.map((cs) => cs.id);
213
144
  }
214
145
  setVisibleColumns(ids) {
215
- // If we have a new visible columns, then we need to check if some need to be initially expanded when made visible
216
- if (ids.length > this.visibleColumnIds.length) {
217
- // Get a list of columns that are just now being made visible.
218
- const newlyVisibleIds = ids.filter((id) => !this.visibleColumnIds.includes(id));
219
- // Figure out if any of these newly visible columns needs to be initially expanded.
220
- const columnsToExpand = this.columns.filter((c) => newlyVisibleIds.includes(c.id) && c.initExpanded);
221
- this.parseAndUpdateExpandedColumns(columnsToExpand);
222
- }
223
- sessionStorage.setItem(this.visibleColumnsStorageKey, JSON.stringify(ids));
224
- this.visibleColumns.replace(ids);
225
- }
226
- get visibleColumnIds() {
227
- return [...this.visibleColumns.values()];
146
+ this.columnStates.setVisibleColumns(ids);
228
147
  }
229
148
  get expandedColumnIds() {
230
- return [...this.expandedColumns.values()];
149
+ return this.columnStates.expandedColumns.map((cs) => cs.column.id);
150
+ }
151
+ isExpandedColumn(columnId) {
152
+ return this.columnStates.get(columnId).isExpanded;
231
153
  }
232
154
  toggleExpandedColumn(columnId) {
233
- if (this.expandedColumns.has(columnId)) {
234
- this.expandedColumns.delete(columnId);
235
- }
236
- else {
237
- this.expandedColumns.add(columnId);
238
- }
239
- if (this.persistCollapse) {
240
- sessionStorage.setItem(getColumnStorageKey(this.persistCollapse), JSON.stringify(this.expandedColumnIds));
241
- }
155
+ this.columnStates.get(columnId).toggleExpanded();
156
+ }
157
+ numberOfExpandedChildren(columnId) {
158
+ var _a, _b;
159
+ // Should this be only _visible_ children?
160
+ return (_b = (_a = this.columnStates.get(columnId).children) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
161
+ }
162
+ loadExpandedColumns(columnId) {
163
+ return this.columnStates.get(columnId).doExpand();
242
164
  }
243
165
  /** Called when GridTable has re-calced the rows that pass the client-side filter, or all rows. */
244
166
  setMatchedRows(rowIds) {
@@ -286,27 +208,9 @@ exports.TableState = TableState;
286
208
  /** Provides a context for rows to access their table's `TableState`. */
287
209
  exports.TableStateContext = react_1.default.createContext({
288
210
  get tableState() {
289
- console.log("wtf?");
290
211
  throw new Error("No TableStateContext provider");
291
212
  },
292
213
  });
293
- function readExpandedColumnsStorage(persistCollapse) {
294
- const expandedGridColumnIds = sessionStorage.getItem(getColumnStorageKey(persistCollapse));
295
- return expandedGridColumnIds ? JSON.parse(expandedGridColumnIds) : [];
296
- }
297
- // Get the columns that are already in the visible state so we keep them toggled.
298
- function readOrSetLocalVisibleColumnState(columns, storageKey) {
299
- const storageValue = sessionStorage.getItem(storageKey);
300
- if (storageValue) {
301
- return JSON.parse(storageValue);
302
- }
303
- const visibleColumnIds = columns.filter((c) => c.initVisible || !c.canHide).map((c) => c.id);
304
- sessionStorage.setItem(storageKey, JSON.stringify(visibleColumnIds));
305
- return visibleColumnIds;
306
- }
307
- function getColumnStorageKey(storageKey) {
308
- return `expandedColumn_${storageKey}`;
309
- }
310
214
  // Exported for testing purposes
311
215
  function deriveSortState(currentSortState, clickedKey, initialSortState) {
312
216
  var _a;
@@ -29,6 +29,6 @@ export declare function collapseColumn<T extends Kinded>(columnDef?: Partial<Gri
29
29
  * Enforces only fixed-sized units (% and px)
30
30
  */
31
31
  export declare function calcColumnSizes(columns: GridColumnWithId<any>[], tableWidth: number | undefined, tableMinWidthPx: number | undefined, expandedColumnIds: string[]): string[];
32
- /** Assign column ids if missing */
32
+ /** Assign column ids if missing. */
33
33
  export declare function assignDefaultColumnIds<T extends Kinded>(columns: GridColumn<T>[]): GridColumnWithId<T>[];
34
34
  export declare const generateColumnId: (columnIndex: number) => string;
@@ -162,13 +162,12 @@ function calcColumnSizes(columns, tableWidth, tableMinWidthPx = 0, expandedColum
162
162
  return sizes;
163
163
  }
164
164
  exports.calcColumnSizes = calcColumnSizes;
165
- /** Assign column ids if missing */
165
+ /** Assign column ids if missing. */
166
166
  function assignDefaultColumnIds(columns) {
167
- // Note: we are not _always_ spreading the `c` property as we need to be able to return the whole proxy object that
168
- // exists as part of `selectColumn` and `collapseColumn`.
169
167
  return columns.map((c, idx) => {
170
168
  var _a;
171
169
  const { expandColumns } = c;
170
+ // If `expandColumns` is a function, we don't instrument it atm.
172
171
  const expandColumnsWithId = (0, utils_2.isFunction)(expandColumns)
173
172
  ? expandColumns
174
173
  : expandColumns === null || expandColumns === void 0 ? void 0 : expandColumns.map((ec, ecIdx) => {
@@ -182,7 +181,13 @@ function assignDefaultColumnIds(columns) {
182
181
  expandColumns: undefined,
183
182
  });
184
183
  });
185
- return Object.assign(c, { id: (_a = c.id) !== null && _a !== void 0 ? _a : (0, exports.generateColumnId)(idx), expandColumns: expandColumnsWithId });
184
+ // We use `Object.assign` instead of spreading the `c` property to maintain
185
+ // the proxy objects if the user used selectColumn/collapseColumn, which have
186
+ // method-missing hooks that render empty cells for any non-header rows.
187
+ return Object.assign(c, {
188
+ id: (_a = c.id) !== null && _a !== void 0 ? _a : (0, exports.generateColumnId)(idx),
189
+ expandColumns: expandColumnsWithId,
190
+ });
186
191
  });
187
192
  }
188
193
  exports.assignDefaultColumnIds = assignDefaultColumnIds;
@@ -32,3 +32,5 @@ export declare const zIndices: {
32
32
  expandableHeaderTitle: number;
33
33
  expandableHeaderIcon: number;
34
34
  };
35
+ /** Loads an array from sessionStorage, if it exists, or `undefined`. */
36
+ export declare function loadArrayOrUndefined(key: string): any;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
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;
3
+ exports.loadArrayOrUndefined = 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");
@@ -166,3 +166,9 @@ exports.zIndices = {
166
166
  expandableHeaderTitle: 2,
167
167
  expandableHeaderIcon: 1,
168
168
  };
169
+ /** Loads an array from sessionStorage, if it exists, or `undefined`. */
170
+ function loadArrayOrUndefined(key) {
171
+ const ids = sessionStorage.getItem(key);
172
+ return ids ? JSON.parse(ids) : undefined;
173
+ }
174
+ exports.loadArrayOrUndefined = loadArrayOrUndefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homebound/beam",
3
- "version": "2.303.0",
3
+ "version": "2.304.0",
4
4
  "author": "Homebound",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",