@homebound/beam 2.231.1 → 2.233.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.
@@ -65,5 +65,7 @@ export type GridDataRow<R extends Kinded> = {
65
65
  initSelected?: boolean;
66
66
  /** Whether row can be selected */
67
67
  selectable?: false;
68
+ /** Whether this row should infer its selected state based on its children's selected state */
69
+ inferSelectedState?: false;
68
70
  } & IfAny<R, {}, DiscriminateUnion<R, "kind", R["kind"]>>;
69
71
  export {};
@@ -258,7 +258,8 @@ class TableState {
258
258
  (0, visitor_1.visit)([curr.row], (row) => this.matchedRows.has(row.id) && map.set(row.id, selected ? "checked" : "unchecked"));
259
259
  // Now walk up the parents and see if they are now-all-checked/now-all-unchecked/some-of-each
260
260
  for (const parent of [...curr.parents].reverse()) {
261
- if (parent.children) {
261
+ // Only derive selected state of the parent row if `inferSelectedState` is not `false`
262
+ if (parent.children && parent.inferSelectedState !== false) {
262
263
  map.set(parent.id, deriveParentSelected(this.getMatchedChildrenStates(parent.children, map)));
263
264
  }
264
265
  }
@@ -327,14 +328,16 @@ class TableState {
327
328
  }
328
329
  }
329
330
  getMatchedChildrenStates(children, map) {
330
- return children
331
+ const respectedChildren = children.flatMap(getChildrenForDerivingSelectState);
332
+ return respectedChildren
331
333
  .filter((row) => row.id !== "header" && this.matchedRows.has(row.id))
332
334
  .map((row) => map.get(row.id) || this.getSelected(row.id));
333
335
  }
334
336
  // Recursively traverse through rows to determine selected state of parent rows based on children
335
337
  setNestedSelectedStates(row, map) {
336
338
  if (this.matchedRows.has(row.id)) {
337
- if (!row.children) {
339
+ // do not derive selected state if there are no children, or if `inferSelectedState` is set to false
340
+ if (!row.children || row.inferSelectedState === false) {
338
341
  return [this.getSelected(row.id)];
339
342
  }
340
343
  const childrenSelectedStates = row.children.flatMap((rc) => this.setNestedSelectedStates(rc, map));
@@ -346,6 +349,13 @@ class TableState {
346
349
  }
347
350
  }
348
351
  exports.TableState = TableState;
352
+ /** Returns the child rows needed for deriving the selected state of a parent/group row */
353
+ function getChildrenForDerivingSelectState(row) {
354
+ if (row.children && row.inferSelectedState === false) {
355
+ return [row, ...row.children.flatMap(getChildrenForDerivingSelectState)];
356
+ }
357
+ return [row];
358
+ }
349
359
  /** Provides a context for rows to access their table's `TableState`. */
350
360
  exports.TableStateContext = react_1.default.createContext({
351
361
  get tableState() {
@@ -0,0 +1,13 @@
1
+ import { FieldState } from "@homebound/form-state";
2
+ import { MultiLineSelectFieldProps, Value } from "../inputs";
3
+ import { HasIdAndName, Optional } from "../types";
4
+ export type BoundMultiLineSelectFieldProps<O, V extends Value> = Omit<MultiLineSelectFieldProps<O, V>, "values" | "onSelect" | "label"> & {
5
+ onSelect?: (values: V[], opts: O[]) => void;
6
+ field: FieldState<any, V[] | null | undefined>;
7
+ label?: string;
8
+ };
9
+ /**
10
+ * Wraps `MultiLineSelectField` and binds it to a form field.
11
+ */
12
+ export declare function BoundMultiLineSelectField<O, V extends Value>(props: BoundMultiLineSelectFieldProps<O, V>): JSX.Element;
13
+ export declare function BoundMultiLineSelectField<O extends HasIdAndName<V>, V extends Value>(props: Optional<BoundMultiLineSelectFieldProps<O, V>, "getOptionLabel" | "getOptionValue">): JSX.Element;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BoundMultiLineSelectField = void 0;
4
+ const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
5
+ const mobx_react_1 = require("mobx-react");
6
+ const inputs_1 = require("../inputs");
7
+ const utils_1 = require("../utils");
8
+ const defaultLabel_1 = require("../utils/defaultLabel");
9
+ const useTestIds_1 = require("../utils/useTestIds");
10
+ function BoundMultiLineSelectField(props) {
11
+ const { field, options, readOnly, getOptionValue = (opt) => opt.id, // if unset, assume O implements HasId
12
+ getOptionLabel = (opt) => opt.name, // if unset, assume O implements HasName
13
+ onSelect = (value) => field.set(value), label = (0, defaultLabel_1.defaultLabel)(field.key), onBlur, onFocus, ...others } = props;
14
+ const testId = (0, useTestIds_1.useTestIds)(props, field.key);
15
+ return ((0, jsx_runtime_1.jsx)(mobx_react_1.Observer, { children: () => {
16
+ var _a;
17
+ return ((0, jsx_runtime_1.jsx)(inputs_1.MultiLineSelectField, { label: label, values: (_a = field.value) !== null && _a !== void 0 ? _a : [], onSelect: (values, options) => {
18
+ onSelect(values, options);
19
+ field.maybeAutoSave();
20
+ }, options: options, readOnly: readOnly !== null && readOnly !== void 0 ? readOnly : field.readOnly, errorMsg: field.touched ? field.errors.join(" ") : undefined, required: field.required, getOptionLabel: getOptionLabel, getOptionValue: getOptionValue, onBlur: () => {
21
+ field.blur();
22
+ (0, utils_1.maybeCall)(onBlur);
23
+ }, onFocus: () => {
24
+ field.focus();
25
+ (0, utils_1.maybeCall)(onFocus);
26
+ }, ...others, ...testId }));
27
+ } }));
28
+ }
29
+ exports.BoundMultiLineSelectField = BoundMultiLineSelectField;
@@ -3,6 +3,7 @@ export * from "./BoundCheckboxGroupField";
3
3
  export * from "./BoundChipSelectField";
4
4
  export * from "./BoundDateField";
5
5
  export * from "./BoundDateRangeField";
6
+ export * from "./BoundMultiLineSelectField";
6
7
  export * from "./BoundMultiSelectField";
7
8
  export * from "./BoundNumberField";
8
9
  export * from "./BoundRadioGroupField";
@@ -19,6 +19,7 @@ __exportStar(require("./BoundCheckboxGroupField"), exports);
19
19
  __exportStar(require("./BoundChipSelectField"), exports);
20
20
  __exportStar(require("./BoundDateField"), exports);
21
21
  __exportStar(require("./BoundDateRangeField"), exports);
22
+ __exportStar(require("./BoundMultiLineSelectField"), exports);
22
23
  __exportStar(require("./BoundMultiSelectField"), exports);
23
24
  __exportStar(require("./BoundNumberField"), exports);
24
25
  __exportStar(require("./BoundRadioGroupField"), exports);
@@ -0,0 +1,11 @@
1
+ import { Value } from "./";
2
+ import { BeamSelectFieldBaseProps } from "./internal/SelectFieldBase";
3
+ import { Optional } from "../types";
4
+ export interface MultiLineSelectFieldProps<O, V extends Value> extends Exclude<BeamSelectFieldBaseProps<O, V>, "unsetLabel"> {
5
+ values: V[];
6
+ options: O[];
7
+ getOptionValue: (opt: O) => V;
8
+ getOptionLabel: (opt: O) => string;
9
+ onSelect: (values: V[], opts: O[]) => void;
10
+ }
11
+ export declare function MultiLineSelectField<O, V extends Value>(props: Optional<MultiLineSelectFieldProps<O, V>, "getOptionLabel" | "getOptionValue">): JSX.Element;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MultiLineSelectField = void 0;
4
+ const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const Button_1 = require("../components/Button");
7
+ const Label_1 = require("../components/Label");
8
+ const inputs_1 = require("./");
9
+ const __1 = require("..");
10
+ function MultiLineSelectField(props) {
11
+ const { options, values, onSelect, getOptionValue = (opt) => opt.id, getOptionLabel = (opt) => opt.name, labelStyle, ...otherProps } = props;
12
+ const tid = (0, __1.useTestIds)(props, "");
13
+ const [isDisplayed, setIsDisplayed] = (0, react_1.useState)(true);
14
+ // Set the available options by filtering already selected options
15
+ const currentOptions = options.filter((o) => !values.includes(getOptionValue(o)));
16
+ return ((0, jsx_runtime_1.jsxs)("div", { css: __1.Css.mt1.if(labelStyle === "left").df.$, children: [labelStyle !== "hidden" && ((0, jsx_runtime_1.jsx)("div", { css: __1.Css.if(labelStyle === "left").w50.$, children: (0, jsx_runtime_1.jsx)(Label_1.Label, { ...tid.label, label: props.label }) })), (0, jsx_runtime_1.jsxs)("div", { css: __1.Css.if(labelStyle === "left").w50.$, children: [values.map((value, index) => {
17
+ return ((0, jsx_runtime_1.jsxs)("div", { css: __1.Css.mb1.pl1.df.$, children: [(0, jsx_runtime_1.jsx)("div", { css: __1.Css.truncate.w100.$, children: (0, jsx_runtime_1.jsx)(inputs_1.SelectField, { ...otherProps, ...tid.selectField, labelStyle: "hidden", value: value, onSelect: () => { }, options: options, getOptionValue: getOptionValue, getOptionLabel: getOptionLabel, compact: true, readOnly: true }) }), (0, jsx_runtime_1.jsx)(Button_1.Button, { ...tid.deleteSelected, variant: "tertiary", label: "", "aria-label": `Delete selected ${otherProps.label}`, icon: "x", onClick: () => {
18
+ const selectedOptions = options.filter((o) => values.includes(getOptionValue(o)) && getOptionValue(o) !== value);
19
+ const selectedValues = selectedOptions.map((o) => getOptionValue(o));
20
+ onSelect(selectedValues, selectedOptions);
21
+ // Display the input field if there are no selected values
22
+ if (selectedOptions.length === 0)
23
+ setIsDisplayed(true);
24
+ } })] }, index));
25
+ }), isDisplayed && ((0, jsx_runtime_1.jsx)("div", { css: __1.Css.mb1.$, children: (0, jsx_runtime_1.jsx)(inputs_1.SelectField, { ...tid.selectField, label: otherProps.label, labelStyle: "hidden", getOptionValue: getOptionValue, getOptionLabel: getOptionLabel, value: "", onSelect: (value) => {
26
+ onSelect([...values, value], options);
27
+ setIsDisplayed(false);
28
+ }, options: currentOptions, disabled: otherProps.disabled }) })), (0, jsx_runtime_1.jsx)(Button_1.Button, { ...tid.addAnother, label: "Add Another", variant: "tertiary", onClick: () => setIsDisplayed(true), disabled: isDisplayed || currentOptions.length === 0 })] })] }));
29
+ }
30
+ exports.MultiLineSelectField = MultiLineSelectField;
@@ -2,6 +2,7 @@ export * from "./Checkbox";
2
2
  export * from "./CheckboxGroup";
3
3
  export * from "./ChipSelectField";
4
4
  export * from "./DateFields";
5
+ export * from "./MultiLineSelectField";
5
6
  export * from "./MultiSelectField";
6
7
  export * from "./NumberField";
7
8
  export type { NumberFieldProps } from "./NumberField";
@@ -19,6 +19,7 @@ __exportStar(require("./Checkbox"), exports);
19
19
  __exportStar(require("./CheckboxGroup"), exports);
20
20
  __exportStar(require("./ChipSelectField"), exports);
21
21
  __exportStar(require("./DateFields"), exports);
22
+ __exportStar(require("./MultiLineSelectField"), exports);
22
23
  __exportStar(require("./MultiSelectField"), exports);
23
24
  __exportStar(require("./NumberField"), exports);
24
25
  var RadioGroupField_1 = require("./RadioGroupField");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homebound/beam",
3
- "version": "2.231.1",
3
+ "version": "2.233.0",
4
4
  "author": "Homebound",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",