@homebound/beam 2.113.1 → 2.116.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.
@@ -54,6 +54,8 @@ export interface GridStyle {
54
54
  levels?: Record<number, {
55
55
  cellCss: Properties;
56
56
  }>;
57
+ /** Allows for customization of the background color used to denote an "active" row */
58
+ activeBgColor?: Palette;
57
59
  }
58
60
  export declare type NestedCardStyleByKind = Record<string, NestedCardStyle>;
59
61
  export interface NestedCardsStyle {
@@ -67,6 +69,8 @@ export interface NestedCardsStyle {
67
69
  * Entries are optional, i.e. you can leave out kinds and they won't be wrapped/turned into cards.
68
70
  */
69
71
  kinds: NestedCardStyleByKind;
72
+ /** Allows for customization of the border color used to denote an "active" row */
73
+ activeBColor?: Palette;
70
74
  }
71
75
  /**
72
76
  * Styles for making cards nested within other cards.
@@ -172,6 +176,12 @@ export interface GridTableProps<R extends Kinded, S, X> {
172
176
  api?: MutableRefObject<GridTableApi<R> | undefined>;
173
177
  /** Experimental, expecting to be removed - Specify the element in which the table should resize its columns against. If not set, the table will resize columns based on its owns container's width */
174
178
  resizeTarget?: MutableRefObject<HTMLElement | null>;
179
+ /**
180
+ * Defines which row in the table should be provided with an "active" styling.
181
+ * Expected format is `${row.kind}_${row.id}`. This helps avoid id conflicts between rows of different types/kinds that may have the same id.
182
+ * Example "data_123"
183
+ */
184
+ activeRowId?: string;
175
185
  }
176
186
  /** NOTE: This API is experimental and primarily intended for story and testing purposes */
177
187
  export declare type GridTableApi<R extends Kinded> = {
@@ -180,6 +190,8 @@ export declare type GridTableApi<R extends Kinded> = {
180
190
  getSelectedRowIds(): string[];
181
191
  /** Returns the currently-selected rows. */
182
192
  getSelectedRows(): GridDataRow<R>[];
193
+ /** Sets the internal state of 'activeRowId' */
194
+ setActiveRowId: (id: string | undefined) => void;
183
195
  };
184
196
  /**
185
197
  * Renders data in our table layout.
@@ -228,7 +240,7 @@ declare type GridRowKind<R extends Kinded, P extends R["kind"]> = DiscriminateUn
228
240
  export declare type GridColumn<R extends Kinded, S = {}> = {
229
241
  [K in R["kind"]]: string | GridCellContent | (DiscriminateUnion<R, "kind", K> extends {
230
242
  data: infer D;
231
- } ? (data: D, row: GridRowKind<R, K>) => ReactNode | GridCellContent : (row: GridRowKind<R, K>) => ReactNode | GridCellContent);
243
+ } ? (data: D, row: GridRowKind<R, K>, api: GridTableApi<R>) => ReactNode | GridCellContent : (row: GridRowKind<R, K>, api: GridTableApi<R>) => ReactNode | GridCellContent);
232
244
  } & {
233
245
  /**
234
246
  * The column's width.
@@ -247,6 +259,8 @@ export declare type GridColumn<R extends Kinded, S = {}> = {
247
259
  serverSideSortKey?: S;
248
260
  /** Allows the column to stay in place when the user scrolls horizontally */
249
261
  sticky?: "left" | "right";
262
+ /** Prevent column from supporting RowStyle.onClick/rowLink in order to avoid nested interactivity. Defaults to true */
263
+ wrapAction?: false;
250
264
  };
251
265
  export declare const nonKindGridColumnKeys: string[];
252
266
  /** Allows rendering a specific cell. */
@@ -267,7 +281,7 @@ export interface RowStyle<R extends Kinded> {
267
281
  /** Whether the row should be a link. */
268
282
  rowLink?: (row: R) => string;
269
283
  /** Fired when the row is clicked, similar to rowLink but for actions that aren't 'go to this link'. */
270
- onClick?: (row: GridDataRow<R>) => void;
284
+ onClick?: (row: GridDataRow<R>, api: GridTableApi<R>) => void;
271
285
  }
