@mirohq/design-system-combobox 0.1.0-combobox.4 → 0.1.0-combobox.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/main.js CHANGED
@@ -6,11 +6,14 @@ var jsxRuntime = require('react/jsx-runtime');
6
6
  var React = require('react');
7
7
  var react = require('@ariakit/react');
8
8
  var designSystemBaseForm = require('@mirohq/design-system-base-form');
9
- var RadixPopover = require('@radix-ui/react-popover');
10
9
  var designSystemUtils = require('@mirohq/design-system-utils');
10
+ var RadixPopover = require('@radix-ui/react-popover');
11
11
  var designSystemStitches = require('@mirohq/design-system-stitches');
12
12
  var designSystemInput = require('@mirohq/design-system-input');
13
+ var reactUseControllableState = require('@radix-ui/react-use-controllable-state');
13
14
  var designSystemIcons = require('@mirohq/design-system-icons');
15
+ var designSystemScrollArea = require('@mirohq/design-system-scroll-area');
16
+ var utils = require('@react-aria/utils');
14
17
  var designSystemUseAriaDisabled = require('@mirohq/design-system-use-aria-disabled');
15
18
  var designSystemStyles = require('@mirohq/design-system-styles');
16
19
  var designSystemPrimitive = require('@mirohq/design-system-primitive');
