@mirohq/design-system-combobox 0.3.6 → 0.4.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.
package/dist/main.js CHANGED
@@ -16,11 +16,11 @@ var designSystemIcons = require('@mirohq/design-system-icons');
16
16
  var designSystemScrollArea = require('@mirohq/design-system-scroll-area');
17
17
  var designSystemBaseSelect = require('@mirohq/design-system-base-select');
18
18
  var designSystemPrimitive = require('@mirohq/design-system-primitive');
19
- var designSystemUseAriaDisabled = require('@mirohq/design-system-use-aria-disabled');
20
- var designSystemUseLayoutEffect = require('@mirohq/design-system-use-layout-effect');
21
19
  var reactDom = require('react-dom');
22
- var designSystemBaseButton = require('@mirohq/design-system-base-button');
23
- var designSystemStyles = require('@mirohq/design-system-styles');
20
+ var designSystemUseLayoutEffect = require('@mirohq/design-system-use-layout-effect');
21
+ var designSystemUseAriaDisabled = require('@mirohq/design-system-use-aria-disabled');
22
+ var designSystemUseId = require('@mirohq/design-system-use-id');
23
+ var designSystemChip = require('@mirohq/design-system-chip');
24
24
 
25
25
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
26
26
 
@@ -60,6 +60,13 @@ const StyledBaseInput = designSystemStitches.styled(designSystemBaseInput.BaseIn
60
60
  },