272
286
  export declare type GridCellAlignment = "left" | "right" | "center";
273
287
  /**
@@ -310,6 +324,6 @@ export declare type GridDataRow<R extends Kinded> = {
310
324
  } & IfAny<R, {}, DiscriminateUnion<R, "kind", R["kind"]>>;
311
325
  declare type IfAny<T, Y, N> = 0 extends 1 & T ? Y : N;
312
326
  /** Return the content for a given column def applied to a given row. */
313
- export declare function applyRowFn<R extends Kinded>(column: GridColumn<R>, row: GridDataRow<R>): ReactNode | GridCellContent;
327
+ export declare function applyRowFn<R extends Kinded>(column: GridColumn<R>, row: GridDataRow<R>, api: MutableRefObject<GridTableApi<R>>): ReactNode | GridCellContent;
314
328
  export declare function matchesFilter(maybeContent: ReactNode | GridCellContent, filter: string): boolean;
315
329
  export {};
@@ -85,30 +85,35 @@ exports.setGridTableDefaults = setGridTableDefaults;
85
85
  */
86
86
  function GridTable(props) {
87
87
  var _a, _b, _c, _d;
88
- const { id = "gridTable", as = "div", columns, rows, style = defaults.style, rowStyles, stickyHeader = defaults.stickyHeader, stickyOffset = "0", xss, sorting, filter, filterMaxRows, fallbackMessage = "No rows found.", infoMessage, setRowCount, observeRows, persistCollapse, api, resizeTarget, } = props;
88
+ const { id = "gridTable", as = "div", columns, rows, style = defaults.style, rowStyles, stickyHeader = defaults.stickyHeader, stickyOffset = "0", xss, sorting, filter, filterMaxRows, fallbackMessage = "No rows found.", infoMessage, setRowCount, observeRows, persistCollapse, api: callerApi, resizeTarget, activeRowId, } = props;
89
89
  // Create a ref that always contains the latest rows, for our effectively-singleton RowState to use
90
90
  const rowsRef = (0, react_1.useRef)(rows);
91
91
  rowsRef.current = rows;
92
- const [rowState] = (0, react_1.useState)(() => new RowState_1.RowState(rowsRef, persistCollapse));
92
+ const [rowState] = (0, react_1.useState)(() => new RowState_1.RowState(rowsRef, persistCollapse, activeRowId));
93
93
  // We only use this in as=virtual mode, but keep this here for rowLookup to use
94
94
  const virtuosoRef = (0, react_1.useRef)(null);
95
95
  const tableRef = (0, react_1.useRef)(null);
96
- if (api) {
97
- api.current = {
98
- scrollToIndex: (index) => virtuosoRef.current && virtuosoRef.current.scrollToIndex(index),
99
- getSelectedRowIds: () => rowState.selectedIds,
100
- getSelectedRows() {
101
- const ids = rowState.selectedIds;
102
- const selected = [];
103
- (0, visitor_1.visit)(rows, (row) => {
104
- if (ids.includes(row.id)) {
105
- selected.push(row);
106
- }
107
- });
108
- return selected;
109
- },
110
- };
96
+ const api = (0, react_1.useRef)({
97
+ scrollToIndex: (index) => virtuosoRef.current && virtuosoRef.current.scrollToIndex(index),
98
+ getSelectedRowIds: () => rowState.selectedIds,
99
+ getSelectedRows() {
100
+ const ids = rowState.selectedIds;
101
+ const selected = [];
102
+ (0, visitor_1.visit)(rows, (row) => {
103
+ if (ids.includes(row.id)) {
104
+ selected.push(row);
105
+ }
106
+ });
107
+ return selected;
108
+ },
109
+ setActiveRowId: (id) => (rowState.activeRowId = id),
110
+ });
111
+ if (callerApi) {
112
+ callerApi.current = api.current;
111
113
  }
114
+ (0, react_1.useEffect)(() => {
115
+ rowState.activeRowId = activeRowId;
116
+ }, [activeRowId]);
112
117
  // We track render count at the table level, which seems odd (we should be able to track this
113
118
  // internally within each GridRow using a useRef), but we have suspicions that react-virtuoso
114
119
  // (or us) is resetting component state more than necessary, so we track render counts from
@@ -146,6 +151,7 @@ function GridTable(props) {
146
151
  columnSizes,
147
152
  level,
148
153
  getCount,
154
+ api,
149
155
  ...sortProps,
150
156
  }), `${row.kind}-${row.id}`));
