@mirohq/design-system-combobox 0.1.0-combobox.0 → 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/module.js CHANGED
@@ -1,17 +1,196 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
2
- import React from 'react';
3
- import { useHover } from '@react-aria/interactions';
4
- import { stringAttrValue, booleanishAttrValue } from '@mirohq/design-system-utils';
5
- import { useFormFieldContext } from '@mirohq/design-system-base-form';
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 { IconChevronDown, IconCheckMark } from '@mirohq/design-system-icons';
8
- import { styled } from '@mirohq/design-system-stitches';
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 * as Ariakit from '@ariakit/react';
11
- import { Group as Group$1, GroupLabel as GroupLabel$1 } from '@ariakit/react';
12
- import { Portal as Portal$1 } from '@radix-ui/react-popover';
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';
13
20
 
14
- const StyledTrigger = styled(Input, {});
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
+ });
55
+
56
+ const ComboboxContext = createContext({});
57
+ const ComboboxProvider = ({
58
+ children,
59
+ open: openProp,
60
+ defaultOpen,
61
+ onOpen,
62
+ onClose,
63
+ valid,
64
+ value: valueProp,
65
+ defaultValue: defaultValueProp,
66
+ onValueChange,
67
+ onSearchValueChange,
68
+ autoFilter = true,
69
+ ...restProps
70
+ }) => {
71
+ const triggerRef = useRef(null);
72
+ const inputRef = useRef(null);
73
+ const contentRef = useRef(null);
74
+ const [defaultValue, setDefaultValue] = useState(defaultValueProp);
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());
96
+ const { valid: formFieldValid } = useFormFieldContext();
97
+ return /* @__PURE__ */ jsx(
98
+ ComboboxContext.Provider,
99
+ {
100
+ value: {
101
+ ...restProps,
102
+ valid: valid != null ? valid : formFieldValid,
103
+ openState,
104
+ setOpenState,
105
+ value,
106
+ setValue,
107
+ setDefaultValue,
108
+ defaultValue,
109
+ onSearchValueChange,
110
+ triggerRef,
111
+ inputRef,
112
+ contentRef,
113
+ autoFilter,
114
+ searchValue,
115
+ setSearchValue,
116
+ filteredItems,
117
+ setFilteredItems,
118
+ itemValueTextMap,
119
+ setItemValueTextMap,
120
+ placeholder,
121
+ setPlaceholder,
122
+ size,
123
+ setSize
124
+ },
125
+ children
126
+ }
127
+ );
128
+ };
129
+ const useComboboxContext = () => useContext(ComboboxContext);
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
+ };
15
194
 
16
195
  const Trigger = React.forwardRef(
17
196
  ({
@@ -19,103 +198,666 @@ const Trigger = React.forwardRef(
19
198
  children,
20
199
  size = "large",
21
200
  "aria-describedby": ariaDescribedBy,
22
- onHoverChange,
23
- onHoverStart,
24
- onHoverEnd,
201
+ "aria-invalid": ariaInvalid,
202
+ placeholder,
203
+ openActionLabel,
204
+ closeActionLabel,
205
+ clearActionLabel,
206
+ onChange,
25
207
  ...restProps
26
208
  }, forwardRef) => {
209
+ const {
210
+ "aria-disabled": ariaDisabled,
211
+ valid: comboboxValid,
212
+ disabled,
213
+ value = [],
214
+ readOnly,
215
+ triggerRef,
216
+ inputRef,
217
+ onSearchValueChange,
218
+ searchValue,
219
+ setSearchValue,
220
+ setOpenState,
221
+ setSize,
222
+ setPlaceholder
223
+ } = useComboboxContext();
27
224
  const {
28
225
  formElementId,
29
- ariaDescribedBy: formFieldContextDescribedBy,
30
- ariaInvalid,
226
+ ariaInvalid: formFieldAriaInvalid,
31
227
  valid: formFieldValid
32
228
  } = useFormFieldContext();
33
- const valid = formFieldValid;
34
- const { hoverProps } = useHover({
35
- onHoverStart,
36
- onHoverEnd,
37
- onHoverChange: (isHovering) => {
38
- onHoverChange == null ? void 0 : onHoverChange(isHovering);
39
- }
40
- });
41
- const commonProps = {
229
+ useEffect(() => {
230
+ setSize(size);
231
+ }, [size, setSize]);
232
+ useEffect(() => {
233
+ setPlaceholder(placeholder);
234
+ }, [setPlaceholder, placeholder]);
235
+ const valid = formFieldValid != null ? formFieldValid : comboboxValid;
236
+ const inputProps = {
42
237
  ...restProps,
43
- ref: forwardRef,
44
- "aria-invalid": ariaInvalid,
45
- "aria-describedby": stringAttrValue(
46
- ariaDescribedBy,
47
- formFieldContextDescribedBy
48
- ),
238
+ "aria-disabled": ariaDisabled,
239
+ "aria-invalid": ariaInvalid != null ? ariaInvalid : formFieldAriaInvalid,
240
+ // todo MDS-1011: use formFieldContextDescribedBy after removing form context from BaseInput
241
+ "aria-describedby": ariaDescribedBy,
49
242
  valid,
243
+ disabled,
244
+ readOnly,
50
245
  invalid: booleanishAttrValue(valid),
51
- id: id != null ? id : formElementId
246
+ id: id != null ? id : formElementId,
247
+ placeholder: value.length === 0 ? placeholder : void 0
52
248
  };
53
- const variants = {
54
- size
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
+ }
55
262
  };
56
- return /* @__PURE__ */ jsxs(StyledTrigger, { ...hoverProps, ...commonProps, ...variants, children: [
57
- children,
58
- /* @__PURE__ */ jsx(Input.ActionButton, { label: "custom label", children: /* @__PURE__ */ jsx(IconChevronDown, { size: "small", weight: "thin" }) })
59
- ] });
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);
267
+ };
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
+ );
60
307
  }
