@nexus-cross/design-system 1.1.0 → 1.1.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.
@@ -19,6 +19,97 @@ var comboboxInputVariants = cva("nexus-combobox", {
19
19
  },
20
20
  defaultVariants: { size: "md", state: "default" }
21
21
  });
22
+ function ComboboxOption(_) {
23
+ return null;
24
+ }
25
+ ComboboxOption.displayName = "Combobox.Option";
26
+ function ComboboxOptionDescription(_) {
27
+ return null;
28
+ }
29
+ ComboboxOptionDescription.displayName = "Combobox.OptionDescription";
30
+ function ComboboxOptionMeta(_) {
31
+ return null;
32
+ }
33
+ ComboboxOptionMeta.displayName = "Combobox.OptionMeta";
34
+ function isElementOfType(node, type) {
35
+ return React.isValidElement(node) && node.type === type;
36
+ }
37
+ function extractText(node) {
38
+ if (node == null || typeof node === "boolean") return "";
39
+ if (typeof node === "string" || typeof node === "number") return String(node);
40
+ if (Array.isArray(node)) {
41
+ return node.map(extractText).filter(Boolean).join(" ");
42
+ }
43
+ if (React.isValidElement(node)) {
44
+ if (node.type === ComboboxOptionDescription || node.type === ComboboxOptionMeta) {
45
+ return "";
46
+ }
47
+ const children = node.props?.children;
48
+ return extractText(children);
49
+ }
50
+ return "";
51
+ }
52
+ function resolveOptionsFromChildren(children) {
53
+ const resolved = [];
54
+ const seenValues = /* @__PURE__ */ new Set();
55
+ React.Children.forEach(children, (child) => {
56
+ if (!React.isValidElement(child)) return;
57
+ if (child.type !== ComboboxOption) {
58
+ if (process.env.NODE_ENV !== "production") {
59
+ const name = typeof child.type === "string" ? child.type : child.type?.displayName ?? child.type?.name ?? "Unknown";
60
+ console.warn(
61
+ `[Combobox] Ignoring child <${name}>. Only <Combobox.Option> is allowed as a direct child of <Combobox>.`
62
+ );
63
+ }
64
+ return;
65
+ }
66
+ const props = child.props;
67
+ const value = props.value;
68
+ if (typeof value !== "string" || value.length === 0) {
69
+ if (process.env.NODE_ENV !== "production") {
70
+ console.error(
71
+ "[Combobox] <Combobox.Option> requires a non-empty string `value` prop."
72
+ );
73
+ }
74
+ return;
75
+ }
76
+ if (seenValues.has(value)) {
77
+ if (process.env.NODE_ENV !== "production") {
78
+ console.error(
79
+ `[Combobox] Duplicate option value: "${value}". Each <Combobox.Option> must have a unique \`value\`. The duplicate will be ignored.`
80
+ );
81
+ }
82
+ return;
83
+ }
84
+ seenValues.add(value);
85
+ const labelNodes = [];
86
+ let description = null;
87
+ let meta = null;
88
+ React.Children.forEach(props.children, (slot) => {
89
+ if (isElementOfType(slot, ComboboxOptionDescription)) {
90
+ description = slot.props.children;
91
+ return;
92
+ }
93
+ if (isElementOfType(slot, ComboboxOptionMeta)) {
94
+ meta = slot.props.children;
95
+ return;
96
+ }
97
+ labelNodes.push(slot);
98
+ });
99
+ const label = labelNodes.length === 1 ? labelNodes[0] : labelNodes;
100
+ const textValue = typeof props.textValue === "string" && props.textValue.length > 0 ? props.textValue : extractText(label);
101
+ resolved.push({
102
+ value,
103
+ disabled: !!props.disabled,
104
+ textValue,
105
+ className: props.className,
106
+ label,
107
+ description,
108
+ meta
109
+ });
110
+ });
111
+ return resolved;
112
+ }
22
113
  var ChevronDown = ({ className }) => /* @__PURE__ */ jsx(
23
114
  "svg",
24
115
  {
@@ -67,8 +158,8 @@ var CloseIcon = ({ className }) => /* @__PURE__ */ jsx(
67
158
  );
68
159
  function defaultFilter(option, query) {
69
160
  if (!query) return true;
70
- const label = typeof option.label === "string" ? option.label : typeof option.label === "number" ? String(option.label) : option.value;
71
- return label.toLowerCase().includes(query.toLowerCase());
161
+ const haystack = option.textValue || option.value;
162
+ return haystack.toLowerCase().includes(query.toLowerCase());
72
163
  }
73
164
  function useDebouncedCallback(callback, delay) {
74
165
  const cbRef = React.useRef(callback);
@@ -89,10 +180,10 @@ function useDebouncedCallback(callback, delay) {
89
180
  [delay]
90
181
  );
91
182
  }
92
- var Combobox = React.forwardRef(
93
- function Combobox2(props, ref) {
183
+ var ComboboxImpl = React.forwardRef(
184
+ function Combobox(props, ref) {
94
185
  const {
95
- options,
186
+ children,
96
187
  onSearch,
97
188
  searchDebounce = 250,
98
189
  loading = false,
@@ -164,11 +255,20 @@ var Combobox = React.forwardRef(
164
255
  const [query, setQuery] = React.useState("");
165
256
  const [activeIndex, setActiveIndex] = React.useState(0);
166
257
  const debouncedSearch = useDebouncedCallback(onSearch, searchDebounce);
258
+ const resolvedOptions = React.useMemo(
259
+ () => resolveOptionsFromChildren(children),
260
+ [children]
261
+ );
167
262
  const visibleOptions = React.useMemo(() => {
168
- if (onSearch) return options;
263
+ if (onSearch) return resolvedOptions;
169
264
  const filterFn = filter ?? defaultFilter;
170
- return options.filter((o) => filterFn(o, query));
171
- }, [onSearch, options, filter, query]);
265
+ return resolvedOptions.filter(
266
+ (o) => filterFn(
267
+ { value: o.value, textValue: o.textValue, disabled: o.disabled },
268
+ query
269
+ )
270
+ );
271
+ }, [onSearch, resolvedOptions, filter, query]);
172
272
  React.useEffect(() => {
173
273
  if (activeIndex >= visibleOptions.length) {
174
274
  setActiveIndex(visibleOptions.length > 0 ? 0 : -1);
@@ -196,7 +296,7 @@ var Combobox = React.forwardRef(
196
296
  inputRef.current?.focus();
197
297
  } else {
198
298
  updateValue(option.value);
199
- const labelText = typeof option.label === "string" ? option.label : option.value;
299
+ const labelText = option.textValue || option.value;
200
300
  setQuery(labelText);
201
301
  setOpen(false);
202
302
  inputRef.current?.blur();
@@ -288,9 +388,9 @@ var Combobox = React.forwardRef(
288
388
  };
289
389
  const selectedOptionMap = React.useMemo(() => {
290
390
  const map = /* @__PURE__ */ new Map();
291
- for (const opt of options) map.set(opt.value, opt);
391
+ for (const opt of resolvedOptions) map.set(opt.value, opt);
292
392
  return map;
293
- }, [options]);
393
+ }, [resolvedOptions]);
294
394
  const prevValueRef = React.useRef(
295
395
  currentValue
296
396
  );
@@ -306,7 +406,7 @@ var Combobox = React.forwardRef(
306
406
  return;
307
407
  }
308
408
  const opt = selectedOptionMap.get(currentValue);
309
- const labelText = opt ? typeof opt.label === "string" ? opt.label : currentValue : currentValue;
409
+ const labelText = opt ? opt.textValue || currentValue : currentValue;
310
410
  if (valueChanged) {
311
411
  setQuery(labelText);
312
412
  } else {
@@ -332,8 +432,9 @@ var Combobox = React.forwardRef(
332
432
  /* @__PURE__ */ jsxs("div", { className: "nexus-combobox__content", children: [
333
433
  isMultiple && selectedValues.map((val) => {
334
434
  const opt = selectedOptionMap.get(val);
435
+ const chipLabel = opt ? opt.label ?? opt.textValue ?? val : val;
335
436
  return /* @__PURE__ */ jsxs("span", { className: "nexus-combobox__chip", children: [
336
- /* @__PURE__ */ jsx("span", { className: "nexus-combobox__chip-label", children: opt?.label ?? val }),
437
+ /* @__PURE__ */ jsx("span", { className: "nexus-combobox__chip-label", children: chipLabel }),
337
438
  !disabled && /* @__PURE__ */ jsx(
338
439
  "button",
339
440
  {
@@ -453,7 +554,8 @@ var Combobox = React.forwardRef(
453
554
  "nexus-combobox-option",
454
555
  active && "nexus-combobox-option--active",
455
556
  selected && "nexus-combobox-option--selected",
456
- option.disabled && "nexus-combobox-option--disabled"
557
+ option.disabled && "nexus-combobox-option--disabled",
558
+ option.className
457
559
  ),
458
560
  onMouseEnter: () => setActiveIndex(idx),
459
561
  onMouseDown: (e) => {
@@ -463,8 +565,9 @@ var Combobox = React.forwardRef(
463
565
  children: [
464
566
  /* @__PURE__ */ jsxs("div", { className: "nexus-combobox-option__body", children: [
465
567
  /* @__PURE__ */ jsx("div", { className: "nexus-combobox-option__label", children: option.label }),
466
- option.description && /* @__PURE__ */ jsx("div", { className: "nexus-combobox-option__description", children: option.description })
568
+ option.description != null && /* @__PURE__ */ jsx("div", { className: "nexus-combobox-option__description", children: option.description })
467
569
  ] }),
570
+ option.meta != null && /* @__PURE__ */ jsx("div", { className: "nexus-combobox-option__meta", children: option.meta }),
468
571
  selected && /* @__PURE__ */ jsx(
469
572
  "svg",
470
573
  {
@@ -509,6 +612,10 @@ var Combobox = React.forwardRef(
509
612
  ] });
510
613
  }
511
614
  );
512
- Combobox.displayName = "Combobox";
615
+ ComboboxImpl.displayName = "Combobox";
616
+ var Combobox2 = ComboboxImpl;
617
+ Combobox2.Option = ComboboxOption;
618
+ Combobox2.OptionDescription = ComboboxOptionDescription;
619
+ Combobox2.OptionMeta = ComboboxOptionMeta;
513
620
 
514
- export { Combobox, comboboxInputVariants };
621
+ export { Combobox2 as Combobox, ComboboxOption, ComboboxOptionDescription, ComboboxOptionMeta, comboboxInputVariants };