151
157
  }
@@ -159,7 +165,7 @@ function GridTable(props) {
159
165
  var _a;
160
166
  const matches = filters.length === 0 ||
161
167
  row.pin ||
162
- filters.every((filter) => columns.map((c) => applyRowFn(c, row)).some((maybeContent) => matchesFilter(maybeContent, filter)));
168
+ filters.every((filter) => columns.map((c) => applyRowFn(c, row, api)).some((maybeContent) => matchesFilter(maybeContent, filter)));
163
169
  let isCard = false;
164
170
  // Even if we don't pass the filter, one of our children might, so we continue on after this check
165
171
  if (matches) {
@@ -452,7 +458,9 @@ function getFirstOrLastCellCss(style, columnIndex, columns) {
452
458
  // We extract GridRow to its own mini-component primarily so we can React.memo'ize it.
453
459
  function GridRow(props) {
454
460
  var _a;
455
- const { as, columns, row, style, rowStyles, stickyHeader, stickyOffset, sorting, sortState, setSortKey, openCards, columnSizes, level, getCount, ...others } = props;
461
+ const { as, columns, row, style, rowStyles, stickyHeader, stickyOffset, sorting, sortState, setSortKey, openCards, columnSizes, level, getCount, api, ...others } = props;
462
+ const { rowState } = (0, react_1.useContext)(RowState_1.RowStateContext);
463
+ const isActive = (0, hooks_1.useComputed)(() => rowState.activeRowId === `${row.kind}_${row.id}`, [row, rowState]);
456
464
  // We treat the "header" kind as special for "good defaults" styling
457
465
  const isHeader = row.kind === "header";
458
466
  const rowStyle = rowStyles === null || rowStyles === void 0 ? void 0 : rowStyles[row.kind];
@@ -474,11 +482,12 @@ function GridRow(props) {
474
482
  ...maybeApplyFunction(row, rowStyle === null || rowStyle === void 0 ? void 0 : rowStyle.rowCss),
475
483
  // Maybe add the sticky header styles
476
484
  ...(isHeader && stickyHeader ? Css_1.Css.sticky.top(stickyOffset).z2.$ : undefined),
477
- ...(0, nestedCards_1.getNestedCardStyles)(row, openCardStyles, style),
485
+ ...(0, nestedCards_1.getNestedCardStyles)(row, openCardStyles, style, isActive),
478
486
  };
479
487
  let currentColspan = 1;
480
488
  const rowNode = ((0, jsx_runtime_1.jsx)(Row, Object.assign({ css: rowCss }, others, { "data-gridrow": true }, getCount(row.id), { children: columns.map((column, columnIndex) => {
481
- var _a, _b, _c, _d;
489
+ var _a, _b, _c, _d, _e;
490
+ const { wrapAction = true } = column;
482
491
  if (column.mw) {
483
492
  // Validate the column's minWidth definition if set.
484
493
  if (!column.mw.endsWith("px") && !column.mw.endsWith("%")) {
@@ -490,7 +499,7 @@ function GridRow(props) {
490
499
  currentColspan -= 1;
491
500
  return null;
492
501
  }
493
- const maybeContent = applyRowFn(column, row);
502
+ const maybeContent = applyRowFn(column, row, api);
494
503
  currentColspan = isGridCellContent(maybeContent) ? (_a = maybeContent.colspan) !== null && _a !== void 0 ? _a : 1 : 1;
495
504
  const canSortColumn = ((sorting === null || sorting === void 0 ? void 0 : sorting.on) === "client" && column.clientSideSort !== false) ||
496
505
  ((sorting === null || sorting === void 0 ? void 0 : sorting.on) === "server" && !!column.serverSideSortKey);
@@ -547,6 +556,10 @@ function GridRow(props) {
547
556
  ...(!isHeader && !!style.levels && ((_d = style.levels[level]) === null || _d === void 0 ? void 0 : _d.cellCss)),
548
557
  // The specific cell's css (if any from GridCellContent)
549
558
  ...rowStyleCellCss,
559
+ // Apply active row styling for non-nested card styles.
560
+ ...(style.nestedCards === undefined && isActive
561
+ ? Css_1.Css.bgColor((_e = style.activeBgColor) !== null && _e !== void 0 ? _e : Css_1.Palette.LightBlue50).$
562
+ : {}),
550
563
  // Add any cell specific style overrides
551
564
  ...(isGridCellContent(maybeContent) && maybeContent.typeScale ? Css_1.Css[maybeContent.typeScale].$ : {}),
552
565
  // Define the width of the column on each cell. Supports col spans.
@@ -557,12 +570,12 @@ function GridRow(props) {
557
570
  },
558
571
  ...(column.mw ? Css_1.Css.mw(column.mw).$ : {}),
559
572
  };
560
- const renderFn = (rowStyle === null || rowStyle === void 0 ? void 0 : rowStyle.renderCell) || (rowStyle === null || rowStyle === void 0 ? void 0 : rowStyle.rowLink)
573
+ const renderFn = ((rowStyle === null || rowStyle === void 0 ? void 0 : rowStyle.renderCell) || (rowStyle === null || rowStyle === void 0 ? void 0 : rowStyle.rowLink)) && wrapAction
561
574
  ? rowLinkRenderFn(as)
562
575
  : isHeader
563
576
  ? headerRenderFn(columns, column, sortState, setSortKey, as)
564
- : (rowStyle === null || rowStyle === void 0 ? void 0 : rowStyle.onClick)
565
- ? rowClickRenderFn(as)
577
+ : (rowStyle === null || rowStyle === void 0 ? void 0 : rowStyle.onClick) && wrapAction
578
+ ? rowClickRenderFn(as, api)
566
579
  : defaultRenderFn(as);
567
580
  return renderFn(columnIndex, cellCss, content, row, rowStyle);
568
581
  }) }), void 0));
@@ -619,16 +632,16 @@ function isContentEmpty(content) {
619
632
  return emptyValues.includes(content);
620
633
  }
621
634
  /** Return the content for a given column def applied to a given row. */
622
- function applyRowFn(column, row) {
635
+ function applyRowFn(column, row, api) {
623
636
  // Usually this is a function to apply against the row, but sometimes it's a hard-coded value, i.e. for headers
624
637
  const maybeContent = column[row.kind];
625
638
  if (typeof maybeContent === "function") {
626
639
  if ("data" in row && "id" in row) {
627
640
  // Auto-destructure data
628
- return maybeContent(row["data"], row["id"]);
641
+ return maybeContent(row["data"], row["id"], api.current);
629
642
  }
630
643
  else {
631
- return maybeContent(row);
644
+ return maybeContent(row, api.current);
632
645
  }
633
646
  }
634
647
  else {
@@ -662,9 +675,9 @@ const rowLinkRenderFn = (as) => (key, css, content, row, rowStyle) => {
662
675
  return ((0, jsx_runtime_1.jsx)(react_router_dom_1.Link, Object.assign({ to: to, css: { ...Css_1.Css.noUnderline.color("unset").$, ...css }, className: CssReset_1.navLink }, { children: content }), key));
663
676
  };
664
677
  /** Renders a cell that will fire the RowStyle.onClick. */
665
- const rowClickRenderFn = (as) => (key, css, content, row, rowStyle) => {
678
+ const rowClickRenderFn = (as, api) => (key, css, content, row, rowStyle) => {
666
679
  const Row = as === "table" ? "tr" : "div";
667
- return ((0, jsx_runtime_1.jsx)(Row, Object.assign({}, { key }, { css: { ...css, ...tableRowStyles(as) }, onClick: () => rowStyle.onClick(row) }, { children: content }), void 0));
680
+ return ((0, jsx_runtime_1.jsx)(Row, Object.assign({}, { key }, { css: { ...css, ...tableRowStyles(as) }, onClick: () => rowStyle.onClick(row, api.current) }, { children: content }), void 0));
668
681
  };
669
682
  const alignmentToJustify = {
670
683
  left: "flex-start",
@@ -21,10 +21,11 @@ export declare class RowState {
21
21
  private persistCollapse;
22
22
  private readonly collapsedRows;
23
23
  private readonly selectedRows;
24
+ activeRowId: string | undefined;
24
25
  /**
25
26
  * Creates the `RowState` for a given `GridTable`.
26
27
  */
27
- constructor(rows: MutableRefObject<GridDataRow<any>[]>, persistCollapse: string | undefined);
28
+ constructor(rows: MutableRefObject<GridDataRow<any>[]>, persistCollapse: string | undefined, activeRowId: string | undefined);
28
29
  get selectedIds(): string[];
29
30
  getSelected(id: string): SelectedState;
30
31
  selectRow(id: string, selected: boolean): void;
@@ -26,11 +26,12 @@ class RowState {
26
26
  /**
27
27
  * Creates the `RowState` for a given `GridTable`.
28
28
  */
29
- constructor(rows, persistCollapse) {
29
+ constructor(rows, persistCollapse, activeRowId) {
30
30
  this.rows = rows;
31
31
  this.persistCollapse = persistCollapse;
32
32
  this.selectedRows = new mobx_1.ObservableMap();
33
33
  this.collapsedRows = new mobx_1.ObservableSet(persistCollapse ? readLocalCollapseState(persistCollapse) : []);
34
+ this.activeRowId = activeRowId;
34
35
  // Make ourselves an observable so that mobx will do caching of .collapseIds so
35
36
  // that it'll be a stable identity for GridTable to useMemo against.
36
37
  (0, mobx_1.makeAutoObservable)(this, { rows: false }); // as any b/c rows is private, so the mapped type doesn't see it
@@ -41,6 +41,7 @@ function selectColumn(columnDef) {
41
41
  align: "center",
42
42
  // Defining `w: 48px` to accommodate for the `16px` wide checkbox and `16px` of padding on either side.
43
43
  w: "48px",
44
+ wrapAction: false,
44
45
  // Use any of the user's per-row kind methods if they have them.
45
46
  ...columnDef,
46
47
  };
@@ -63,6 +64,7 @@ function collapseColumn(columnDef) {
63
64
  align: "center",
64
65
  // Defining `w: 38px` based on the designs
65
66
  w: "38px",
67
+ wrapAction: false,
66
68
  ...columnDef,
67
69
  };
68
70
  return (0, index_1.newMethodMissingProxy)(base, (key) => {
@@ -77,7 +77,8 @@ export declare function makeOpenOrCloseCard(openCards: string[], cardStyles: Nes
77
77
  * <div parent> <div child> <div grandchild /> </div> </div>
78
78
  */
79
79
  export declare function wrapCard(openCards: NestedCardStyle[], row: JSX.Element): JSX.Element;
80
- export declare function getNestedCardStyles(row: GridDataRow<any>, openCardStyles: NestedCardStyle[] | undefined, style: GridStyle): {
80
+ export declare function getNestedCardStyles(row: GridDataRow<any>, openCardStyles: NestedCardStyle[] | undefined, style: GridStyle, isActive: boolean): {
81
+ boxShadow?: import("csstype").Property.BoxShadow | undefined;
81
82
  paddingTop?: import("csstype").Property.PaddingTop<0 | (string & {})> | undefined;
82
83
  paddingBottom?: import("csstype").Property.PaddingBottom<0 | (string & {})> | undefined;
83
84
  borderRadius?: import("csstype").Property.BorderRadius<0 | (string & {})> | undefined;
@@ -143,15 +143,13 @@ exports.makeOpenOrCloseCard = makeOpenOrCloseCard;
143
143
  function wrapCard(openCards, row) {
144
144
  let div = row;
145
145
  [...openCards].reverse().forEach((card) => {
146
- div = ((0, jsx_runtime_1.jsx)("div", Object.assign({ css: {
147
- ...Css_1.Css.h100.pxPx(card.pxPx).bgColor(card.bgColor).if(!!card.bColor).bc(card.bColor).bl.br.$,
148
- } }, { children: div }), void 0));
146
+ div = ((0, jsx_runtime_1.jsx)("div", Object.assign({ css: Css_1.Css.h100.pxPx(card.pxPx).bgColor(card.bgColor).if(!!card.bColor).bc(card.bColor).bl.br.$ }, { children: div }), void 0));
149
147
  });
150
148
  return div;
151
149
  }
152
150
  exports.wrapCard = wrapCard;
153
- function getNestedCardStyles(row, openCardStyles, style) {
154
- var _a, _b, _c;
151
+ function getNestedCardStyles(row, openCardStyles, style, isActive) {
152
+ var _a, _b, _c, _d, _e;
155
153
  const leafCardStyles = isLeafRow(row) ? (_a = style.nestedCards) === null || _a === void 0 ? void 0 : _a.kinds[row.kind] : undefined;
156
154
  // Calculate the horizontal space already allocated by the open cards (paddings and borders)
157
155
  const openCardWidth = openCardStyles ? openCardStyles.reduce((acc, o) => acc + o.pxPx + (o.bColor ? 1 : 0), 0) : 0;
@@ -167,11 +165,16 @@ function getNestedCardStyles(row, openCardStyles, style) {
167
165
  // When it is not a leaf then it has chrome rows that create the top and bottom "padding" based on border-radius size. (brPx = "chrome" row height)
168
166
  // When it is a leaf, then we need to apply the brPx to the row to ensure consistent spacing between leaf & non-leaf renders
169
167
  // Additionally, if the leaf card has a border, then subtract the 1px border width from the padding to keep consistent with the "chrome" row
170
- Css_1.Css.pyPx(leafCardStyles.brPx - (leafCardStyles.bColor ? 1 : 0))
171
- .borderRadius(`${leafCardStyles.brPx}px`)
172
- .bgColor(leafCardStyles.bgColor)
173
- .if(!!leafCardStyles.bColor)
174
- .bc(leafCardStyles.bColor).ba.$
168
+ {
169
+ ...Css_1.Css.pyPx(leafCardStyles.brPx - (leafCardStyles.bColor ? 1 : 0))
170
+ .borderRadius(`${leafCardStyles.brPx}px`)
171
+ .bgColor(leafCardStyles.bgColor)
172
+ .if(!!leafCardStyles.bColor)
173
+ .bc(leafCardStyles.bColor).ba.$,
174
+ ...(isActive
175
+ ? Css_1.Css.boxShadow(`0px 0px 0px 2px rgba(254,254,254,1), 0px 0px 0px 4px ${(_e = (_d = style.nestedCards) === null || _d === void 0 ? void 0 : _d.activeBColor) !== null && _e !== void 0 ? _e : Css_1.Palette.LightBlue700}`).$
176
+ : {}),
177
+ }
175
178
  : undefined),
176
179
  };
177
180
  }
@@ -21,8 +21,8 @@ function sortBatch(columns, batch, sortState) {
21
21
  const invert = direction === "DESC";
22
22
  // Make a shallow copy for sorting to avoid mutating the original list
23
23
  return [...batch].sort((a, b) => {
24
- const v1 = sortValue((0, GridTable_1.applyRowFn)(column, a));
25
- const v2 = sortValue((0, GridTable_1.applyRowFn)(column, b));
24
+ const v1 = sortValue((0, GridTable_1.applyRowFn)(column, a, {}));
25
+ const v2 = sortValue((0, GridTable_1.applyRowFn)(column, b, {}));
26
26
  const v1e = v1 === null || v1 === undefined;
27
27
  const v2e = v2 === null || v2 === undefined;
28
28
  if (a.pin || b.pin) {
@@ -13,7 +13,7 @@ exports.defaultStyle = {
13
13
  indentOneCss: Css_1.Css.pl4.$,
14
14
  indentTwoCss: Css_1.Css.pl7.$,
15
15
  headerCellCss: Css_1.Css.nowrap.py1.bgGray100.aife.$,
16
- firstRowMessageCss: Css_1.Css.px1.py2.$,
16
+ firstRowMessageCss: Css_1.Css.tc.p3.$,
17
17
  };
18
18
  /** Tightens up the padding of rows, great for rows that have form elements in them. */
19
19
  exports.condensedStyle = {
@@ -7,33 +7,45 @@ const react_1 = require("react");
7
7
  function useComputed(fn, deps) {
8
8
  // We always return the useRef value, and use this just to trigger re-renders
9
9
  const [, setValue] = (0, react_1.useState)(0);
10
- const autoRunner = (0, react_1.useRef)();
11
- const autoRanValue = (0, react_1.useRef)();
10
+ const ref = (0, react_1.useRef)({
11
+ runner: undefined,
12
+ value: undefined,
13
+ hasRan: false,
14
+ });
15
+ // We use a `useMemo` b/c we want this to synchronously calc, so that even
16
+ // the very 1st render can use the result of our computed, i.e. instead of
17
+ // with `useEffect`, which would only get calc'd after the 1st render has
18
+ // already been done.
12
19
  (0, react_1.useMemo)(() => {
13
20
  let tick = 0;
21
+ const { current } = ref;
14
22
  // If deps has changed, unhook the previous observer
15
- if (autoRunner.current) {
16
- autoRunner.current();
23
+ if (current.runner) {
24
+ current.runner();
17
25
  }
18
- autoRunner.current = (0, mobx_1.autorun)(() => {
26
+ current.runner = (0, mobx_1.autorun)(() => {
19
27
  // Always eval fn() (even on 1st render) to register our observable.
20
28
  const newValue = fn();
21
- // We could eventually use a deep equals to handle objects
22
- if (newValue === autoRanValue.current) {
23
- return;
24
- }
25
- autoRanValue.current = newValue;
29
+ const oldValue = current.value;
30
+ current.value = newValue;
31
+ current.hasRan = true;
26
32
  // Only trigger a re-render if this is not the 1st autorun. Note
27
33
  // that if deps has changed, we're inherently in a re-render so also
28
34
  // don't need to trigger an additional re-render.
29
- if (tick > 0) {
35
+ if (tick > 0 && newValue !== oldValue) {
30
36
  setValue(tick);
31
37
  }
32
38
  tick++;
33
39
  });
34
40
  // eslint-disable-next-line react-hooks/exhaustive-deps
35
41
  }, deps);
42
+ // Occasionally autorun will not have run yet, in which case we have to just
43
+ // accept running the eval fn twice (here to get the value for the 1st render,
44
+ // and again for mobx to watch what observables we touch).
45
+ if (!ref.current.hasRan) {
46
+ ref.current.value = fn();
47
+ }
36
48
  // We can use `!` here b/c we know that `autorun` set current
37
- return autoRanValue.current;
49
+ return ref.current.value;
38
50
  }
39
51
  exports.useComputed = useComputed;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homebound/beam",
3
- "version": "2.113.1",
3
+ "version": "2.116.0",
4
4
  "author": "Homebound",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",