61
308
  );
62
309
 
63
- const StyledContent = styled(Primitive.div, {
310
+ const NoResultPlaceholder = styled(Primitive.div, {
311
+ padding: "$100"
312
+ });
313
+ const StyledContent = styled(RadixPopover.Content, {
64
314
  backgroundColor: "$background-neutrals-container",
65
315
  borderRadius: "$50",
66
316
  boxShadow: "$50",
67
- minWidth: "var(--radix-select-trigger-width)",
317
+ fontSize: "$175",
318
+ fontWeight: "normal",
319
+ lineHeight: "20px",
320
+ width: "var(--radix-popover-trigger-width)",
321
+ zIndex: "$select",
322
+ overflowY: "auto",
68
323
  padding: "$50",
69
- zIndex: "$select"
324
+ boxSizing: "border-box",
325
+ outline: "1px solid transparent"
70
326
  });
71
327
 
72
- const Content = React.forwardRef(({ children, ...restProps }, forwardRef) => /* @__PURE__ */ jsx(StyledContent, { ...restProps, ref: forwardRef, children }));
73
-
74
- const StyledItem = styled(Primitive.div, {
328
+ const StyledItemCheck = styled(Primitive.span, {
329
+ color: "$icon-primary"
330
+ });
331
+ const StyledItem = styled(ComboboxItem, {
332
+ display: "grid",
333
+ gridTemplateColumns: "20px 1fr",
75
334
  borderRadius: "$50",
76
335
  boxSizing: "border-box",
77
336
  color: "$text-neutrals",
78
337
  cursor: "pointer",
79
338
  fontSize: "$175",
80
- lineHeight: 1.5,
339
+ lineHeight: "20px",
81
340
  position: "relative",
82
341
  userSelect: "none",
83
- padding: "6px 0",
84
- paddingInline: "$150 $100",
85
- '&:hover:not([aria-disabled="true"])': {
86
- background: "$background-primary-subtle-hover",
87
- color: "$text-primary-hover"
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"
88
364
  }
89
365
  });
90
366
 
91
- const StyledItemIndicator = styled(Ariakit.ComboboxItemCheck, {});
92
-
93
367
  const Item = React.forwardRef(
94
- ({ textValue, children, ...restProps }, forwardRef) => /* @__PURE__ */ jsxs(StyledItem, { ref: forwardRef, ...restProps, children: [
95
- /* @__PURE__ */ jsx(StyledItemIndicator, { children: /* @__PURE__ */ jsx(IconCheckMark, { size: "small" }) }),
96
- children
97
- ] })
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
+ );
565
+ }
98
566
  );
99
567
 
100
568
  const Portal = (props) => /* @__PURE__ */ jsx(Portal$1, { ...props });
101
569
 
102
- const StyledGroup = styled(Group$1, {});
570
+ const StyledGroup = styled(Group$1);
103
571
 
104
- const Group = React.forwardRef((props, forwardRef) => /* @__PURE__ */ jsx(StyledGroup, { ...props, ref: 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
+ });
105
592
 
106
- const StyledGroupLabel = styled(GroupLabel$1, {});
593
+ const StyledGroupLabel = styled(GroupLabel$1, {
594
+ padding: "6px $100",
595
+ color: "$text-neutrals-subtle"
596
+ });
107
597
 
108
598
  const GroupLabel = React.forwardRef((props, forwardRef) => /* @__PURE__ */ jsx(StyledGroupLabel, { ...props, ref: forwardRef }));
109
599
 
110
- const StyledCombobox = styled(Primitive.div, {});
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
+ });
111
624
 
112
- const Combobox = React.forwardRef((props, forwardRef) => /* @__PURE__ */ jsx(StyledCombobox, { ...props, ref: forwardRef }));
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
+ ] })
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
+ };
680
+
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
+ });
704
+
705
+ const Root = React.forwardRef(
706
+ ({ value: valueProp, onValueChange, name, children, ...restProps }, forwardRef) => {
707
+ var _a;
708
+ const {
709
+ openState,
710
+ setOpenState,
711
+ defaultValue,
712
+ value = [],
713
+ setValue,
714
+ required,
715
+ readOnly,
716
+ "aria-disabled": ariaDisabled,
717
+ disabled,
718
+ direction,
719
+ size,
720
+ placeholder,
721
+ triggerRef
722
+ } = useComboboxContext();
723
+ const {
724
+ setRequired,
725
+ setDisabled,
726
+ setAriaDisabled,
727
+ setReadOnly,
728
+ label,
729
+ isFloatingLabel,
730
+ focused,
731
+ formElementRef
732
+ } = useFormFieldContext();
733
+ useEffect(() => {
734
+ setRequired == null ? void 0 : setRequired(required);
735
+ setDisabled == null ? void 0 : setDisabled(disabled);
736
+ setAriaDisabled == null ? void 0 : setAriaDisabled(ariaDisabled);
737
+ setReadOnly == null ? void 0 : setReadOnly(readOnly);
738
+ }, [
739
+ readOnly,
740
+ disabled,
741
+ ariaDisabled,
742
+ required,
743
+ setRequired,
744
+ setDisabled,
745
+ setAriaDisabled,
746
+ setReadOnly
747
+ ]);
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
+ ] });
805
+ }
806
+ );
807
+ const Combobox = React.forwardRef(
808
+ ({
809
+ "aria-disabled": ariaDisabled,
810
+ defaultOpen = false,
811
+ open,
812
+ valid,
813
+ disabled,
814
+ readOnly,
815
+ required,
816
+ value,
817
+ defaultValue,
818
+ onOpen,
819
+ onClose,
820
+ onSearchValueChange,
821
+ onValueChange,
822
+ direction = "ltr",
823
+ autoFilter = true,
824
+ noResultsText,
825
+ ...restProps
826
+ }, forwardRef) => /* @__PURE__ */ jsx(
827
+ ComboboxProvider,
828
+ {
829
+ defaultValue,
830
+ value,
831
+ onValueChange,
832
+ onSearchValueChange,
833
+ defaultOpen,
834
+ open,
835
+ onOpen,
836
+ onClose,
837
+ valid,
838
+ required,
839
+ disabled,
840
+ readOnly,
841
+ "aria-disabled": ariaDisabled,
842
+ direction,
843
+ autoFilter,
844
+ noResultsText,
845
+ children: /* @__PURE__ */ jsx(Root, { ...restProps, value, ref: forwardRef })
846
+ }
847
+ )
848
+ );
113
849
  Combobox.Portal = Portal;
114
850
  Combobox.Trigger = Trigger;
115
851
  Combobox.Content = Content;
116
852
  Combobox.Item = Item;
117
853
  Combobox.Group = Group;
118
854
  Combobox.GroupLabel = GroupLabel;
855
+ Combobox.Value = Value;
856
+ Combobox.Separator = Separator;
857
+
858
+ var types = /*#__PURE__*/Object.freeze({
859
+ __proto__: null
860
+ });
119
861
 
120
- export { Combobox };
862
+ export { Combobox, types as ComboboxTypes };
121
863
  //# sourceMappingURL=module.js.map