@nexub/react 5.1.0 → 5.3.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.
@@ -0,0 +1,142 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Check, Plus } from "@nexub/icons";
5
+ import { cn } from "../../utils/cn.js";
6
+ import { useControllableState } from "../../hooks/useControllableState.js";
7
+ import { useNexubMessages } from "../NexubProvider/NexubProvider.js";
8
+ import { Chip } from "../Chip/Chip.js";
9
+ import { Combobox } from "../Combobox/Combobox.js";
10
+ import { ComboboxLoading, ComboboxPopover } from "../Combobox/ComboboxPopover.js";
11
+ function MultiSelect({
12
+ options,
13
+ value: valueProp,
14
+ defaultValue,
15
+ onValueChange,
16
+ placeholder,
17
+ emptyText,
18
+ loading = false,
19
+ inputValue,
20
+ onInputChange,
21
+ onCreate,
22
+ maxVisibleChips = 3,
23
+ disabled,
24
+ className,
25
+ id,
26
+ "aria-label": ariaLabel,
27
+ "aria-labelledby": ariaLabelledBy
28
+ }) {
29
+ const messages = useNexubMessages();
30
+ const [value = [], setValue] = useControllableState({
31
+ value: valueProp,
32
+ defaultValue: defaultValue ?? [],
33
+ onChange: onValueChange
34
+ });
35
+ const [innerSearch, setInnerSearch] = React.useState("");
36
+ const [open, setOpen] = React.useState(false);
37
+ const search = inputValue ?? innerSearch;
38
+ const setSearch = (text2) => {
39
+ setInnerSearch(text2);
40
+ onInputChange?.(text2);
41
+ };
42
+ const [labels, setLabels] = React.useState(() => /* @__PURE__ */ new Map());
43
+ const missing = options.some((o) => labels.get(o.value) !== o.label);
44
+ if (missing) setLabels(new Map([...labels, ...options.map((o) => [o.value, o.label])]));
45
+ const labelOf = (v) => labels.get(v) ?? options.find((o) => o.value === v)?.label ?? v;
46
+ const toggle = (v) => setValue(value.includes(v) ? value.filter((x) => x !== v) : [...value, v]);
47
+ const remove = (v) => setValue(value.filter((x) => x !== v));
48
+ const text = search.trim();
49
+ const canCreate = !!onCreate && !loading && text !== "" && !options.some((o) => o.label.toLowerCase() === text.toLowerCase());
50
+ const hiddenCount = Math.max(0, value.length - maxVisibleChips);
51
+ const chips = /* @__PURE__ */ jsxs(Fragment, { children: [
52
+ value.slice(0, maxVisibleChips).map((v) => /* @__PURE__ */ jsx(
53
+ Chip,
54
+ {
55
+ size: "sm",
56
+ onRemove: disabled ? void 0 : () => remove(v),
57
+ removeLabel: messages.removeOption(labelOf(v)),
58
+ children: labelOf(v)
59
+ },
60
+ v
61
+ )),
62
+ hiddenCount > 0 && /* @__PURE__ */ jsxs(Chip, { size: "sm", children: [
63
+ "+",
64
+ messages.moreCount(hiddenCount)
65
+ ] })
66
+ ] });
67
+ return /* @__PURE__ */ jsx(
68
+ Combobox.Root,
69
+ {
70
+ label: ariaLabel,
71
+ className: cn("overflow-visible rounded-none bg-transparent", className),
72
+ shouldFilter: !onInputChange,
73
+ children: /* @__PURE__ */ jsx(
74
+ ComboboxPopover,
75
+ {
76
+ open: open && !disabled,
77
+ onOpenChange: setOpen,
78
+ before: chips,
79
+ disabled,
80
+ input: {
81
+ id,
82
+ "aria-label": ariaLabel,
83
+ "aria-labelledby": ariaLabelledBy,
84
+ placeholder: value.length ? void 0 : placeholder ?? messages.searchPlaceholder,
85
+ value: search,
86
+ onValueChange: (t) => {
87
+ setSearch(t);
88
+ setOpen(true);
89
+ },
90
+ onKeyDown: (e) => {
91
+ if (e.key === "Backspace" && search === "" && value.length) remove(value[value.length - 1]);
92
+ }
93
+ },
94
+ children: /* @__PURE__ */ jsx(Combobox.List, { "aria-multiselectable": "true", children: loading ? /* @__PURE__ */ jsx(ComboboxLoading, {}) : /* @__PURE__ */ jsxs(Fragment, { children: [
95
+ !canCreate && /* @__PURE__ */ jsx(Combobox.Empty, { children: emptyText ?? messages.noResults }),
96
+ options.map((opt) => {
97
+ const checked = value.includes(opt.value);
98
+ return /* @__PURE__ */ jsxs(
99
+ Combobox.Item,
100
+ {
101
+ value: opt.label,
102
+ keywords: [opt.value],
103
+ "aria-checked": checked,
104
+ onSelect: () => {
105
+ toggle(opt.value);
106
+ if (search) setSearch("");
107
+ },
108
+ className: "gap-2",
109
+ children: [
110
+ /* @__PURE__ */ jsx(Check, { size: "sm", "aria-hidden": "true", className: cn(!checked && "invisible") }),
111
+ opt.label
112
+ ]
113
+ },
114
+ opt.value
115
+ );
116
+ }),
117
+ canCreate && /* @__PURE__ */ jsxs(
118
+ Combobox.Item,
119
+ {
120
+ forceMount: true,
121
+ value: "\0",
122
+ onSelect: () => {
123
+ onCreate(text);
124
+ setSearch("");
125
+ },
126
+ className: "gap-2",
127
+ children: [
128
+ /* @__PURE__ */ jsx(Plus, { size: "sm", "aria-hidden": "true" }),
129
+ messages.createOption(text)
130
+ ]
131
+ }
132
+ )
133
+ ] }) })
134
+ }
135
+ )
136
+ }
137
+ );
138
+ }
139
+ MultiSelect.displayName = "MultiSelect";
140
+ export {
141
+ MultiSelect
142
+ };
@@ -0,0 +1,2 @@
1
+ export { MultiSelect } from './MultiSelect.js';
2
+ export type { MultiSelectProps, MultiSelectOption } from './MultiSelect.js';
@@ -0,0 +1,4 @@
1
+ import { MultiSelect } from "./MultiSelect.js";
2
+ export {
3
+ MultiSelect
4
+ };
package/dist/index.d.ts CHANGED
@@ -160,6 +160,8 @@ export { Select } from './components/Select/index.js';
160
160
  export { Combobox } from './components/Combobox/index.js';
