@onesaz/ui 0.3.4 → 0.3.6

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.ts CHANGED
@@ -678,11 +678,14 @@ interface ComboboxOption {
678
678
  label: string;
679
679
  disabled?: boolean;
680
680
  }
681
- interface ComboboxSingleProps {
682
- options: ComboboxOption[];
683
- value?: ComboboxOption | null;
684
- defaultValue?: ComboboxOption | null;
685
- onChange?: (value: ComboboxOption | null) => void;
681
+ type ComboboxPrimitiveOption = string;
682
+ type ComboboxObjectOption = object;
683
+ type ComboboxOptionInput = ComboboxPrimitiveOption | ComboboxObjectOption;
684
+ interface ComboboxSingleProps<T extends ComboboxOptionInput = ComboboxOptionInput> {
685
+ options: T[];
686
+ value?: T | null;
687
+ defaultValue?: T | null;
688
+ onChange?: (value: T | null) => void;
686
689
  placeholder?: string;
687
690
  searchPlaceholder?: string;
688
691
  emptyMessage?: string;
@@ -693,12 +696,15 @@ interface ComboboxSingleProps {
693
696
  openOnFocus?: boolean;
694
697
  inputValue?: string;
695
698
  onInputChange?: (value: string) => void;
696
- }
697
- interface ComboboxMultipleProps {
698
- options: ComboboxOption[];
699
- value?: ComboboxOption[];
700
- defaultValue?: ComboboxOption[];
701
- onChange?: (value: ComboboxOption[]) => void;
699
+ simpleOptions?: boolean;
700
+ labelKey?: string;
701
+ valueKey?: string;
702
+ }
703
+ interface ComboboxMultipleProps<T extends ComboboxOptionInput = ComboboxOptionInput> {
704
+ options: T[];
705
+ value?: T[];
706
+ defaultValue?: T[];
707
+ onChange?: (value: T[]) => void;
702
708
  placeholder?: string;
703
709
  searchPlaceholder?: string;
704
710
  emptyMessage?: string;
@@ -713,6 +719,9 @@ interface ComboboxMultipleProps {
713
719
  selectAll?: boolean;
714
720
  /** Label for select-all option */
715
721
  selectAllLabel?: string;
722
+ simpleOptions?: boolean;
723
+ labelKey?: string;
724
+ valueKey?: string;
716
725
  /** Maximum number of items to display as chips before showing "+N more" */
717
726
  maxDisplayItems?: number;
718
727
  }
package/dist/index.js CHANGED
@@ -2975,6 +2975,38 @@ var Combobox = React30.forwardRef(
2975
2975
  openOnFocus = true,
2976
2976
  className
2977
2977
  } = props;
2978
+ const labelKey = props.labelKey ?? "label";
2979
+ const valueKey = props.valueKey ?? "value";
2980
+ const getOptionLabel = React30.useCallback(
2981
+ (option) => {
2982
+ if (typeof option === "string") return option;
2983
+ const record = option;
2984
+ const maybeLabel = record[labelKey];
2985
+ return typeof maybeLabel === "string" ? maybeLabel : String(maybeLabel ?? "");
2986
+ },
2987
+ [labelKey]
2988
+ );
2989
+ const getOptionValue = React30.useCallback(
2990
+ (option) => {
2991
+ if (typeof option === "string") return option;
2992
+ const record = option;
2993
+ const maybeValue = record[valueKey];
2994
+ if (maybeValue !== void 0 && maybeValue !== null) {
2995
+ return String(maybeValue);
2996
+ }
2997
+ return getOptionLabel(option);
2998
+ },
2999
+ [valueKey, getOptionLabel]
3000
+ );
3001
+ const normalizedOptions = React30.useMemo(
3002
+ () => (options ?? []).map((option) => ({
3003
+ raw: option,
3004
+ label: getOptionLabel(option),
3005
+ value: getOptionValue(option),
3006
+ disabled: Boolean(option.disabled)
3007
+ })),
3008
+ [options, getOptionLabel, getOptionValue]
3009
+ );
2978
3010
  const [open, setOpen] = React30.useState(false);
2979
3011
  const [internalSearch, setInternalSearch] = React30.useState("");
2980
3012
  const containerRef = React30.useRef(null);
@@ -2988,19 +3020,22 @@ var Combobox = React30.forwardRef(
2988
3020
  const multiValue = isMultiple ? props.value !== void 0 ? props.value : internalMultiValue : [];
2989
3021
  const search = props.inputValue !== void 0 ? props.inputValue : internalSearch;
2990
3022
  const filteredOptions = React30.useMemo(() => {
2991
- if (!search) return options;
2992
- return options.filter(
3023
+ if (!search) return normalizedOptions;
3024
+ return normalizedOptions.filter(
2993
3025
  (option) => option.label.toLowerCase().includes(search.toLowerCase())
2994
3026
  );
2995
- }, [options, search]);
3027
+ }, [normalizedOptions, search]);
2996
3028
  const selectedOptions = isMultiple ? multiValue : [];
2997
- const selectableOptions = React30.useMemo(
2998
- () => options.filter((option) => !option.disabled),
2999
- [options]
3029
+ const selectedValueKeys = React30.useMemo(
3030
+ () => new Set(selectedOptions.map((option) => getOptionValue(option))),
3031
+ [selectedOptions, getOptionValue]
3000
3032
  );
3001
- const allSelected = isMultiple && selectableOptions.length > 0 && selectableOptions.every(
3002
- (option) => multiValue.some((item) => item.value === option.value)
3033
+ const singleValueKey = singleValue ? getOptionValue(singleValue) : null;
3034
+ const selectableOptions = React30.useMemo(
3035
+ () => normalizedOptions.filter((option) => !option.disabled),
3036
+ [normalizedOptions]
3003
3037
  );
3038
+ const allSelected = isMultiple && selectableOptions.length > 0 && selectableOptions.every((option) => selectedValueKeys.has(option.value));
3004
3039
  const handleSingleSelect = (option) => {
3005
3040
  if (!isMultiple) {
3006
3041
  if (props.value === void 0) {
@@ -3015,8 +3050,9 @@ var Combobox = React30.forwardRef(
3015
3050
  };
3016
3051
  const handleMultiSelect = (option) => {
3017
3052
  if (isMultiple) {
3018
- const exists = multiValue.some((item) => item.value === option.value);
3019
- const newValue = exists ? multiValue.filter((item) => item.value !== option.value) : [...multiValue, option];
3053
+ const optionKey = getOptionValue(option);
3054
+ const exists = multiValue.some((item) => getOptionValue(item) === optionKey);
3055
+ const newValue = exists ? multiValue.filter((item) => getOptionValue(item) !== optionKey) : [...multiValue, option];
3020
3056
  if (props.value === void 0) {
3021
3057
  setInternalMultiValue(newValue);
3022
3058
  }
@@ -3026,7 +3062,7 @@ var Combobox = React30.forwardRef(
3026
3062
  const handleRemoveItem = (optionValue, e) => {
3027
3063
  e.stopPropagation();
3028
3064
  if (isMultiple) {
3029
- const newValue = multiValue.filter((v) => v.value !== optionValue);
3065
+ const newValue = multiValue.filter((v) => getOptionValue(v) !== optionValue);
3030
3066
  if (props.value === void 0) {
3031
3067
  setInternalMultiValue(newValue);
3032
3068
  }
@@ -3047,9 +3083,9 @@ var Combobox = React30.forwardRef(
3047
3083
  if (!isMultiple) return;
3048
3084
  const nextValue = allSelected ? [] : selectableOptions;
3049
3085
  if (props.value === void 0) {
3050
- setInternalMultiValue(nextValue);
3086
+ setInternalMultiValue(nextValue.map((option) => option.raw));
3051
3087
  }
3052
- props.onChange?.(nextValue);
3088
+ props.onChange?.(nextValue.map((option) => option.raw));
3053
3089
  };
3054
3090
  const handleClearSingle = (e) => {
3055
3091
  e.stopPropagation();
@@ -3112,12 +3148,12 @@ var Combobox = React30.forwardRef(
3112
3148
  {
3113
3149
  className: "inline-flex items-center gap-1 rounded-md bg-muted px-2 py-0.5 text-xs font-medium",
3114
3150
  children: [
3115
- option.label,
3151
+ getOptionLabel(option),
3116
3152
  /* @__PURE__ */ jsx30(
3117
3153
  "button",
3118
3154
  {
3119
3155
  type: "button",
3120
- onClick: (e) => handleRemoveItem(option.value, e),
3156
+ onClick: (e) => handleRemoveItem(getOptionValue(option), e),
3121
3157
  className: "ml-1 rounded-full hover:bg-background/50",
3122
3158
  children: /* @__PURE__ */ jsx30(
3123
3159
  "svg",
@@ -3138,14 +3174,14 @@ var Combobox = React30.forwardRef(
3138
3174
  )
3139
3175
  ]
3140
3176
  },
3141
- option.value
3177
+ getOptionValue(option)
3142
3178
  )),
3143
3179
  remainingCount > 0 && /* @__PURE__ */ jsxs15("span", { className: "text-xs text-muted-foreground", children: [
3144
3180
  "+",
3145
3181
  remainingCount,
3146
3182
  " more"
3147
3183
  ] })
3148
- ] }) }) : /* @__PURE__ */ jsx30("span", { className: cn(!singleValue && "text-muted-foreground"), children: singleValue?.label ?? placeholder }),
3184
+ ] }) }) : /* @__PURE__ */ jsx30("span", { className: cn(!singleValue && "text-muted-foreground"), children: singleValue ? getOptionLabel(singleValue) : placeholder }),
3149
3185
  /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-1", children: [
3150
3186
  isMultiple && selectedOptions.length > 0 && /* @__PURE__ */ jsx30(
3151
3187
  "button",
@@ -3302,13 +3338,13 @@ var Combobox = React30.forwardRef(
3302
3338
  }
3303
3339
  ),
3304
3340
  filteredOptions.map((option) => {
3305
- const isSelected = isMultiple ? multiValue.some((item) => item.value === option.value) : option.value === singleValue?.value;
3341
+ const isSelected = isMultiple ? selectedValueKeys.has(option.value) : option.value === singleValueKey;
3306
3342
  return /* @__PURE__ */ jsxs15(
3307
3343
  "button",
3308
3344
  {
3309
3345
  type: "button",
3310
3346
  disabled: option.disabled,
3311
- onClick: () => isMultiple ? handleMultiSelect(option) : handleSingleSelect(option),
3347
+ onClick: () => isMultiple ? handleMultiSelect(option.raw) : handleSingleSelect(option.raw),
3312
3348
  className: cn(
3313
3349
  "relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none",
3314
3350
  "hover:bg-muted hover:text-foreground",
@@ -5916,7 +5952,9 @@ var PlaygroundContent = () => {
5916
5952
  {
5917
5953
  options: comboboxOptions,
5918
5954
  value: comboboxValue,
5919
- onChange: setComboboxValue,
5955
+ onChange: (nextValue) => {
5956
+ setComboboxValue(nextValue);
5957
+ },
5920
5958
  placeholder: "Search frameworks..."
5921
5959
  }
5922
5960
  )