@homebound/beam 2.93.0 → 2.94.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.
@@ -6,13 +6,14 @@ const react_1 = require("react");
6
6
  const components_1 = require("..");
7
7
  function CollapseToggle(props) {
8
8
  const { row } = props;
9
- const { isCollapsed, toggleCollapse } = (0, react_1.useContext)(components_1.GridCollapseContext);
10
- const iconKey = isCollapsed ? "chevronRight" : "chevronDown";
11
- const headerIconKey = isCollapsed ? "chevronsRight" : "chevronsDown";
9
+ const { isCollapsed, toggleCollapsed } = (0, react_1.useContext)(components_1.GridCollapseContext);
10
+ const toggleOnClick = (0, react_1.useCallback)(() => toggleCollapsed(row.id), [row.id, toggleCollapsed]);
11
+ const iconKey = isCollapsed(row.id) ? "chevronRight" : "chevronDown";
12
+ const headerIconKey = isCollapsed(row.id) ? "chevronsRight" : "chevronsDown";
12
13
  const isHeader = row.kind === "header";
13
14
  if (!isHeader && (!props.row.children || props.row.children.length === 0)) {
14
15
  return null;
15
16
  }
16
- return (0, jsx_runtime_1.jsx)(components_1.IconButton, { onClick: toggleCollapse, icon: isHeader ? headerIconKey : iconKey }, void 0);
17
+ return (0, jsx_runtime_1.jsx)(components_1.IconButton, { onClick: toggleOnClick, icon: isHeader ? headerIconKey : iconKey }, void 0);
17
18
  }
18
19
  exports.CollapseToggle = CollapseToggle;
@@ -40,6 +40,7 @@ export interface GridStyle {
40
40
  /** Default content to put into an empty cell */
41
41
  emptyCell?: ReactNode;
42
42
  }
43
+ export declare type NestedCardStyleByKind = Record<string, NestedCardStyle>;
43
44
  export interface NestedCardsStyle {
44
45
  /** Space between each card. */
45
46
  spacerPx: number;
@@ -50,7 +51,7 @@ export interface NestedCardsStyle {
50
51
  *
51
52
  * Entries are optional, i.e. you can leave out kinds and they won't be wrapped/turned into cards.
52
53
  */
53
- kinds: Record<string, NestedCardStyle>;
54
+ kinds: NestedCardStyleByKind;
54
55
  }
55
56
  /**
56
57
  * Styles for making cards nested within other cards.
@@ -308,8 +309,8 @@ export declare function applyRowFn<R extends Kinded>(column: GridColumn<R>, row:
308
309
  * prop).
309
310
  */
310
311
  declare type GridCollapseContextProps = {
311
- isCollapsed: boolean;
312
- toggleCollapse(): void;
312
+ isCollapsed: (id: string) => boolean;
313
+ toggleCollapsed(id: string): void;
313
314
  };
314
315
  export declare const GridCollapseContext: React.Context<GridCollapseContextProps>;
315
316
  export declare function matchesFilter(maybeContent: ReactNode | GridCellContent, filter: string): boolean;
@@ -83,7 +83,7 @@ exports.setGridTableDefaults = setGridTableDefaults;
83
83
  function GridTable(props) {
84
84
  var _a;
85
85
  const { id = "grid-table", 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, } = props;
86
- const [collapsedIds, toggleCollapsedId] = useToggleIds(rows, persistCollapse);
86
+ const [collapsedIds, collapseAllContext, collapseRowContext] = useToggleIds(rows, persistCollapse);
87
87
  // We only use this in as=virtual mode, but keep this here for rowLookup to use
88
88
  const virtuosoRef = (0, react_1.useRef)(null);
89
89
  if (api) {
@@ -109,26 +109,18 @@ function GridTable(props) {
109
109
  // We only pass sortState to header rows, b/c non-headers rows shouldn't have to re-render on sorting
110
110
  // changes, and so by not passing the sortProps, it means the data rows' React.memo will still cache them.
111
111
  const sortProps = row.kind === "header" ? { sorting, sortState, setSortKey } : { sorting };
112
- // We only pass `isCollapsed` as a prop so that the row only re-renders when it itself has
113
- // changed from collapsed/non-collapsed, and not other row's entering/leaving collapsedIds.
114
- // Note that we do memoized on toggleCollapsedId, but it's stable thanks to useToggleIds.
115
- const isCollapsed = collapsedIds.includes(row.id);
116
112
  const RowComponent = observeRows ? ObservedGridRow : MemoizedGridRow;
117
- return ((0, jsx_runtime_1.jsx)(RowComponent, Object.assign({}, {
118
- as,
119
- columns,
120
- row,
121
- style,
122
- rowStyles,
123
- stickyHeader,
124
- stickyOffset,
125
- isCollapsed,
126
- toggleCollapsedId,
127
- // TODO: How will this effect with memoization?
128
- // At least for non-nested card tables, we make this undefined so it will be fine.
129
- openCards: nestedCards ? nestedCards.currentOpenCards() : undefined,
130
- ...sortProps,
131
- }), `${row.kind}-${row.id}`));
113
+ return ((0, jsx_runtime_1.jsx)(exports.GridCollapseContext.Provider, Object.assign({ value: row.kind === "header" ? collapseAllContext : collapseRowContext }, { children: (0, jsx_runtime_1.jsx)(RowComponent, Object.assign({}, {
114
+ as,
115
+ columns,
116
+ row,
117
+ style,
118
+ rowStyles,
119
+ stickyHeader,
120
+ stickyOffset,
121
+ openCards: nestedCards ? nestedCards.currentOpenCards() : undefined,
122
+ ...sortProps,
123
+ }), `${row.kind}-${row.id}`) }), void 0));
132
124
  }
133
125
  // Split out the header rows from the data rows so that we can put an `infoMessage` in between them (if needed).
134
126
  const headerRows = [];
@@ -184,7 +176,8 @@ function GridTable(props) {
184
176
  stickyHeader,
185
177
  stickyOffset,
186
178
  collapsedIds,
187
- toggleCollapsedId,
179
+ collapseAllContext,
180
+ collapseRowContext,
188
181
  observeRows,
189
182
  ]);
190
183
  let tooManyClientSideRows = false;
@@ -418,7 +411,7 @@ function getFirstOrLastCellCss(style, columnIndex, columns) {
418
411
  }
419
412
  // We extract GridRow to its own mini-component primarily so we can React.memo'ize it.
420
413
  function GridRow(props) {
421
- const { as, columns, row, style, rowStyles, stickyHeader, stickyOffset, sorting, sortState, setSortKey, isCollapsed, toggleCollapsedId, openCards, ...others } = props;
414
+ const { as, columns, row, style, rowStyles, stickyHeader, stickyOffset, sorting, sortState, setSortKey, openCards, ...others } = props;
422
415
  // We treat the "header" kind as special for "good defaults" styling
423
416
  const isHeader = row.kind === "header";
424
417
  const rowStyle = rowStyles === null || rowStyles === void 0 ? void 0 : rowStyles[row.kind];
@@ -438,10 +431,16 @@ function GridRow(props) {
438
431
  ...maybeApplyFunction(row, rowStyle === null || rowStyle === void 0 ? void 0 : rowStyle.rowCss),
439
432
  };
440
433
  const Row = as === "table" ? "tr" : "div";
441
- const currentCard = openCards && openCards.length > 0 && openCards[openCards.length - 1];
434
+ const openCardStyles = typeof openCards === "string"
435
+ ? openCards
436
+ .split(":")
437
+ .map((openCardKind) => style.nestedCards.kinds[openCardKind])
438
+ .filter((style) => style)
439
+ : undefined;
440
+ const currentCard = openCardStyles && openCardStyles[openCardStyles.length - 1];
442
441
  let currentColspan = 1;
443
442
  const maybeStickyHeaderStyles = isHeader && stickyHeader ? Css_1.Css.sticky.top(stickyOffset).z1.$ : undefined;
444
- const div = ((0, jsx_runtime_1.jsxs)(Row, Object.assign({ css: rowCss }, others, { children: [openCards && (0, nestedCards_1.maybeAddCardPadding)(openCards, "first", maybeStickyHeaderStyles), columns.map((column, columnIndex) => {
443
+ 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) => {
445
444
  var _a;
446
445
  // Decrement colspan count and skip if greater than 1.
447
446
  if (currentColspan > 1) {
@@ -491,15 +490,7 @@ function GridRow(props) {
491
490
  ? rowClickRenderFn(as)
492
491
  : defaultRenderFn(as);
493
492
  return renderFn(columnIndex, cellCss, content, row, rowStyle);
494
- }), openCards && (0, nestedCards_1.maybeAddCardPadding)(openCards, "final", maybeStickyHeaderStyles)] }), void 0));
495
- // Because of useToggleIds, this provider should basically never trigger a re-render, which is
496
- // good because we don't want the context to change and re-render every row just because some
497
- // other unrelated rows have collapsed/uncollapsed.
498
- const collapseContext = (0, react_1.useMemo)(() => ({
499
- isCollapsed,
500
- toggleCollapse: () => toggleCollapsedId(row.id),
501
- }), [isCollapsed, toggleCollapsedId, row]);
502
- return (0, jsx_runtime_1.jsx)(exports.GridCollapseContext.Provider, Object.assign({ value: collapseContext }, { children: div }), void 0);
493
+ }), openCardStyles && (0, nestedCards_1.maybeAddCardPadding)(openCardStyles, "final", maybeStickyHeaderStyles)] }), void 0));
503
494
  }
