@homebound/beam 4.56.3 → 4.57.1

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.
package/dist/index.d.cts CHANGED
@@ -8724,8 +8724,8 @@ type MenuSelectFieldBaseProps<O, V extends Value> = {
8724
8724
  getOptionValue?: (opt: O) => V;
8725
8725
  /** String used in the trigger and the option's accessible name. Defaults to `option.name`. */
8726
8726
  getOptionLabel?: (opt: O) => string;
8727
- /** Custom content for the menu row. Falls back to `getOptionLabel`. */
8728
- getOptionMenuLabel?: (opt: O) => ReactNode;
8727
+ /** Custom content for the menu row. Falls back to `getOptionLabel`. `isUnsetOpt` is true for the `unsetLabel` row. */
8728
+ getOptionMenuLabel?: (opt: O, isUnsetOpt: boolean) => ReactNode;
8729
8729
  /** Extra text the menu search matches, in addition to `getOptionLabel`. */
8730
8730
  getOptionSearchText?: (opt: O) => string | readonly string[];
8731
8731
  /** Sort options by `getOptionLabel`. On by default. `MenuTreeSelectField` defaults this to false. */
@@ -8756,6 +8756,8 @@ type MenuSelectFieldProps<O, V extends Value> = MenuSelectFieldBaseProps<O, V> &
8756
8756
  /** Value proposed by an AI model; puts the field in AI mode. */
8757
8757
  proposedValue?: V;
8758
8758
  onSelect: (value: V | undefined, opt: O | undefined) => void;
8759
+ /** `unsetLabel` row that clears the field. Stays first when options are sorted. Unlike `nothingSelectedText`, it is a selectable option. */
8760
+ unsetLabel?: string;
8759
8761
  };
8760
8762
  /** Which selected options to surface: every one, only leaves, or only the highest selected ancestors. */
8761
8763
  type TreeSelectionDisplay = "all" | "leaf" | "root";
package/dist/index.d.ts CHANGED
@@ -8724,8 +8724,8 @@ type MenuSelectFieldBaseProps<O, V extends Value> = {
8724
8724
  getOptionValue?: (opt: O) => V;
8725
8725
  /** String used in the trigger and the option's accessible name. Defaults to `option.name`. */
8726
8726
  getOptionLabel?: (opt: O) => string;
8727
- /** Custom content for the menu row. Falls back to `getOptionLabel`. */
8728
- getOptionMenuLabel?: (opt: O) => ReactNode;
8727
+ /** Custom content for the menu row. Falls back to `getOptionLabel`. `isUnsetOpt` is true for the `unsetLabel` row. */
8728
+ getOptionMenuLabel?: (opt: O, isUnsetOpt: boolean) => ReactNode;
8729
8729
  /** Extra text the menu search matches, in addition to `getOptionLabel`. */
8730
8730
  getOptionSearchText?: (opt: O) => string | readonly string[];
8731
8731
  /** Sort options by `getOptionLabel`. On by default. `MenuTreeSelectField` defaults this to false. */
@@ -8756,6 +8756,8 @@ type MenuSelectFieldProps<O, V extends Value> = MenuSelectFieldBaseProps<O, V> &
8756
8756
  /** Value proposed by an AI model; puts the field in AI mode. */
8757
8757
  proposedValue?: V;
8758
8758
  onSelect: (value: V | undefined, opt: O | undefined) => void;
8759
+ /** `unsetLabel` row that clears the field. Stays first when options are sorted. Unlike `nothingSelectedText`, it is a selectable option. */
8760
+ unsetLabel?: string;
8759
8761
  };
8760
8762
  /** Which selected options to surface: every one, only leaves, or only the highest selected ancestors. */
8761
8763
  type TreeSelectionDisplay = "all" | "leaf" | "root";
package/dist/index.js CHANGED
@@ -5413,8 +5413,8 @@ var Icons = {
5413
5413
  /* @__PURE__ */ jsx2("rect", { x: "6", y: "5", width: "10", height: "2" }),
5414
5414
  /* @__PURE__ */ jsx2("rect", { x: "12", y: "10", width: "10", height: "2" })
5415
5415
  ] }),