@@ -43,24 +46,29 @@ const StyledInput = designSystemStitches.styled(designSystemInput.Input, {
43
46
  flexWrap: "wrap",
44
47
  flexGrow: 1,
45
48
  gap: "0 $50",
46
- "&&&": {
47
- height: "max-content"
49
+ overflowY: "scroll",
50
+ "&[data-valid], &[data-invalid]": {
51
+ // we don't need a bigger padding here as Input component will render its own icon
52
+ paddingRight: "$100"
48
53
  },
49
54
  "& input": {
50
- minWidth: "30px",
55
+ minWidth: "$8",
51
56
  flexBasis: 0,
52
- flexGrow: 1,
53
- marginRight: "$300"
57
+ flexGrow: 1
54
58
  },
55
59
  variants: {
56
60
  size: {
57
61
  large: {
58
62
  minHeight: "$10",
59
- padding: "5px $100"
63
+ height: "auto",
64
+ padding: "5px $100",
65
+ paddingRight: "$500"
60
66
  },
61
67
  "x-large": {
62
68
  minHeight: "$12",
63
- padding: "9px $150"
69
+ height: "auto",
70
+ padding: "5px $100",
71
+ paddingRight: "$500"
64
72
  }
65
73
  }
66
74
  },
@@ -72,19 +80,38 @@ const StyledInput = designSystemStitches.styled(designSystemInput.Input, {
72
80
  const ComboboxContext = React.createContext({});
73
81
  const ComboboxProvider = ({
74
82
  children,
83
+ open: openProp,
75
84
  defaultOpen,
85
+ onOpen,
86
+ onClose,
76
87
  valid,
77
88
  value: valueProp,
78
89
  defaultValue: defaultValueProp,
90
+ onValueChange,
79
91
  onSearchValueChange,
80
92
  autoFilter = true,
81
93
  ...restProps
82
94
  }) => {
83
95
  const triggerRef = React.useRef(null);
96
+ const inputRef = React.useRef(null);
84
97
  const contentRef = React.useRef(null);
85
- const [openState, setOpenState] = React.useState(defaultOpen != null ? defaultOpen : false);
86
- const [value, setValue] = React.useState(valueProp != null ? valueProp : []);
87
98
  const [defaultValue, setDefaultValue] = React.useState(defaultValueProp);
99
+ const [openState = false, setOpenState] = reactUseControllableState.useControllableState({
100
+ prop: openProp,
101
+ defaultProp: defaultOpen,
102
+ onChange: (state) => {
103
+ if (state) {
104
+ onOpen == null ? void 0 : onOpen();
105
+ } else {
106
+ onClose == null ? void 0 : onClose();
107
+ }
108
+ }
109
+ });
110
+ const [value, setValue] = reactUseControllableState.useControllableState({
111
+ prop: valueProp,
112
+ defaultProp: defaultValueProp,
113
+ onChange: onValueChange
114
+ });
88
115
  const [filteredItems, setFilteredItems] = React.useState(/* @__PURE__ */ new Set());
89
116
  const [searchValue, setSearchValue] = React.useState("");
90
117
  const { valid: formFieldValid } = designSystemBaseForm.useFormFieldContext();
@@ -102,6 +129,7 @@ const ComboboxProvider = ({
102
129
  defaultValue,
103
130
  onSearchValueChange,
104
131
  triggerRef,
132
+ inputRef,
105
133
  contentRef,
106
134
  autoFilter,
107
135
  searchValue,
@@ -138,8 +166,8 @@ const TriggerActionButton = ({
138
166
  clearActionLabel,
139
167
  size
140
168
  }) => {
141
- const { setOpenState, value, setValue } = useComboboxContext();
142
- const isEmpty = value === void 0 || value.length === 0;
169
+ const { setOpenState, value = [], setValue } = useComboboxContext();
170
+ const isEmpty = value.length === 0;
143
171
  const ActionButtonIcon = isEmpty ? designSystemIcons.IconChevronDown : designSystemIcons.IconCross;
144
172
  const label = isEmpty ? openActionLabel : clearActionLabel;
145
173
  const onActionButtonClick = React.useCallback(
@@ -147,7 +175,7 @@ const TriggerActionButton = ({
147
175
  if (!isEmpty) {
148
176
  setValue([]);
149
177
  } else {
150
- setOpenState((prevOpen) => !prevOpen);
178
+ setOpenState((prevOpen = false) => !prevOpen);
151
179
  }
152
180
  event.stopPropagation();
153
181
  },
@@ -173,8 +201,9 @@ const Trigger = React__default["default"].forwardRef(
173
201
  "aria-disabled": ariaDisabled,
174
202
  valid: comboboxValid,
175
203
  disabled,
176
- value,
204
+ value = [],
177
205
  triggerRef,
206
+ inputRef,
178
207
  onSearchValueChange,
179
208
  searchValue,
180
209
  setSearchValue
@@ -201,41 +230,59 @@ const Trigger = React__default["default"].forwardRef(
201
230
  disabled,
202
231
  invalid: designSystemUtils.booleanishAttrValue(valid),
203
232
  id: id != null ? id : formElementId,
204
- placeholder: (value == null ? void 0 : value.length) === 0 ? placeholder : void 0
233
+ placeholder: value.length === 0 ? placeholder : void 0
205
234
  };
206
235
  const shouldUseFloatingLabel = label !== null && isFloatingLabel;
207
- const isFloating = placeholder !== void 0 || (value == null ? void 0 : value.length) !== 0 || focused;
236
+ const isFloating = placeholder !== void 0 || value.length !== 0 || focused;
237
+ const scrollIntoView = (event) => {
238
+ var _a;
239
+ const trigger = triggerRef == null ? void 0 : triggerRef.current;
240
+ const baseInput = (_a = inputRef == null ? void 0 : inputRef.current) == null ? void 0 : _a.parentElement;
241
+ if (baseInput != null && trigger != null) {
242
+ event.preventDefault();
243
+ baseInput.scrollTo({
244
+ top: trigger.scrollHeight
245
+ });
246
+ }
247
+ if (restProps.onFocus !== void 0) {
248
+ restProps.onFocus(event);
249
+ }
250
+ };
208
251
  const onInputChange = (e) => {
209
252
  setSearchValue(e.target.value);
210
253
  onSearchValueChange == null ? void 0 : onSearchValueChange(e.target.value);
211
254
  onChange == null ? void 0 : onChange(e);
212
255
  };
213
- return /* @__PURE__ */ jsxRuntime.jsx(RadixPopover__namespace.Anchor, { ref: designSystemUtils.mergeRefs([triggerRef, forwardRef]), children: /* @__PURE__ */ jsxRuntime.jsx(
214
- react.Combobox,
215
- {
216
- render: /* @__PURE__ */ jsxRuntime.jsxs(
217
- StyledInput,
218
- {
219
- value: searchValue,
220
- onChange: onInputChange,
221
- ...inputProps,
222
- size,
223
- children: [
224
- shouldUseFloatingLabel && /* @__PURE__ */ jsxRuntime.jsx(designSystemBaseForm.FloatingLabel, { floating: isFloating, size, children: label }),
225
- children,
226
- /* @__PURE__ */ jsxRuntime.jsx(
227
- TriggerActionButton,
228
- {
229
- openActionLabel,
230
- clearActionLabel,
231
- size
232
- }
233
- )
234
- ]
235
- }
236
- )
237
- }
238
- ) });
256
+ return /* @__PURE__ */ jsxRuntime.jsxs(RadixPopover__namespace.Anchor, { ref: designSystemUtils.mergeRefs([triggerRef, forwardRef]), children: [
257
+ shouldUseFloatingLabel && /* @__PURE__ */ jsxRuntime.jsx(designSystemBaseForm.FloatingLabel, { floating: isFloating, size, children: label }),
258
+ /* @__PURE__ */ jsxRuntime.jsx(
259
+ react.Combobox,
260
+ {
261
+ render: /* @__PURE__ */ jsxRuntime.jsxs(
262
+ StyledInput,
263
+ {
264
+ ...inputProps,
265
+ value: searchValue,
266
+ size,
267
+ ref: inputRef,
268
+ onChange: onInputChange,
269
+ onFocus: scrollIntoView,
270
+ children: [
271
+ children,
272
+ /* @__PURE__ */ jsxRuntime.jsx(
273
+ TriggerActionButton,
274
+ {
275
+ openActionLabel,
276
+ clearActionLabel,
277
+ size
278
+ }
279
+ )
280
+ ]
281
+ }
282
+ )
283
+ }
284
+ )
285
+ ] });
239
286
  }
240
287
  );
241
288
 
@@ -249,7 +296,6 @@ const StyledContent = designSystemStitches.styled(RadixPopover__namespace.Conten
249
296
  width: "var(--radix-popover-trigger-width)",
250
297
  zIndex: "$select",
251
298
  overflowY: "auto",
252
- marginTop: "$200",
253
299
  padding: "$150",
254
300
  boxSizing: "border-box",
255
301
  outline: "1px solid transparent"
@@ -295,20 +341,31 @@ const StyledItem = designSystemStitches.styled(react.ComboboxItem, {
295
341
  const Item = React__default["default"].forwardRef(
296
342
  ({ disabled = false, value, textValue, children, ...restProps }, forwardRef) => {
297
343
  const { "aria-disabled": ariaDisabled, ...restAriaDisabledProps } = designSystemUseAriaDisabled.useAriaDisabled(restProps, { allowArrows: true });
298
- const { autoFilter, filteredItems } = useComboboxContext();
344
+ const { autoFilter, filteredItems, triggerRef, inputRef } = useComboboxContext();
299
345
  if (autoFilter !== false && !filteredItems.has(value)) {
300
346
  return null;
301
347
  }
348
+ const scrollIntoView = (event) => {
349
+ var _a;
350
+ if (((_a = inputRef == null ? void 0 : inputRef.current) == null ? void 0 : _a.parentElement) != null && (triggerRef == null ? void 0 : triggerRef.current) != null) {
351
+ inputRef.current.parentElement.scrollTo({
352
+ top: triggerRef.current.scrollHeight
353
+ });
354
+ }
355
+ if (restProps.onClick !== void 0) {
356
+ restProps.onClick(event);
357
+ }
358
+ };
302
359
  return /* @__PURE__ */ jsxRuntime.jsxs(
303
360
  StyledItem,
304
361
  {
305
- ...restProps,
306
- ...restAriaDisabledProps,
362
+ ...utils.mergeProps(restProps, restAriaDisabledProps),
307
363
  focusable: true,
308
364
  accessibleWhenDisabled: ariaDisabled === true,
309
365
  disabled: ariaDisabled === true || disabled,
310
366
  ref: forwardRef,
311
367
  value,
368
+ onClick: scrollIntoView,
312
369
  children: [
313
370
  children,
314
371
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -318,7 +375,13 @@ const Item = React__default["default"].forwardRef(
318
375
  // AriakitComboboxItemCheck adds its owm inline styles which we want to omit here
319
376
  /* @__PURE__ */ jsxRuntime.jsx(StyledItemCheck, { ...props })
320
377
  ),
321
- children: /* @__PURE__ */ jsxRuntime.jsx(designSystemIcons.IconCheckMark, { size: "small" })
378
+ children: /* @__PURE__ */ jsxRuntime.jsx(
379
+ designSystemIcons.IconCheckMark,
380
+ {
381
+ size: "small",
382
+ "data-testid": process.env.NODE_ENV === "test" ? "combobox-item-check" : void 0
383
+ }
384
+ )
322
385
  }
323
386
  )
324
387
  ]
@@ -349,48 +412,65 @@ const getChildrenItemValues = (componentChildren) => {
349
412
  return values;
350
413
  };
351
414
 
415
+ const CONTENT_OFFSET = parseInt(designSystemStitches.theme.space[50]);
352
416
  const isInsideRef = (element, ref) => {
353
417
  var _a, _b;
354
418
  return (_b = element != null && ((_a = ref.current) == null ? void 0 : _a.contains(element))) != null ? _b : false;
355
419
  };
356
- const Content = React__default["default"].forwardRef(({ children, ...restProps }, forwardRef) => {
357
- const {
358
- triggerRef,
359
- contentRef,
360
- autoFilter,
361
- filteredItems,
362
- setFilteredItems,
363
- searchValue,
364
- noResultsText
365
- } = useComboboxContext();
366
- React.useEffect(() => {
367
- const childrenItemValues = getChildrenItemValues(children);
368
- setFilteredItems(
369
- new Set(
370
- autoFilter === false ? childrenItemValues : childrenItemValues.filter(
371
- (child) => child.toLowerCase().includes(searchValue.toLowerCase())
420
+ const Content = React__default["default"].forwardRef(
421
+ ({ sideOffset = CONTENT_OFFSET, maxHeight, children, ...restProps }, forwardRef) => {
422
+ const {
423
+ triggerRef,
424
+ contentRef,
425
+ autoFilter,
426
+ filteredItems,
427
+ setFilteredItems,
428
+ searchValue,
429
+ noResultsText,
430
+ direction
431
+ } = useComboboxContext();
432
+ React.useEffect(() => {
433
+ const childrenItemValues = getChildrenItemValues(children);
434
+ setFilteredItems(
435
+ new Set(
436
+ autoFilter === false ? childrenItemValues : childrenItemValues.filter(
437
+ (child) => child.toLowerCase().includes(searchValue.toLowerCase())
438
+ )
372
439
  )
373
- )
440
+ );
441
+ }, [children, autoFilter, setFilteredItems, searchValue]);
442
+ const content = filteredItems.size === 0 ? noResultsText : children;
443
+ return /* @__PURE__ */ jsxRuntime.jsx(
444
+ StyledContent,
445
+ {
446
+ ...restProps,
447
+ sideOffset,
448
+ ref: designSystemUtils.mergeRefs([forwardRef, contentRef]),
449
+ onOpenAutoFocus: (event) => event.preventDefault(),
450
+ onInteractOutside: (event) => {
451
+ const target = event.target;
452
+ const isTrigger = isInsideRef(target, triggerRef);
453
+ const isContent = isInsideRef(target, contentRef);
454
+ if (isTrigger || isContent) {
455
+ event.preventDefault();
456
+ }
457
+ },
458
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystemScrollArea.ScrollArea, { type: "always", dir: direction, children: [
459
+ /* @__PURE__ */ jsxRuntime.jsx(
460
+ designSystemScrollArea.ScrollArea.Viewport,
461
+ {
462
+ availableHeight: "var(--radix-popover-content-available-height)",
463
+ verticalGap: "calc(var(--space-150) * 2)",
464
+ maxHeight,
465
+ children: content
466
+ }
467
+ ),
468
+ /* @__PURE__ */ jsxRuntime.jsx(designSystemScrollArea.ScrollArea.Scrollbar, { orientation: "vertical", children: /* @__PURE__ */ jsxRuntime.jsx(designSystemScrollArea.ScrollArea.Thumb, {}) })
469
+ ] })
470
+ }
374
471
  );
375
- }, [children, autoFilter, setFilteredItems, searchValue]);
376
- return /* @__PURE__ */ jsxRuntime.jsx(
377
- StyledContent,
378
- {
379
- ...restProps,
380
- ref: designSystemUtils.mergeRefs([forwardRef, contentRef]),
381
- onOpenAutoFocus: (event) => event.preventDefault(),
382
- onInteractOutside: (event) => {
383
- const target = event.target;
384
- const isTrigger = isInsideRef(target, triggerRef);
385
- const isContent = isInsideRef(target, contentRef);
386
- if (isTrigger || isContent) {
387
- event.preventDefault();
388
- }
389
- },
390
- children: filteredItems.size === 0 ? noResultsText : children
391
- }
392
- );
393
- });
472
+ }
473
+ );
394
474
 
395
475
  const Portal = (props) => /* @__PURE__ */ jsxRuntime.jsx(RadixPopover.Portal, { ...props });
396
476
 
@@ -470,17 +550,16 @@ const StyledValue = designSystemStitches.styled(Chip, {
470
550
 
471
551
  const Value = ({ removeChipAriaLabel }) => {
472
552
  const {
473
- value,
553
+ value = [],
474
554
  setValue,
475
555
  disabled,
476
556
  "aria-disabled": ariaDisabled
477
557
  } = useComboboxContext();
478
- const isEmpty = value === void 0 || value.length === 0;
479
558
  const isDisabled = ariaDisabled === true || disabled;
480
559
  const onItemRemove = (item) => {
481
- setValue((prevValue) => prevValue.filter((value2) => value2 !== item));
560
+ setValue((prevValue) => prevValue == null ? void 0 : prevValue.filter((value2) => value2 !== item));
482
561
  };
483
- if (isEmpty) {
562
+ if (value.length === 0) {
484
563
  return null;
485
564
  }
486
565
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: value.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -489,6 +568,7 @@ const Value = ({ removeChipAriaLabel }) => {
489
568
  onRemove: () => onItemRemove(item),
490
569
  disabled: isDisabled,
491
570
  removeChipAriaLabel,
571
+ "data-testid": process.env.NODE_ENV === "test" ? "combobox-value-".concat(item) : void 0,
492
572
  children: item
493
573
  },
494
574
  item
@@ -502,9 +582,18 @@ const StyledSeparator = designSystemStitches.styled(designSystemPrimitive.Primit
502
582
  margin: "$100 0"
503
583
  });
504
584
 
505
- const Separator = React__default["default"].forwardRef((props, forwardRef) => /* @__PURE__ */ jsxRuntime.jsx(StyledSeparator, { ...props, ref: forwardRef, "aria-hidden": true }));
585
+ const Separator = React__default["default"].forwardRef((props, forwardRef) => {
586
+ const { autoFilter, searchValue } = useComboboxContext();
587
+ if (autoFilter === true && searchValue.length > 0) {
588
+ return null;
589
+ }
590
+ return /* @__PURE__ */ jsxRuntime.jsx(StyledSeparator, { ...props, ref: forwardRef, "aria-hidden": true });
591
+ });
506
592
 
507
- const StyledComboboxContent = designSystemStitches.styled(designSystemPrimitive.Primitive.div, {});
593
+ const StyledComboboxContent = designSystemStitches.styled(designSystemPrimitive.Primitive.div, {
594
+ position: "relative",
595
+ width: "100%"
596
+ });
508
597
 
509
598
  const Root = React__default["default"].forwardRef(({ value: valueProp, onValueChange, children, ...restProps }, forwardRef) => {
510
599
  const {
@@ -536,8 +625,18 @@ const Root = React__default["default"].forwardRef(({ value: valueProp, onValueCh
536
625
  setReadOnly
537
626
  ]);
538
627
  const onSetSelectedValue = (newValue) => {
539
- onValueChange == null ? void 0 : onValueChange(newValue);
540
- setValue(newValue);
628
+ setValue(typeof newValue === "string" ? [newValue] : newValue);
629
+ };
630
+ const comboboxProps = {
631
+ ...restProps,
632
+ onClick: (event) => {
633
+ if (!designSystemUtils.booleanify(disabled)) {
634
+ setOpenState(true);
635
+ }
636
+ if (restProps.onClick !== void 0) {
637
+ restProps.onClick(event);
638
+ }
639
+ }
541
640
  };
542
641
  return /* @__PURE__ */ jsxRuntime.jsx(RadixPopover__namespace.Root, { open: openState, onOpenChange: setOpenState, children: /* @__PURE__ */ jsxRuntime.jsx(
543
642
  react.ComboboxProvider,
@@ -547,7 +646,15 @@ const Root = React__default["default"].forwardRef(({ value: valueProp, onValueCh
547
646
  defaultSelectedValue: defaultValue,
548
647
  selectedValue: value,
549
648
  setSelectedValue: onSetSelectedValue,
550
- children: /* @__PURE__ */ jsxRuntime.jsx(StyledComboboxContent, { ...restProps, ref: forwardRef, dir: direction, children })
649
+ children: /* @__PURE__ */ jsxRuntime.jsx(
650
+ StyledComboboxContent,
651
+ {
652
+ ...comboboxProps,
653
+ ref: forwardRef,
654
+ dir: direction,
655
+ children
656
+ }
657
+ )
551
658
  }
552
659
  ) });
553
660
  });
@@ -562,8 +669,9 @@ const Combobox = React__default["default"].forwardRef(
562
669
  required,
563
670
  value,
564
671
  defaultValue,
565
- onSearchValueChange,
566
672
  onOpen,
673
+ onClose,
674
+ onSearchValueChange,
567
675
  onValueChange,
568
676
  direction = "ltr",
569
677
  autoFilter = true,
@@ -574,9 +682,12 @@ const Combobox = React__default["default"].forwardRef(
574
682
  {
575
683
  defaultValue,
576
684
  value,
685
+ onValueChange,
577
686
  onSearchValueChange,
578
687
  defaultOpen,
579
688
  open,
689
+ onOpen,
690
+ onClose,
580
691
  valid,
581
692
  required,
582
693
  disabled,
@@ -585,15 +696,7 @@ const Combobox = React__default["default"].forwardRef(
585
696
  direction,
586
697
  autoFilter,
587
698
  noResultsText,
588
- children: /* @__PURE__ */ jsxRuntime.jsx(
589
- Root,
590
- {
591
- ...restProps,
592
- noResultsText,
593
- value,
594
- ref: forwardRef
595
- }
596
- )
699
+ children: /* @__PURE__ */ jsxRuntime.jsx(Root, { ...restProps, value, ref: forwardRef })
597
700
  }
598
701
  )
599
702
  );