@homebound/beam 2.97.5 → 2.99.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.
@@ -1,4 +1,4 @@
1
- import { ReactNode } from "react";
1
+ import { MutableRefObject, ReactNode } from "react";
2
2
  import { Only, Xss } from "../../Css";
3
3
  import { Callback } from "../../types";
4
4
  export declare type ModalSize = "sm" | "md" | "lg" | "xl";
@@ -19,7 +19,12 @@ export interface ModalProps {
19
19
  forceScrolling?: boolean;
20
20
  /** Adds a callback that is called _after_ close has definitely happened. */
21
21
  onClose?: Callback;
22
+ /** Imperative API for interacting with the Modal */
23
+ api?: MutableRefObject<ModalApi | undefined>;
22
24
  }
25
+ export declare type ModalApi = {
26
+ setSize: (size: ModalProps["size"]) => void;
27
+ };
23
28
  /**
24
29
  * Internal component for displaying a Modal; see `useModal` for the public API.
25
30
  *
@@ -20,7 +20,7 @@ const utils_1 = require("../../utils");
20
20
  * Provides underlay, modal container, and header. Will disable scrolling of page under the modal.
21
21
  */
22
22
  function Modal(props) {
23
- const { size = "md", content, forceScrolling } = props;
23
+ const { size = "md", content, forceScrolling, api } = props;
24
24
  const isFixedHeight = typeof size !== "string";
25
25
  const ref = (0, react_1.useRef)(null);
26
26
  const { modalBodyDiv, modalFooterDiv, modalHeaderDiv, drawerContentStack } = (0, BeamContext_1.useBeamContext)();
@@ -37,13 +37,16 @@ function Modal(props) {
37
37
  }, ref);
38
38
  const { modalProps } = (0, react_aria_1.useModal)();
39
39
  const { dialogProps, titleProps } = (0, react_aria_1.useDialog)({ role: "dialog" }, ref);
40
- const [width, height] = getSize(size);
40
+ const [[width, height], setSize] = (0, react_1.useState)(getSize(size));
41
41
  const contentRef = (0, react_1.useRef)(null);
42
42
  const modalBodyRef = (0, react_1.useRef)(null);
43
43
  const modalFooterRef = (0, react_1.useRef)(null);
44
44
  const modalHeaderRef = (0, react_1.useRef)(null);
45
45
  const testId = (0, utils_1.useTestIds)({}, testIdPrefix);
46
46
  (0, react_aria_1.usePreventScroll)();
47
+ if (api) {
48
+ api.current = { setSize: (size = "md") => setSize(getSize(size)) };
49
+ }
47
50
  const [hasScroll, setHasScroll] = (0, react_1.useState)(forceScrolling !== null && forceScrolling !== void 0 ? forceScrolling : false);
