@mirohq/design-system-combobox 0.1.0-combobox.7 → 0.1.0-combobox.9
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 +141 -49
- package/dist/main.js.map +1 -1
- package/dist/module.js +144 -52
- package/dist/module.js.map +1 -1
- package/dist/types.d.ts +17 -13
- package/package.json +12 -11
package/dist/module.js
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import React, { createContext, useContext, useRef, useState, useCallback, useEffect, useMemo } from 'react';
|
|
3
|
-
import { Combobox as Combobox$1, ComboboxItem, ComboboxItemCheck, Group as Group$1, GroupLabel as GroupLabel$1, ComboboxProvider as ComboboxProvider$1 } from '@ariakit/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
4
|
import { useFormFieldContext, FloatingLabel } from '@mirohq/design-system-base-form';
|
|
5
5
|
import * as RadixPopover from '@radix-ui/react-popover';
|
|
6
|
-
import { Anchor, Portal as Portal$1 } from '@radix-ui/react-popover';
|
|
7
|
-
import {
|
|
6
|
+
import { Anchor, Trigger as Trigger$1, Portal as Portal$1 } from '@radix-ui/react-popover';
|
|
7
|
+
import { booleanishAttrValue, mergeRefs, booleanify } from '@mirohq/design-system-utils';
|
|
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';
|
|
11
11
|
import { IconChevronDown, IconCross, IconCheckMark } from '@mirohq/design-system-icons';
|
|
12
12
|
import { ScrollArea } from '@mirohq/design-system-scroll-area';
|
|
13
|
+
import { Primitive } from '@mirohq/design-system-primitive';
|
|
13
14
|
import { mergeProps } from '@react-aria/utils';
|
|
14
15
|
import { useAriaDisabled } from '@mirohq/design-system-use-aria-disabled';
|
|
16
|
+
import { useLayoutEffect } from '@mirohq/design-system-use-layout-effect';
|
|
15
17
|
import { focus } from '@mirohq/design-system-styles';
|
|
16
|
-
import {
|
|
18
|
+
import { createPortal } from 'react-dom';
|
|
17
19
|
import { BaseButton } from '@mirohq/design-system-base-button';
|
|
18
20
|
|
|
19
21
|
const StyledAnchor = styled(Anchor, {
|
|
@@ -92,6 +94,7 @@ const ComboboxProvider = ({
|
|
|
92
94
|
});
|
|
93
95
|
const [filteredItems, setFilteredItems] = useState(/* @__PURE__ */ new Set());
|
|
94
96
|
const [searchValue, setSearchValue] = useState("");
|
|
97
|
+
const [itemValueTextMap, setItemValueTextMap] = useState(/* @__PURE__ */ new Map());
|
|
95
98
|
const { valid: formFieldValid } = useFormFieldContext();
|
|
96
99
|
return /* @__PURE__ */ jsx(
|
|
97
100
|
ComboboxContext.Provider,
|
|
@@ -113,7 +116,9 @@ const ComboboxProvider = ({
|
|
|
113
116
|
searchValue,
|
|
114
117
|
setSearchValue,
|
|
115
118
|
filteredItems,
|
|
116
|
-
setFilteredItems
|
|
119
|
+
setFilteredItems,
|
|
120
|
+
itemValueTextMap,
|
|
121
|
+
setItemValueTextMap
|
|
117
122
|
},
|
|
118
123
|
children
|
|
119
124
|
}
|
|
@@ -141,25 +146,48 @@ const StyledActionButton = styled(Input.ActionButton, {
|
|
|
141
146
|
|
|
142
147
|
const TriggerActionButton = ({
|
|
143
148
|
openActionLabel,
|
|
149
|
+
closeActionLabel,
|
|
144
150
|
clearActionLabel,
|
|
145
151
|
size
|
|
146
152
|
}) => {
|
|
147
|
-
const { setOpenState, value = [], setValue } = useComboboxContext();
|
|
153
|
+
const { openState, setOpenState, value = [], setValue } = useComboboxContext();
|
|
148
154
|
const isEmpty = value.length === 0;
|
|
149
|
-
const
|
|
150
|
-
const label = isEmpty ? openActionLabel : clearActionLabel;
|
|
151
|
-
const onActionButtonClick = useCallback(
|
|
155
|
+
const onToggleClick = useCallback(
|
|
152
156
|
(event) => {
|
|
153
|
-
if (
|
|
154
|
-
|
|
155
|
-
} else {
|
|
156
|
-
setOpenState((prevOpen = false) => !prevOpen);
|
|
157
|
+
if (openState) {
|
|
158
|
+
setOpenState(false);
|
|
157
159
|
}
|
|
158
160
|
event.stopPropagation();
|
|
159
161
|
},
|
|
160
|
-
[
|
|
162
|
+
[setOpenState, openState]
|
|
163
|
+
);
|
|
164
|
+
const onClearClick = useCallback(
|
|
165
|
+
(event) => {
|
|
166
|
+
setValue([]);
|
|
167
|
+
event.stopPropagation();
|
|
168
|
+
},
|
|
169
|
+
[setValue]
|
|
170
|
+
);
|
|
171
|
+
if (isEmpty) {
|
|
172
|
+
return /* @__PURE__ */ jsx(Trigger$1, { asChild: true, "aria-haspopup": "listbox", children: /* @__PURE__ */ jsx(
|
|
173
|
+
StyledActionButton,
|
|
174
|
+
{
|
|
175
|
+
label: openState ? closeActionLabel : openActionLabel,
|
|
176
|
+
size,
|
|
177
|
+
onClick: onToggleClick,
|
|
178
|
+
children: /* @__PURE__ */ jsx(IconChevronDown, { size: "small", weight: "thin" })
|
|
179
|
+
}
|
|
180
|
+
) });
|
|
181
|
+
}
|
|
182
|
+
return /* @__PURE__ */ jsx(
|
|
183
|
+
StyledActionButton,
|
|
184
|
+
{
|
|
185
|
+
label: clearActionLabel,
|
|
186
|
+
size,
|
|
187
|
+
onClick: onClearClick,
|
|
188
|
+
children: /* @__PURE__ */ jsx(IconCross, { size: "small", weight: "thin" })
|
|
189
|
+
}
|
|
161
190
|
);
|
|
162
|
-
return /* @__PURE__ */ jsx(StyledActionButton, { label, size, onClick: onActionButtonClick, children: /* @__PURE__ */ jsx(ActionButtonIcon, { size: "small", weight: "thin" }) });
|
|
163
191
|
};
|
|
164
192
|
|
|
165
193
|
const Trigger = React.forwardRef(
|
|
@@ -171,6 +199,7 @@ const Trigger = React.forwardRef(
|
|
|
171
199
|
"aria-invalid": ariaInvalid,
|
|
172
200
|
placeholder,
|
|
173
201
|
openActionLabel,
|
|
202
|
+
closeActionLabel,
|
|
174
203
|
clearActionLabel,
|
|
175
204
|
onChange,
|
|
176
205
|
css,
|
|
@@ -191,7 +220,6 @@ const Trigger = React.forwardRef(
|
|
|
191
220
|
} = useComboboxContext();
|
|
192
221
|
const {
|
|
193
222
|
formElementId,
|
|
194
|
-
ariaDescribedBy: formFieldContextDescribedBy,
|
|
195
223
|
ariaInvalid: formFieldAriaInvalid,
|
|
196
224
|
valid: formFieldValid,
|
|
197
225
|
label,
|
|
@@ -203,10 +231,8 @@ const Trigger = React.forwardRef(
|
|
|
203
231
|
...restProps,
|
|
204
232
|
"aria-disabled": ariaDisabled,
|
|
205
233
|
"aria-invalid": ariaInvalid != null ? ariaInvalid : formFieldAriaInvalid,
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
formFieldContextDescribedBy
|
|
209
|
-
),
|
|
234
|
+
// todo MDS-1011: use formFieldContextDescribedBy after removing form context from BaseInput
|
|
235
|
+
"aria-describedby": ariaDescribedBy,
|
|
210
236
|
valid,
|
|
211
237
|
disabled,
|
|
212
238
|
readOnly,
|
|
@@ -265,6 +291,7 @@ const Trigger = React.forwardRef(
|
|
|
265
291
|
TriggerActionButton,
|
|
266
292
|
{
|
|
267
293
|
openActionLabel,
|
|
294
|
+
closeActionLabel,
|
|
268
295
|
clearActionLabel,
|
|
269
296
|
size
|
|
270
297
|
}
|
|
@@ -280,6 +307,9 @@ const Trigger = React.forwardRef(
|
|
|
280
307
|
}
|
|
281
308
|
);
|
|
282
309
|
|
|
310
|
+
const NoResultPlaceholder = styled(Primitive.div, {
|
|
311
|
+
padding: "$100"
|
|
312
|
+
});
|
|
283
313
|
const StyledContent = styled(RadixPopover.Content, {
|
|
284
314
|
backgroundColor: "$background-neutrals-container",
|
|
285
315
|
borderRadius: "$50",
|
|
@@ -337,7 +367,23 @@ const StyledItem = styled(ComboboxItem, {
|
|
|
337
367
|
const Item = React.forwardRef(
|
|
338
368
|
({ disabled = false, value, textValue, children, ...restProps }, forwardRef) => {
|
|
339
369
|
const { "aria-disabled": ariaDisabled, ...restAriaDisabledProps } = useAriaDisabled(restProps, { allowArrows: true });
|
|
340
|
-
const {
|
|
370
|
+
const {
|
|
371
|
+
autoFilter,
|
|
372
|
+
filteredItems,
|
|
373
|
+
setItemValueTextMap,
|
|
374
|
+
triggerRef,
|
|
375
|
+
inputRef
|
|
376
|
+
} = useComboboxContext();
|
|
377
|
+
useLayoutEffect(() => {
|
|
378
|
+
const textToSet = textValue !== void 0 ? textValue : typeof children === "string" ? children : "";
|
|
379
|
+
setItemValueTextMap((prevState) => new Map(prevState.set(value, textToSet)));
|
|
380
|
+
return () => {
|
|
381
|
+
setItemValueTextMap((prevState) => {
|
|
382
|
+
prevState.delete(value);
|
|
383
|
+
return new Map(prevState);
|
|
384
|
+
});
|
|
385
|
+
};
|
|
386
|
+
}, [setItemValueTextMap, value, textValue, children]);
|
|
341
387
|
if (autoFilter !== false && !filteredItems.has(value)) {
|
|
342
388
|
return null;
|
|
343
389
|
}
|
|
@@ -409,13 +455,35 @@ const getChildrenItemValues = (componentChildren) => {
|
|
|
409
455
|
return values;
|
|
410
456
|
};
|
|
411
457
|
|
|
458
|
+
const useDocumentFragment = () => {
|
|
459
|
+
const [fragment, setFragment] = React.useState();
|
|
460
|
+
useLayoutEffect(() => {
|
|
461
|
+
setFragment(new DocumentFragment());
|
|
462
|
+
}, []);
|
|
463
|
+
return fragment;
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
const useInvisibleContent = () => {
|
|
467
|
+
const fragment = useDocumentFragment();
|
|
468
|
+
return useCallback(
|
|
469
|
+
(children) => fragment !== void 0 ? createPortal(/* @__PURE__ */ jsx("div", { children }), fragment) : null,
|
|
470
|
+
[fragment]
|
|
471
|
+
);
|
|
472
|
+
};
|
|
473
|
+
|
|
412
474
|
const CONTENT_OFFSET = parseInt(theme.space[50]);
|
|
413
475
|
const isInsideRef = (element, ref) => {
|
|
414
476
|
var _a, _b;
|
|
415
477
|
return (_b = element != null && ((_a = ref.current) == null ? void 0 : _a.contains(element))) != null ? _b : false;
|
|
416
478
|
};
|
|
417
479
|
const Content = React.forwardRef(
|
|
418
|
-
({
|
|
480
|
+
({
|
|
481
|
+
sideOffset = CONTENT_OFFSET,
|
|
482
|
+
maxHeight,
|
|
483
|
+
overflow,
|
|
484
|
+
children,
|
|
485
|
+
...restProps
|
|
486
|
+
}, forwardRef) => {
|
|
419
487
|
const {
|
|
420
488
|
triggerRef,
|
|
421
489
|
contentRef,
|
|
@@ -424,7 +492,8 @@ const Content = React.forwardRef(
|
|
|
424
492
|
setFilteredItems,
|
|
425
493
|
searchValue,
|
|
426
494
|
noResultsText,
|
|
427
|
-
direction
|
|
495
|
+
direction,
|
|
496
|
+
openState
|
|
428
497
|
} = useComboboxContext();
|
|
429
498
|
useEffect(() => {
|
|
430
499
|
const childrenItemValues = getChildrenItemValues(children);
|
|
@@ -436,11 +505,20 @@ const Content = React.forwardRef(
|
|
|
436
505
|
)
|
|
437
506
|
);
|
|
438
507
|
}, [children, autoFilter, setFilteredItems, searchValue]);
|
|
439
|
-
const
|
|
508
|
+
const getInvisibleContent = useInvisibleContent();
|
|
509
|
+
if (!openState) {
|
|
510
|
+
return getInvisibleContent(children);
|
|
511
|
+
}
|
|
512
|
+
const content = filteredItems.size === 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
513
|
+
/* @__PURE__ */ jsx(NoResultPlaceholder, { children: noResultsText }),
|
|
514
|
+
getInvisibleContent(children)
|
|
515
|
+
] }) : children;
|
|
440
516
|
return /* @__PURE__ */ jsx(
|
|
441
517
|
StyledContent,
|
|
442
518
|
{
|
|
519
|
+
asChild: true,
|
|
443
520
|
...restProps,
|
|
521
|
+
dir: direction,
|
|
444
522
|
sideOffset,
|
|
445
523
|
ref: mergeRefs([forwardRef, contentRef]),
|
|
446
524
|
onOpenAutoFocus: (event) => event.preventDefault(),
|
|
@@ -452,7 +530,7 @@ const Content = React.forwardRef(
|
|
|
452
530
|
event.preventDefault();
|
|
453
531
|
}
|
|
454
532
|
},
|
|
455
|
-
children: /* @__PURE__ */ jsxs(ScrollArea, { type: "always", dir: direction, children: [
|
|
533
|
+
children: /* @__PURE__ */ jsx(ComboboxList, { role: "listbox", children: overflow === "auto" ? /* @__PURE__ */ jsxs(ScrollArea, { type: "always", dir: direction, children: [
|
|
456
534
|
/* @__PURE__ */ jsx(
|
|
457
535
|
ScrollArea.Viewport,
|
|
458
536
|
{
|
|
@@ -463,7 +541,7 @@ const Content = React.forwardRef(
|
|
|
463
541
|
}
|
|
464
542
|
),
|
|
465
543
|
/* @__PURE__ */ jsx(ScrollArea.Scrollbar, { orientation: "vertical", children: /* @__PURE__ */ jsx(ScrollArea.Thumb, {}) })
|
|
466
|
-
] })
|
|
544
|
+
] }) : content })
|
|
467
545
|
}
|
|
468
546
|
);
|
|
469
547
|
}
|
|
@@ -471,6 +549,8 @@ const Content = React.forwardRef(
|
|
|
471
549
|
|
|
472
550
|
const Portal = (props) => /* @__PURE__ */ jsx(Portal$1, { ...props });
|
|
473
551
|
|
|
552
|
+
const StyledGroup = styled(Group$1);
|
|
553
|
+
|
|
474
554
|
const Group = React.forwardRef(({ children, ...rest }, forwardRef) => {
|
|
475
555
|
const { autoFilter, filteredItems } = useComboboxContext();
|
|
476
556
|
const childValues = useMemo(
|
|
@@ -485,10 +565,11 @@ const Group = React.forwardRef(({ children, ...rest }, forwardRef) => {
|
|
|
485
565
|
),
|
|
486
566
|
[childValues, filteredItems, autoFilter]
|
|
487
567
|
);
|
|
568
|
+
const getInvisibleContent = useInvisibleContent();
|
|
488
569
|
if (!hasVisibleChildren) {
|
|
489
|
-
return
|
|
570
|
+
return getInvisibleContent(children);
|
|
490
571
|
}
|
|
491
|
-
return /* @__PURE__ */ jsx(
|
|
572
|
+
return /* @__PURE__ */ jsx(StyledGroup, { ...rest, ref: forwardRef, children });
|
|
492
573
|
});
|
|
493
574
|
|
|
494
575
|
const StyledGroupLabel = styled(GroupLabel$1, {
|
|
@@ -507,13 +588,13 @@ const StyledChip = styled(Primitive.div, {
|
|
|
507
588
|
gap: "$50",
|
|
508
589
|
whiteSpace: "nowrap",
|
|
509
590
|
maxWidth: "$35",
|
|
510
|
-
|
|
511
|
-
color: "$
|
|
591
|
+
backgroundColor: "$background-neutrals-subtle",
|
|
592
|
+
color: "$text-neutrals"
|
|
512
593
|
});
|
|
513
594
|
const StyledChipButton = styled(BaseButton, {
|
|
595
|
+
color: "$icon-neutrals-inactive",
|
|
514
596
|
...focus.css({
|
|
515
|
-
boxShadow: "$focus-small-outline"
|
|
516
|
-
borderColor: "$blue-400 !important"
|
|
597
|
+
boxShadow: "$focus-small-outline"
|
|
517
598
|
})
|
|
518
599
|
});
|
|
519
600
|
const StyledChipContent = styled(Primitive.div, {
|
|
@@ -531,9 +612,9 @@ const StyledLeftSlot = styled(Primitive.span, {
|
|
|
531
612
|
const LeftSlot = StyledLeftSlot;
|
|
532
613
|
|
|
533
614
|
const Chip = React.forwardRef(
|
|
534
|
-
({ children, disabled = false, onRemove,
|
|
615
|
+
({ children, disabled = false, onRemove, removeAriaLabel, ...restProps }, forwardRef) => /* @__PURE__ */ jsxs(StyledChip, { ...restProps, ref: forwardRef, children: [
|
|
535
616
|
/* @__PURE__ */ jsx(StyledChipContent, { children }),
|
|
536
|
-
!booleanify(disabled) && /* @__PURE__ */ jsx(StyledChipButton, { onClick: onRemove, "aria-label":
|
|
617
|
+
!booleanify(disabled) && /* @__PURE__ */ jsx(StyledChipButton, { onClick: onRemove, "aria-label": removeAriaLabel, children: /* @__PURE__ */ jsx(IconCross, { size: "small", weight: "thin", "aria-hidden": true }) })
|
|
537
618
|
] })
|
|
538
619
|
);
|
|
539
620
|
Chip.LeftSlot = LeftSlot;
|
|
@@ -542,31 +623,42 @@ const StyledValue = styled(Chip, {
|
|
|
542
623
|
marginTop: "$50"
|
|
543
624
|
});
|
|
544
625
|
|
|
545
|
-
const Value = ({
|
|
626
|
+
const Value = ({ unselectAriaLabel }) => {
|
|
546
627
|
const {
|
|
547
628
|
value = [],
|
|
548
629
|
setValue,
|
|
549
630
|
disabled,
|
|
550
|
-
"aria-disabled": ariaDisabled
|
|
631
|
+
"aria-disabled": ariaDisabled,
|
|
632
|
+
itemValueTextMap
|
|
551
633
|
} = useComboboxContext();
|
|
552
634
|
const isDisabled = ariaDisabled === true || disabled;
|
|
553
|
-
const onItemRemove = (
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
if (value.length === 0) {
|
|
557
|
-
return null;
|
|
558
|
-
}
|
|
559
|
-
return /* @__PURE__ */ jsx(Fragment, { children: value.map((item) => /* @__PURE__ */ jsx(
|
|
560
|
-
StyledValue,
|
|
561
|
-
{
|
|
562
|
-
onRemove: () => onItemRemove(item),
|
|
563
|
-
disabled: isDisabled,
|
|
564
|
-
removeChipAriaLabel,
|
|
565
|
-
"data-testid": process.env.NODE_ENV === "test" ? "combobox-value-".concat(item) : void 0,
|
|
566
|
-
children: item
|
|
635
|
+
const onItemRemove = useCallback(
|
|
636
|
+
(item) => {
|
|
637
|
+
setValue((prevValue) => prevValue == null ? void 0 : prevValue.filter((value2) => value2 !== item));
|
|
567
638
|
},
|
|
568
|
-
|
|
569
|
-
)
|
|
639
|
+
[setValue]
|
|
640
|
+
);
|
|
641
|
+
const getItemText = useCallback(
|
|
642
|
+
(itemValue) => {
|
|
643
|
+
const textValue = itemValueTextMap.get(itemValue);
|
|
644
|
+
if (textValue === void 0 || textValue === "") {
|
|
645
|
+
return null;
|
|
646
|
+
}
|
|
647
|
+
return /* @__PURE__ */ jsx(
|
|
648
|
+
StyledValue,
|
|
649
|
+
{
|
|
650
|
+
onRemove: () => onItemRemove(itemValue),
|
|
651
|
+
disabled: isDisabled,
|
|
652
|
+
removeAriaLabel: "".concat(unselectAriaLabel, " ").concat(textValue),
|
|
653
|
+
"data-testid": process.env.NODE_ENV === "test" ? "combobox-value-".concat(itemValue) : void 0,
|
|
654
|
+
children: textValue
|
|
655
|
+
},
|
|
656
|
+
itemValue
|
|
657
|
+
);
|
|
658
|
+
},
|
|
659
|
+
[isDisabled, itemValueTextMap, onItemRemove, unselectAriaLabel]
|
|
660
|
+
);
|
|
661
|
+
return /* @__PURE__ */ jsx(Fragment, { children: value.map(getItemText) });
|
|
570
662
|
};
|
|
571
663
|
|
|
572
664
|
const StyledSeparator = styled(Primitive.div, {
|