@mirohq/design-system-combobox 0.1.0-combobox.9 → 0.1.1

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,10 +1,10 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import React, { createContext, useContext, useRef, useState, useCallback, useEffect, useMemo } from 'react';
2
+ import React, { createContext, useRef, useState, useContext, useCallback, useEffect, useMemo } from 'react';
3
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
4
  import { useFormFieldContext, FloatingLabel } from '@mirohq/design-system-base-form';
5
- import * as RadixPopover from '@radix-ui/react-popover';
6
- import { Anchor, Trigger as Trigger$1, Portal as Portal$1 } from '@radix-ui/react-popover';
7
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
8
  import { styled, theme } from '@mirohq/design-system-stitches';
9
9
  import { Input } from '@mirohq/design-system-input';
10
10
  import { useControllableState } from '@radix-ui/react-use-controllable-state';
@@ -18,15 +18,10 @@ import { focus } from '@mirohq/design-system-styles';
18
18
  import { createPortal } from 'react-dom';
19
19
  import { BaseButton } from '@mirohq/design-system-base-button';
20
20
 
21
- const StyledAnchor = styled(Anchor, {
22
- position: "relative",
23
- width: "100%"
24
- });
25
21
  const StyledInput = styled(Input, {
26
22
  flexWrap: "wrap",
27
23
  flexGrow: 1,
28
- gap: "0 $50",
29
- overflowY: "scroll",
24
+ gap: "$50",
30
25
  "&[data-valid], &[data-invalid]": {
31
26
  // we don't need a bigger padding here as Input component will render its own icon
32
27
  paddingRight: "$100"
@@ -94,6 +89,8 @@ const ComboboxProvider = ({
94
89
  });
95
90
  const [filteredItems, setFilteredItems] = useState(/* @__PURE__ */ new Set());
96
91
  const [searchValue, setSearchValue] = useState("");
92
+ const [size, setSize] = useState();
93
+ const [placeholder, setPlaceholder] = useState();
97
94
  const [itemValueTextMap, setItemValueTextMap] = useState(/* @__PURE__ */ new Map());
98
95
  const { valid: formFieldValid } = useFormFieldContext();
99
96
  return /* @__PURE__ */ jsx(
@@ -118,7 +115,11 @@ const ComboboxProvider = ({
118
115
  filteredItems,
119
116
  setFilteredItems,
120
117
  itemValueTextMap,
121
- setItemValueTextMap
118
+ setItemValueTextMap,
119
+ placeholder,
120
+ setPlaceholder,
121
+ size,
122
+ setSize
122
123
  },
123
124
  children
124
125
  }
@@ -202,7 +203,6 @@ const Trigger = React.forwardRef(
202
203
  closeActionLabel,
203
204
  clearActionLabel,
204
205
  onChange,
205
- css,
206
206
  ...restProps
207
207
  }, forwardRef) => {
208
208
  const {
@@ -216,16 +216,21 @@ const Trigger = React.forwardRef(
216
216
  onSearchValueChange,
217
217
  searchValue,
218
218
  setSearchValue,
219
- setOpenState
219
+ setOpenState,
220
+ setSize,
221
+ setPlaceholder
220
222
  } = useComboboxContext();
221
223
  const {
222
224
  formElementId,
223
225
  ariaInvalid: formFieldAriaInvalid,
224
- valid: formFieldValid,
225
- label,
226
- isFloatingLabel,
227
- focused
226
+ valid: formFieldValid
228
227
  } = useFormFieldContext();
228
+ useEffect(() => {
229
+ setSize(size);
230
+ }, [size, setSize]);
231
+ useEffect(() => {
232
+ setPlaceholder(placeholder);
233
+ }, [setPlaceholder, placeholder]);
229
234
  const valid = formFieldValid != null ? formFieldValid : comboboxValid;
230
235
  const inputProps = {
231
236
  ...restProps,
@@ -240,8 +245,6 @@ const Trigger = React.forwardRef(
240
245
  id: id != null ? id : formElementId,
241
246
  placeholder: value.length === 0 ? placeholder : void 0
242
247
  };
243
- const shouldUseFloatingLabel = label !== null && isFloatingLabel;
244
- const isFloating = placeholder !== void 0 || value.length !== 0 || focused || searchValue !== "";
245
248
  const scrollIntoView = (event) => {
246
249
  var _a;
247
250
  const trigger = triggerRef == null ? void 0 : triggerRef.current;
@@ -261,47 +264,43 @@ const Trigger = React.forwardRef(
261
264
  onSearchValueChange == null ? void 0 : onSearchValueChange(e.target.value);
262
265
  onChange == null ? void 0 : onChange(e);
263
266
  };
264
- return /* @__PURE__ */ jsxs(
265
- StyledAnchor,
267
+ return /* @__PURE__ */ jsx(
268
+ Anchor,
266
269
  {
267
270
  ref: mergeRefs([triggerRef, forwardRef]),
268
- css,
269
271
  onClick: () => {
270
272
  if (!booleanify(disabled) && !booleanify(ariaDisabled) && !booleanify(readOnly)) {
271
273
  setOpenState(true);
272
274
  }
273
275
  },
274
- children: [
275
- shouldUseFloatingLabel && /* @__PURE__ */ jsx(FloatingLabel, { floating: isFloating, size, children: label }),
276
- /* @__PURE__ */ jsx(
277
- Combobox$1,
278
- {
279
- render: /* @__PURE__ */ jsxs(
280
- StyledInput,
281
- {
282
- ...inputProps,
283
- value: searchValue,
284
- size,
285
- ref: inputRef,
286
- onChange: onInputChange,
287
- onFocus: scrollIntoView,
288
- children: [
289
- children,
290
- /* @__PURE__ */ jsx(
291
- TriggerActionButton,
292
- {
293
- openActionLabel,
294
- closeActionLabel,
295
- clearActionLabel,
296
- size
297
- }
298
- )
299
- ]
300
- }
301
- )
302
- }
303
- )
304
- ]
276
+ children: /* @__PURE__ */ jsx(
277
+ Combobox$1,
278
+ {
279
+ render: /* @__PURE__ */ jsxs(
280
+ StyledInput,
281
+ {
282
+ ...inputProps,
283
+ value: searchValue,
284
+ size,
285
+ ref: inputRef,
286
+ onChange: onInputChange,
287
+ onFocus: scrollIntoView,
288
+ children: [
289
+ children,
290
+ /* @__PURE__ */ jsx(
291
+ TriggerActionButton,
292
+ {
293
+ openActionLabel,
294
+ closeActionLabel,
295
+ clearActionLabel,
296
+ size
297
+ }
298
+ )
299
+ ]
300
+ }
301
+ )
302
+ }
303
+ )
305
304
  }
306
305
  );
307
306
  }
@@ -372,7 +371,8 @@ const Item = React.forwardRef(
372
371
  filteredItems,
373
372
  setItemValueTextMap,
374
373
  triggerRef,
375
- inputRef
374
+ inputRef,
375
+ value: comboboxValue = []
376
376
  } = useComboboxContext();
377
377
  useLayoutEffect(() => {
378
378
  const textToSet = textValue !== void 0 ? textValue : typeof children === "string" ? children : "";
@@ -398,6 +398,7 @@ const Item = React.forwardRef(
398
398
  restProps.onClick(event);
399
399
  }
400
400
  };
401
+ const isSelected = comboboxValue.includes(value);
401
402
  return /* @__PURE__ */ jsxs(
402
403
  StyledItem,
403
404
  {
@@ -409,10 +410,12 @@ const Item = React.forwardRef(
409
410
  ref: forwardRef,
410
411
  value,
411
412
  onClick: scrollIntoView,
413
+ "aria-selected": isSelected,
412
414
  children: [
413
415
  /* @__PURE__ */ jsx(
414
416
  ComboboxItemCheck,
415
417
  {
418
+ checked: isSelected,
416
419
  render: ({ style, ...props }) => (
417
420
  // AriakitComboboxItemCheck adds its owm inline styles which we want to omit here
418
421
  /* @__PURE__ */ jsx(StyledItemCheck, { ...props })
@@ -478,9 +481,16 @@ const isInsideRef = (element, ref) => {
478
481
  };
479
482
  const Content = React.forwardRef(
480
483
  ({
484
+ side = "bottom",
481
485
  sideOffset = CONTENT_OFFSET,
486
+ align = "center",
487
+ alignOffset = 0,
488
+ collisionPadding = 0,
489
+ avoidCollisions = true,
490
+ sticky = "partial",
491
+ hideWhenDetached = true,
492
+ overflow = "visible",
482
493
  maxHeight,
483
- overflow,
484
494
  children,
485
495
  ...restProps
486
496
  }, forwardRef) => {
@@ -519,7 +529,14 @@ const Content = React.forwardRef(
519
529
  asChild: true,
520
530
  ...restProps,
521
531
  dir: direction,
532
+ side,
522
533
  sideOffset,
534
+ align,
535
+ alignOffset,
536
+ avoidCollisions,
537
+ collisionPadding,
538
+ sticky,
539
+ hideWhenDetached,
523
540
  ref: mergeRefs([forwardRef, contentRef]),
524
541
  onOpenAutoFocus: (event) => event.preventDefault(),
525
542
  onInteractOutside: (event) => {
@@ -619,10 +636,6 @@ const Chip = React.forwardRef(
619
636
  );
620
637
  Chip.LeftSlot = LeftSlot;
621
638
 
622
- const StyledValue = styled(Chip, {
623
- marginTop: "$50"
624
- });
625
-
626
639
  const Value = ({ unselectAriaLabel }) => {
627
640
  const {
628
641
  value = [],
@@ -645,9 +658,12 @@ const Value = ({ unselectAriaLabel }) => {
645
658
  return null;
646
659
  }
647
660
  return /* @__PURE__ */ jsx(
648
- StyledValue,
661
+ Chip,
649
662
  {
650
- onRemove: () => onItemRemove(itemValue),
663
+ onRemove: (e) => {
664
+ onItemRemove(itemValue);
665
+ e.stopPropagation();
666
+ },
651
667
  disabled: isDisabled,
652
668
  removeAriaLabel: "".concat(unselectAriaLabel, " ").concat(textValue),
653
669
  "data-testid": process.env.NODE_ENV === "test" ? "combobox-value-".concat(itemValue) : void 0,
@@ -676,53 +692,71 @@ const Separator = React.forwardRef((props, forwardRef) => {
676
692
  return /* @__PURE__ */ jsx(StyledSeparator, { ...props, ref: forwardRef, "aria-hidden": true });
677
693
  });
678
694
 
679
- const Root = ({
680
- value: valueProp,
681
- children,
682
- ...restProps
683
- }) => {
684
- const {
685
- openState,
686
- setOpenState,
687
- defaultValue,
688
- value,
689
- setValue,
690
- required,
691
- readOnly,
692
- "aria-disabled": ariaDisabled,
693
- disabled
694
- } = useComboboxContext();
695
- const { setRequired, setDisabled, setAriaDisabled, setReadOnly } = useFormFieldContext();
696
- useEffect(() => {
697
- setRequired == null ? void 0 : setRequired(required);
698
- setDisabled == null ? void 0 : setDisabled(disabled);
699
- setAriaDisabled == null ? void 0 : setAriaDisabled(ariaDisabled);
700
- setReadOnly == null ? void 0 : setReadOnly(readOnly);
701
- }, [
702
- readOnly,
703
- disabled,
704
- ariaDisabled,
705
- required,
706
- setRequired,
707
- setDisabled,
708
- setAriaDisabled,
709
- setReadOnly
710
- ]);
711
- const onSetSelectedValue = (newValue) => {
712
- setValue(typeof newValue === "string" ? [newValue] : newValue);
713
- };
714
- const onOpenChange = (value2) => {
715
- if (!booleanify(readOnly)) {
716
- setOpenState(value2);
717
- }
718
- };
719
- return /* @__PURE__ */ jsx(
720
- RadixPopover.Root,
721
- {
722
- open: openState,
723
- onOpenChange,
724
- ...restProps,
725
- children: /* @__PURE__ */ jsx(
695
+ const StyledNativeSelect = styled(Primitive.select, {
696
+ // if we support autoComplete, we would have to use visually-hidden styles here
697
+ display: "none"
698
+ });
699
+ const StyledComboboxContent = styled(Primitive.div, {
700
+ position: "relative",
701
+ width: "100%"
702
+ });
703
+
704
+ const Root = React.forwardRef(
705
+ ({ value: valueProp, onValueChange, name, children, ...restProps }, forwardRef) => {
706
+ var _a;
707
+ const {
708
+ openState,
709
+ setOpenState,
710
+ defaultValue,
711
+ value = [],
712
+ setValue,
713
+ required,
714
+ readOnly,
715
+ "aria-disabled": ariaDisabled,
716
+ disabled,
717
+ direction,
718
+ size,
719
+ placeholder,
720
+ triggerRef
721
+ } = useComboboxContext();
722
+ const {
723
+ setRequired,
724
+ setDisabled,
725
+ setAriaDisabled,
726
+ setReadOnly,
727
+ label,
728
+ isFloatingLabel,
729
+ focused,
730
+ formElementRef
731
+ } = useFormFieldContext();
732
+ useEffect(() => {
733
+ setRequired == null ? void 0 : setRequired(required);
734
+ setDisabled == null ? void 0 : setDisabled(disabled);
735
+ setAriaDisabled == null ? void 0 : setAriaDisabled(ariaDisabled);
736
+ setReadOnly == null ? void 0 : setReadOnly(readOnly);
737
+ }, [
738
+ readOnly,
739
+ disabled,
740
+ ariaDisabled,
741
+ required,
742
+ setRequired,
743
+ setDisabled,
744
+ setAriaDisabled,
745
+ setReadOnly
746
+ ]);
747
+ const shouldUseFloatingLabel = label !== null && isFloatingLabel;
748
+ const isFloating = placeholder !== void 0 || value.length !== 0 || focused;
749
+ const onSetSelectedValue = (newValue) => {
750
+ setValue(typeof newValue === "string" ? [newValue] : newValue);
751
+ };
752
+ const onOpenChange = (value2) => {
753
+ if (!booleanify(readOnly)) {
754
+ setOpenState(value2);
755
+ }
756
+ };
757
+ const isFormControl = Boolean((_a = triggerRef.current) == null ? void 0 : _a.closest("form"));
758
+ return /* @__PURE__ */ jsxs(RadixPopover.Root, { open: openState, onOpenChange, children: [
759
+ /* @__PURE__ */ jsx(
726
760
  ComboboxProvider$1,
727
761
  {
728
762
  open: openState,
@@ -730,51 +764,86 @@ const Root = ({
730
764
  defaultSelectedValue: defaultValue,
731
765
  selectedValue: value,
732
766
  setSelectedValue: onSetSelectedValue,
733
- children
767
+ children: /* @__PURE__ */ jsxs(
768
+ StyledComboboxContent,
769
+ {
770
+ ref: forwardRef,
771
+ ...restProps,
772
+ dir: direction,
773
+ "data-form-element": "select",
774
+ children: [
775
+ shouldUseFloatingLabel && /* @__PURE__ */ jsx(FloatingLabel, { floating: isFloating, size, children: label }),
776
+ children
777
+ ]
778
+ }
779
+ )
780
+ }
781
+ ),
782
+ isFormControl && /* @__PURE__ */ jsx(
783
+ StyledNativeSelect,
784
+ {
785
+ multiple: true,
786
+ autoComplete: "off",
787
+ name,
788
+ tabIndex: -1,
789
+ "aria-hidden": "true",
790
+ ref: formElementRef,
791
+ required,
792
+ disabled,
793
+ "aria-disabled": ariaDisabled,
794
+ value,
795
+ onChange: () => {
796
+ },
797
+ children: value.length === 0 ? /* @__PURE__ */ jsx("option", { value: "" }) : (
798
+ // since we don't support autoComplete we can render here only selected values
799
+ value.map((itemValue) => /* @__PURE__ */ jsx("option", { value: itemValue, children: itemValue }, itemValue))
800
+ )
734
801
  }
735
802
  )
736
- }
737
- );
738
- };
739
- const Combobox = ({
740
- "aria-disabled": ariaDisabled,
741
- defaultOpen = false,
742
- open,
743
- valid,
744
- disabled,
745
- readOnly,
746
- required,
747
- value,
748
- defaultValue,
749
- onOpen,
750
- onClose,
751
- onSearchValueChange,
752
- onValueChange,
753
- direction = "ltr",
754
- autoFilter = true,
755
- noResultsText,
756
- ...restProps
757
- }) => /* @__PURE__ */ jsx(
758
- ComboboxProvider,
759
- {
760
- defaultValue,
761
- value,
762
- onValueChange,
763
- onSearchValueChange,
764
- defaultOpen,
803
+ ] });
804
+ }
805
+ );
806
+ const Combobox = React.forwardRef(
807
+ ({
808
+ "aria-disabled": ariaDisabled,
809
+ defaultOpen = false,
765
810
  open,
766
- onOpen,
767
- onClose,
768
811
  valid,
769
- required,
770
812
  disabled,
771
813
  readOnly,
772
- "aria-disabled": ariaDisabled,
773
- direction,
774
- autoFilter,
814
+ required,
815
+ value,
816
+ defaultValue,
817
+ onOpen,
818
+ onClose,
819
+ onSearchValueChange,
820
+ onValueChange,
821
+ direction = "ltr",
822
+ autoFilter = true,
775
823
  noResultsText,
776
- children: /* @__PURE__ */ jsx(Root, { ...restProps, value })
777
- }
824
+ ...restProps
825
+ }, forwardRef) => /* @__PURE__ */ jsx(
826
+ ComboboxProvider,
827
+ {
828
+ defaultValue,
829
+ value,
830
+ onValueChange,
831
+ onSearchValueChange,
832
+ defaultOpen,
833
+ open,
834
+ onOpen,
835
+ onClose,
836
+ valid,
837
+ required,
838
+ disabled,
839
+ readOnly,
840
+ "aria-disabled": ariaDisabled,
841
+ direction,
842
+ autoFilter,
843
+ noResultsText,
844
+ children: /* @__PURE__ */ jsx(Root, { ...restProps, value, ref: forwardRef })
845
+ }
846
+ )
778
847
  );
779
848
  Combobox.Portal = Portal;
780
849
  Combobox.Trigger = Trigger;