5416
- cube: /* @__PURE__ */ jsx2("path", { d: "M19.406 4.08601L10.406 0.0860059C10.146 -0.0289941 9.853 -0.0289941 9.593 0.0860059L0.593 4.08601C0.573 4.09501 0.559 4.11001 0.539 4.12101C0.511 4.13501 0.481 4.14401 0.455 4.16101C0.433 4.17601 0.416 4.19501 0.395 4.21101C0.367 4.23201 0.34 4.25501 0.315 4.27901C0.274 4.31801 0.238 4.36001 0.205 4.40501C0.185 4.43301 0.164 4.45801 0.146 4.48601C0.115 4.53901 0.0919999 4.59401 0.0699999 4.65101C0.0609999 4.67801 0.047 4.70301 0.039 4.73001C0.015 4.81701 0 4.90701 0 5.00001V15C0 15.396 0.232 15.753 0.594 15.914L9.594 19.914C9.724 19.972 9.862 20 10 20C10.139 20 10.273 19.961 10.402 19.904L10.406 19.914L19.406 15.914C19.768 15.753 20 15.396 20 15V5.00001C20 4.60401 19.768 4.24701 19.406 4.08601ZM10 2.09501L16.538 5.00001L10 7.90501L8.692 7.32401L3.463 5.00001L10 2.09501ZM2 14.351V6.53901L9 9.65001V17.461L2 14.351ZM11 17.461V9.65001L18 6.53901V14.351L11 17.461Z" }),
5417
5416
  // Figma's 1.75px strokes, scaled 8/7 to Beam's 2px
