@mirohq/design-system-combobox 0.1.0-combobox.9 → 0.1.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/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,14 +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",
24
+ gap: "$50",
29
25
  overflowY: "scroll",
30
26
  "&[data-valid], &[data-invalid]": {
31
27
  // we don't need a bigger padding here as Input component will render its own icon
@@ -94,6 +90,8 @@ const ComboboxProvider = ({
94
90
  });
95
91
  const [filteredItems, setFilteredItems] = useState(/* @__PURE__ */ new Set());
96
92
  const [searchValue, setSearchValue] = useState("");
93
+ const [size, setSize] = useState();
94
+ const [placeholder, setPlaceholder] = useState();
97
95
  const [itemValueTextMap, setItemValueTextMap] = useState(/* @__PURE__ */ new Map());
98
96
  const { valid: formFieldValid } = useFormFieldContext();
99
97
  return /* @__PURE__ */ jsx(
@@ -118,7 +116,11 @@ const ComboboxProvider = ({
118
116
  filteredItems,
119
117
  setFilteredItems,
120
118
  itemValueTextMap,
121
- setItemValueTextMap
119
+ setItemValueTextMap,
120
+ placeholder,
121
+ setPlaceholder,
122
+ size,
123
+ setSize
122
124
  },
123
125
  children
124
126
  }
@@ -202,7 +204,6 @@ const Trigger = React.forwardRef(
202
204
  closeActionLabel,
203
205
  clearActionLabel,
204
206
  onChange,
205
- css,
206
207
  ...restProps
207
208
  }, forwardRef) => {
208
209
  const {
@@ -216,16 +217,21 @@ const Trigger = React.forwardRef(
216
217
  onSearchValueChange,
217
218
  searchValue,
218
219
  setSearchValue,
219
- setOpenState
220
+ setOpenState,
221
+ setSize,
222
+ setPlaceholder
220
223
  } = useComboboxContext();
221
224
  const {
222
225
  formElementId,
223
226
  ariaInvalid: formFieldAriaInvalid,
224
- valid: formFieldValid,
225
- label,
226
- isFloatingLabel,
227
- focused
227
+ valid: formFieldValid
228
228
  } = useFormFieldContext();
229
+ useEffect(() => {
230
+ setSize(size);
231
+ }, [size, setSize]);
232
+ useEffect(() => {
233
+ setPlaceholder(placeholder);
234
+ }, [setPlaceholder, placeholder]);
229
235
  const valid = formFieldValid != null ? formFieldValid : comboboxValid;
230
236
  const inputProps = {
231
237
  ...restProps,
@@ -240,8 +246,6 @@ const Trigger = React.forwardRef(
240
246
  id: id != null ? id : formElementId,
241
247
  placeholder: value.length === 0 ? placeholder : void 0
242
248
  };
243
- const shouldUseFloatingLabel = label !== null && isFloatingLabel;
244
- const isFloating = placeholder !== void 0 || value.length !== 0 || focused || searchValue !== "";
245
249
  const scrollIntoView = (event) => {
246
250
  var _a;
247
251
  const trigger = triggerRef == null ? void 0 : triggerRef.current;
@@ -261,47 +265,43 @@ const Trigger = React.forwardRef(
261
265
  onSearchValueChange == null ? void 0 : onSearchValueChange(e.target.value);
262
266
  onChange == null ? void 0 : onChange(e);
263
267
  };
264
- return /* @__PURE__ */ jsxs(
265
- StyledAnchor,
268
+ return /* @__PURE__ */ jsx(
269
+ Anchor,
266
270
  {
267
271
  ref: mergeRefs([triggerRef, forwardRef]),
268
- css,
269
272
  onClick: () => {
270
273
  if (!booleanify(disabled) && !booleanify(ariaDisabled) && !booleanify(readOnly)) {
271
274
  setOpenState(true);
272
275
  }
273
276
  },
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
- ]
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
305
  }
306
306
  );
307
307
  }
@@ -372,7 +372,8 @@ const Item = React.forwardRef(
372
372
  filteredItems,
373
373
  setItemValueTextMap,
374
374
  triggerRef,
375
- inputRef
375
+ inputRef,
376
+ value: comboboxValue = []
376
377
  } = useComboboxContext();
377
378
  useLayoutEffect(() => {
378
379
  const textToSet = textValue !== void 0 ? textValue : typeof children === "string" ? children : "";
@@ -398,6 +399,7 @@ const Item = React.forwardRef(
398
399
  restProps.onClick(event);
399
400
  }
400
401
  };
402
+ const isSelected = comboboxValue.includes(value);
401
403
  return /* @__PURE__ */ jsxs(
402
404
  StyledItem,
403
405
  {
@@ -409,10 +411,12 @@ const Item = React.forwardRef(
409
411
  ref: forwardRef,
410
412
  value,
411
413
  onClick: scrollIntoView,
414
+ "aria-selected": isSelected,
412
415
  children: [
413
416
  /* @__PURE__ */ jsx(
414
417
  ComboboxItemCheck,
415
418
  {
419
+ checked: isSelected,
416
420
  render: ({ style, ...props }) => (
417
421
  // AriakitComboboxItemCheck adds its owm inline styles which we want to omit here
418
422
  /* @__PURE__ */ jsx(StyledItemCheck, { ...props })
@@ -478,9 +482,16 @@ const isInsideRef = (element, ref) => {
478
482
  };
479
483
  const Content = React.forwardRef(
480
484
  ({
485
+ side = "bottom",
481
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",
482
494
  maxHeight,
483
- overflow,
484
495
  children,
485
496
  ...restProps
486
497
  }, forwardRef) => {
@@ -519,7 +530,14 @@ const Content = React.forwardRef(
519
530
  asChild: true,
520
531
  ...restProps,
521
532
  dir: direction,
533
+ side,
522
534
  sideOffset,
535
+ align,
536
+ alignOffset,
537
+ avoidCollisions,
538
+ collisionPadding,
539
+ sticky,
540
+ hideWhenDetached,
523
541
  ref: mergeRefs([forwardRef, contentRef]),
524
542
  onOpenAutoFocus: (event) => event.preventDefault(),
525
543
  onInteractOutside: (event) => {
@@ -619,10 +637,6 @@ const Chip = React.forwardRef(
619
637
  );
620
638
  Chip.LeftSlot = LeftSlot;
621
639
 
622
- const StyledValue = styled(Chip, {
623
- marginTop: "$50"
624
- });
625
-
626
640
  const Value = ({ unselectAriaLabel }) => {
627
641
  const {
628
642
  value = [],
@@ -645,9 +659,12 @@ const Value = ({ unselectAriaLabel }) => {
645
659
  return null;
646
660
  }
647
661
  return /* @__PURE__ */ jsx(
648
- StyledValue,
662
+ Chip,
649
663
  {
650
- onRemove: () => onItemRemove(itemValue),
664
+ onRemove: (e) => {
665
+ onItemRemove(itemValue);
666
+ e.stopPropagation();
667
+ },
651
668
  disabled: isDisabled,
652
669
  removeAriaLabel: "".concat(unselectAriaLabel, " ").concat(textValue),
653
670
  "data-testid": process.env.NODE_ENV === "test" ? "combobox-value-".concat(itemValue) : void 0,
@@ -676,53 +693,71 @@ const Separator = React.forwardRef((props, forwardRef) => {
676
693
  return /* @__PURE__ */ jsx(StyledSeparator, { ...props, ref: forwardRef, "aria-hidden": true });
677
694
  });
678
695
 
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(
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(
726
761
  ComboboxProvider$1,
727
762
  {
728
763
  open: openState,
@@ -730,51 +765,86 @@ const Root = ({
730
765
  defaultSelectedValue: defaultValue,
731
766
  selectedValue: value,
732
767
  setSelectedValue: onSetSelectedValue,
733
- children
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
+ )
734
802
  }
735
803
  )
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,
804
+ ] });
805
+ }
806
+ );
807
+ const Combobox = React.forwardRef(
808
+ ({
809
+ "aria-disabled": ariaDisabled,
810
+ defaultOpen = false,
765
811
  open,
766
- onOpen,
767
- onClose,
768
812
  valid,
769
- required,
770
813
  disabled,
771
814
  readOnly,
772
- "aria-disabled": ariaDisabled,
773
- direction,
774
- autoFilter,
815
+ required,
816
+ value,
817
+ defaultValue,
818
+ onOpen,
819
+ onClose,
820
+ onSearchValueChange,
821
+ onValueChange,
822
+ direction = "ltr",
823
+ autoFilter = true,
775
824
  noResultsText,
776
- children: /* @__PURE__ */ jsx(Root, { ...restProps, value })
777
- }
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
+ )
778
848
  );
779
849
  Combobox.Portal = Portal;
780
850
  Combobox.Trigger = Trigger;