@homebound/beam 2.109.0 → 2.112.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.
@@ -12,6 +12,8 @@ export interface IconButtonProps extends BeamButtonProps, BeamFocusableProps {
12
12
  /** HTML attributes to apply to the button element when it is being used to trigger a menu. */
13
13
  menuTriggerProps?: AriaButtonProps;
14
14
  buttonRef?: RefObject<HTMLButtonElement>;
15
+ /** Whether to show a 16x16px version of the IconButton */
16
+ compact?: boolean;
15
17
  }
16
18
  export declare function IconButton(props: IconButtonProps): import("@emotion/react/jsx-runtime").JSX.Element;
17
19
  export declare const iconButtonStylesHover: {
@@ -10,7 +10,7 @@ const Css_1 = require("../Css");
10
10
  const utils_1 = require("../utils");
11
11
  const useTestIds_1 = require("../utils/useTestIds");
12
12
  function IconButton(props) {
13
- const { onClick: onPress, disabled, color, icon, autoFocus, inc, buttonRef, tooltip, menuTriggerProps, openInNew, } = props;
13
+ const { onClick: onPress, disabled, color, icon, autoFocus, inc, buttonRef, tooltip, menuTriggerProps, openInNew, compact = false, } = props;
14
14
  const isDisabled = !!disabled;
15
15
  const ariaProps = { onPress, isDisabled, autoFocus, ...menuTriggerProps };
16
16
  // eslint-disable-next-line react-hooks/rules-of-hooks
@@ -25,12 +25,13 @@ function IconButton(props) {
25
25
  const testIds = (0, useTestIds_1.useTestIds)(props, icon);
26
26
  const styles = (0, react_1.useMemo)(() => ({
27
27
  ...iconButtonStylesReset,
28
+ ...(compact ? iconButtonCompact : iconButtonNormal),
28
29
  ...(isHovered && exports.iconButtonStylesHover),
29
30
  ...(isFocusVisible && iconButtonStylesFocus),
30
31
  ...(isDisabled && iconButtonStylesDisabled),
31
- }), [isHovered, isFocusVisible, isDisabled]);
32
+ }), [isHovered, isFocusVisible, isDisabled, compact]);
32
33
  const buttonAttrs = { ...testIds, ...buttonProps, ...focusProps, ...hoverProps, ref: ref, css: styles };
33
- const buttonContent = ((0, jsx_runtime_1.jsx)(components_1.Icon, { icon: icon, color: color || (isDisabled ? Css_1.Palette.Gray400 : Css_1.Palette.Gray900), inc: inc }, void 0));
34
+ const buttonContent = ((0, jsx_runtime_1.jsx)(components_1.Icon, { icon: icon, color: color || (isDisabled ? Css_1.Palette.Gray400 : Css_1.Palette.Gray900), inc: compact ? 2 : inc }, void 0));
34
35
  const button = typeof onPress === "string" ? ((0, utils_1.isAbsoluteUrl)(onPress) || openInNew ? ((0, jsx_runtime_1.jsx)("a", Object.assign({}, buttonAttrs, { href: onPress, className: components_1.navLink, target: "_blank", rel: "noreferrer noopener" }, { children: buttonContent }), void 0)) : ((0, jsx_runtime_1.jsx)(react_router_dom_1.Link, Object.assign({}, buttonAttrs, { to: onPress, className: components_1.navLink }, { children: buttonContent }), void 0))) : ((0, jsx_runtime_1.jsx)("button", Object.assign({}, buttonAttrs, { children: buttonContent }), void 0));
35
36
  // If we're disabled b/c of a non-boolean ReactNode, or the caller specified tooltip text, then show it in a tooltip
36
37
  return (0, components_1.maybeTooltip)({
@@ -40,7 +41,9 @@ function IconButton(props) {
40
41
  });
41
42
  }
42
43
  exports.IconButton = IconButton;
43
- const iconButtonStylesReset = Css_1.Css.hPx(28).wPx(28).br8.bTransparent.bsSolid.bw2.bgTransparent.cursorPointer.outline0.p0.dif.aic.jcc.transition.$;
44
+ const iconButtonStylesReset = Css_1.Css.bTransparent.bsSolid.bgTransparent.cursorPointer.outline0.dif.aic.jcc.transition.$;
45
+ const iconButtonNormal = Css_1.Css.hPx(28).wPx(28).br8.bw2.$;
46
+ const iconButtonCompact = Css_1.Css.hPx(18).wPx(18).br4.bw1.$;
44
47
  exports.iconButtonStylesHover = Css_1.Css.bgGray200.$;