504
495
  // Fix to work with generics, see https://github.com/DefinitelyTyped/DefinitelyTyped/issues/37087#issuecomment-656596623
505
496
  const MemoizedGridRow = react_1.default.memo(GridRow);
@@ -553,8 +544,8 @@ const defaultRenderFn = (as) => (key, css, content) => {
553
544
  return ((0, jsx_runtime_1.jsx)(Row, Object.assign({ css: { ...css, ...tableRowStyles(as) } }, { children: content }), key));
554
545
  };
555
546
  exports.GridCollapseContext = react_1.default.createContext({
556
- isCollapsed: false,
557
- toggleCollapse: () => { },
547
+ isCollapsed: (id) => false,
548
+ toggleCollapsed: (id) => { },
558
549
  });
559
550
  /** Sets up the `GridContext` so that header cells can access the current sort settings. */
560
551
  const headerRenderFn = (columns, column, sortState, setSortKey, as) => (key, css, content) => {
@@ -659,10 +650,15 @@ function useToggleIds(rows, persistCollapse) {
659
650
  const [collapsedIds] = (0, react_1.useState)(getCollapsedRows(persistCollapse));
660
651
  // Use this to trigger the component to re-render even though we're not calling `setList`
661
652
  const [tick, setTick] = (0, react_1.useState)("");
662
- // Create the stable `toggleId`, i.e. we are purposefully passing an (almost) empty dep list
663
- const toggleId = (0, react_1.useCallback)((id) => {
664
- // We have different behavior when going from expand/collapse all.
665
- if (id === "header") {
653
+ // Checking whether something is collapsed does not depend on anything
654
+ const isCollapsed = (0, react_1.useCallback)((id) => collapsedIds.includes(id),
655
+ // eslint-disable-next-line react-hooks/exhaustive-deps
656
+ []);
657
+ const collapseAllContext = (0, react_1.useMemo)(() => {
658
+ // Create the stable `toggleCollapsed`, i.e. we are purposefully passing an (almost) empty dep list
659
+ // Since only toggling all rows required knowledge of what the rows are
660
+ const toggleAll = (_id) => {
661
+ // We have different behavior when going from expand/collapse all.
666
662
  const isAllCollapsed = collapsedIds[0] === "header";
667
663
  collapsedIds.splice(0, collapsedIds.length);
668
664
  if (isAllCollapsed) {
@@ -685,8 +681,20 @@ function useToggleIds(rows, persistCollapse) {
685
681
  // And then mark all parent rows as collapsed.
686
682
  collapsedIds.push(...parentIds);
687
683
  }
688
- }
689
- else {
684
+ if (persistCollapse) {
685
+ localStorage.setItem(persistCollapse, JSON.stringify(collapsedIds));
686
+ }
687
+ // Trigger a re-render
688
+ setTick(collapsedIds.join(","));
689
+ };
690
+ return { isCollapsed, toggleCollapsed: toggleAll };
691
+ },
692
+ // eslint-disable-next-line react-hooks/exhaustive-deps
693
+ [rows]);
694
+ const collapseRowContext = (0, react_1.useMemo)(() => {
695
+ // Create the stable `toggleCollapsed`, i.e. we are purposefully passing an empty dep list
696
+ // Since toggling a single row does not need to know about the other rows
697
+ const toggleRow = (id) => {
690
698
  // This is the regular/non-header behavior to just add/remove the individual row id
691
699
  const i = collapsedIds.indexOf(id);
692
700
  if (i === -1) {
@@ -695,21 +703,22 @@ function useToggleIds(rows, persistCollapse) {
695
703
  else {
696
704
  collapsedIds.splice(i, 1);
697
705
  }
698
- }
699
- if (persistCollapse) {
700
- localStorage.setItem(persistCollapse, JSON.stringify(collapsedIds));
701
- }
702
- // Trigger a re-render
703
- setTick(collapsedIds.join(","));
706
+ if (persistCollapse) {
707
+ localStorage.setItem(persistCollapse, JSON.stringify(collapsedIds));
708
+ }
709
+ // Trigger a re-render
710
+ setTick(collapsedIds.join(","));
711
+ };
712
+ return { isCollapsed, toggleCollapsed: toggleRow };
704
713
  },
705
714
  // eslint-disable-next-line react-hooks/exhaustive-deps
706
- [rows]);
715
+ []);
707
716
  // Return a copy of the list, b/c we want external useMemos that do explicitly use the
708
717
  // entire list as a dep to re-render whenever the list is changed (which they need to
709
718
  // see as new list identity).
710
719
  // eslint-disable-next-line react-hooks/exhaustive-deps
711
720
  const copy = (0, react_1.useMemo)(() => [...collapsedIds], [tick, collapsedIds]);
712
- return [copy, toggleId];
721
+ return [copy, collapseAllContext, collapseRowContext];
713
722
  }
714
723
  /** GridTable as Table utility to apply <tr> element override styles */
715
724
  const tableRowStyles = (as, column) => {
@@ -1,5 +1,5 @@
1
1
  import { ReactElement } from "react";
2
- import { GridColumn, GridDataRow, GridStyle, Kinded, NestedCardStyle, RowTuple } from "./GridTable";
2
+ import { GridColumn, GridDataRow, GridStyle, Kinded, NestedCardsStyle, NestedCardStyle, NestedCardStyleByKind, RowTuple } from "./GridTable";
3
3
  /**
4
4
  * A helper class to create our nested card DOM shenanigans.
5
5
  *
@@ -27,7 +27,7 @@ export declare class NestedCards {
27
27
  addSpacer(): void;
28
28
  done(): void;
29
29
  /** Return a stable copy of the cards, so it won't change as we keep going. */
30
- currentOpenCards(): NestedCardStyle[];
30
+ currentOpenCards(): string;
31
31
  }
32
32
  /**
33
33
  * Draws the rounded corners (either open or close) for a new card.
@@ -42,7 +42,7 @@ export declare class NestedCards {
42
42
  * I.e. due to the flatness of our DOM, we inherently have to add a "close"
43
43
  * row separate from the card's actual content.
44
44
  */
45
- export declare function makeOpenOrCloseCard(openCards: NestedCardStyle[], kind: "open" | "close"): Chrome;
45
+ export declare function makeOpenOrCloseCard(openCards: string[], cardStyles: NestedCardStyleByKind, kind: "open" | "close"): Chrome;
46
46
  /**
47
47
  * For the first or last cell of actual content, wrap them in divs that re-create the
48
48
  * outer cards' padding + background.
@@ -54,7 +54,7 @@ export declare function maybeAddCardPadding(openCards: NestedCardStyle[], column
54
54
  * Our height is not based on `openCards`, b/c for the top-most level, we won't
55
55
  * have any open cards, but still want a space between top-level cards.
56
56
  */
57
- export declare function makeSpacer(height: number, openCards: NestedCardStyle[]): Chrome;
57
+ export declare function makeSpacer(height: number, openCards: string[], styles: NestedCardsStyle): Chrome;
58
58
  /**
59
59
  * Takes the current buffer of close row(s), spacers, and open row, and creates a single chrome DOM row.
60
60
  *
@@ -19,26 +19,26 @@ class NestedCards {
19
19
  const card = this.styles.kinds[row.kind];
20
20
  // If this kind doesn't have a card defined, don't put it on the card stack
21
21
  if (card) {
22
- this.openCards.push(card);
23
- this.chromeBuffer.push(makeOpenOrCloseCard(this.openCards, "open"));
22
+ this.openCards.push(row.kind);
23
+ this.chromeBuffer.push(makeOpenOrCloseCard(this.openCards, this.styles.kinds, "open"));
24
24
  }
25
25
  // But always close previous cards if needed
26
26
  maybeCreateChromeRow(this.columns, this.filteredRows, this.chromeBuffer);
27
27
  return !!card;
28
28
  }
29
29
  closeCard() {
30
- this.chromeBuffer.push(makeOpenOrCloseCard(this.openCards, "close"));
30
+ this.chromeBuffer.push(makeOpenOrCloseCard(this.openCards, this.styles.kinds, "close"));
31
31
  this.openCards.pop();
32
32
  }
33
33
  addSpacer() {
34
- this.chromeBuffer.push(makeSpacer(this.styles.spacerPx, this.openCards));
34
+ this.chromeBuffer.push(makeSpacer(this.styles.spacerPx, this.openCards, this.styles));
35
35
  }
36
36
  done() {
37
37
  maybeCreateChromeRow(this.columns, this.filteredRows, this.chromeBuffer);
38
38
  }
39
39
  /** Return a stable copy of the cards, so it won't change as we keep going. */
40
40
  currentOpenCards() {
41
- return [...this.openCards];
41
+ return this.openCards.join(":");
42
42
  }
43
43
  }
44
44
  exports.NestedCards = NestedCards;
@@ -55,7 +55,7 @@ exports.NestedCards = NestedCards;
55
55
  * I.e. due to the flatness of our DOM, we inherently have to add a "close"
56
56
  * row separate from the card's actual content.
57
57
  */
58
- function makeOpenOrCloseCard(openCards, kind) {
58
+ function makeOpenOrCloseCard(openCards, cardStyles, kind) {
59
59
  const scopeCards = [...openCards];
60
60
  return () => {
61
61
  let div = (0, jsx_runtime_1.jsx)("div", {}, void 0);
@@ -67,7 +67,10 @@ function makeOpenOrCloseCard(openCards, kind) {
67
67
  // | card1 | card2 / ... card3 ... \ card2 | card1 |
68
68
  // | card1 | card2 | ... card3 ... | card2 | card1 |
69
69
  //
70
- [...scopeCards].reverse().forEach((card, i) => {
70
+ [...scopeCards]
71
+ .map((cardKind) => cardStyles[cardKind])
72
+ .reverse()
73
+ .forEach((card, i) => {
71
74
  const first = i === 0;
72
75
  div = ((0, jsx_runtime_1.jsx)("div", Object.assign({ css: {
73
76
  ...Css_1.Css.bgColor(card.bgColor).pxPx(card.pxPx).$,
@@ -106,14 +109,17 @@ exports.maybeAddCardPadding = maybeAddCardPadding;
106
109
  * Our height is not based on `openCards`, b/c for the top-most level, we won't
107
110
  * have any open cards, but still want a space between top-level cards.
108
111
  */
109
- function makeSpacer(height, openCards) {
112
+ function makeSpacer(height, openCards, styles) {
110
113
  const scopeCards = [...openCards];
111
114
  return () => {
112
115
  let div = (0, jsx_runtime_1.jsx)("div", { css: Css_1.Css.hPx(height).$ }, void 0);
113
116
  // Start at the current/inside card, and wrap outward padding + borders.
114
117
  // | card1 | card2 | ... card3 ... | card2 | card1 |
115
- [...scopeCards].reverse().forEach((card) => {
116
- div = (0, jsx_runtime_1.jsx)("div", Object.assign({ css: Css_1.Css.bgColor(card.bgColor).pxPx(card.pxPx).if(!!card.bColor).bc(card.bColor).bl.br.$ }, { children: div }), void 0);
118
+ [...scopeCards]
119
+ .map((cardKind) => styles.kinds[cardKind])
120
+ .reverse()
121
+ .forEach((card) => {
122
+ div = ((0, jsx_runtime_1.jsx)("div", Object.assign({ css: Css_1.Css.bgColor(card.bgColor).pxPx(card.pxPx).if(!!card.bColor).bc(card.bColor).bl.br.$ }, { children: div }), void 0));
117
123
  });
118
124
  return div;
119
125
  };
@@ -10,5 +10,5 @@ interface TooltipProps {
10
10
  export declare function Tooltip(props: TooltipProps): import("@emotion/react/jsx-runtime").JSX.Element;
11
11
  export declare type Placement = "top" | "bottom" | "left" | "right" | "auto";
12
12
  export declare function maybeTooltip(props: TooltipProps): import("@emotion/react/jsx-runtime").JSX.Element;
13
- export declare function resolveTooltip(disabled?: boolean | ReactNode, tooltip?: ReactNode): ReactNode | undefined;
13
+ export declare function resolveTooltip(disabled?: boolean | ReactNode, tooltip?: ReactNode, readOnly?: boolean | ReactNode): ReactNode | undefined;
14
14
  export {};
@@ -54,8 +54,12 @@ function maybeTooltip(props) {
54
54
  }
55
55
  exports.maybeTooltip = maybeTooltip;
56
56
  // Helper function for resolving showing the Tooltip text via a 'disabled' prop, or the 'tooltip' prop.
57
- function resolveTooltip(disabled, tooltip) {
57
+ function resolveTooltip(disabled, tooltip, readOnly) {
58
58
  // If `disabled` is a ReactNode, then return that. Otherwise, return `tooltip`
59
- return typeof disabled !== "boolean" && disabled ? disabled : tooltip !== null && tooltip !== void 0 ? tooltip : undefined;
59
+ return typeof disabled !== "boolean" && disabled
60
+ ? disabled
61
+ : typeof readOnly !== "boolean" && readOnly
62
+ ? readOnly
63
+ : tooltip !== null && tooltip !== void 0 ? tooltip : undefined;
60
64
  }
61
65
  exports.resolveTooltip = resolveTooltip;
@@ -14,6 +14,6 @@ function DateField(props) {
14
14
  const { value } = e.target;
15
15
  setValue(value);
16
16
  onChange((0, date_fns_1.parse)(value, "MM/dd/yy", new Date()));
17
- }, onBlur: () => (0, utils_1.maybeCall)(onBlur), onFocus: () => (0, utils_1.maybeCall)(onFocus) }), void 0));
17
+ }, onBlur: () => (0, utils_1.maybeCall)(onBlur), onFocus: () => (0, utils_1.maybeCall)(onFocus), disabled: !!props.disabled, readOnly: props.readOnly }), void 0));
18
18
  }
19
19
  exports.DateField = DateField;
@@ -7,7 +7,7 @@ const utils_1 = require("../utils");
7
7
  function MultiSelectField(props) {
8
8
  const { getOptionValue = (o) => o.id, // if unset, assume O implements HasId
9
9
  getOptionLabel = (o) => o.name, // if unset, assume O implements HasName
10
- values, options, onSelect, readOnly = false, errorMsg, onFocus, onBlur, } = props;
10
+ values, options, onSelect, readOnly = false, errorMsg, onFocus, onBlur, disabled, } = props;
11
11
  const tid = (0, utils_1.useTestIds)(props, "multiSelect");
12
12
  return ((0, jsx_runtime_1.jsxs)("select", Object.assign({}, tid, {
13
13
  // We're cheating and assume the values are strings...what we should really do is either:
@@ -37,7 +37,7 @@ function MultiSelectField(props) {
37
37
  onBlur();
38
38
  },
39
39
  // Read Only does not apply to `select` fields, instead we'll add in disabled for tests to verify.
40
- disabled: readOnly, "data-error": !!errorMsg, "data-errormsg": errorMsg, "data-readonly": readOnly }, { children: [(0, jsx_runtime_1.jsx)("option", { disabled: true, value: "" }, void 0), options.map((option, i) => {
40
+ disabled: !!(readOnly || disabled), "data-error": !!errorMsg, "data-errormsg": errorMsg, "data-readonly": readOnly }, { children: [(0, jsx_runtime_1.jsx)("option", { disabled: true, value: "" }, void 0), options.map((option, i) => {
41
41
  return ((0, jsx_runtime_1.jsx)("option", Object.assign({ value: getOptionValue(option) }, { children: getOptionLabel(option) }), i));
42
42
  })] }), void 0));
43
43
  }
@@ -23,7 +23,7 @@ function SelectField(props) {
23
23
  onBlur();
24
24
  },
25
25
  // Read Only does not apply to `select` fields, instead we'll add in disabled for tests to verify.
26
- disabled: disabled || readOnly, "data-error": !!errorMsg, "data-errormsg": errorMsg, "data-readonly": readOnly }, { children: [(0, jsx_runtime_1.jsx)("option", { disabled: true, value: "" }, void 0), options.map((option, i) => {
26
+ disabled: !!(disabled || readOnly), "data-error": !!errorMsg, "data-errormsg": errorMsg, "data-readonly": readOnly }, { children: [(0, jsx_runtime_1.jsx)("option", { disabled: true, value: "" }, void 0), options.map((option, i) => {
27
27
  return ((0, jsx_runtime_1.jsx)("option", Object.assign({ value: `${getOptionValue(option)}` }, { children: getOptionLabel(option) }), i));
28
28
  })] }), void 0));
29
29
  }
@@ -77,36 +77,35 @@ function TextFieldBase(props) {
77
77
  }, onFocus);
78
78
  return ((0, jsx_runtime_1.jsxs)("div", Object.assign({ css: fieldStyles.container }, groupProps, focusWithinProps, { children: [label && !inlineLabel && (
79
79
  // set `hidden` if being rendered as a compound field
80
- (0, jsx_runtime_1.jsx)(Label_1.Label, Object.assign({ labelProps: labelProps, hidden: hideLabel || compound, label: label, suffix: labelSuffix, contrast: contrast }, tid.label), void 0)), readOnly && ((0, jsx_runtime_1.jsxs)("div", Object.assign({ css: {
81
- // Use input wrapper to get common styles, but then we need to override some
82
- ...fieldStyles.inputWrapperReadOnly,
83
- ...(multiline ? Css_1.Css.fdc.aifs.childGap2.$ : Css_1.Css.truncate.$),
84
- ...xss,
85
- }, "data-readonly": "true" }, tid, { children: [!multiline && inlineLabel && label && !hideLabel && ((0, jsx_runtime_1.jsx)(Label_1.InlineLabel, Object.assign({ labelProps: labelProps, label: label }, tid.label), void 0)), multiline
86
- ? (_f = inputProps.value) === null || _f === void 0 ? void 0 : _f.split("\n\n").map((p, i) => ((0, jsx_runtime_1.jsx)("p", Object.assign({ css: Css_1.Css.my1.$ }, { children: p.split("\n").map((sentence, j) => ((0, jsx_runtime_1.jsxs)("span", { children: [sentence, (0, jsx_runtime_1.jsx)("br", {}, void 0)] }, j))) }), i)))
87
- : inputProps.value] }), void 0)), !readOnly &&
88
- (0, components_1.maybeTooltip)({
89
- title: tooltip,
90
- placement: "top",
91
- children: ((0, jsx_runtime_1.jsxs)("div", Object.assign({ css: {
92
- ...fieldStyles.inputWrapper,
93
- ...(inputProps.disabled ? fieldStyles.disabled : {}),
94
- ...(isFocused && !readOnly ? fieldStyles.focus : {}),
95
- ...(isHovered && !inputProps.disabled && !readOnly && !isFocused ? fieldStyles.hover : {}),
96
- ...(errorMsg ? fieldStyles.error : {}),
97
- ...Css_1.Css.if(multiline).aifs.px0.mhPx(textAreaMinHeight).$,
98
- } }, hoverProps, { ref: inputWrapRef }, { children: [!multiline && inlineLabel && label && !hideLabel && ((0, jsx_runtime_1.jsx)(Label_1.InlineLabel, Object.assign({ labelProps: labelProps, label: label }, tid.label), void 0)), !multiline && startAdornment && (0, jsx_runtime_1.jsx)("span", Object.assign({ css: Css_1.Css.df.aic.fs0.br4.pr1.$ }, { children: startAdornment }), void 0), (0, jsx_runtime_1.jsx)(ElementType, Object.assign({}, (0, react_aria_1.mergeProps)(inputProps, { onBlur, onFocus: onFocusChained, onChange: onDomChange }, { "aria-invalid": Boolean(errorMsg), ...(hideLabel ? { "aria-label": label } : {}) }), (errorMsg ? { "aria-errormessage": errorMessageId } : {}), { ref: fieldRef, rows: multiline ? 1 : undefined, css: {
99
- ...fieldStyles.input,
100
- ...(inputProps.disabled ? fieldStyles.disabled : {}),
101
- ...(isHovered && !inputProps.disabled && !readOnly && !isFocused ? fieldStyles.hover : {}),
102
- ...(multiline ? Css_1.Css.h100.p1.add("resize", "none").if(borderless).pPx(4).$ : Css_1.Css.truncate.$),
103
- ...xss,
104
- } }, tid), void 0), isFocused && clearable && onChange && inputProps.value && ((0, jsx_runtime_1.jsx)(components_1.IconButton, { icon: "xCircle", color: Css_1.Palette.Gray700, onClick: () => {
105
- var _a;
106
- onChange(undefined);
107
- // Reset focus to input element
108
- (_a = fieldRef.current) === null || _a === void 0 ? void 0 : _a.focus();
109
- } }, void 0)), !multiline && endAdornment && (0, jsx_runtime_1.jsx)("span", Object.assign({ css: Css_1.Css.df.aic.pl1.fs0.$ }, { children: endAdornment }), void 0)] }), void 0)),
110
- }), errorMsg && !compound && (0, jsx_runtime_1.jsx)(ErrorMessage_1.ErrorMessage, Object.assign({ id: errorMessageId, errorMsg: errorMsg }, tid.errorMsg), void 0), helperText && !compound && (0, jsx_runtime_1.jsx)(HelperText_1.HelperText, Object.assign({ helperText: helperText }, tid.helperText), void 0)] }), void 0));
80
+ (0, jsx_runtime_1.jsx)(Label_1.Label, Object.assign({ labelProps: labelProps, hidden: hideLabel || compound, label: label, suffix: labelSuffix, contrast: contrast }, tid.label), void 0)), (0, components_1.maybeTooltip)({
81
+ title: tooltip,
82
+ placement: "top",
83
+ children: readOnly ? ((0, jsx_runtime_1.jsxs)("div", Object.assign({ css: {
84
+ // Use input wrapper to get common styles, but then we need to override some
85
+ ...fieldStyles.inputWrapperReadOnly,
86
+ ...(multiline ? Css_1.Css.fdc.aifs.childGap2.$ : Css_1.Css.truncate.$),
87
+ ...xss,
88
+ }, "data-readonly": "true" }, tid, { children: [!multiline && inlineLabel && label && !hideLabel && ((0, jsx_runtime_1.jsx)(Label_1.InlineLabel, Object.assign({ labelProps: labelProps, label: label }, tid.label), void 0)), multiline
89
+ ? (_f = inputProps.value) === null || _f === void 0 ? void 0 : _f.split("\n\n").map((p, i) => ((0, jsx_runtime_1.jsx)("p", Object.assign({ css: Css_1.Css.my1.$ }, { children: p.split("\n").map((sentence, j) => ((0, jsx_runtime_1.jsxs)("span", { children: [sentence, (0, jsx_runtime_1.jsx)("br", {}, void 0)] }, j))) }), i)))
90
+ : inputProps.value] }), void 0)) : ((0, jsx_runtime_1.jsxs)("div", Object.assign({ css: {
91
+ ...fieldStyles.inputWrapper,
92
+ ...(inputProps.disabled ? fieldStyles.disabled : {}),
93
+ ...(isFocused && !readOnly ? fieldStyles.focus : {}),
94
+ ...(isHovered && !inputProps.disabled && !readOnly && !isFocused ? fieldStyles.hover : {}),
95
+ ...(errorMsg ? fieldStyles.error : {}),
96
+ ...Css_1.Css.if(multiline).aifs.px0.mhPx(textAreaMinHeight).$,
97
+ } }, hoverProps, { ref: inputWrapRef }, { children: [!multiline && inlineLabel && label && !hideLabel && ((0, jsx_runtime_1.jsx)(Label_1.InlineLabel, Object.assign({ labelProps: labelProps, label: label }, tid.label), void 0)), !multiline && startAdornment && (0, jsx_runtime_1.jsx)("span", Object.assign({ css: Css_1.Css.df.aic.fs0.br4.pr1.$ }, { children: startAdornment }), void 0), (0, jsx_runtime_1.jsx)(ElementType, Object.assign({}, (0, react_aria_1.mergeProps)(inputProps, { onBlur, onFocus: onFocusChained, onChange: onDomChange }, { "aria-invalid": Boolean(errorMsg), ...(hideLabel ? { "aria-label": label } : {}) }), (errorMsg ? { "aria-errormessage": errorMessageId } : {}), { ref: fieldRef, rows: multiline ? 1 : undefined, css: {
98
+ ...fieldStyles.input,
99
+ ...(inputProps.disabled ? fieldStyles.disabled : {}),
100
+ ...(isHovered && !inputProps.disabled && !readOnly && !isFocused ? fieldStyles.hover : {}),
101
+ ...(multiline ? Css_1.Css.h100.p1.add("resize", "none").if(borderless).pPx(4).$ : Css_1.Css.truncate.$),
102
+ ...xss,
103
+ } }, tid), void 0), isFocused && clearable && onChange && inputProps.value && ((0, jsx_runtime_1.jsx)(components_1.IconButton, { icon: "xCircle", color: Css_1.Palette.Gray700, onClick: () => {
104
+ var _a;
105
+ onChange(undefined);
106
+ // Reset focus to input element
107
+ (_a = fieldRef.current) === null || _a === void 0 ? void 0 : _a.focus();
108
+ } }, void 0)), !multiline && endAdornment && (0, jsx_runtime_1.jsx)("span", Object.assign({ css: Css_1.Css.df.aic.pl1.fs0.$ }, { children: endAdornment }), void 0)] }), void 0)),
109
+ }), errorMsg && !compound && (0, jsx_runtime_1.jsx)(ErrorMessage_1.ErrorMessage, Object.assign({ id: errorMessageId, errorMsg: errorMsg }, tid.errorMsg), void 0), helperText && !compound && (0, jsx_runtime_1.jsx)(HelperText_1.HelperText, Object.assign({ helperText: helperText }, tid.helperText), void 0)] }), void 0));
111
110
  }
112
111
  exports.TextFieldBase = TextFieldBase;
@@ -25,7 +25,7 @@ export interface SelectFieldBaseProps<O, V extends Value> extends BeamSelectFiel
25
25
  export declare function SelectFieldBase<O, V extends Value>(props: SelectFieldBaseProps<O, V>): JSX.Element;
26
26
  export interface BeamSelectFieldBaseProps<T, V extends Value> extends BeamFocusableProps, PresentationFieldProps {
27
27
  disabledOptions?: V[];
28
- disabled?: boolean;
28
+ disabled?: boolean | ReactNode;
29
29
  required?: boolean;
30
30
  errorMsg?: string;
31
31
  helperText?: string | ReactNode;
@@ -35,7 +35,7 @@ export interface BeamSelectFieldBaseProps<T, V extends Value> extends BeamFocusa
35
35
  label: string;
36
36
  /** Renders the label inside the input field, i.e. for filters. */
37
37
  inlineLabel?: boolean;
38
- readOnly?: boolean;
38
+ readOnly?: boolean | ReactNode;
39
39
  onBlur?: () => void;
40
40
  onFocus?: () => void;
41
41
  sizeToContent?: boolean;
@@ -5,6 +5,7 @@ const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const react_aria_1 = require("react-aria");
7
7
  const react_stately_1 = require("react-stately");
8
+ const components_1 = require("../../components");
8
9
  const internal_1 = require("../../components/internal");
9
10
  const Css_1 = require("../../Css");
10
11
  const ListBox_1 = require("./ListBox");
@@ -21,8 +22,10 @@ const Value_1 = require("../Value");
21
22
  */
22
23
  function SelectFieldBase(props) {
23
24
  var _a;
24
- const { compact, disabled: isDisabled = false, errorMsg, helperText, label, hideLabel, required, inlineLabel, readOnly: isReadOnly = false, onSelect, fieldDecoration, options, onBlur, onFocus, multiselect = false, getOptionLabel, getOptionValue, getOptionMenuLabel = getOptionLabel, sizeToContent = false, values, nothingSelectedText = "", contrast, disabledOptions, borderless, ...otherProps } = props;
25
+ const { compact, disabled, errorMsg, helperText, label, hideLabel, required, inlineLabel, readOnly, onSelect, fieldDecoration, options, onBlur, onFocus, multiselect = false, getOptionLabel, getOptionValue, getOptionMenuLabel = getOptionLabel, sizeToContent = false, values, nothingSelectedText = "", contrast, disabledOptions, borderless, ...otherProps } = props;
25
26
  const { contains } = (0, react_aria_1.useFilter)({ sensitivity: "base" });
27
+ const isDisabled = !!disabled;
28
+ const isReadOnly = !!readOnly;
26
29
  function onSelectionChange(keys) {
27
30
  var _a;
28
31
  // Close menu upon selection change only for Single selection mode
@@ -185,6 +188,6 @@ function SelectFieldBase(props) {
185
188
  // Ensures the menu never gets too small.
186
189
  minWidth: 200,
187
190
  };
188
- return ((0, jsx_runtime_1.jsxs)("div", Object.assign({ css: Css_1.Css.df.fdc.w100.maxw((0, Css_1.px)(550)).$, ref: comboBoxRef }, { children: [(0, jsx_runtime_1.jsx)(SelectFieldInput_1.SelectFieldInput, Object.assign({}, otherProps, { buttonProps: buttonProps, buttonRef: triggerRef, compact: compact, errorMsg: errorMsg, helperText: helperText, fieldDecoration: fieldDecoration, inputProps: inputProps, inputRef: inputRef, inputWrapRef: inputWrapRef, isDisabled: isDisabled, required: required, isReadOnly: isReadOnly, state: state, onBlur: onBlur, onFocus: onFocus, inlineLabel: inlineLabel, label: label, hideLabel: hideLabel, labelProps: labelProps, selectedOptions: fieldState.selectedOptions, getOptionValue: getOptionValue, getOptionLabel: getOptionLabel, sizeToContent: sizeToContent, contrast: contrast, nothingSelectedText: nothingSelectedText, borderless: borderless }), void 0), state.isOpen && ((0, jsx_runtime_1.jsx)(internal_1.Popover, Object.assign({ triggerRef: triggerRef, popoverRef: popoverRef, positionProps: positionProps, onClose: () => state.close(), isOpen: state.isOpen, minWidth: 200 }, { children: (0, jsx_runtime_1.jsx)(ListBox_1.ListBox, Object.assign({}, listBoxProps, { positionProps: positionProps, state: state, listBoxRef: listBoxRef, selectedOptions: fieldState.selectedOptions, getOptionLabel: getOptionLabel, getOptionValue: (o) => (0, Value_1.valueToKey)(getOptionValue(o)), contrast: contrast }), void 0) }), void 0))] }), void 0));
191
+ return ((0, jsx_runtime_1.jsxs)("div", Object.assign({ css: Css_1.Css.df.fdc.w100.maxw((0, Css_1.px)(550)).$, ref: comboBoxRef }, { children: [(0, jsx_runtime_1.jsx)(SelectFieldInput_1.SelectFieldInput, Object.assign({}, otherProps, { buttonProps: buttonProps, buttonRef: triggerRef, compact: compact, errorMsg: errorMsg, helperText: helperText, fieldDecoration: fieldDecoration, inputProps: inputProps, inputRef: inputRef, inputWrapRef: inputWrapRef, isDisabled: isDisabled, required: required, isReadOnly: isReadOnly, state: state, onBlur: onBlur, onFocus: onFocus, inlineLabel: inlineLabel, label: label, hideLabel: hideLabel, labelProps: labelProps, selectedOptions: fieldState.selectedOptions, getOptionValue: getOptionValue, getOptionLabel: getOptionLabel, sizeToContent: sizeToContent, contrast: contrast, nothingSelectedText: nothingSelectedText, borderless: borderless, tooltip: (0, components_1.resolveTooltip)(disabled, undefined, readOnly) }), void 0), state.isOpen && ((0, jsx_runtime_1.jsx)(internal_1.Popover, Object.assign({ triggerRef: triggerRef, popoverRef: popoverRef, positionProps: positionProps, onClose: () => state.close(), isOpen: state.isOpen, minWidth: 200 }, { children: (0, jsx_runtime_1.jsx)(ListBox_1.ListBox, Object.assign({}, listBoxProps, { positionProps: positionProps, state: state, listBoxRef: listBoxRef, selectedOptions: fieldState.selectedOptions, getOptionLabel: getOptionLabel, getOptionValue: (o) => (0, Value_1.valueToKey)(getOptionValue(o)), contrast: contrast }), void 0) }), void 0))] }), void 0));
189
192
  }
190
193
  exports.SelectFieldBase = SelectFieldBase;
@@ -26,6 +26,7 @@ interface SelectFieldInputProps<O, V extends Value> extends PresentationFieldPro
26
26
  sizeToContent: boolean;
27
27
  contrast?: boolean;
28
28
  nothingSelectedText: string;
29
+ tooltip?: ReactNode;
29
30
  }
30
31
  export declare function SelectFieldInput<O, V extends Value>(props: SelectFieldInputProps<O, V>): import("@emotion/react/jsx-runtime").JSX.Element;
31
32
  export {};
@@ -16,7 +16,7 @@ function SelectFieldInput(props) {
16
16
  const showNumSelection = isMultiSelect && state.selectionManager.selectedKeys.size > 1;
17
17
  // For MultiSelect only show the `fieldDecoration` when input is not in focus.
18
18
  const showFieldDecoration = (!isMultiSelect || (isMultiSelect && !isFocused)) && fieldDecoration && selectedOptions.length === 1;
19
- return ((0, jsx_runtime_1.jsx)(TextFieldBase_1.TextFieldBase, Object.assign({}, otherProps, { inputRef: inputRef, inputWrapRef: inputWrapRef, label: label, readOnly: isReadOnly, hideLabel: hideLabel, labelProps: labelProps, inlineLabel: inlineLabel, compact: compact, required: required, errorMsg: errorMsg, helperText: helperText, contrast: contrast, xss: !inlineLabel ? Css_1.Css.fw5.$ : {}, startAdornment: (showNumSelection && ((0, jsx_runtime_1.jsx)("span", Object.assign({ css: Css_1.Css.wPx(16).hPx(16).fs0.br100.bgLightBlue700.white.tinyEm.df.aic.jcc.$ }, { children: state.selectionManager.selectedKeys.size }), void 0))) ||
19
+ return ((0, jsx_runtime_1.jsx)(TextFieldBase_1.TextFieldBase, Object.assign({}, otherProps, { inputRef: inputRef, inputWrapRef: inputWrapRef, label: label, readOnly: isReadOnly, hideLabel: hideLabel, labelProps: labelProps, inlineLabel: inlineLabel, compact: compact, required: required, errorMsg: errorMsg, helperText: helperText, contrast: contrast, xss: !inlineLabel && !isReadOnly ? Css_1.Css.fw5.$ : {}, startAdornment: (showNumSelection && ((0, jsx_runtime_1.jsx)("span", Object.assign({ css: Css_1.Css.wPx(16).hPx(16).fs0.br100.bgLightBlue700.white.tinyEm.df.aic.jcc.$ }, { children: state.selectionManager.selectedKeys.size }), void 0))) ||
20
20
  (showFieldDecoration && fieldDecoration(selectedOptions[0])), endAdornment: !isReadOnly && ((0, jsx_runtime_1.jsx)("button", Object.assign({}, buttonProps, { disabled: isDisabled, ref: buttonRef, css: {
21
21
  ...Css_1.Css.br4.outline0.gray700.if(contrast).gray400.$,
22
22
  ...(isDisabled ? Css_1.Css.cursorNotAllowed.gray400.if(contrast).gray600.$ : {}),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homebound/beam",
3
- "version": "2.93.0",
3
+ "version": "2.94.0",
4
4
  "author": "Homebound",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",