@homebound/beam 3.23.0 → 3.24.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.
package/dist/index.d.cts CHANGED
@@ -1219,6 +1219,18 @@ declare class CssBuilder<T extends Properties, S extends StyleKind = "buildtime"
1219
1219
  float(value: Properties["float"]): CssBuilder<T & {
1220
1220
  float: csstype.Property.Float | undefined;
1221
1221
  }, S>;
1222
+ /** Sets `fontStyle: "italic"`. */
1223
+ get fsyi(): CssBuilder<T & {
1224
+ fontStyle: csstype.Property.FontStyle | undefined;
1225
+ }, S>;
1226
+ /** Sets `fontStyle: "normal"`. */
1227
+ get fsynm(): CssBuilder<T & {
1228
+ fontStyle: csstype.Property.FontStyle | undefined;
1229
+ }, S>;
1230
+ /** Sets `fontStyle: value`. */
1231
+ fsy(value: Properties["fontStyle"]): CssBuilder<T & {
1232
+ fontStyle: csstype.Property.FontStyle | undefined;
1233
+ }, S>;
1222
1234
  /** Sets `fontWeight: "normal"`. */
1223
1235
  get fwn(): CssBuilder<T & {
1224
1236
  fontWeight: csstype.Property.FontWeight | undefined;
@@ -6708,6 +6720,22 @@ type ComboBoxBaseProps<O, V extends Value> = {
6708
6720
  hideErrorMessage?: boolean;
6709
6721
  multiline?: boolean;
6710
6722
  onSearch?: (search: string) => void;
6723
+ /**
6724
+ * Called when the user explicitly selects the synthetic add-new row from the dropdown.
6725
+ * Only supported on single-select fields.
6726
+ *
6727
+ * While the user types, a pseudo-option (e.g. `Add "my value"`) appears at the bottom of the
6728
+ * filtered list when the trimmed input does not exactly match an existing option label
6729
+ * (case-insensitive). It is not shown when the input is empty, or when the input exactly matches
6730
+ * an existing label (e.g. typing "banana" when "Banana" exists).
6731
+ *
6732
+ * The user must focus that row (e.g. arrow down) and confirm with Enter or click; pressing Enter
6733
+ * on the typed text alone does not invoke this callback.
6734
+ *
6735
+ * This callback does not update the field value or call `onSelect` — callers create the new
6736
+ * option (often async) and set `value` themselves, same as before. Beam cannot auto-select here
6737
+ * because the new option's id typically comes from the caller's save/create logic after this fires.
6738
+ */
6711
6739
  onAddNew?: (v: string) => void;
6712
6740
  /**
6713
6741
  * When true (default), options are sorted alphabetically by their label.
package/dist/index.d.ts CHANGED
@@ -1219,6 +1219,18 @@ declare class CssBuilder<T extends Properties, S extends StyleKind = "buildtime"
1219
1219
  float(value: Properties["float"]): CssBuilder<T & {
1220
1220
  float: csstype.Property.Float | undefined;
1221
1221
  }, S>;
1222
+ /** Sets `fontStyle: "italic"`. */
1223
+ get fsyi(): CssBuilder<T & {
1224
+ fontStyle: csstype.Property.FontStyle | undefined;
1225
+ }, S>;
1226
+ /** Sets `fontStyle: "normal"`. */
1227
+ get fsynm(): CssBuilder<T & {
1228
+ fontStyle: csstype.Property.FontStyle | undefined;
1229
+ }, S>;
1230
+ /** Sets `fontStyle: value`. */
1231
+ fsy(value: Properties["fontStyle"]): CssBuilder<T & {
1232
+ fontStyle: csstype.Property.FontStyle | undefined;
1233
+ }, S>;
1222
1234
  /** Sets `fontWeight: "normal"`. */
1223
1235
  get fwn(): CssBuilder<T & {
1224
1236
  fontWeight: csstype.Property.FontWeight | undefined;
@@ -6708,6 +6720,22 @@ type ComboBoxBaseProps<O, V extends Value> = {
6708
6720
  hideErrorMessage?: boolean;
6709
6721
  multiline?: boolean;
6710
6722
  onSearch?: (search: string) => void;
6723
+ /**
6724
+ * Called when the user explicitly selects the synthetic add-new row from the dropdown.
6725
+ * Only supported on single-select fields.
6726
+ *
6727
+ * While the user types, a pseudo-option (e.g. `Add "my value"`) appears at the bottom of the
6728
+ * filtered list when the trimmed input does not exactly match an existing option label
6729
+ * (case-insensitive). It is not shown when the input is empty, or when the input exactly matches
6730
+ * an existing label (e.g. typing "banana" when "Banana" exists).
6731
+ *
6732
+ * The user must focus that row (e.g. arrow down) and confirm with Enter or click; pressing Enter
6733
+ * on the typed text alone does not invoke this callback.
6734
+ *
6735
+ * This callback does not update the field value or call `onSelect` — callers create the new
6736
+ * option (often async) and set `value` themselves, same as before. Beam cannot auto-select here
6737
+ * because the new option's id typically comes from the caller's save/create logic after this fires.
6738
+ */
6711
6739
  onAddNew?: (v: string) => void;
6712
6740
  /**
6713
6741
  * When true (default), options are sorted alphabetically by their label.
package/dist/index.js CHANGED
@@ -1166,6 +1166,19 @@ var CssBuilder = class _CssBuilder {
1166
1166
  float(value) {
1167
1167
  return this.add("float", value);
1168
1168
  }
1169
+ // fontStyle
1170
+ /** Sets `fontStyle: "italic"`. */
1171
+ get fsyi() {
1172
+ return this.add("fontStyle", "italic");
1173
+ }
1174
+ /** Sets `fontStyle: "normal"`. */
1175
+ get fsynm() {
1176
+ return this.add("fontStyle", "normal");
1177
+ }
1178
+ /** Sets `fontStyle: value`. */
1179
+ fsy(value) {
1180
+ return this.add("fontStyle", value);
1181
+ }
1169
1182
  // fontWeight
1170
1183
  /** Sets `fontWeight: "normal"`. */
1171
1184
  get fwn() {
@@ -12441,29 +12454,36 @@ function ComboBoxBase(props) {
12441
12454
  } = props;
12442
12455
  const labelStyle = otherProps.labelStyle ?? fieldProps?.labelStyle ?? "above";
12443
12456
  const getOptionLabel = useCallback10(
12444
- (o) => unsetLabel && o === unsetOption ? unsetLabel : onAddNew && o === addNewOption ? addNewOption.name : propOptionLabel(o),
12457
+ (o) => unsetLabel && o === unsetOption ? unsetLabel : onAddNew && isAddNewOption(o) ? o.name : propOptionLabel(o),
12445
12458
  // propOptionLabel is basically always a lambda, so don't dep on it
12446
12459
  // eslint-disable-next-line react-hooks/exhaustive-deps
12447
- [unsetLabel]
12460
+ [unsetLabel, onAddNew]
12448
12461
  );
12449
12462
  const getOptionValue = useCallback10(
12450
- (o) => unsetLabel && o === unsetOption ? void 0 : onAddNew && o === addNewOption ? addNewOption.id : propOptionValue(o),
12463
+ (o) => unsetLabel && o === unsetOption ? void 0 : onAddNew && isAddNewOption(o) ? o.id : propOptionValue(o),
12451
12464
  // propOptionValue is basically always a lambda, so don't dep on it
12452
12465
  // eslint-disable-next-line react-hooks/exhaustive-deps
12453
- [unsetLabel]
12466
+ [unsetLabel, onAddNew]
12454
12467
  );
12455
12468
  const getOptionMenuLabel = useCallback10(
12456
- (o) => propOptionMenuLabel ? propOptionMenuLabel(o, Boolean(unsetLabel) && o === unsetOption, Boolean(onAddNew) && o === addNewOption) : getOptionLabel(o),
12469
+ (o) => {
12470
+ const isAddNew = Boolean(onAddNew) && isAddNewOption(o);
12471
+ if (propOptionMenuLabel) {
12472
+ return propOptionMenuLabel(o, Boolean(unsetLabel) && o === unsetOption, isAddNew);
12473
+ }
12474
+ const label = getOptionLabel(o);
12475
+ return isAddNew ? /* @__PURE__ */ jsx57("span", { className: "fsyi", children: label }) : label;
12476
+ },
12457
12477
  // propOptionMenuLabel is basically always a lambda, so don't dep on it
12458
12478
  // eslint-disable-next-line react-hooks/exhaustive-deps
12459
- [unsetLabel, getOptionLabel]
12479
+ [unsetLabel, getOptionLabel, onAddNew]
12460
12480
  );
12461
12481
  const options = useMemo16(
12462
- () => initializeOptions(propOptions, getOptionValue, getOptionLabel, unsetLabel, !!onAddNew, autoSort),
12482
+ () => initializeOptions(propOptions, getOptionValue, getOptionLabel, unsetLabel, autoSort),
12463
12483
  // If the caller is using { current, load, options }, memoize on only `current` and `options` values.
12464
12484
  // ...and don't bother on memoizing on getOptionValue b/c it's basically always a lambda
12465
12485
  // eslint-disable-next-line react-hooks/exhaustive-deps
12466
- Array.isArray(propOptions) ? [propOptions, unsetLabel, onAddNew, autoSort] : [propOptions.current, propOptions.options, unsetLabel, onAddNew, autoSort]
12486
+ Array.isArray(propOptions) ? [propOptions, unsetLabel, autoSort] : [propOptions.current, propOptions.options, unsetLabel, autoSort]
12467
12487
  );
12468
12488
  const values = useMemo16(() => propValues ?? [], [propValues]);
12469
12489
  const inputStylePalette = useMemo16(() => propsInputStylePalette, [propsInputStylePalette]);
@@ -12485,9 +12505,8 @@ function ComboBoxBase(props) {
12485
12505
  const {
12486
12506
  searchValue
12487
12507
  } = fieldState;
12488
- const filteredOptions = useMemo16(() => {
12489
- return !searchValue ? options : options.filter((o) => contains(getOptionLabel(o), searchValue) || o === addNewOption);
12490
- }, [options, searchValue, getOptionLabel, contains]);
12508
+ const addNewOption = useMemo16(() => getAddNewOption(searchValue, options, getOptionLabel, onAddNew), [searchValue, options, getOptionLabel, onAddNew]);
12509
+ const filteredOptions = useMemo16(() => !searchValue ? options : addNewOption ? [...options.filter((o) => contains(getOptionLabel(o), searchValue)), addNewOption] : options.filter((o) => contains(getOptionLabel(o), searchValue)), [options, searchValue, getOptionLabel, contains, addNewOption]);
12491
12510
  function resetField() {
12492
12511
  setFieldState((prevState) => ({
12493
12512
  ...prevState,
@@ -12578,9 +12597,9 @@ function ComboBoxBase(props) {
12578
12597
  onSelect([], []);
12579
12598
  return;
12580
12599
  }
12581
- const selectedOption = options.find((o) => valueToKey(getOptionValue(o)) === key);
12582
- if (selectedOption === addNewOption && onAddNew) {
12583
- onAddNew(fieldState.inputValue);
12600
+ const selectedOption = filteredOptions.find((o) => valueToKey(getOptionValue(o)) === key);
12601
+ if (onAddNew && isAddNewOption(selectedOption) && fieldState.inputValue) {
12602
+ onAddNew(fieldState.inputValue.trim());
12584
12603
  return;
12585
12604
  }
12586
12605
  onSelect([keyToValue(key)], selectedOption ? [selectedOption] : []);
@@ -12675,7 +12694,7 @@ function ComboBoxBase(props) {
12675
12694
  function getInputValue(selectedOptions, getOptionLabel, multiselect, nothingSelectedText, readOnly) {
12676
12695
  return selectedOptions.length === 1 ? getOptionLabel(selectedOptions[0]) : readOnly && selectedOptions.length > 0 ? selectedOptions.map(getOptionLabel).join(", ") : multiselect && selectedOptions.length === 0 ? nothingSelectedText : "";
12677
12696
  }
12678
- function initializeOptions(optionsOrLoad, getOptionValue, getOptionLabel, unsetLabel, addNew, autoSort) {
12697
+ function initializeOptions(optionsOrLoad, getOptionValue, getOptionLabel, unsetLabel, autoSort) {
12679
12698
  const result = [];
12680
12699
  if (unsetLabel) {
12681
12700
  result.push(unsetOption);
@@ -12703,9 +12722,6 @@ function initializeOptions(optionsOrLoad, getOptionValue, getOptionLabel, unsetL
12703
12722
  }
12704
12723
  }
12705
12724
  result.push(...autoSort ? sortOptions(userOptions, getOptionLabel) : userOptions);
12706
- if (addNew) {
12707
- result.push(addNewOption);
12708
- }
12709
12725
  return result;
12710
12726
  }
12711
12727
  function sortOptions(options, getOptionLabel) {
@@ -12716,10 +12732,25 @@ function sortOptions(options, getOptionLabel) {
12716
12732
  });
12717
12733
  }
12718
12734
  var unsetOption = {};
12719
- var addNewOption = {
12720
- id: "new",
12721
- name: "Add New"
12722
- };
12735
+ var addNewOptionId = "beam_new";
12736
+ function isAddNewOption(o) {
12737
+ return typeof o === "object" && o !== null && "id" in o && o.id === addNewOptionId;
12738
+ }
12739
+ function getAddNewOption(searchValue, options, getOptionLabel, onAddNew) {
12740
+ if (!onAddNew) return void 0;
12741
+ const normalizedSearch = searchValue?.trim().toLowerCase();
12742
+ if (!normalizedSearch) return void 0;
12743
+ const trimmedSearch = searchValue.trim();
12744
+ const matchesExistingOption = options.some((o) => {
12745
+ if (o === unsetOption) return false;
12746
+ return getOptionLabel(o).trim().toLowerCase() === normalizedSearch;
12747
+ });
12748
+ if (matchesExistingOption) return void 0;
12749
+ return {
12750
+ id: addNewOptionId,
12751
+ name: `Add "${trimmedSearch}"`
12752
+ };
12753
+ }
12723
12754
  function disabledOptionToKeyedTuple(disabledOption) {
12724
12755
  if (typeof disabledOption === "object" && disabledOption !== null) {
12725
12756
  return [valueToKey(disabledOption.value), disabledOption.reason];
@@ -19164,7 +19195,7 @@ function hydrateDateRange(value) {
19164
19195
 
19165
19196
  // src/components/Filters/selectedFilterLabelUtils.ts
19166
19197
  function resolveOptionSelectedFilterLabel(optionsOrLoad, getOptionValue, getOptionLabel, value) {
19167
- const options = initializeOptions(optionsOrLoad, getOptionValue, getOptionLabel, void 0, false, false);
19198
+ const options = initializeOptions(optionsOrLoad, getOptionValue, getOptionLabel, void 0, false);
19168
19199
  const match = options.find((o) => getOptionValue(o) === value);
19169
19200
  return match ? getOptionLabel(match) : String(value);
19170
19201
  }
@@ -20028,7 +20059,7 @@ function GridTableLayoutComponent(props) {
20028
20059
  }, [visibleColumnIds, layoutState]);
20029
20060
  const visibleColumnsStorageKey = layoutState?.persistedColumnsStorageKey;
20030
20061
  const tableActionsEl = /* @__PURE__ */ jsx147(TableActions, { right: (hasHideableColumns || withCardView) && /* @__PURE__ */ jsxs74("div", { className: "df gap1", children: [
20031
- hasHideableColumns && /* @__PURE__ */ jsx147(EditColumnsButton, { columns, api, tooltip: "Display columns", ...tid.editColumnsButton }),
20062
+ hasHideableColumns && view === "list" && /* @__PURE__ */ jsx147(EditColumnsButton, { columns, api, tooltip: "Display columns", ...tid.editColumnsButton }),
20032
20063
  withCardView && /* @__PURE__ */ jsx147(ViewToggleButton, { view, onChange: setView })
20033
20064
  ] }), xss: {
20034
20065
  paddingTop: "pt3",