45
48
  const iconButtonStylesFocus = Css_1.Css.bLightBlue700.$;
46
49
  const iconButtonStylesDisabled = Css_1.Css.cursorNotAllowed.$;
@@ -245,6 +245,8 @@ export declare type GridColumn<R extends Kinded, S = {}> = {
245
245
  clientSideSort?: boolean;
246
246
  /** This column's sort by value (if server-side sorting). */
247
247
  serverSideSortKey?: S;
248
+ /** Allows the column to stay in place when the user scrolls horizontally */
249
+ sticky?: "left" | "right";
248
250
  };
249
251
  export declare const nonKindGridColumnKeys: string[];
250
252
  /** Allows rendering a specific cell. */
@@ -284,6 +286,8 @@ export declare type GridCellContent = {
284
286
  indent?: 1 | 2;
285
287
  colspan?: number;
286
288
  typeScale?: Typography;
289
+ /** Allows the cell to stay in place when the user scrolls horizontally */
290
+ sticky?: "left" | "right";
287
291
  };
288
292
  declare type MaybeFn<T> = T | (() => T);
289
293
  /**
@@ -42,6 +42,7 @@ const useSortState_1 = require("./useSortState");
42
42
  const visitor_1 = require("./visitor");
43
43
  const Css_1 = require("../../Css");
44
44
  const hooks_1 = require("../../hooks");
45
+ const useRenderCount_1 = require("../../hooks/useRenderCount");
45
46
  const tinycolor2_1 = __importDefault(require("tinycolor2"));
46
47
  const _1 = require(".");
47
48
  exports.ASC = "ASC";
@@ -109,6 +110,11 @@ function GridTable(props) {
109
110
  },
110
111
  };
111
112
  }
113
+ // We track render count at the table level, which seems odd (we should be able to track this
114
+ // internally within each GridRow using a useRef), but we have suspicions that react-virtuoso
115
+ // (or us) is resetting component state more than necessary, so we track render counts from
116
+ // here instead.
117
+ const { getCount } = (0, useRenderCount_1.useRenderCount)();
112
118
  const [sortState, setSortKey] = (0, useSortState_1.useSortState)(columns, sorting);
113
119
  const maybeSorted = (0, react_1.useMemo)(() => {
114
120
  if ((sorting === null || sorting === void 0 ? void 0 : sorting.on) === "client" && sortState) {
@@ -140,6 +146,7 @@ function GridTable(props) {
140
146
  openCards: nestedCards ? nestedCards.currentOpenCards() : undefined,
141
147
  columnSizes,
142
148
  level,
149
+ getCount,
143
150
  ...sortProps,
144
151
  }), `${row.kind}-${row.id}`));
145
152
  }
@@ -445,7 +452,7 @@ function getFirstOrLastCellCss(style, columnIndex, columns) {
445
452
  }
446
453
  // We extract GridRow to its own mini-component primarily so we can React.memo'ize it.
447
454
  function GridRow(props) {
448
- const { as, columns, row, style, rowStyles, stickyHeader, stickyOffset, sorting, sortState, setSortKey, openCards, columnSizes, level, ...others } = props;
455
+ const { as, columns, row, style, rowStyles, stickyHeader, stickyOffset, sorting, sortState, setSortKey, openCards, columnSizes, level, getCount, ...others } = props;
449
456
  // We treat the "header" kind as special for "good defaults" styling
450
457
  const isHeader = row.kind === "header";
451
458
  const rowStyle = rowStyles === null || rowStyles === void 0 ? void 0 : rowStyles[row.kind];
@@ -467,12 +474,12 @@ function GridRow(props) {
467
474
  }),
468
475
  ...maybeApplyFunction(row, rowStyle === null || rowStyle === void 0 ? void 0 : rowStyle.rowCss),
469
476
  // Maybe add the sticky header styles
470
- ...(isHeader && stickyHeader ? Css_1.Css.sticky.top(stickyOffset).z1.$ : undefined),
477
+ ...(isHeader && stickyHeader ? Css_1.Css.sticky.top(stickyOffset).z2.$ : undefined),
471
478
  ...(0, nestedCards_1.getNestedCardStyles)(row, openCardStyles, style),
472
479
  };
473
480
  let currentColspan = 1;
474
- const rowNode = ((0, jsx_runtime_1.jsx)(Row, Object.assign({ css: rowCss }, others, { "data-gridrow": true }, { children: columns.map((column, columnIndex) => {
475
- var _a, _b;
481
+ const rowNode = ((0, jsx_runtime_1.jsx)(Row, Object.assign({ css: rowCss }, others, { "data-gridrow": true }, getCount(row.id), { children: columns.map((column, columnIndex) => {
482
+ var _a, _b, _c, _d;
476
483
  if (column.mw) {
477
484
  // Validate the column's minWidth definition if set.
478
485
  if (!column.mw.endsWith("px") && !column.mw.endsWith("%")) {
@@ -493,6 +500,27 @@ function GridRow(props) {
493
500
  const content = toContent(maybeContent, isHeader, canSortColumn, (sorting === null || sorting === void 0 ? void 0 : sorting.on) === "client", style, as, alignment);
494
501
  (0, sortRows_1.ensureClientSideSortValueIsSortable)(sorting, isHeader, column, columnIndex, maybeContent);
495
502
  const maybeNestedCardColumnIndex = columnIndex + (style.nestedCards ? 1 : 0);
503
+ const maybeSticky = (_b = ((isGridCellContent(maybeContent) && maybeContent.sticky) || column.sticky)) !== null && _b !== void 0 ? _b : undefined;
504
+ const maybeStickyColumnStyles = maybeSticky && columnSizes
505
+ ? {
506
+ ...Css_1.Css.sticky.z1.$,
507
+ // Need to apply a background to this cell to avoid content beneath it being shown when sticky.
508
+ // Assumes background is white for non-nestedCard styles. This can be overridden via cellCss.
509
+ ...(((_c = style.nestedCards) === null || _c === void 0 ? void 0 : _c.kinds)
510
+ ? Css_1.Css.bgColor((0, nestedCards_1.getCurrentBgColor)(row, openCardStyles, style.nestedCards.kinds)).$
511
+ : Css_1.Css.bgWhite.$),
512
+ ...(maybeSticky === "left"
513
+ ? Css_1.Css.left(maybeNestedCardColumnIndex === 0
514
+ ? 0
515
+ : `calc(${columnSizes.slice(0, maybeNestedCardColumnIndex).join(" + ")})`).$
516
+ : {}),
517
+ ...(maybeSticky === "right"
518
+ ? Css_1.Css.right(maybeNestedCardColumnIndex + 1 === columnSizes.length
519
+ ? 0
520
+ : `calc(${columnSizes.slice(maybeNestedCardColumnIndex + 1 - columnSizes.length).join(" + ")})`).$
521
+ : {}),
522
+ }
523
+ : {};
496
524
  // Note that it seems expensive to calc a per-cell class name/CSS-in-JS output,
497
525
  // vs. setting global/table-wide CSS like `style.cellCss` on the root grid div with
498
526
  // a few descendent selectors. However, that approach means the root grid-applied
@@ -505,6 +533,8 @@ function GridRow(props) {
505
533
  const cellCss = {
506
534
  // Adding display flex so we can align content within the cells
507
535
  ...Css_1.Css.df.$,
536
+ // Apply sticky column/cell styles
537
+ ...maybeStickyColumnStyles,
508
538
  // Apply any static/all-cell styling
509
539
  ...style.cellCss,
510
540
  // Then override with first/last cell styling
@@ -515,7 +545,7 @@ function GridRow(props) {
515
545
  // Then apply any header-specific override
516
546
  ...(isHeader && style.headerCellCss),
517
547
  // Or level-specific styling
518
- ...(!isHeader && !!style.levels && ((_b = style.levels[level]) === null || _b === void 0 ? void 0 : _b.cellCss)),
548
+ ...(!isHeader && !!style.levels && ((_d = style.levels[level]) === null || _d === void 0 ? void 0 : _d.cellCss)),
519
549
  // The specific cell's css (if any from GridCellContent)
520
550
  ...rowStyleCellCss,
521
551
  // Add any cell specific style overrides
@@ -88,6 +88,12 @@ export declare function getNestedCardStyles(row: GridDataRow<any>, openCardStyle
88
88
  paddingLeft?: import("csstype").Property.PaddingLeft<0 | (string & {})> | undefined;
89
89
  paddingRight?: import("csstype").Property.PaddingRight<0 | (string & {})> | undefined;
90
90
  };
91
+ /**
92
+ * Returns the background color for the row.
93
+ * Either based on the current row's card having a defined background color, or the last opened card's background color
94
+ * Defaults to White.
95
+ */
96
+ export declare function getCurrentBgColor(row: GridDataRow<any>, openCardStyles: NestedCardStyle[] | undefined, nestedKinds: NestedCardStyleByKind): string;
91
97
  /**
92
98
  * Create a spacer between rows of children.
93
99
  *
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isLeafRow = exports.dropChromeRows = exports.ChromeRow = exports.makeSpacer = exports.getNestedCardStyles = exports.wrapCard = exports.makeOpenOrCloseCard = exports.NestedCards = void 0;
3
+ exports.isLeafRow = exports.dropChromeRows = exports.ChromeRow = exports.makeSpacer = exports.getCurrentBgColor = exports.getNestedCardStyles = exports.wrapCard = exports.makeOpenOrCloseCard = exports.NestedCards = void 0;
4
4
  const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const Css_1 = require("../../Css");
@@ -176,6 +176,21 @@ function getNestedCardStyles(row, openCardStyles, style) {
176
176
  };
177
177
  }
178
178
  exports.getNestedCardStyles = getNestedCardStyles;
179
+ /**
180
+ * Returns the background color for the row.
181
+ * Either based on the current row's card having a defined background color, or the last opened card's background color
182
+ * Defaults to White.
183
+ */
184
+ function getCurrentBgColor(row, openCardStyles, nestedKinds) {
185
+ if (nestedKinds[row.kind]) {
186
+ return nestedKinds[row.kind].bgColor;
187
+ }
188
+ if (openCardStyles && openCardStyles.length > 0) {
189
+ return openCardStyles[openCardStyles.length - 1].bgColor;
190
+ }
191
+ return Css_1.Palette.White;
192
+ }
193
+ exports.getCurrentBgColor = getCurrentBgColor;
179
194
  /**
180
195
  * Create a spacer between rows of children.
181
196
  *
@@ -0,0 +1,17 @@
1
+ /**
2
+ * A hook to add `data-render=${count}` to an element.
3
+ *
4
+ * This will output even in production mode, which in theory is not necessary
5
+ * and we could/should avoid, but it should also be np.
6
+ *
7
+ * The intent is to leave it "always on" for dev & tests, so that engineers
8
+ * and test suites can very easily grab "what was the render count of that?"
9
+ * w/o bringing in one-off modes/tools.
10
+ *
11
+ * (One-off modes/tools are more appropriate for truly adhoc performance
12
+ * but the intent is to use this in GridTable where "what's the render count?"
13
+ * will be a common question.)
14
+ */
15
+ export declare function useRenderCount(): {
16
+ getCount: (id: string) => object;
17
+ };
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useRenderCount = void 0;
4
+ const react_1 = require("react");
5
+ /**
6
+ * A hook to add `data-render=${count}` to an element.
7
+ *
8
+ * This will output even in production mode, which in theory is not necessary
9
+ * and we could/should avoid, but it should also be np.
10
+ *
11
+ * The intent is to leave it "always on" for dev & tests, so that engineers
12
+ * and test suites can very easily grab "what was the render count of that?"
13
+ * w/o bringing in one-off modes/tools.
14
+ *
15
+ * (One-off modes/tools are more appropriate for truly adhoc performance
16
+ * but the intent is to use this in GridTable where "what's the render count?"
17
+ * will be a common question.)
18
+ */
19
+ function useRenderCount() {
20
+ const ref = (0, react_1.useRef)(new Map());
21
+ const getCount = (0, react_1.useCallback)((id) => {
22
+ const count = ref.current.get(id) || 1;
23
+ ref.current.set(id, count + 1);
24
+ return { "data-render": count };
25
+ }, []);
26
+ return { getCount };
27
+ }
28
+ exports.useRenderCount = useRenderCount;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homebound/beam",
3
- "version": "2.109.0",
3
+ "version": "2.112.0",
4
4
  "author": "Homebound",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",