@mirohq/design-system-combobox 0.1.0-combobox.1 → 0.1.0-combobox.10
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/main.js +714 -83
- package/dist/main.js.map +1 -1
- package/dist/module.js +700 -88
- package/dist/module.js.map +1 -1
- package/dist/types.d.ts +271 -2470
- package/package.json +18 -14
package/dist/module.js
CHANGED
|
@@ -1,30 +1,98 @@
|
|
|
1
|
-
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
-
import React, { createContext, useState, useContext, useEffect } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import React, { createContext, useRef, useState, useContext, useCallback, useEffect, useMemo } from 'react';
|
|
3
|
+
import { Combobox as Combobox$1, ComboboxItem, ComboboxItemCheck, ComboboxList, Group as Group$1, GroupLabel as GroupLabel$1, ComboboxProvider as ComboboxProvider$1 } from '@ariakit/react';
|
|
4
|
+
import { useFormFieldContext, FloatingLabel } from '@mirohq/design-system-base-form';
|
|
5
|
+
import { booleanishAttrValue, mergeRefs, booleanify } from '@mirohq/design-system-utils';
|
|
6
|
+
import * as RadixPopover from '@radix-ui/react-popover';
|
|
7
|
+
import { Trigger as Trigger$1, Anchor, Portal as Portal$1 } from '@radix-ui/react-popover';
|
|
8
|
+
import { styled, theme } from '@mirohq/design-system-stitches';
|
|
6
9
|
import { Input } from '@mirohq/design-system-input';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
10
|
+
import { useControllableState } from '@radix-ui/react-use-controllable-state';
|
|
11
|
+
import { IconChevronDown, IconCross, IconCheckMark } from '@mirohq/design-system-icons';
|
|
12
|
+
import { ScrollArea } from '@mirohq/design-system-scroll-area';
|
|
9
13
|
import { Primitive } from '@mirohq/design-system-primitive';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
14
|
+
import { mergeProps } from '@react-aria/utils';
|
|
15
|
+
import { useAriaDisabled } from '@mirohq/design-system-use-aria-disabled';
|
|
16
|
+
import { useLayoutEffect } from '@mirohq/design-system-use-layout-effect';
|
|
17
|
+
import { focus } from '@mirohq/design-system-styles';
|
|
18
|
+
import { createPortal } from 'react-dom';
|
|
19
|
+
import { BaseButton } from '@mirohq/design-system-base-button';
|
|
12
20
|
|
|
13
|
-
const
|
|
21
|
+
const StyledInput = styled(Input, {
|
|
22
|
+
flexWrap: "wrap",
|
|
23
|
+
flexGrow: 1,
|
|
24
|
+
gap: "$50",
|
|
25
|
+
overflowY: "scroll",
|
|
26
|
+
"&[data-valid], &[data-invalid]": {
|
|
27
|
+
// we don't need a bigger padding here as Input component will render its own icon
|
|
28
|
+
paddingRight: "$100"
|
|
29
|
+
},
|
|
30
|
+
"& input": {
|
|
31
|
+
minWidth: "$8",
|
|
32
|
+
flexBasis: 0,
|
|
33
|
+
flexGrow: 1
|
|
34
|
+
},
|
|
35
|
+
variants: {
|
|
36
|
+
size: {
|
|
37
|
+
large: {
|
|
38
|
+
minHeight: "$10",
|
|
39
|
+
height: "auto",
|
|
40
|
+
padding: "5px $100",
|
|
41
|
+
paddingRight: "$500"
|
|
42
|
+
},
|
|
43
|
+
"x-large": {
|
|
44
|
+
minHeight: "$12",
|
|
45
|
+
height: "auto",
|
|
46
|
+
padding: "5px $100",
|
|
47
|
+
paddingRight: "$500"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
defaultVariants: {
|
|
52
|
+
size: "large"
|
|
53
|
+
}
|
|
54
|
+
});
|
|
14
55
|
|
|
15
56
|
const ComboboxContext = createContext({});
|
|
16
57
|
const ComboboxProvider = ({
|
|
17
58
|
children,
|
|
59
|
+
open: openProp,
|
|
18
60
|
defaultOpen,
|
|
61
|
+
onOpen,
|
|
62
|
+
onClose,
|
|
19
63
|
valid,
|
|
20
64
|
value: valueProp,
|
|
21
65
|
defaultValue: defaultValueProp,
|
|
66
|
+
onValueChange,
|
|
67
|
+
onSearchValueChange,
|
|
68
|
+
autoFilter = true,
|
|
22
69
|
...restProps
|
|
23
70
|
}) => {
|
|
24
|
-
const
|
|
25
|
-
const
|
|
71
|
+
const triggerRef = useRef(null);
|
|
72
|
+
const inputRef = useRef(null);
|
|
73
|
+
const contentRef = useRef(null);
|
|
26
74
|
const [defaultValue, setDefaultValue] = useState(defaultValueProp);
|
|
27
|
-
const [
|
|
75
|
+
const [openState = false, setOpenState] = useControllableState({
|
|
76
|
+
prop: openProp,
|
|
77
|
+
defaultProp: defaultOpen,
|
|
78
|
+
onChange: (state) => {
|
|
79
|
+
if (state) {
|
|
80
|
+
onOpen == null ? void 0 : onOpen();
|
|
81
|
+
} else {
|
|
82
|
+
onClose == null ? void 0 : onClose();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
const [value, setValue] = useControllableState({
|
|
87
|
+
prop: valueProp,
|
|
88
|
+
defaultProp: defaultValueProp,
|
|
89
|
+
onChange: onValueChange
|
|
90
|
+
});
|
|
91
|
+
const [filteredItems, setFilteredItems] = useState(/* @__PURE__ */ new Set());
|
|
92
|
+
const [searchValue, setSearchValue] = useState("");
|
|
93
|
+
const [size, setSize] = useState();
|
|
94
|
+
const [placeholder, setPlaceholder] = useState();
|
|
95
|
+
const [itemValueTextMap, setItemValueTextMap] = useState(/* @__PURE__ */ new Map());
|
|
28
96
|
const { valid: formFieldValid } = useFormFieldContext();
|
|
29
97
|
return /* @__PURE__ */ jsx(
|
|
30
98
|
ComboboxContext.Provider,
|
|
@@ -38,8 +106,21 @@ const ComboboxProvider = ({
|
|
|
38
106
|
setValue,
|
|
39
107
|
setDefaultValue,
|
|
40
108
|
defaultValue,
|
|
109
|
+
onSearchValueChange,
|
|
110
|
+
triggerRef,
|
|
111
|
+
inputRef,
|
|
112
|
+
contentRef,
|
|
113
|
+
autoFilter,
|
|
41
114
|
searchValue,
|
|
42
|
-
setSearchValue
|
|
115
|
+
setSearchValue,
|
|
116
|
+
filteredItems,
|
|
117
|
+
setFilteredItems,
|
|
118
|
+
itemValueTextMap,
|
|
119
|
+
setItemValueTextMap,
|
|
120
|
+
placeholder,
|
|
121
|
+
setPlaceholder,
|
|
122
|
+
size,
|
|
123
|
+
setSize
|
|
43
124
|
},
|
|
44
125
|
children
|
|
45
126
|
}
|
|
@@ -47,6 +128,70 @@ const ComboboxProvider = ({
|
|
|
47
128
|
};
|
|
48
129
|
const useComboboxContext = () => useContext(ComboboxContext);
|
|
49
130
|
|
|
131
|
+
const StyledActionButton = styled(Input.ActionButton, {
|
|
132
|
+
position: "absolute",
|
|
133
|
+
right: "$100",
|
|
134
|
+
variants: {
|
|
135
|
+
size: {
|
|
136
|
+
large: {
|
|
137
|
+
top: "5px"
|
|
138
|
+
},
|
|
139
|
+
"x-large": {
|
|
140
|
+
top: "9px"
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
defaultVariants: {
|
|
145
|
+
size: "large"
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const TriggerActionButton = ({
|
|
150
|
+
openActionLabel,
|
|
151
|
+
closeActionLabel,
|
|
152
|
+
clearActionLabel,
|
|
153
|
+
size
|
|
154
|
+
}) => {
|
|
155
|
+
const { openState, setOpenState, value = [], setValue } = useComboboxContext();
|
|
156
|
+
const isEmpty = value.length === 0;
|
|
157
|
+
const onToggleClick = useCallback(
|
|
158
|
+
(event) => {
|
|
159
|
+
if (openState) {
|
|
160
|
+
setOpenState(false);
|
|
161
|
+
}
|
|
162
|
+
event.stopPropagation();
|
|
163
|
+
},
|
|
164
|
+
[setOpenState, openState]
|
|
165
|
+
);
|
|
166
|
+
const onClearClick = useCallback(
|
|
167
|
+
(event) => {
|
|
168
|
+
setValue([]);
|
|
169
|
+
event.stopPropagation();
|
|
170
|
+
},
|
|
171
|
+
[setValue]
|
|
172
|
+
);
|
|
173
|
+
if (isEmpty) {
|
|
174
|
+
return /* @__PURE__ */ jsx(Trigger$1, { asChild: true, "aria-haspopup": "listbox", children: /* @__PURE__ */ jsx(
|
|
175
|
+
StyledActionButton,
|
|
176
|
+
{
|
|
177
|
+
label: openState ? closeActionLabel : openActionLabel,
|
|
178
|
+
size,
|
|
179
|
+
onClick: onToggleClick,
|
|
180
|
+
children: /* @__PURE__ */ jsx(IconChevronDown, { size: "small", weight: "thin" })
|
|
181
|
+
}
|
|
182
|
+
) });
|
|
183
|
+
}
|
|
184
|
+
return /* @__PURE__ */ jsx(
|
|
185
|
+
StyledActionButton,
|
|
186
|
+
{
|
|
187
|
+
label: clearActionLabel,
|
|
188
|
+
size,
|
|
189
|
+
onClick: onClearClick,
|
|
190
|
+
children: /* @__PURE__ */ jsx(IconCross, { size: "small", weight: "thin" })
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
};
|
|
194
|
+
|
|
50
195
|
const Trigger = React.forwardRef(
|
|
51
196
|
({
|
|
52
197
|
id,
|
|
@@ -55,142 +200,536 @@ const Trigger = React.forwardRef(
|
|
|
55
200
|
"aria-describedby": ariaDescribedBy,
|
|
56
201
|
"aria-invalid": ariaInvalid,
|
|
57
202
|
placeholder,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
203
|
+
openActionLabel,
|
|
204
|
+
closeActionLabel,
|
|
205
|
+
clearActionLabel,
|
|
206
|
+
onChange,
|
|
61
207
|
...restProps
|
|
62
208
|
}, forwardRef) => {
|
|
63
209
|
const {
|
|
64
210
|
"aria-disabled": ariaDisabled,
|
|
65
211
|
valid: comboboxValid,
|
|
66
212
|
disabled,
|
|
67
|
-
value
|
|
213
|
+
value = [],
|
|
214
|
+
readOnly,
|
|
215
|
+
triggerRef,
|
|
216
|
+
inputRef,
|
|
217
|
+
onSearchValueChange,
|
|
218
|
+
searchValue,
|
|
219
|
+
setSearchValue,
|
|
220
|
+
setOpenState,
|
|
221
|
+
setSize,
|
|
222
|
+
setPlaceholder
|
|
68
223
|
} = useComboboxContext();
|
|
69
224
|
const {
|
|
70
225
|
formElementId,
|
|
71
|
-
ariaDescribedBy: formFieldContextDescribedBy,
|
|
72
226
|
ariaInvalid: formFieldAriaInvalid,
|
|
73
227
|
valid: formFieldValid
|
|
74
228
|
} = useFormFieldContext();
|
|
229
|
+
useEffect(() => {
|
|
230
|
+
setSize(size);
|
|
231
|
+
}, [size, setSize]);
|
|
232
|
+
useEffect(() => {
|
|
233
|
+
setPlaceholder(placeholder);
|
|
234
|
+
}, [setPlaceholder, placeholder]);
|
|
75
235
|
const valid = formFieldValid != null ? formFieldValid : comboboxValid;
|
|
76
|
-
const
|
|
77
|
-
onHoverStart,
|
|
78
|
-
onHoverEnd,
|
|
79
|
-
onHoverChange: (isHovering) => {
|
|
80
|
-
onHoverChange == null ? void 0 : onHoverChange(isHovering);
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
const commonProps = {
|
|
236
|
+
const inputProps = {
|
|
84
237
|
...restProps,
|
|
85
|
-
ref: forwardRef,
|
|
86
238
|
"aria-disabled": ariaDisabled,
|
|
87
239
|
"aria-invalid": ariaInvalid != null ? ariaInvalid : formFieldAriaInvalid,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
formFieldContextDescribedBy
|
|
91
|
-
),
|
|
240
|
+
// todo MDS-1011: use formFieldContextDescribedBy after removing form context from BaseInput
|
|
241
|
+
"aria-describedby": ariaDescribedBy,
|
|
92
242
|
valid,
|
|
93
243
|
disabled,
|
|
244
|
+
readOnly,
|
|
94
245
|
invalid: booleanishAttrValue(valid),
|
|
95
246
|
id: id != null ? id : formElementId,
|
|
96
|
-
placeholder: value ===
|
|
247
|
+
placeholder: value.length === 0 ? placeholder : void 0
|
|
248
|
+
};
|
|
249
|
+
const scrollIntoView = (event) => {
|
|
250
|
+
var _a;
|
|
251
|
+
const trigger = triggerRef == null ? void 0 : triggerRef.current;
|
|
252
|
+
const baseInput = (_a = inputRef == null ? void 0 : inputRef.current) == null ? void 0 : _a.parentElement;
|
|
253
|
+
if (baseInput != null && trigger != null) {
|
|
254
|
+
event.preventDefault();
|
|
255
|
+
baseInput.scrollTo({
|
|
256
|
+
top: trigger.scrollHeight
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
if (restProps.onFocus !== void 0) {
|
|
260
|
+
restProps.onFocus(event);
|
|
261
|
+
}
|
|
97
262
|
};
|
|
98
|
-
const
|
|
99
|
-
|
|
263
|
+
const onInputChange = (e) => {
|
|
264
|
+
setSearchValue(e.target.value);
|
|
265
|
+
onSearchValueChange == null ? void 0 : onSearchValueChange(e.target.value);
|
|
266
|
+
onChange == null ? void 0 : onChange(e);
|
|
100
267
|
};
|
|
101
|
-
return /* @__PURE__ */
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
268
|
+
return /* @__PURE__ */ jsx(
|
|
269
|
+
Anchor,
|
|
270
|
+
{
|
|
271
|
+
ref: mergeRefs([triggerRef, forwardRef]),
|
|
272
|
+
onClick: () => {
|
|
273
|
+
if (!booleanify(disabled) && !booleanify(ariaDisabled) && !booleanify(readOnly)) {
|
|
274
|
+
setOpenState(true);
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
children: /* @__PURE__ */ jsx(
|
|
278
|
+
Combobox$1,
|
|
279
|
+
{
|
|
280
|
+
render: /* @__PURE__ */ jsxs(
|
|
281
|
+
StyledInput,
|
|
282
|
+
{
|
|
283
|
+
...inputProps,
|
|
284
|
+
value: searchValue,
|
|
285
|
+
size,
|
|
286
|
+
ref: inputRef,
|
|
287
|
+
onChange: onInputChange,
|
|
288
|
+
onFocus: scrollIntoView,
|
|
289
|
+
children: [
|
|
290
|
+
children,
|
|
291
|
+
/* @__PURE__ */ jsx(
|
|
292
|
+
TriggerActionButton,
|
|
293
|
+
{
|
|
294
|
+
openActionLabel,
|
|
295
|
+
closeActionLabel,
|
|
296
|
+
clearActionLabel,
|
|
297
|
+
size
|
|
298
|
+
}
|
|
299
|
+
)
|
|
300
|
+
]
|
|
301
|
+
}
|
|
302
|
+
)
|
|
303
|
+
}
|
|
304
|
+
)
|
|
305
|
+
}
|
|
306
|
+
);
|
|
105
307
|
}
|
|
106
308
|
);
|
|
107
309
|
|
|
108
|
-
const
|
|
310
|
+
const NoResultPlaceholder = styled(Primitive.div, {
|
|
311
|
+
padding: "$100"
|
|
312
|
+
});
|
|
313
|
+
const StyledContent = styled(RadixPopover.Content, {
|
|
109
314
|
backgroundColor: "$background-neutrals-container",
|
|
110
315
|
borderRadius: "$50",
|
|
111
316
|
boxShadow: "$50",
|
|
112
|
-
|
|
317
|
+
fontSize: "$175",
|
|
318
|
+
fontWeight: "normal",
|
|
319
|
+
lineHeight: "20px",
|
|
320
|
+
width: "var(--radix-popover-trigger-width)",
|
|
321
|
+
zIndex: "$select",
|
|
322
|
+
overflowY: "auto",
|
|
113
323
|
padding: "$50",
|
|
114
|
-
|
|
324
|
+
boxSizing: "border-box",
|
|
325
|
+
outline: "1px solid transparent"
|
|
115
326
|
});
|
|
116
327
|
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
if (!booleanify(open != null ? open : openState))
|
|
120
|
-
return null;
|
|
121
|
-
return /* @__PURE__ */ jsx(StyledContent, { ...restProps, ref: forwardRef, children });
|
|
328
|
+
const StyledItemCheck = styled(Primitive.span, {
|
|
329
|
+
color: "$icon-primary"
|
|
122
330
|
});
|
|
123
|
-
|
|
124
|
-
|
|
331
|
+
const StyledItem = styled(ComboboxItem, {
|
|
332
|
+
display: "grid",
|
|
333
|
+
gridTemplateColumns: "20px 1fr",
|
|
125
334
|
borderRadius: "$50",
|
|
126
335
|
boxSizing: "border-box",
|
|
127
336
|
color: "$text-neutrals",
|
|
128
337
|
cursor: "pointer",
|
|
129
338
|
fontSize: "$175",
|
|
130
|
-
lineHeight:
|
|
339
|
+
lineHeight: "20px",
|
|
131
340
|
position: "relative",
|
|
132
341
|
userSelect: "none",
|
|
133
|
-
padding: "6px
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
342
|
+
padding: "6px $100 6px $150",
|
|
343
|
+
...focus.css({
|
|
344
|
+
boxShadow: "$focus-small"
|
|
345
|
+
}),
|
|
346
|
+
'&:not([aria-disabled="true"])': {
|
|
347
|
+
_hover: {
|
|
348
|
+
background: "$background-primary-subtle-hover",
|
|
349
|
+
color: "$text-primary-hover",
|
|
350
|
+
["".concat(StyledItemCheck)]: {
|
|
351
|
+
color: "$icon-primary-hover"
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
"&:disabled, &[aria-disabled=true], &[data-disabled]": {
|
|
356
|
+
cursor: "default",
|
|
357
|
+
color: "$text-neutrals-disabled",
|
|
358
|
+
["".concat(StyledItemCheck)]: {
|
|
359
|
+
color: "$icon-neutrals-disabled"
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
'&[aria-selected="true"]:not(:disabled,[aria-disabled=true],[data-disabled])': {
|
|
363
|
+
color: "$text-primary-selected"
|
|
138
364
|
}
|
|
139
365
|
});
|
|
140
366
|
|
|
141
367
|
const Item = React.forwardRef(
|
|
142
|
-
({ value, textValue, children, ...restProps }, forwardRef) => {
|
|
143
|
-
const {
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
368
|
+
({ disabled = false, value, textValue, children, ...restProps }, forwardRef) => {
|
|
369
|
+
const { "aria-disabled": ariaDisabled, ...restAriaDisabledProps } = useAriaDisabled(restProps, { allowArrows: true });
|
|
370
|
+
const {
|
|
371
|
+
autoFilter,
|
|
372
|
+
filteredItems,
|
|
373
|
+
setItemValueTextMap,
|
|
374
|
+
triggerRef,
|
|
375
|
+
inputRef,
|
|
376
|
+
value: comboboxValue = []
|
|
377
|
+
} = useComboboxContext();
|
|
378
|
+
useLayoutEffect(() => {
|
|
379
|
+
const textToSet = textValue !== void 0 ? textValue : typeof children === "string" ? children : "";
|
|
380
|
+
setItemValueTextMap((prevState) => new Map(prevState.set(value, textToSet)));
|
|
381
|
+
return () => {
|
|
382
|
+
setItemValueTextMap((prevState) => {
|
|
383
|
+
prevState.delete(value);
|
|
384
|
+
return new Map(prevState);
|
|
385
|
+
});
|
|
386
|
+
};
|
|
387
|
+
}, [setItemValueTextMap, value, textValue, children]);
|
|
388
|
+
if (autoFilter !== false && !filteredItems.has(value)) {
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
const scrollIntoView = (event) => {
|
|
392
|
+
var _a;
|
|
393
|
+
if (((_a = inputRef == null ? void 0 : inputRef.current) == null ? void 0 : _a.parentElement) != null && (triggerRef == null ? void 0 : triggerRef.current) != null) {
|
|
394
|
+
inputRef.current.parentElement.scrollTo({
|
|
395
|
+
top: triggerRef.current.scrollHeight
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
if (restProps.onClick !== void 0) {
|
|
399
|
+
restProps.onClick(event);
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
const isSelected = comboboxValue.includes(value);
|
|
403
|
+
return /* @__PURE__ */ jsxs(
|
|
404
|
+
StyledItem,
|
|
405
|
+
{
|
|
406
|
+
...mergeProps(restProps, restAriaDisabledProps),
|
|
407
|
+
focusable: true,
|
|
408
|
+
hideOnClick: false,
|
|
409
|
+
accessibleWhenDisabled: booleanify(ariaDisabled),
|
|
410
|
+
disabled: booleanify(ariaDisabled) || disabled,
|
|
411
|
+
ref: forwardRef,
|
|
412
|
+
value,
|
|
413
|
+
onClick: scrollIntoView,
|
|
414
|
+
"aria-selected": isSelected,
|
|
415
|
+
children: [
|
|
416
|
+
/* @__PURE__ */ jsx(
|
|
417
|
+
ComboboxItemCheck,
|
|
418
|
+
{
|
|
419
|
+
checked: isSelected,
|
|
420
|
+
render: ({ style, ...props }) => (
|
|
421
|
+
// AriakitComboboxItemCheck adds its owm inline styles which we want to omit here
|
|
422
|
+
/* @__PURE__ */ jsx(StyledItemCheck, { ...props })
|
|
423
|
+
),
|
|
424
|
+
children: /* @__PURE__ */ jsx(
|
|
425
|
+
IconCheckMark,
|
|
426
|
+
{
|
|
427
|
+
size: "small",
|
|
428
|
+
"data-testid": process.env.NODE_ENV === "test" ? "combobox-item-check" : void 0
|
|
429
|
+
}
|
|
430
|
+
)
|
|
431
|
+
}
|
|
432
|
+
),
|
|
433
|
+
children
|
|
434
|
+
]
|
|
435
|
+
}
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
const itemType = React.createElement(Item).type;
|
|
441
|
+
const getChildrenItemValues = (componentChildren) => {
|
|
442
|
+
const values = [];
|
|
443
|
+
const recurse = (children) => {
|
|
444
|
+
React.Children.forEach(children, (child) => {
|
|
445
|
+
if (!React.isValidElement(child)) {
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
if (child.type === itemType) {
|
|
449
|
+
const props = child.props;
|
|
450
|
+
values.push(props.value);
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (child.props.children) {
|
|
454
|
+
recurse(child.props.children);
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
};
|
|
458
|
+
recurse(componentChildren);
|
|
459
|
+
return values;
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
const useDocumentFragment = () => {
|
|
463
|
+
const [fragment, setFragment] = React.useState();
|
|
464
|
+
useLayoutEffect(() => {
|
|
465
|
+
setFragment(new DocumentFragment());
|
|
466
|
+
}, []);
|
|
467
|
+
return fragment;
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
const useInvisibleContent = () => {
|
|
471
|
+
const fragment = useDocumentFragment();
|
|
472
|
+
return useCallback(
|
|
473
|
+
(children) => fragment !== void 0 ? createPortal(/* @__PURE__ */ jsx("div", { children }), fragment) : null,
|
|
474
|
+
[fragment]
|
|
475
|
+
);
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
const CONTENT_OFFSET = parseInt(theme.space[50]);
|
|
479
|
+
const isInsideRef = (element, ref) => {
|
|
480
|
+
var _a, _b;
|
|
481
|
+
return (_b = element != null && ((_a = ref.current) == null ? void 0 : _a.contains(element))) != null ? _b : false;
|
|
482
|
+
};
|
|
483
|
+
const Content = React.forwardRef(
|
|
484
|
+
({
|
|
485
|
+
side = "bottom",
|
|
486
|
+
sideOffset = CONTENT_OFFSET,
|
|
487
|
+
align = "center",
|
|
488
|
+
alignOffset = 0,
|
|
489
|
+
collisionPadding = 0,
|
|
490
|
+
avoidCollisions = true,
|
|
491
|
+
sticky = "partial",
|
|
492
|
+
hideWhenDetached = true,
|
|
493
|
+
overflow = "visible",
|
|
494
|
+
maxHeight,
|
|
495
|
+
children,
|
|
496
|
+
...restProps
|
|
497
|
+
}, forwardRef) => {
|
|
498
|
+
const {
|
|
499
|
+
triggerRef,
|
|
500
|
+
contentRef,
|
|
501
|
+
autoFilter,
|
|
502
|
+
filteredItems,
|
|
503
|
+
setFilteredItems,
|
|
504
|
+
searchValue,
|
|
505
|
+
noResultsText,
|
|
506
|
+
direction,
|
|
507
|
+
openState
|
|
508
|
+
} = useComboboxContext();
|
|
509
|
+
useEffect(() => {
|
|
510
|
+
const childrenItemValues = getChildrenItemValues(children);
|
|
511
|
+
setFilteredItems(
|
|
512
|
+
new Set(
|
|
513
|
+
autoFilter === false ? childrenItemValues : childrenItemValues.filter(
|
|
514
|
+
(child) => child.toLowerCase().includes(searchValue.toLowerCase())
|
|
515
|
+
)
|
|
516
|
+
)
|
|
517
|
+
);
|
|
518
|
+
}, [children, autoFilter, setFilteredItems, searchValue]);
|
|
519
|
+
const getInvisibleContent = useInvisibleContent();
|
|
520
|
+
if (!openState) {
|
|
521
|
+
return getInvisibleContent(children);
|
|
522
|
+
}
|
|
523
|
+
const content = filteredItems.size === 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
524
|
+
/* @__PURE__ */ jsx(NoResultPlaceholder, { children: noResultsText }),
|
|
525
|
+
getInvisibleContent(children)
|
|
526
|
+
] }) : children;
|
|
527
|
+
return /* @__PURE__ */ jsx(
|
|
528
|
+
StyledContent,
|
|
529
|
+
{
|
|
530
|
+
asChild: true,
|
|
531
|
+
...restProps,
|
|
532
|
+
dir: direction,
|
|
533
|
+
side,
|
|
534
|
+
sideOffset,
|
|
535
|
+
align,
|
|
536
|
+
alignOffset,
|
|
537
|
+
avoidCollisions,
|
|
538
|
+
collisionPadding,
|
|
539
|
+
sticky,
|
|
540
|
+
hideWhenDetached,
|
|
541
|
+
ref: mergeRefs([forwardRef, contentRef]),
|
|
542
|
+
onOpenAutoFocus: (event) => event.preventDefault(),
|
|
543
|
+
onInteractOutside: (event) => {
|
|
544
|
+
const target = event.target;
|
|
545
|
+
const isTrigger = isInsideRef(target, triggerRef);
|
|
546
|
+
const isContent = isInsideRef(target, contentRef);
|
|
547
|
+
if (isTrigger || isContent) {
|
|
548
|
+
event.preventDefault();
|
|
549
|
+
}
|
|
550
|
+
},
|
|
551
|
+
children: /* @__PURE__ */ jsx(ComboboxList, { role: "listbox", children: overflow === "auto" ? /* @__PURE__ */ jsxs(ScrollArea, { type: "always", dir: direction, children: [
|
|
552
|
+
/* @__PURE__ */ jsx(
|
|
553
|
+
ScrollArea.Viewport,
|
|
554
|
+
{
|
|
555
|
+
availableHeight: "var(--radix-popover-content-available-height)",
|
|
556
|
+
verticalGap: "var(--space-50) * 2",
|
|
557
|
+
maxHeight,
|
|
558
|
+
children: content
|
|
559
|
+
}
|
|
560
|
+
),
|
|
561
|
+
/* @__PURE__ */ jsx(ScrollArea.Scrollbar, { orientation: "vertical", children: /* @__PURE__ */ jsx(ScrollArea.Thumb, {}) })
|
|
562
|
+
] }) : content })
|
|
563
|
+
}
|
|
564
|
+
);
|
|
149
565
|
}
|
|
150
566
|
);
|
|
151
567
|
|
|
152
568
|
const Portal = (props) => /* @__PURE__ */ jsx(Portal$1, { ...props });
|
|
153
569
|
|
|
154
|
-
const StyledGroup = styled(Group$1
|
|
570
|
+
const StyledGroup = styled(Group$1);
|
|
155
571
|
|
|
156
|
-
const Group = React.forwardRef((
|
|
572
|
+
const Group = React.forwardRef(({ children, ...rest }, forwardRef) => {
|
|
573
|
+
const { autoFilter, filteredItems } = useComboboxContext();
|
|
574
|
+
const childValues = useMemo(
|
|
575
|
+
// don't perform calculation if auto filter is disabled
|
|
576
|
+
() => autoFilter !== false ? getChildrenItemValues(children) : [],
|
|
577
|
+
[children, autoFilter]
|
|
578
|
+
);
|
|
579
|
+
const hasVisibleChildren = useMemo(
|
|
580
|
+
() => (
|
|
581
|
+
// don't perform calculation if auto filter is disabled
|
|
582
|
+
autoFilter !== false ? childValues.some((value) => filteredItems.has(value)) : true
|
|
583
|
+
),
|
|
584
|
+
[childValues, filteredItems, autoFilter]
|
|
585
|
+
);
|
|
586
|
+
const getInvisibleContent = useInvisibleContent();
|
|
587
|
+
if (!hasVisibleChildren) {
|
|
588
|
+
return getInvisibleContent(children);
|
|
589
|
+
}
|
|
590
|
+
return /* @__PURE__ */ jsx(StyledGroup, { ...rest, ref: forwardRef, children });
|
|
591
|
+
});
|
|
157
592
|
|
|
158
|
-
const StyledGroupLabel = styled(GroupLabel$1, {
|
|
593
|
+
const StyledGroupLabel = styled(GroupLabel$1, {
|
|
594
|
+
padding: "6px $100",
|
|
595
|
+
color: "$text-neutrals-subtle"
|
|
596
|
+
});
|
|
159
597
|
|
|
160
598
|
const GroupLabel = React.forwardRef((props, forwardRef) => /* @__PURE__ */ jsx(StyledGroupLabel, { ...props, ref: forwardRef }));
|
|
161
599
|
|
|
162
|
-
const
|
|
600
|
+
const StyledChip = styled(Primitive.div, {
|
|
601
|
+
fontSize: "$150",
|
|
602
|
+
padding: "$50 $100",
|
|
603
|
+
borderRadius: "$round",
|
|
604
|
+
display: "flex",
|
|
605
|
+
alignItems: "center",
|
|
606
|
+
gap: "$50",
|
|
607
|
+
whiteSpace: "nowrap",
|
|
608
|
+
maxWidth: "$35",
|
|
609
|
+
backgroundColor: "$background-neutrals-subtle",
|
|
610
|
+
color: "$text-neutrals"
|
|
611
|
+
});
|
|
612
|
+
const StyledChipButton = styled(BaseButton, {
|
|
613
|
+
color: "$icon-neutrals-inactive",
|
|
614
|
+
...focus.css({
|
|
615
|
+
boxShadow: "$focus-small-outline"
|
|
616
|
+
})
|
|
617
|
+
});
|
|
618
|
+
const StyledChipContent = styled(Primitive.div, {
|
|
619
|
+
textOverflow: "ellipsis",
|
|
620
|
+
whiteSpace: "nowrap",
|
|
621
|
+
overflow: "hidden",
|
|
622
|
+
lineHeight: 1.3
|
|
623
|
+
});
|
|
163
624
|
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
625
|
+
const StyledLeftSlot = styled(Primitive.span, {
|
|
626
|
+
order: -1,
|
|
627
|
+
marginRight: "$50"
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
const LeftSlot = StyledLeftSlot;
|
|
631
|
+
|
|
632
|
+
const Chip = React.forwardRef(
|
|
633
|
+
({ children, disabled = false, onRemove, removeAriaLabel, ...restProps }, forwardRef) => /* @__PURE__ */ jsxs(StyledChip, { ...restProps, ref: forwardRef, children: [
|
|
634
|
+
/* @__PURE__ */ jsx(StyledChipContent, { children }),
|
|
635
|
+
!booleanify(disabled) && /* @__PURE__ */ jsx(StyledChipButton, { onClick: onRemove, "aria-label": removeAriaLabel, children: /* @__PURE__ */ jsx(IconCross, { size: "small", weight: "thin", "aria-hidden": true }) })
|
|
636
|
+
] })
|
|
173
637
|
);
|
|
638
|
+
Chip.LeftSlot = LeftSlot;
|
|
639
|
+
|
|
640
|
+
const Value = ({ unselectAriaLabel }) => {
|
|
641
|
+
const {
|
|
642
|
+
value = [],
|
|
643
|
+
setValue,
|
|
644
|
+
disabled,
|
|
645
|
+
"aria-disabled": ariaDisabled,
|
|
646
|
+
itemValueTextMap
|
|
647
|
+
} = useComboboxContext();
|
|
648
|
+
const isDisabled = ariaDisabled === true || disabled;
|
|
649
|
+
const onItemRemove = useCallback(
|
|
650
|
+
(item) => {
|
|
651
|
+
setValue((prevValue) => prevValue == null ? void 0 : prevValue.filter((value2) => value2 !== item));
|
|
652
|
+
},
|
|
653
|
+
[setValue]
|
|
654
|
+
);
|
|
655
|
+
const getItemText = useCallback(
|
|
656
|
+
(itemValue) => {
|
|
657
|
+
const textValue = itemValueTextMap.get(itemValue);
|
|
658
|
+
if (textValue === void 0 || textValue === "") {
|
|
659
|
+
return null;
|
|
660
|
+
}
|
|
661
|
+
return /* @__PURE__ */ jsx(
|
|
662
|
+
Chip,
|
|
663
|
+
{
|
|
664
|
+
onRemove: (e) => {
|
|
665
|
+
onItemRemove(itemValue);
|
|
666
|
+
e.stopPropagation();
|
|
667
|
+
},
|
|
668
|
+
disabled: isDisabled,
|
|
669
|
+
removeAriaLabel: "".concat(unselectAriaLabel, " ").concat(textValue),
|
|
670
|
+
"data-testid": process.env.NODE_ENV === "test" ? "combobox-value-".concat(itemValue) : void 0,
|
|
671
|
+
children: textValue
|
|
672
|
+
},
|
|
673
|
+
itemValue
|
|
674
|
+
);
|
|
675
|
+
},
|
|
676
|
+
[isDisabled, itemValueTextMap, onItemRemove, unselectAriaLabel]
|
|
677
|
+
);
|
|
678
|
+
return /* @__PURE__ */ jsx(Fragment, { children: value.map(getItemText) });
|
|
679
|
+
};
|
|
174
680
|
|
|
175
|
-
const
|
|
681
|
+
const StyledSeparator = styled(Primitive.div, {
|
|
682
|
+
backgroundColor: "$border-neutrals-subtle",
|
|
683
|
+
height: "1px",
|
|
684
|
+
width: "100%",
|
|
685
|
+
margin: "$100 0"
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
const Separator = React.forwardRef((props, forwardRef) => {
|
|
689
|
+
const { autoFilter, searchValue } = useComboboxContext();
|
|
690
|
+
if (autoFilter === true && searchValue.length > 0) {
|
|
691
|
+
return null;
|
|
692
|
+
}
|
|
693
|
+
return /* @__PURE__ */ jsx(StyledSeparator, { ...props, ref: forwardRef, "aria-hidden": true });
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
const StyledNativeSelect = styled(Primitive.select, {
|
|
697
|
+
// if we support autoComplete, we would have to use visually-hidden styles here
|
|
698
|
+
display: "none"
|
|
699
|
+
});
|
|
700
|
+
const StyledComboboxContent = styled(Primitive.div, {
|
|
701
|
+
position: "relative",
|
|
702
|
+
width: "100%"
|
|
703
|
+
});
|
|
176
704
|
|
|
177
705
|
const Root = React.forwardRef(
|
|
178
|
-
({
|
|
179
|
-
|
|
180
|
-
onClose,
|
|
181
|
-
onValueChange,
|
|
182
|
-
value: valueProp,
|
|
183
|
-
children,
|
|
184
|
-
...restProps
|
|
185
|
-
}, forwardRef) => {
|
|
706
|
+
({ value: valueProp, onValueChange, name, children, ...restProps }, forwardRef) => {
|
|
707
|
+
var _a;
|
|
186
708
|
const {
|
|
709
|
+
openState,
|
|
710
|
+
setOpenState,
|
|
711
|
+
defaultValue,
|
|
712
|
+
value = [],
|
|
713
|
+
setValue,
|
|
187
714
|
required,
|
|
188
715
|
readOnly,
|
|
189
716
|
"aria-disabled": ariaDisabled,
|
|
190
717
|
disabled,
|
|
191
|
-
direction
|
|
718
|
+
direction,
|
|
719
|
+
size,
|
|
720
|
+
placeholder,
|
|
721
|
+
triggerRef
|
|
192
722
|
} = useComboboxContext();
|
|
193
|
-
const {
|
|
723
|
+
const {
|
|
724
|
+
setRequired,
|
|
725
|
+
setDisabled,
|
|
726
|
+
setAriaDisabled,
|
|
727
|
+
setReadOnly,
|
|
728
|
+
label,
|
|
729
|
+
isFloatingLabel,
|
|
730
|
+
focused,
|
|
731
|
+
formElementRef
|
|
732
|
+
} = useFormFieldContext();
|
|
194
733
|
useEffect(() => {
|
|
195
734
|
setRequired == null ? void 0 : setRequired(required);
|
|
196
735
|
setDisabled == null ? void 0 : setDisabled(disabled);
|
|
@@ -206,7 +745,63 @@ const Root = React.forwardRef(
|
|
|
206
745
|
setAriaDisabled,
|
|
207
746
|
setReadOnly
|
|
208
747
|
]);
|
|
209
|
-
|
|
748
|
+
const shouldUseFloatingLabel = label !== null && isFloatingLabel;
|
|
749
|
+
const isFloating = placeholder !== void 0 || value.length !== 0 || focused;
|
|
750
|
+
const onSetSelectedValue = (newValue) => {
|
|
751
|
+
setValue(typeof newValue === "string" ? [newValue] : newValue);
|
|
752
|
+
};
|
|
753
|
+
const onOpenChange = (value2) => {
|
|
754
|
+
if (!booleanify(readOnly)) {
|
|
755
|
+
setOpenState(value2);
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
const isFormControl = Boolean((_a = triggerRef.current) == null ? void 0 : _a.closest("form"));
|
|
759
|
+
return /* @__PURE__ */ jsxs(RadixPopover.Root, { open: openState, onOpenChange, children: [
|
|
760
|
+
/* @__PURE__ */ jsx(
|
|
761
|
+
ComboboxProvider$1,
|
|
762
|
+
{
|
|
763
|
+
open: openState,
|
|
764
|
+
setOpen: onOpenChange,
|
|
765
|
+
defaultSelectedValue: defaultValue,
|
|
766
|
+
selectedValue: value,
|
|
767
|
+
setSelectedValue: onSetSelectedValue,
|
|
768
|
+
children: /* @__PURE__ */ jsxs(
|
|
769
|
+
StyledComboboxContent,
|
|
770
|
+
{
|
|
771
|
+
ref: forwardRef,
|
|
772
|
+
...restProps,
|
|
773
|
+
dir: direction,
|
|
774
|
+
"data-form-element": "select",
|
|
775
|
+
children: [
|
|
776
|
+
shouldUseFloatingLabel && /* @__PURE__ */ jsx(FloatingLabel, { floating: isFloating, size, children: label }),
|
|
777
|
+
children
|
|
778
|
+
]
|
|
779
|
+
}
|
|
780
|
+
)
|
|
781
|
+
}
|
|
782
|
+
),
|
|
783
|
+
isFormControl && /* @__PURE__ */ jsx(
|
|
784
|
+
StyledNativeSelect,
|
|
785
|
+
{
|
|
786
|
+
multiple: true,
|
|
787
|
+
autoComplete: "off",
|
|
788
|
+
name,
|
|
789
|
+
tabIndex: -1,
|
|
790
|
+
"aria-hidden": "true",
|
|
791
|
+
ref: formElementRef,
|
|
792
|
+
required,
|
|
793
|
+
disabled,
|
|
794
|
+
"aria-disabled": ariaDisabled,
|
|
795
|
+
value,
|
|
796
|
+
onChange: () => {
|
|
797
|
+
},
|
|
798
|
+
children: value.length === 0 ? /* @__PURE__ */ jsx("option", { value: "" }) : (
|
|
799
|
+
// since we don't support autoComplete we can render here only selected values
|
|
800
|
+
value.map((itemValue) => /* @__PURE__ */ jsx("option", { value: itemValue, children: itemValue }, itemValue))
|
|
801
|
+
)
|
|
802
|
+
}
|
|
803
|
+
)
|
|
804
|
+
] });
|
|
210
805
|
}
|
|
211
806
|
);
|
|
212
807
|
const Combobox = React.forwardRef(
|
|
@@ -220,21 +815,33 @@ const Combobox = React.forwardRef(
|
|
|
220
815
|
required,
|
|
221
816
|
value,
|
|
222
817
|
defaultValue,
|
|
818
|
+
onOpen,
|
|
819
|
+
onClose,
|
|
820
|
+
onSearchValueChange,
|
|
821
|
+
onValueChange,
|
|
223
822
|
direction = "ltr",
|
|
823
|
+
autoFilter = true,
|
|
824
|
+
noResultsText,
|
|
224
825
|
...restProps
|
|
225
826
|
}, forwardRef) => /* @__PURE__ */ jsx(
|
|
226
827
|
ComboboxProvider,
|
|
227
828
|
{
|
|
228
829
|
defaultValue,
|
|
229
830
|
value,
|
|
831
|
+
onValueChange,
|
|
832
|
+
onSearchValueChange,
|
|
230
833
|
defaultOpen,
|
|
231
834
|
open,
|
|
835
|
+
onOpen,
|
|
836
|
+
onClose,
|
|
232
837
|
valid,
|
|
233
838
|
required,
|
|
234
839
|
disabled,
|
|
235
840
|
readOnly,
|
|
236
841
|
"aria-disabled": ariaDisabled,
|
|
237
842
|
direction,
|
|
843
|
+
autoFilter,
|
|
844
|
+
noResultsText,
|
|
238
845
|
children: /* @__PURE__ */ jsx(Root, { ...restProps, value, ref: forwardRef })
|
|
239
846
|
}
|
|
240
847
|
)
|
|
@@ -246,6 +853,11 @@ Combobox.Item = Item;
|
|
|
246
853
|
Combobox.Group = Group;
|
|
247
854
|
Combobox.GroupLabel = GroupLabel;
|
|
248
855
|
Combobox.Value = Value;
|
|
856
|
+
Combobox.Separator = Separator;
|
|
857
|
+
|
|
858
|
+
var types = /*#__PURE__*/Object.freeze({
|
|
859
|
+
__proto__: null
|
|
860
|
+
});
|
|
249
861
|
|
|
250
|
-
export { Combobox };
|
|
862
|
+
export { Combobox, types as ComboboxTypes };
|
|
251
863
|
//# sourceMappingURL=module.js.map
|