5417
+ cube: /* @__PURE__ */ jsx2("path", { d: "M13.1016 12.6739V18.4562L18.3962 15.4686V9.6862L13.1016 12.6739ZM6 15.5433L11.1016 18.4485V12.6705L6 9.7654V15.5433ZM6.9933 8.0299L12.1038 10.9406L17.3616 7.973L12.1127 5.0109L6.9933 8.0299ZM20.3962 16.0522C20.3962 16.4129 20.2024 16.7465 19.8883 16.9239L12.5937 21.04C12.2876 21.2128 11.9127 21.2127 11.6071 21.0389L4.5056 16.9942C4.1933 16.8163 4 16.4841 4 16.1248V8.0456C4.0001 7.6917 4.1875 7.3648 4.4922 7.1851L11.5937 2.9953L11.7121 2.9362C11.9954 2.8162 12.3215 2.8328 12.5937 2.9864L19.8883 7.1025C20.2024 7.2798 20.3962 7.6134 20.3962 7.9741V16.0522Z" }),
5418
5418
  cubeDashed: /* @__PURE__ */ jsxs2(Fragment2, { children: [
5419
5419
  /* @__PURE__ */ jsx2("path", { d: "M9.812 7.1132C9.4955 7.1132 9.1873 6.9506 9.0149 6.6583C8.7555 6.2186 8.9017 5.6519 9.3415 5.3925L11.485 4.1282C11.7694 3.9605 12.1216 3.9571 12.409 4.1193L14.5763 5.3425C15.0209 5.5934 15.1779 6.1572 14.927 6.6018C14.6761 7.0464 14.1122 7.2034 13.6678 6.9525L11.9649 5.9915L10.2807 6.9849C10.1333 7.0718 9.9715 7.1132 9.812 7.1132Z" }),
5420
5420
  /* @__PURE__ */ jsx2("path", { d: "M7.0861 11.2271C6.9311 11.2271 6.7739 11.188 6.6295 11.1058L4.4669 9.8742C4.1804 9.7111 4.0025 9.4077 4 9.078C3.9975 8.7484 4.1708 8.4423 4.4547 8.2748L6.5983 7.0104C7.038 6.751 7.6047 6.8973 7.8641 7.337C8.1235 7.7767 7.9773 8.3435 7.5376 8.6028L6.7676 9.057L7.5444 9.4993C7.988 9.7519 8.1428 10.3163 7.8902 10.76C7.7198 11.0592 7.4075 11.2271 7.0861 11.2271Z" }),
@@ -18647,6 +18647,89 @@ import { trussProps as trussProps76 } from "@homebound/truss/runtime";
18647
18647
  // src/inputs/MenuSelectField/MenuSelectField.tsx
18648
18648
  import { useMemo as useMemo29 } from "react";
18649
18649
 
18650
+ // src/inputs/Value.ts
18651
+ var VALUE_PREFIX = "__VALUE--";
18652
+ function keyToValue(key) {
18653
+ if (typeof key === "number") {
18654
+ return key;
18655
+ } else if (typeof key === "string") {
18656
+ if (key === `${VALUE_PREFIX}null` || key === "__VALUE:null") {
18657
+ return null;
18658
+ } else if (key === `${VALUE_PREFIX}undefined` || key === "__VALUE:undefined") {
18659
+ return void 0;
18660
+ } else if (key.startsWith(`${VALUE_PREFIX}boolean--`) || key.startsWith("__VALUE:boolean:")) {
18661
+ const parts = key.includes("--boolean--") ? key.split("--") : key.split(":");
18662
+ return parts[parts.length - 1] === "true";
18663
+ } else if (key.startsWith(`${VALUE_PREFIX}number--`) || key.startsWith("__VALUE:number:")) {
18664
+ const parts = key.includes("--number--") ? key.split("--") : key.split(":");
18665
+ return Number(parts[parts.length - 1]);
18666
+ } else {
18667
+ return key;
18668
+ }
18669
+ } else {
18670
+ throw new Error(`Unsupported key ${key}`);
18671
+ }
18672
+ }
18673
+ function valueToKey(value) {
18674
+ if (typeof value === "string") {
18675
+ return value;
18676
+ } else if (typeof value === "number") {
18677
+ return `${VALUE_PREFIX}number--${value}`;
18678
+ } else if (typeof value === "boolean") {
18679
+ return `${VALUE_PREFIX}boolean--${value}`;
18680
+ } else if (value === null) {
18681
+ return `${VALUE_PREFIX}null`;
18682
+ } else if (value === void 0) {
18683
+ return `${VALUE_PREFIX}undefined`;
18684
+ } else {
18685
+ throw new Error(`Unsupported value ${value}`);
18686
+ }
18687
+ }
18688
+
18689
+ // src/inputs/internal/selectFieldHelpers.ts
18690
+ function initializeOptions(optionsOrLoad, getOptionValue, getOptionLabel, unsetLabel, autoSort) {
18691
+ const result = [];
18692
+ if (unsetLabel) {
18693
+ result.push(unsetOption);
18694
+ }
18695
+ const userOptions = [];
18696
+ if (Array.isArray(optionsOrLoad)) {
18697
+ userOptions.push(...optionsOrLoad);
18698
+ } else {
18699
+ const { options, current: current2 } = optionsOrLoad;
18700
+ if (options) {
18701
+ userOptions.push(...options);
18702
+ }
18703
+ if (current2) {
18704
+ const toCheck = Array.isArray(current2) ? current2 : [current2];
18705
+ toCheck.forEach((current3) => {
18706
+ const value = getOptionValue(current3);
18707
+ const found = options && options.find((o) => getOptionValue(o) === value);
18708
+ if (!found) {
18709
+ userOptions.push(current3);
18710
+ }
18711
+ });
18712
+ }
18713
+ }
18714
+ result.push(...autoSort ? sortOptions(userOptions, getOptionLabel) : userOptions);
18715
+ return result;
18716
+ }
18717
+ var unsetOption = {};
18718
+ function disabledOptionToKeyedTuple(disabledOption) {
18719
+ if (typeof disabledOption === "object" && disabledOption !== null) {
18720
+ return [valueToKey(disabledOption.value), disabledOption.reason];
18721
+ } else {
18722
+ return [valueToKey(disabledOption), void 0];
18723
+ }
18724
+ }
18725
+ function sortOptions(options, getOptionLabel) {
18726
+ return [...options].sort((a, b) => {
18727
+ const labelA = getOptionLabel(a).toLowerCase();
18728
+ const labelB = getOptionLabel(b).toLowerCase();
18729
+ return labelA.localeCompare(labelB);
18730
+ });
18731
+ }
18732
+
18650
18733
  // src/inputs/MenuSelectField/internal/AnchoredOverlay.tsx
18651
18734
  import { useRef as useRef32 } from "react";
18652
18735
  import { FocusScope as FocusScope4 } from "react-aria";
@@ -19646,89 +19729,6 @@ import { useId as useId4, useLayoutEffect as useLayoutEffect8, useMemo as useMem
19646
19729
  import { ListKeyboardDelegate, useFilter as useFilter2, useFocusRing as useFocusRing10, useHover as useHover13 } from "react-aria";
19647
19730
  import { Item as Item2, useListState } from "react-stately";
19648
19731
 
19649
- // src/inputs/Value.ts
19650
- var VALUE_PREFIX = "__VALUE--";
19651
- function keyToValue(key) {
19652
- if (typeof key === "number") {
19653
- return key;
19654
- } else if (typeof key === "string") {
19655
- if (key === `${VALUE_PREFIX}null` || key === "__VALUE:null") {
19656
- return null;
19657
- } else if (key === `${VALUE_PREFIX}undefined` || key === "__VALUE:undefined") {
19658
- return void 0;
19659
- } else if (key.startsWith(`${VALUE_PREFIX}boolean--`) || key.startsWith("__VALUE:boolean:")) {
19660
- const parts = key.includes("--boolean--") ? key.split("--") : key.split(":");
19661
- return parts[parts.length - 1] === "true";
19662
- } else if (key.startsWith(`${VALUE_PREFIX}number--`) || key.startsWith("__VALUE:number:")) {
19663
- const parts = key.includes("--number--") ? key.split("--") : key.split(":");
19664
- return Number(parts[parts.length - 1]);
19665
- } else {
19666
- return key;
19667
- }
19668
- } else {
19669
- throw new Error(`Unsupported key ${key}`);
19670
- }
19671
- }
19672
- function valueToKey(value) {
19673
- if (typeof value === "string") {
19674
- return value;
19675
- } else if (typeof value === "number") {
19676
- return `${VALUE_PREFIX}number--${value}`;
19677
- } else if (typeof value === "boolean") {
19678
- return `${VALUE_PREFIX}boolean--${value}`;
19679
- } else if (value === null) {
19680
- return `${VALUE_PREFIX}null`;
19681
- } else if (value === void 0) {
19682
- return `${VALUE_PREFIX}undefined`;
19683
- } else {
19684
- throw new Error(`Unsupported value ${value}`);
19685
- }
19686
- }
19687
-
19688
- // src/inputs/internal/selectFieldHelpers.ts
19689
- function initializeOptions(optionsOrLoad, getOptionValue, getOptionLabel, unsetLabel, autoSort) {
19690
- const result = [];
19691
- if (unsetLabel) {
19692
- result.push(unsetOption);
19693
- }
19694
- const userOptions = [];
19695
- if (Array.isArray(optionsOrLoad)) {
19696
- userOptions.push(...optionsOrLoad);
19697
- } else {
19698
- const { options, current: current2 } = optionsOrLoad;
19699
- if (options) {
19700
- userOptions.push(...options);
19701
- }
19702
- if (current2) {
19703
- const toCheck = Array.isArray(current2) ? current2 : [current2];
19704
- toCheck.forEach((current3) => {
19705
- const value = getOptionValue(current3);
19706
- const found = options && options.find((o) => getOptionValue(o) === value);
19707
- if (!found) {
19708
- userOptions.push(current3);
19709
- }
19710
- });
19711
- }
19712
- }
19713
- result.push(...autoSort ? sortOptions(userOptions, getOptionLabel) : userOptions);
19714
- return result;
19715
- }
19716
- var unsetOption = {};
19717
- function disabledOptionToKeyedTuple(disabledOption) {
19718
- if (typeof disabledOption === "object" && disabledOption !== null) {
19719
- return [valueToKey(disabledOption.value), disabledOption.reason];
19720
- } else {
19721
- return [valueToKey(disabledOption), void 0];
19722
- }
19723
- }
19724
- function sortOptions(options, getOptionLabel) {
19725
- return [...options].sort((a, b) => {
19726
- const labelA = getOptionLabel(a).toLowerCase();
19727
- const labelB = getOptionLabel(b).toLowerCase();
19728
- return labelA.localeCompare(labelB);
19729
- });
19730
- }
19731
-
19732
19732
  // src/inputs/MenuSelectField/internal/menuTree.ts
19733
19733
  function buildMenuTree(roots, getKey, getChildren) {
19734
19734
  const nodes = /* @__PURE__ */ new Map();
@@ -19910,10 +19910,11 @@ import { jsx as jsx115 } from "react/jsx-runtime";
19910
19910
  function useMenuSelectField(props) {
19911
19911
  const {
19912
19912
  options: optionsProp,
19913
- getOptionValue = defaultOptionValue,
19914
- getOptionLabel = defaultOptionLabel,
19913
+ getOptionValue: propGetOptionValue = defaultOptionValue,
19914
+ getOptionLabel: propGetOptionLabel = defaultOptionLabel,
19915
19915
  getOptionMenuLabel,
19916
19916
  getOptionSearchText,
19917
+ unsetLabel,
19917
19918
  autoSort = true,
19918
19919
  onSearch,
19919
19920
  disabledOptions,
@@ -19956,11 +19957,13 @@ function useMenuSelectField(props) {
19956
19957
  const [search, setSearch] = useState31("");
19957
19958
  const [optionsLoading, setOptionsLoading] = useState31(false);
19958
19959
  const didRequestOptionsRef = useRef36(false);
19960
+ const getOptionLabel = (option) => unsetLabel && option === unsetOption ? unsetLabel : propGetOptionLabel(option);
19961
+ const getOptionValue = (option) => unsetLabel && option === unsetOption ? void 0 : propGetOptionValue(option);
19959
19962
  const options = useMemo28(
19960
- () => initializeOptions(optionsProp, getOptionValue, getOptionLabel, void 0, autoSort),
19963
+ () => initializeOptions(optionsProp, getOptionValue, getOptionLabel, unsetLabel, autoSort),
19961
19964
  // getOptionValue and getOptionLabel are typically inline lambdas
19962
19965
  // oxlint-disable-next-line react-hooks/exhaustive-deps
19963
- [optionsProp, autoSort]
19966
+ [optionsProp, autoSort, unsetLabel]
19964
19967
  );
19965
19968
  const { hoverProps, isHovered } = useHover13({});
19966
19969
  const { focusProps, isFocusVisible } = useFocusRing10();
@@ -20027,10 +20030,12 @@ function useMenuSelectField(props) {
20027
20030
  );
20028
20031
  }, [menuTree, allOptions, search, serverSearch, contains, expandedKeys, searchCollapsedKeys]);
20029
20032
  const filteredOptions = useMemo28(
20030
- () => menuTree || !search || serverSearch ? options : options.filter((o) => matchesSearch(o, search, getOptionLabel, getOptionSearchText, contains)),
20033
+ () => menuTree || !search || serverSearch ? options : options.filter(
20034
+ (option) => unsetLabel && option === unsetOption ? contains(getOptionLabel(option), search) : matchesSearch(option, search, getOptionLabel, getOptionSearchText, contains)
20035
+ ),
20031
20036
  // getOptionLabel and getOptionSearchText are typically inline lambdas
20032
20037
  // oxlint-disable-next-line react-hooks/exhaustive-deps
20033
- [menuTree, options, search, serverSearch, contains]
20038
+ [menuTree, options, search, serverSearch, contains, unsetLabel]
20034
20039
  );
20035
20040
  function onSelectionChange(keys) {
20036
20041
  if (keys === "all") return;
@@ -20080,7 +20085,7 @@ function useMenuSelectField(props) {
20080
20085
  }, [treeRows, filteredOptions]);
20081
20086
  const state = useListState({
20082
20087
  items: collectionItems,
20083
- children: (item) => /* @__PURE__ */ jsx115(Item2, { textValue: getOptionLabel(item.option), children: getOptionMenuLabel ? getOptionMenuLabel(item.option) : getOptionLabel(item.option) }, item.key),
20088
+ children: (item) => /* @__PURE__ */ jsx115(Item2, { textValue: getOptionLabel(item.option), children: getOptionMenuLabel ? getOptionMenuLabel(item.option, Boolean(unsetLabel) && item.option === unsetOption) : getOptionLabel(item.option) }, item.key),
20084
20089
  selectionMode: multiple ? "multiple" : "single",
20085
20090
  selectedKeys: selectedRowKeys,
20086
20091
  disabledKeys: disabledRowKeys,
@@ -20471,17 +20476,24 @@ function BaseMenuSelectField(props) {
20471
20476
  // src/inputs/MenuSelectField/MenuSelectField.tsx
20472
20477
  import { jsx as jsx117 } from "react/jsx-runtime";
20473
20478
  function MenuSelectField(props) {
20474
- const { value, proposedValue, onSelect, ...rest } = props;
20475
- const values = useMemo29(() => value === void 0 ? [] : [value], [value]);
20479
+ const { value, proposedValue, onSelect, unsetLabel, ...rest } = props;
20480
+ const values = useMemo29(() => {
20481
+ if (value !== void 0) return [value];
20482
+ return unsetLabel ? [void 0] : [];
20483
+ }, [value, unsetLabel]);
20476
20484
  const proposedValues = useMemo29(() => proposedValue === void 0 ? void 0 : [proposedValue], [proposedValue]);
20477
20485
  return /* @__PURE__ */ jsx117(
20478
20486
  BaseMenuSelectField,
20479
20487
  {
20480
20488
  ...rest,
20489
+ unsetLabel,
20481
20490
  selectionMode: "single",
20482
20491
  values,
20483
20492
  proposedValues,
20484
- onCommit: (next, opts) => onSelect(next[0], opts[0])
20493
+ onCommit: (next, opts) => {
20494
+ const option = opts[0];
20495
+ onSelect(next[0], option === unsetOption ? void 0 : option);
20496
+ }
20485
20497
  }
20486
20498
  );
20487
20499
  }
@@ -31499,7 +31511,6 @@ import { jsx as jsx241 } from "react/jsx-runtime";
31499
31511
  function singleFilter(props) {
31500
31512
  return (key) => new SingleFilter(key, props);
31501
31513
  }
31502
- var allOption = {};
31503
31514
  var SingleFilter = class extends BaseFilter {
31504
31515
  formatSelectedFilterLabel(value) {
31505
31516
  const { options } = this.props;
@@ -31507,26 +31518,15 @@ var SingleFilter = class extends BaseFilter {
31507
31518
  return resolveOptionSelectedFilterLabel(options, getOptionValue, getOptionLabel, value);
31508
31519
  }
31509
31520
  render(value, setValue, tid, inModal, vertical) {
31510
- const { label, defaultValue, options: maybeOptions, nothingSelectedText, autoSort, ...props } = this.props;
31511
- const { getOptionValue, getOptionLabel } = optionReaders2(this.props);
31512
- const unsetText = nothingSelectedText ?? "All";
31513
- const isStaticList = Array.isArray(maybeOptions);
31514
- const options = isStaticList ? [
31515
- allOption,
31516
- ...initializeOptions(maybeOptions, getOptionValue, getOptionLabel, void 0, autoSort ?? true)
31517
- ] : maybeOptions;
31521
+ const { label, defaultValue, nothingSelectedText, unsetLabel, ...props } = this.props;
31518
31522
  return /* @__PURE__ */ jsx241(
31519
31523
  MenuSelectField,
31520
31524
  {
31521
31525
  ...props,
31522
- autoSort: isStaticList ? false : autoSort,
31523
- options,
31524
- getOptionValue: (option) => option === allOption ? void 0 : getOptionValue(option),
31525
- getOptionLabel: (option) => option === allOption ? unsetText : getOptionLabel(option),
31526
+ unsetLabel: unsetLabel ?? nothingSelectedText ?? "All",
31526
31527
  value,
31527
31528
  label: this.label,
31528
31529
  labelStyle: inModal ? "hidden" : !inModal && !vertical ? "inline" : "above",
31529
- nothingSelectedText: unsetText,
31530
31530
  onSelect: (next) => setValue(next || void 0),
31531
31531
  ...this.testId(tid)
31532
31532
  }