48
51
  (0, resize_observer_1.default)(contentRef, ({ target }) => {
49
52
  if (forceScrolling === undefined && !isFixedHeight) {
@@ -4,5 +4,6 @@ export interface UseModalHook {
4
4
  openModal: (props: ModalProps) => void;
5
5
  closeModal: Callback;
6
6
  addCanClose: (canClose: CheckFn) => void;
7
+ setSize: (size: ModalProps["size"]) => void;
7
8
  }
8
9
  export declare function useModal(): UseModalHook;
@@ -7,6 +7,7 @@ const utils_1 = require("../../utils");
7
7
  function useModal() {
8
8
  const { modalState, modalCanCloseChecks } = (0, BeamContext_1.useBeamContext)();
9
9
  const lastCanClose = (0, react_1.useRef)();
10
+ const api = (0, react_1.useRef)();
10
11
  (0, react_1.useEffect)(() => {
11
12
  return () => {
12
13
  modalCanCloseChecks.current = modalCanCloseChecks.current.filter((c) => c !== lastCanClose.current);
@@ -16,7 +17,7 @@ function useModal() {
16
17
  openModal(props) {
17
18
  // TODO Check already open?
18
19
  // TODO Check can leave?
19
- modalState.current = props;
20
+ modalState.current = { ...props, api };
20
21
  },
21
22
  closeModal() {
22
23
  var _a;
@@ -38,6 +39,12 @@ function useModal() {
38
39
  ];
39
40
  lastCanClose.current = canClose;
40
41
  },
42
+ setSize(size) {
43
+ var _a;
44
+ if (modalState.current && ((_a = modalState.current.api) === null || _a === void 0 ? void 0 : _a.current)) {
45
+ modalState.current.api.current.setSize(size);
46
+ }
47
+ },
41
48
  }), [modalState, modalCanCloseChecks]);
42
49
  }
43
50
  exports.useModal = useModal;
@@ -188,7 +188,7 @@ export declare function GridTable<R extends Kinded, S = {}, X extends Only<GridT
188
188
  *
189
189
  * When we're as=virtual, we change our default + enforce only fixed-sized units (% and px)
190
190
  */
191
- export declare function calcVirtualGridColumns(columns: GridColumn<any>[], firstLastColumnWidth: number | undefined): string;
191
+ export declare function calcVirtualGridColumns(columns: GridColumn<any>[], firstLastColumnWidth: number | undefined): string[];
192
192
  export declare function calcDivGridColumns(columns: GridColumn<any>[], firstLastColumnWidth: number | undefined): string;
193
193
  /**
194
194
  * Given an ADT of type T, performs a look up and returns the type of kind K.
@@ -103,8 +103,10 @@ function GridTable(props) {
103
103
  }, [columns, rows, sorting, sortState]);
104
104
  // Filter + flatten + component-ize the sorted rows.
105
105
  let [headerRows, filteredRows] = (0, react_1.useMemo)(() => {
106
+ var _a;
106
107
  // Break up "foo bar" into `[foo, bar]` and a row must match both `foo` and `bar`
107
108
  const filters = (filter && filter.split(/ +/)) || [];
109
+ const columnSizes = as === "virtual" ? calcVirtualGridColumns(columns, (_a = style.nestedCards) === null || _a === void 0 ? void 0 : _a.firstLastColumnWidth) : undefined;
108
110
  function makeRowComponent(row) {
109
111
  // We only pass sortState to header rows, b/c non-headers rows shouldn't have to re-render on sorting
110
112
  // changes, and so by not passing the sortProps, it means the data rows' React.memo will still cache them.
@@ -119,6 +121,7 @@ function GridTable(props) {
119
121
  stickyHeader,
120
122
  stickyOffset,
121
123
  openCards: nestedCards ? nestedCards.currentOpenCards() : undefined,
124
+ columnSizes,
122
125
  ...sortProps,
123
126
  }), `${row.kind}-${row.id}`) }), `${row.kind}-${row.id}`));
124
127
  }
@@ -302,12 +305,9 @@ function renderVirtual(style, id, columns, headerRows, filteredRows, firstRowMes
302
305
  const VirtualRoot = (0, memoize_one_1.default)((gs, columns, id, firstLastColumnWidth, xss) => {
303
306
  return react_1.default.forwardRef(function VirtualRoot({ style, children }, ref) {
304
307
  // This re-renders each time we have new children in the view port
305
- return ((0, jsx_runtime_1.jsx)("div", Object.assign({ ref: ref, style: style, css: {
306
- ...Css_1.Css.dg.gtc(calcVirtualGridColumns(columns, firstLastColumnWidth)).$,
308
+ return ((0, jsx_runtime_1.jsx)("div", Object.assign({ ref: ref, style: { ...style, ...(gs.nestedCards ? { minWidth: "fit-content" } : {}) }, css: {
307
309
  // Add an extra `> div` due to Item + itemContent both having divs
308
310
  ...Css_1.Css.addIn("& > div + div > div > *", gs.betweenRowsCss || {}).$,
309
- // Add `display:contents` to Item to flatten it like we do GridRow
310
- ...Css_1.Css.addIn("& > div", Css_1.Css.display("contents").$).$,
311
311
  ...gs.rootCss,
312
312
  ...xss,
313
313
  }, "data-testid": id }, { children: children }), void 0));
@@ -355,7 +355,7 @@ function calcVirtualGridColumns(columns, firstLastColumnWidth) {
355
355
  }, { claimedPercentages: 0, claimedPixels: firstLastColumnWidth ? firstLastColumnWidth * 2 : 0, totalFr: 0 });
356
356
  // This is our "fake but for some reason it lines up better" fr calc
357
357
  function fr(myFr) {
358
- return `calc((100% - ${claimedPercentages}% - ${claimedPixels}px) * (${myFr} / ${totalFr}))`;
358
+ return `((100% - ${claimedPercentages}% - ${claimedPixels}px) * (${myFr} / ${totalFr}))`;
359
359
  }
360
360
  let sizes = columns.map(({ w }) => {
361
361
  if (typeof w === "undefined") {
@@ -394,12 +394,12 @@ function calcDivGridColumns(columns, firstLastColumnWidth) {
394
394
  return `${w}fr`;
395
395
  }
396
396
  });
397
- return maybeAddCardColumns(sizes, firstLastColumnWidth);
397
+ return maybeAddCardColumns(sizes, firstLastColumnWidth).join(" ");
398
398
  }
399
399
  exports.calcDivGridColumns = calcDivGridColumns;
400
400
  // If we're doing nested cards, we add extra 1st/last cells...
401
401
  function maybeAddCardColumns(sizes, firstLastColumnWidth) {
402
- return (!firstLastColumnWidth ? sizes : [`${firstLastColumnWidth}px`, ...sizes, `${firstLastColumnWidth}px`]).join(" ");
402
+ return !firstLastColumnWidth ? sizes : [`${firstLastColumnWidth}px`, ...sizes, `${firstLastColumnWidth}px`];
403
403
  }
404
404
  function getIndentationCss(style, rowStyle, columnIndex, maybeContent) {
405
405
  // Look for cell-specific indent or row-specific indent (row-specific is only one the first column)
@@ -414,7 +414,7 @@ function getFirstOrLastCellCss(style, columnIndex, columns) {
414
414
  }
415
415
  // We extract GridRow to its own mini-component primarily so we can React.memo'ize it.
416
416
  function GridRow(props) {
417
- const { as, columns, row, style, rowStyles, stickyHeader, stickyOffset, sorting, sortState, setSortKey, openCards, ...others } = props;
417
+ const { as, columns, row, style, rowStyles, stickyHeader, stickyOffset, sorting, sortState, setSortKey, openCards, columnSizes, ...others } = props;
418
418
  // We treat the "header" kind as special for "good defaults" styling
419
419
  const isHeader = row.kind === "header";
420
420
  const rowStyle = rowStyles === null || rowStyles === void 0 ? void 0 : rowStyles[row.kind];
@@ -425,7 +425,8 @@ function GridRow(props) {
425
425
  // root-element > row-element > cell-elements, so that we can have a hook for
426
426
  // hovers and styling. In theory this would change with subgrids.
427
427
  // Only enable when using div as elements
428
- ...(as === "table" ? {} : Css_1.Css.display("contents").$),
428
+ // For virtual tables use `display: flex` to keep all cells on the same row, but use `flexNone` to ensure the cells stay their defined widths
429
+ ...(as === "table" ? {} : as === "virtual" ? Css_1.Css.df.addIn("&>*", Css_1.Css.flexNone.$).$ : Css_1.Css.display("contents").$),
429
430
  ...(((rowStyle === null || rowStyle === void 0 ? void 0 : rowStyle.rowLink) || (rowStyle === null || rowStyle === void 0 ? void 0 : rowStyle.onClick)) &&
430
431
  style.rowHoverColor && {
431
432
  // Even though backgroundColor is set on the cellCss (due to display: content), the hover target is the row.
@@ -443,7 +444,11 @@ function GridRow(props) {
443
444
  const currentCard = openCardStyles && openCardStyles[openCardStyles.length - 1];
444
445
  let currentColspan = 1;
445
446
  const maybeStickyHeaderStyles = isHeader && stickyHeader ? Css_1.Css.sticky.top(stickyOffset).z1.$ : undefined;
446
- return ((0, jsx_runtime_1.jsxs)(Row, Object.assign({ css: rowCss }, others, { children: [openCardStyles && (0, nestedCards_1.maybeAddCardPadding)(openCardStyles, "first", maybeStickyHeaderStyles), columns.map((column, columnIndex) => {
447
+ return ((0, jsx_runtime_1.jsxs)(Row, Object.assign({ css: rowCss }, others, { children: [openCardStyles &&
448
+ (0, nestedCards_1.maybeAddCardPadding)(openCardStyles, "first", {
449
+ ...maybeStickyHeaderStyles,
450
+ ...(columnSizes ? Css_1.Css.w(columnSizes[0]).$ : {}),
451
+ }), columns.map((column, columnIndex) => {
447
452
  var _a;
448
453
  // Decrement colspan count and skip if greater than 1.
449
454
  if (currentColspan > 1) {
@@ -456,6 +461,7 @@ function GridRow(props) {
456
461
  ((sorting === null || sorting === void 0 ? void 0 : sorting.on) === "server" && !!column.serverSideSortKey);
457
462
  const content = toContent(maybeContent, isHeader, canSortColumn, (sorting === null || sorting === void 0 ? void 0 : sorting.on) === "client", style, as);
458
463
  (0, sortRows_1.ensureClientSideSortValueIsSortable)(sorting, isHeader, column, columnIndex, maybeContent);
464
+ const maybeNestedCardColumnIndex = columnIndex + (style.nestedCards ? 1 : 0);
459
465
  // Note that it seems expensive to calc a per-cell class name/CSS-in-JS output,
460
466
  // vs. setting global/table-wide CSS like `style.cellCss` on the root grid div with
461
467
  // a few descendent selectors. However, that approach means the root grid-applied
@@ -477,6 +483,10 @@ function GridRow(props) {
477
483
  ...getIndentationCss(style, rowStyle, columnIndex, maybeContent),
478
484
  // Then apply any header-specific override
479
485
  ...(isHeader && style.headerCellCss),
486
+ // Non-virtualized tables use h100 so that all cells are the same height across the row.
487
+ // Virtualized table rows use `display: flex;`, so the flex children are set to `align-self: stretch` by default, which achieves the same goal.
488
+ // Though, we need to omit setting `h100` on the flex children, as a flex container needs a defined height set for `h100` to work on flex children
489
+ ...(isHeader && as !== "virtual" ? Css_1.Css.h100.$ : undefined),
480
490
  ...maybeStickyHeaderStyles,
481
491
  // If we're within a card, use its background color
482
492
  ...(currentCard && Css_1.Css.bgColor(currentCard.bgColor).$),
@@ -484,6 +494,12 @@ function GridRow(props) {
484
494
  ...(currentColspan > 1 ? Css_1.Css.gc(`span ${currentColspan}`).$ : {}),
485
495
  // And finally the specific cell's css (if any from GridCellContent)
486
496
  ...rowStyleCellCss,
497
+ // For virtual tables we define the width of the column on each cell. Supports col spans.
498
+ ...(columnSizes && {
499
+ width: `calc(${columnSizes
500
+ .slice(maybeNestedCardColumnIndex, maybeNestedCardColumnIndex + currentColspan)
501
+ .join(" + ")})`,
502
+ }),
487
503
  };
488
504
  const renderFn = (rowStyle === null || rowStyle === void 0 ? void 0 : rowStyle.renderCell) || (rowStyle === null || rowStyle === void 0 ? void 0 : rowStyle.rowLink)
489
505
  ? rowLinkRenderFn(as)
@@ -493,7 +509,11 @@ function GridRow(props) {
493
509
  ? rowClickRenderFn(as)
494
510
  : defaultRenderFn(as);
495
511
  return renderFn(columnIndex, cellCss, content, row, rowStyle);
496
- }), openCardStyles && (0, nestedCards_1.maybeAddCardPadding)(openCardStyles, "final", maybeStickyHeaderStyles)] }), void 0));
512
+ }), openCardStyles &&
513
+ (0, nestedCards_1.maybeAddCardPadding)(openCardStyles, "final", {
514
+ ...maybeStickyHeaderStyles,
515
+ ...(columnSizes ? Css_1.Css.w(columnSizes[columnSizes.length - 1]).$ : {}),
516
+ })] }), void 0));
497
517
  }
498
518
  // Fix to work with generics, see https://github.com/DefinitelyTyped/DefinitelyTyped/issues/37087#issuecomment-656596623
499
519
  const MemoizedGridRow = react_1.default.memo(GridRow);
@@ -12,8 +12,7 @@ exports.defaultStyle = {
12
12
  lastCellCss: Css_1.Css.$,
13
13
  indentOneCss: Css_1.Css.pl4.$,
14
14
  indentTwoCss: Css_1.Css.pl7.$,
15
- // Use h100 so that all cells are the same height when scrolled; set bgWhite for when we're laid over other rows.
16
- headerCellCss: Css_1.Css.asfe.nowrap.py1.bgGray100.h100.aife.$,
15
+ headerCellCss: Css_1.Css.nowrap.py1.bgGray100.aife.$,
17
16
  firstRowMessageCss: Css_1.Css.px1.py2.$,
18
17
  rowHoverColor: Css_1.Palette.Gray200,
19
18
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homebound/beam",
3
- "version": "2.97.5",
3
+ "version": "2.99.0",
4
4
  "author": "Homebound",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -48,7 +48,7 @@
48
48
  "react-router": "^5.2.0",
49
49
  "react-router-dom": "^5.2.0",
50
50
  "react-stately": "^3.9.0",
51
- "react-virtuoso": "^2.3.1",
51
+ "react-virtuoso": "^2.4.0",
52
52
  "tinycolor2": "^1.4.2",
53
53
  "tributejs": "^5.1.3",
54
54
  "trix": "^1.3.1",