61
61
  variants: {
62
62
  size: {
63
+ medium: {
64
+ minHeight: "$8",
65
+ height: "auto",
66
+ padding: "5px $100",
67
+ paddingRight: "$500",
68
+ fontSize: "$175"
69
+ },
63
70
  large: {
64
71
  minHeight: "$10",
65
72
  height: "auto",
@@ -79,6 +86,10 @@ const StyledBaseInput = designSystemStitches.styled(designSystemBaseInput.BaseIn
79
86
  }
80
87
  });
81
88
 
89
+ function searchQueryMatch(displayedText, searchValue) {
90
+ return displayedText.toLowerCase().includes(searchValue.toLowerCase());
91
+ }
92
+
82
93
  const ComboboxContext = React.createContext({});
83
94
  const ComboboxProvider = ({
84
95
  children,
@@ -92,13 +103,12 @@ const ComboboxProvider = ({
92
103
  onValueChange,
93
104
  searchValue: searchValueProp,
94
105
  onSearchValueChange,
95
- autoFilter = true,
106
+ autoFilter,
96
107
  ...restProps
97
108
  }) => {
98
109
  const triggerRef = React.useRef(null);
99
110
  const inputRef = React.useRef(null);
100
111
  const contentRef = React.useRef(null);
101
- const [defaultValue, setDefaultValue] = React.useState(defaultValueProp);
102
112
  const [openState = false, setOpenState] = reactUseControllableState.useControllableState({
103
113
  prop: openProp,
104
114
  defaultProp: defaultOpen,
@@ -115,16 +125,23 @@ const ComboboxProvider = ({
115
125
  defaultProp: defaultValueProp,
116
126
  onChange: onValueChange
117
127
  });
118
- const [filteredItems, setFilteredItems] = React.useState(/* @__PURE__ */ new Set());
119
- const [searchValue, setSearchValue] = reactUseControllableState.useControllableState({
128
+ const [searchValue = "", setSearchValue] = reactUseControllableState.useControllableState({
120
129
  prop: searchValueProp,
121
130
  defaultProp: "",
122
131
  onChange: onSearchValueChange
123
132
  });
124
133
  const [size, setSize] = React.useState();
125
134
  const [placeholder, setPlaceholder] = React.useState();
126
- const [itemValueTextMap, setItemValueTextMap] = React.useState(/* @__PURE__ */ new Map());
135
+ const [itemsMap, setItemsMap] = React.useState(/* @__PURE__ */ new Map());
127
136
  const { valid: formFieldValid } = designSystemBaseForm.useFormFieldContext();
137
+ const filteredItems = React.useMemo(() => {
138
+ if (searchValue.length > 0) {
139
+ return Array.from(itemsMap.values()).filter(
140
+ (item) => searchQueryMatch(item.displayedText, searchValue)
141
+ );
142
+ }
143
+ return [];
144
+ }, [itemsMap, searchValue]);
128
145
  return /* @__PURE__ */ jsxRuntime.jsx(
129
146
  ComboboxContext.Provider,
130
147
  {
@@ -135,18 +152,15 @@ const ComboboxProvider = ({
135
152
  setOpenState,
136
153
  value,
137
154
  setValue,
138
- setDefaultValue,
139
- defaultValue,
140
155
  triggerRef,
141
156
  inputRef,
142
157
  contentRef,
143
158
  autoFilter,
144
159
  searchValue,
145
160
  setSearchValue,
161
+ itemsMap,
162
+ setItemsMap,
146
163
  filteredItems,
147
- setFilteredItems,
148
- itemValueTextMap,
149
- setItemValueTextMap,
150
164
  placeholder,
151
165
  setPlaceholder,
152
166
  size,
@@ -163,6 +177,9 @@ const StyledActionButton = designSystemStitches.styled(designSystemBaseInput.Bas
163
177
  right: "$100",
164
178
  variants: {
165
179
  size: {
180
+ medium: {
181
+ top: "3px"
182
+ },
166
183
  large: {
167
184
  top: "5px"
168
185
  },
@@ -233,7 +250,7 @@ const Trigger = React__default["default"].forwardRef(
233
250
  placeholder,
234
251
  openActionLabel,
235
252
  closeActionLabel,
236
- clearable,
253
+ clearable = true,
237
254
  clearActionLabel,
238
255
  onChange,
239
256
  onFocus,
@@ -359,103 +376,6 @@ const StyledContent = designSystemStitches.styled(RadixPopover__namespace.Conten
359
376
  boxSizing: "border-box"
360
377
  });
361
378
 
362
- const StyledItem = designSystemStitches.styled(react.ComboboxItem, designSystemBaseSelect.itemStyles);
363
-
364
- const Item = React__default["default"].forwardRef(
365
- ({ disabled = false, value, textValue, children, ...restProps }, forwardRef) => {
366
- const { "aria-disabled": ariaDisabled, ...restAriaDisabledProps } = designSystemUseAriaDisabled.useAriaDisabled(restProps, { allowArrows: true });
367
- const {
368
- autoFilter,
369
- filteredItems,
370
- setItemValueTextMap,
371
- triggerRef,
372
- inputRef,
373
- value: comboboxValue = []
374
- } = useComboboxContext();
375
- designSystemUseLayoutEffect.useLayoutEffect(() => {
376
- const textToSet = textValue !== void 0 ? textValue : typeof children === "string" ? children : "";
377
- setItemValueTextMap((prevState) => new Map(prevState.set(value, textToSet)));
378
- return () => {
379
- setItemValueTextMap((prevState) => {
380
- prevState.delete(value);
381
- return new Map(prevState);
382
- });
383
- };
384
- }, [setItemValueTextMap, value, textValue, children]);
385
- if (autoFilter !== false && !filteredItems.has(value)) {
386
- return null;
387
- }
388
- const scrollIntoView = (event) => {
389
- var _a;
390
- if (((_a = inputRef == null ? void 0 : inputRef.current) == null ? void 0 : _a.parentElement) != null && (triggerRef == null ? void 0 : triggerRef.current) != null) {
391
- inputRef.current.parentElement.scrollTo({
392
- top: triggerRef.current.scrollHeight
393
- });
394
- }
395
- if (restProps.onClick !== void 0) {
396
- restProps.onClick(event);
397
- }
398
- };
399
- const isSelected = comboboxValue.includes(value);
400
- return /* @__PURE__ */ jsxRuntime.jsxs(
401
- StyledItem,
402
- {
403
- ...utils.mergeProps(restProps, restAriaDisabledProps),
404
- focusable: true,
405
- hideOnClick: false,
406
- accessibleWhenDisabled: designSystemUtils.booleanify(ariaDisabled),
407
- disabled: designSystemUtils.booleanify(ariaDisabled) || disabled,
408
- ref: forwardRef,
409
- value,
410
- onClick: scrollIntoView,
411
- "aria-selected": isSelected,
412
- children: [
413
- /* @__PURE__ */ jsxRuntime.jsx(
414
- react.ComboboxItemCheck,
415
- {
416
- checked: isSelected,
417
- render: ({ style, ...props }) => (
418
- // AriakitComboboxItemCheck adds its owm inline styles which we want to omit here
419
- /* @__PURE__ */ jsxRuntime.jsx(designSystemBaseSelect.StyledItemCheck, { ...props })
420
- ),
421
- children: /* @__PURE__ */ jsxRuntime.jsx(
422
- designSystemIcons.IconCheckMark,
423
- {
424
- size: "small",
425
- "data-testid": process.env.NODE_ENV === "test" ? "combobox-item-check" : void 0
426
- }
427
- )
428
- }
429
- ),
430
- children
431
- ]
432
- }
433
- );
434
- }
435
- );
436
-
437
- const itemType = React__default["default"].createElement(Item).type;
438
- const getChildrenItemValues = (componentChildren) => {
439
- const values = [];
440
- const recurse = (children) => {
441
- React__default["default"].Children.forEach(children, (child) => {
442
- if (!React__default["default"].isValidElement(child)) {
443
- return;
444
- }
445
- if (child.type === itemType) {
446
- const props = child.props;
447
- values.push(props.value);
448
- return;
449
- }
450
- if (child.props.children) {
451
- recurse(child.props.children);
452
- }
453
- });
454
- };
455
- recurse(componentChildren);
456
- return values;
457
- };
458
-
459
379
  const useDocumentFragment = () => {
460
380
  const [fragment, setFragment] = React__default["default"].useState();
461
381
  designSystemUseLayoutEffect.useLayoutEffect(() => {
@@ -493,23 +413,7 @@ const Content = React__default["default"].forwardRef(
493
413
  children,
494
414
  ...restProps
495
415
  }, forwardRef) => {
496
- const {
497
- triggerRef,
498
- contentRef,
499
- autoFilter,
500
- setFilteredItems,
501
- searchValue,
502
- direction,
503
- openState
504
- } = useComboboxContext();
505
- React.useEffect(() => {
506
- const childrenItemValues = getChildrenItemValues(children);
507
- const shouldFilter = autoFilter !== false && searchValue !== void 0 && searchValue.length > 0;
508
- const items = shouldFilter ? childrenItemValues.filter(
509
- (child) => child.toLowerCase().includes(searchValue.toLowerCase())
510
- ) : childrenItemValues;
511
- setFilteredItems(new Set(items));
512
- }, [children, autoFilter, setFilteredItems, searchValue]);
416
+ const { triggerRef, contentRef, direction, openState } = useComboboxContext();
513
417
  const getInvisibleContent = useInvisibleContent();
514
418
  if (!openState) {
515
419
  return getInvisibleContent(children);
@@ -555,84 +459,133 @@ const Content = React__default["default"].forwardRef(
555
459
  }
556
460
  );
557
461
 
462
+ const StyledItem = designSystemStitches.styled(react.ComboboxItem, designSystemBaseSelect.itemStyles);
463
+
464
+ const GroupContext = React.createContext({});
465
+ const GroupProvider = ({
466
+ children,
467
+ ...restProps
468
+ }) => /* @__PURE__ */ jsxRuntime.jsx(
469
+ GroupContext.Provider,
470
+ {
471
+ value: {
472
+ ...restProps
473
+ },
474
+ children
475
+ }
476
+ );
477
+ const useGroupContext = () => React.useContext(GroupContext);
478
+
479
+ const Item = React__default["default"].forwardRef(
480
+ ({ disabled = false, value, textValue, children, ...restProps }, forwardRef) => {
481
+ const { "aria-disabled": ariaDisabled, ...restAriaDisabledProps } = designSystemUseAriaDisabled.useAriaDisabled(restProps, { allowArrows: true });
482
+ const {
483
+ searchValue,
484
+ autoFilter,
485
+ setItemsMap,
486
+ triggerRef,
487
+ inputRef,
488
+ value: comboboxValue = []
489
+ } = useComboboxContext();
490
+ const { groupId } = useGroupContext();
491
+ const displayedText = React.useMemo(() => {
492
+ if (textValue !== void 0) {
493
+ return textValue;
494
+ }
495
+ return typeof children === "string" ? children : "";
496
+ }, [textValue, children]);
497
+ designSystemUseLayoutEffect.useLayoutEffect(() => {
498
+ setItemsMap(
499
+ (prevState) => new Map(prevState.set(value, { displayedText, groupId }))
500
+ );
501
+ return () => {
502
+ setItemsMap((prevState) => {
503
+ prevState.delete(value);
504
+ return new Map(prevState);
505
+ });
506
+ };
507
+ }, [setItemsMap, groupId, value, displayedText]);
508
+ if (autoFilter && searchValue.length > 0 && !searchQueryMatch(displayedText, searchValue)) {
509
+ return null;
510
+ }
511
+ const scrollIntoView = (event) => {
512
+ var _a;
513
+ if (((_a = inputRef == null ? void 0 : inputRef.current) == null ? void 0 : _a.parentElement) != null && (triggerRef == null ? void 0 : triggerRef.current) != null) {
514
+ inputRef.current.parentElement.scrollTo({
515
+ top: triggerRef.current.scrollHeight
516
+ });
517
+ }
518
+ if (restProps.onClick !== void 0) {
519
+ restProps.onClick(event);
520
+ }
521
+ };
522
+ const isSelected = comboboxValue.includes(value);
523
+ return /* @__PURE__ */ jsxRuntime.jsxs(
524
+ StyledItem,
525
+ {
526
+ ...utils.mergeProps(restProps, restAriaDisabledProps),
527
+ focusable: true,
528
+ hideOnClick: false,
529
+ accessibleWhenDisabled: designSystemUtils.booleanify(ariaDisabled),
530
+ disabled: designSystemUtils.booleanify(ariaDisabled) || disabled,
531
+ ref: forwardRef,
532
+ value,
533
+ onClick: scrollIntoView,
534
+ "aria-selected": isSelected,
535
+ children: [
536
+ /* @__PURE__ */ jsxRuntime.jsx(
537
+ react.ComboboxItemCheck,
538
+ {
539
+ checked: isSelected,
540
+ render: ({ style, ...props }) => (
541
+ // AriakitComboboxItemCheck adds its owm inline styles which we want to omit here
542
+ /* @__PURE__ */ jsxRuntime.jsx(designSystemBaseSelect.StyledItemCheck, { ...props })
543
+ ),
544
+ children: /* @__PURE__ */ jsxRuntime.jsx(
545
+ designSystemIcons.IconCheckMark,
546
+ {
547
+ size: "small",
548
+ "data-testid": process.env.NODE_ENV === "test" ? "combobox-item-check" : void 0
549
+ }
550
+ )
551
+ }
552
+ ),
553
+ children
554
+ ]
555
+ }
556
+ );
557
+ }
558
+ );
559
+
558
560
  const Portal = (props) => /* @__PURE__ */ jsxRuntime.jsx(RadixPopover.Portal, { ...props });
559
561
 
560
562
  const StyledGroup = designSystemStitches.styled(react.Group, {});
561
563
 
562
564
  const Group = React__default["default"].forwardRef(({ children, ...rest }, forwardRef) => {
563
- const { autoFilter, filteredItems } = useComboboxContext();
564
- const childValues = React.useMemo(
565
- // don't perform calculation if auto filter is disabled
566
- () => autoFilter !== false ? getChildrenItemValues(children) : [],
567
- [children, autoFilter]
568
- );
569
- const hasVisibleChildren = React.useMemo(
570
- () => (
571
- // don't perform calculation if auto filter is disabled
572
- autoFilter !== false ? childValues.some((value) => filteredItems.has(value)) : true
573
- ),
574
- [childValues, filteredItems, autoFilter]
575
- );
565
+ const { autoFilter, searchValue, filteredItems } = useComboboxContext();
566
+ const id = designSystemUseId.useId();
576
567
  const getInvisibleContent = useInvisibleContent();
577
- if (!hasVisibleChildren) {
578
- return getInvisibleContent(children);
568
+ let hasVisibleContent = true;
569
+ if (autoFilter && searchValue.length > 0) {
570
+ hasVisibleContent = filteredItems.some((item) => item.groupId === id);
579
571
  }
580
- return /* @__PURE__ */ jsxRuntime.jsx(StyledGroup, { ...rest, ref: forwardRef, children });
572
+ return /* @__PURE__ */ jsxRuntime.jsx(GroupProvider, { groupId: id, children: hasVisibleContent ? /* @__PURE__ */ jsxRuntime.jsx(StyledGroup, { ...rest, ref: forwardRef, children }) : getInvisibleContent(children) });
581
573
  });
582
574
 
583
575
  const StyledGroupLabel = designSystemStitches.styled(react.GroupLabel, designSystemBaseSelect.groupLabelStyles);
584
576
 
585
577
  const GroupLabel = React__default["default"].forwardRef((props, forwardRef) => /* @__PURE__ */ jsxRuntime.jsx(StyledGroupLabel, { ...props, ref: forwardRef }));
586
578
 
587
- const StyledChip = designSystemStitches.styled(designSystemPrimitive.Primitive.div, {
588
- fontSize: "$150",
589
- padding: "$50 $100",
590
- borderRadius: "$round",
591
- display: "flex",
592
- alignItems: "center",
593
- gap: "$50",
594
- whiteSpace: "nowrap",
595
- maxWidth: "$35",
596
- backgroundColor: "$background-neutrals-subtle",
597
- color: "$text-neutrals"
598
- });
599
- const StyledChipButton = designSystemStitches.styled(designSystemBaseButton.BaseButton, {
600
- color: "$icon-neutrals-inactive",
601
- ...designSystemStyles.focus.css({
602
- boxShadow: "$focus-small-outline"
603
- })
604
- });
605
- const StyledChipContent = designSystemStitches.styled(designSystemPrimitive.Primitive.div, {
606
- textOverflow: "ellipsis",
607
- whiteSpace: "nowrap",
608
- overflow: "hidden",
609
- lineHeight: 1.3
610
- });
611
-
612
- const StyledLeftSlot = designSystemStitches.styled(designSystemPrimitive.Primitive.span, {
613
- order: -1,
614
- marginRight: "$50"
615
- });
616
-
617
- const LeftSlot = StyledLeftSlot;
618
-
619
- const Chip = React__default["default"].forwardRef(
620
- ({ children, disabled = false, onRemove, removeAriaLabel, ...restProps }, forwardRef) => /* @__PURE__ */ jsxRuntime.jsxs(StyledChip, { ...restProps, ref: forwardRef, children: [
621
- /* @__PURE__ */ jsxRuntime.jsx(StyledChipContent, { children }),
622
- !designSystemUtils.booleanify(disabled) && /* @__PURE__ */ jsxRuntime.jsx(StyledChipButton, { onClick: onRemove, "aria-label": removeAriaLabel, children: /* @__PURE__ */ jsxRuntime.jsx(designSystemIcons.IconCross, { size: "small", weight: "thin", "aria-hidden": true }) })
623
- ] })
624
- );
625
- Chip.LeftSlot = LeftSlot;
626
-
627
579
  const Value = ({ unselectAriaLabel }) => {
628
580
  const {
629
581
  value = [],
630
582
  setValue,
631
583
  disabled,
584
+ readOnly,
632
585
  "aria-disabled": ariaDisabled,
633
- itemValueTextMap
586
+ itemsMap
634
587
  } = useComboboxContext();
635
- const isDisabled = ariaDisabled === true || disabled;
588
+ const canRemoveItem = !designSystemUtils.booleanify(ariaDisabled) && !designSystemUtils.booleanify(disabled) && !designSystemUtils.booleanify(readOnly);
636
589
  const onItemRemove = React.useCallback(
637
590
  (item) => {
638
591
  setValue((prevValue) => prevValue == null ? void 0 : prevValue.filter((value2) => value2 !== item));
@@ -641,26 +594,26 @@ const Value = ({ unselectAriaLabel }) => {
641
594
  );
642
595
  const getItemText = React.useCallback(
643
596
  (itemValue) => {
644
- const textValue = itemValueTextMap.get(itemValue);
645
- if (textValue === void 0 || textValue === "") {
597
+ const itemData = itemsMap.get(itemValue);
598
+ if (itemData === void 0 || itemData.displayedText === "") {
646
599
  return null;
647
600
  }
648
601
  return /* @__PURE__ */ jsxRuntime.jsx(
649
- Chip,
602
+ designSystemChip.Chip,
650
603
  {
651
604
  onRemove: (e) => {
652
605
  onItemRemove(itemValue);
653
606
  e.stopPropagation();
654
607
  },
655
- disabled: isDisabled,
656
- removeAriaLabel: "".concat(unselectAriaLabel, " ").concat(textValue),
608
+ removable: canRemoveItem,
609
+ removeAriaLabel: "".concat(unselectAriaLabel, " ").concat(itemData.displayedText),
657
610
  "data-testid": process.env.NODE_ENV === "test" ? "combobox-value-".concat(itemValue) : void 0,
658
- children: textValue
611
+ children: itemData.displayedText
659
612
  },
660
613
  itemValue
661
614
  );
662
615
  },
663
- [isDisabled, itemValueTextMap, onItemRemove, unselectAriaLabel]
616
+ [canRemoveItem, itemsMap, onItemRemove, unselectAriaLabel]
664
617
  );
665
618
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: value.map(getItemText) });
666
619
  };
@@ -669,7 +622,7 @@ const StyledSeparator = designSystemStitches.styled(designSystemPrimitive.Primit
669
622
 
670
623
  const Separator = React__default["default"].forwardRef((props, forwardRef) => {
671
624
  const { autoFilter, searchValue } = useComboboxContext();
672
- if (autoFilter === true && searchValue !== void 0 && searchValue.length > 0) {
625
+ if (autoFilter && searchValue.length > 0) {
673
626
  return null;
674
627
  }
675
628
  return /* @__PURE__ */ jsxRuntime.jsx(StyledSeparator, { ...props, ref: forwardRef, "aria-hidden": true });
@@ -689,11 +642,13 @@ const StyledNoResult = designSystemStitches.styled(designSystemPrimitive.Primiti
689
642
  });
690
643
 
691
644
  const NoResult = React__default["default"].forwardRef((props, forwardRef) => {
692
- const { filteredItems } = useComboboxContext();
693
- if (filteredItems.size !== 0) {
694
- return null;
645
+ const { autoFilter, searchValue, filteredItems, itemsMap } = useComboboxContext();
646
+ const noActiveFiltering = !autoFilter || autoFilter && searchValue.length === 0;
647
+ const isVisible = noActiveFiltering ? itemsMap.size === 0 : filteredItems.length === 0;
648
+ if (isVisible) {
649
+ return /* @__PURE__ */ jsxRuntime.jsx(StyledNoResult, { ...props, ref: forwardRef });
695
650
  }
696
- return /* @__PURE__ */ jsxRuntime.jsx(StyledNoResult, { ...props, ref: forwardRef });
651
+ return null;
697
652
  });
698
653
 
699
654
  const Root = React__default["default"].forwardRef(
@@ -702,7 +657,6 @@ const Root = React__default["default"].forwardRef(
702
657
  const {
703
658
  openState,
704
659
  setOpenState,
705
- defaultValue,
706
660
  value = [],
707
661
  setValue,
708
662
  required,
@@ -756,7 +710,6 @@ const Root = React__default["default"].forwardRef(
756
710
  {
757
711
  open: openState,
758
712
  setOpen: onOpenChange,
759
- defaultSelectedValue: defaultValue,
760
713
  selectedValue: value,
761
714
  setSelectedValue: onSetSelectedValue,
762
715
  children: /* @__PURE__ */ jsxRuntime.jsxs(