161
161
  export { Autocomplete } from './components/Autocomplete/index.js';
162
162
  export type { AutocompleteOption, AutocompleteProps } from './components/Autocomplete/index.js';
163
+ export { MultiSelect } from './components/MultiSelect/index.js';
164
+ export type { MultiSelectOption, MultiSelectProps } from './components/MultiSelect/index.js';
163
165
  export { Switch } from './components/Switch/index.js';
164
166
  export type { SwitchProps } from './components/Switch/index.js';
165
167
  export { Checkbox } from './components/Checkbox/index.js';
package/dist/index.js CHANGED
@@ -89,6 +89,7 @@ import { Drawer, Sheet } from "./components/Drawer/index.js";
89
89
  import { Select } from "./components/Select/index.js";
90
90
  import { Combobox } from "./components/Combobox/index.js";
91
91
  import { Autocomplete } from "./components/Autocomplete/index.js";
92
+ import { MultiSelect } from "./components/MultiSelect/index.js";
92
93
  import { Switch } from "./components/Switch/index.js";
93
94
  import { Checkbox } from "./components/Checkbox/index.js";
94
95
  import { RadioGroup } from "./components/RadioGroup/index.js";
@@ -201,6 +202,7 @@ export {
201
202
  MessageActions,
202
203
  MessageBubble,
203
204
  MoneyInput,
205
+ MultiSelect,
204
206
  NavigationMenu,
205
207
  NavigationRail,
206
208
  NexubProvider,