@homebound/beam 4.59.0 → 4.59.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
@@ -8730,7 +8730,7 @@ type MenuSelectFieldBaseProps<O, V extends Value> = {
8730
8730
  getOptionSearchText?: (opt: O) => string | readonly string[];
8731
8731
  /** Sort options by `getOptionLabel`. On by default. `MenuTreeSelectField` defaults this to false. */
8732
8732
  autoSort?: boolean;
8733
- /** Debounced menu search text. The caller replaces `options` and should keep the current selection and `mixedValues` in that list. Closing calls this with `""`. */
8733
+ /** Debounced menu search text. The caller replaces `options` and should keep `mixedValues` in that list. Closing calls this with `""`. */
8734
8734
  onSearch?: (search: string) => void;
8735
8735
  disabledOptions?: (V | {
8736
8736
  value: V;
package/dist/index.d.ts CHANGED
@@ -8730,7 +8730,7 @@ type MenuSelectFieldBaseProps<O, V extends Value> = {
8730
8730
  getOptionSearchText?: (opt: O) => string | readonly string[];
8731
8731
  /** Sort options by `getOptionLabel`. On by default. `MenuTreeSelectField` defaults this to false. */
8732
8732
  autoSort?: boolean;
8733
- /** Debounced menu search text. The caller replaces `options` and should keep the current selection and `mixedValues` in that list. Closing calls this with `""`. */
8733
+ /** Debounced menu search text. The caller replaces `options` and should keep `mixedValues` in that list. Closing calls this with `""`. */
8734
8734
  onSearch?: (search: string) => void;
8735
8735
  disabledOptions?: (V | {
8736
8736
  value: V;
package/dist/index.js CHANGED
@@ -20100,9 +20100,20 @@ function useMenuSelectField(props) {
20100
20100
  () => menuTree ? menuTree.orderedKeys.filter((key) => disabledKeySet.has(menuTree.nodes.get(key).valueKey)) : disabledKeys,
20101
20101
  [menuTree, disabledKeySet, disabledKeys]
20102
20102
  );
20103
+ const rememberedSelectedRef = useRef36(/* @__PURE__ */ new Map());
20104
+ syncRememberedOptions(rememberedSelectedRef.current, values, optionsByKey);
20103
20105
  const selectedOptions = useMemo28(
20104
- () => menuTree ? selectedTreeOptions(menuTree, selectedKeys) : values.map((v) => optionsByKey.get(valueToKey(v))).filter((o) => o !== void 0),
20105
- [menuTree, selectedKeys, values, optionsByKey]
20106
+ () => resolveSelectedOptions(
20107
+ values,
20108
+ menuTree,
20109
+ selectedKeys,
20110
+ optionsByKey,
20111
+ rememberedSelectedRef.current,
20112
+ getOptionValue
20113
+ ),
20114
+ // getOptionValue is typically an inline lambda
20115
+ // oxlint-disable-next-line react-hooks/exhaustive-deps
20116
+ [values, optionsByKey, menuTree, selectedKeys]
20106
20117
  );
20107
20118
  const dottedKeys = useMemo28(
20108
20119
  () => new Set(mixedValues?.map(valueToKey).filter((key) => optionsByKey.has(key) && !selectedKeys.has(key))),
@@ -20144,9 +20155,8 @@ function useMenuSelectField(props) {
20144
20155
  const valueKey = key === void 0 ? void 0 : menuTree.nodes.get(key)?.valueKey;
20145
20156
  if (valueKey === void 0) return;
20146
20157
  const next2 = toggleTreeSelection(menuTree, selectedKeys, valueKey, added !== void 0, disabledKeySet);
20147
- const nextOptions = selectedTreeOptions(menuTree, next2);
20148
20158
  proposalProps.onUserEdit?.();
20149
- onCommit(nextOptions.map(getOptionValue), nextOptions);
20159
+ commitTree(next2);
20150
20160
  return;
20151
20161
  }
20152
20162
  if (!multiple) {
@@ -20161,16 +20171,17 @@ function useMenuSelectField(props) {
20161
20171
  close({ restoreFocus: true });
20162
20172
  return;
20163
20173
  }
20164
- const next = values.filter((v) => keys.has(valueToKey(v)));
20174
+ const next = values.filter((value) => {
20175
+ const key = valueToKey(value);
20176
+ if (!optionsByKey.has(key)) return true;
20177
+ return keys.has(key);
20178
+ });
20165
20179
  for (const k of keys) {
20166
20180
  const opt = optionsByKey.get(String(k));
20167
20181
  if (opt !== void 0 && !selectedKeys.has(String(k))) next.push(getOptionValue(opt));
20168
20182
  }
20169
20183
  proposalProps.onUserEdit?.();
20170
- onCommit(
20171
- next,
20172
- next.map((v) => optionsByKey.get(valueToKey(v))).filter((o) => o !== void 0)
20173
- );
20184
+ onCommit(next, optionsForValues(next));
20174
20185
  }
20175
20186
  const collectionItems = useMemo28(() => {
20176
20187
  if (treeRows) {
@@ -20382,14 +20393,35 @@ function useMenuSelectField(props) {
20382
20393
  proposalProps.onUserEdit?.();
20383
20394
  if (menuTree) {
20384
20395
  const toggledKeys = toggleTreeSelection(menuTree, selectedKeys, key, false, disabledKeySet);
20385
- const toggledOptions = selectedTreeOptions(menuTree, toggledKeys);
20386
- onCommit(toggledOptions.map(getOptionValue), toggledOptions);
20396
+ commitTree(toggledKeys, key);
20387
20397
  return;
20388
20398
  }
20389
20399
  const next = values.filter((value) => valueToKey(value) !== key);
20400
+ onCommit(next, optionsForValues(next));
20401
+ }
20402
+ function optionsForValues(next) {
20403
+ return next.flatMap((value) => {
20404
+ const option = optionForKey(valueToKey(value), optionsByKey, rememberedSelectedRef.current);
20405
+ return option ? [option] : [];
20406
+ });
20407
+ }
20408
+ function commitTree(nextKeys, clearedKey) {
20409
+ if (!menuTree) return;
20410
+ const selectedInTree = selectedTreeOptions(menuTree, nextKeys);
20411
+ const valueKeysInTree = new Set(menuTree.orderedKeys.map((rowKey) => menuTree.nodes.get(rowKey).valueKey));
20412
+ const seen = /* @__PURE__ */ new Set();
20413
+ const keptSelections = values.flatMap((value) => {
20414
+ const key = valueToKey(value);
20415
+ if (valueKeysInTree.has(key) || key === clearedKey || seen.has(key)) return [];
20416
+ const option = optionForKey(key, optionsByKey, rememberedSelectedRef.current);
20417
+ if (!option) return [];
20418
+ seen.add(key);
20419
+ return [option];
20420
+ });
20421
+ const nextOptions = [...selectedInTree, ...keptSelections];
20390
20422
  onCommit(
20391
- next,
20392
- next.map((value) => optionsByKey.get(valueToKey(value))).filter((option) => option !== void 0)
20423
+ nextOptions.map((option) => getOptionValue(option)),
20424
+ nextOptions
20393
20425
  );
20394
20426
  }
20395
20427
  const triggerProps = {
@@ -20455,6 +20487,41 @@ function useMenuSelectField(props) {
20455
20487
  }
20456
20488
  };
20457
20489
  }
20490
+ function syncRememberedOptions(remembered, values, optionsByKey) {
20491
+ const selected = /* @__PURE__ */ new Set();
20492
+ for (const value of values) {
20493
+ const key = valueToKey(value);
20494
+ selected.add(key);
20495
+ const option = optionsByKey.get(key);
20496
+ if (option) remembered.set(key, option);
20497
+ }
20498
+ for (const key of remembered.keys()) {
20499
+ if (!selected.has(key)) remembered.delete(key);
20500
+ }
20501
+ }
20502
+ function optionForKey(key, optionsByKey, remembered) {
20503
+ return optionsByKey.get(key) ?? remembered.get(key);
20504
+ }
20505
+ function resolveSelectedOptions(values, menuTree, selectedKeys, optionsByKey, remembered, getOptionValue) {
20506
+ if (!menuTree) {
20507
+ return values.flatMap((value) => {
20508
+ const option = optionForKey(valueToKey(value), optionsByKey, remembered);
20509
+ return option ? [option] : [];
20510
+ });
20511
+ }
20512
+ const selectedInTree = selectedTreeOptions(menuTree, selectedKeys);
20513
+ const seen = new Set(selectedInTree.map((option) => valueToKey(getOptionValue(option))));
20514
+ const keptSelections = [];
20515
+ for (const value of values) {
20516
+ const key = valueToKey(value);
20517
+ if (seen.has(key)) continue;
20518
+ const option = optionForKey(key, optionsByKey, remembered);
20519
+ if (!option) continue;
20520
+ seen.add(key);
20521
+ keptSelections.push(option);
20522
+ }
20523
+ return [...selectedInTree, ...keptSelections];
20524
+ }
20458
20525
  function labelsFor(values, options, getOptionValue, getOptionLabel) {
20459
20526
  const keys = new Set(values.map(valueToKey));
20460
20527
  const seen = /* @__PURE__ */ new Set();