@carbon/react 1.60.3 → 1.61.0-rc.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/.playwright/INTERNAL_AVT_REPORT_DO_NOT_USE.json +952 -1034
- package/es/components/ComposedModal/ComposedModal.js +4 -2
- package/es/components/DatePicker/DatePicker.js +6 -1
- package/es/components/Dropdown/Dropdown.d.ts +4 -0
- package/es/components/Dropdown/Dropdown.js +53 -8
- package/es/components/Modal/Modal.js +4 -2
- package/es/components/MultiSelect/FilterableMultiSelect.d.ts +20 -14
- package/es/components/MultiSelect/FilterableMultiSelect.js +54 -5
- package/es/components/MultiSelect/MultiSelect.d.ts +7 -38
- package/es/components/MultiSelect/MultiSelect.js +52 -5
- package/es/components/MultiSelect/MultiSelectPropTypes.d.ts +19 -16
- package/es/components/OverflowMenu/OverflowMenu.js +0 -1
- package/es/components/OverflowMenu/next/index.d.ts +4 -0
- package/es/components/OverflowMenu/next/index.js +41 -2
- package/es/components/UIShell/HeaderContainer.d.ts +11 -9
- package/es/components/UIShell/HeaderContainer.js +9 -7
- package/lib/components/ComposedModal/ComposedModal.js +6 -4
- package/lib/components/DatePicker/DatePicker.js +6 -1
- package/lib/components/Dropdown/Dropdown.d.ts +4 -0
- package/lib/components/Dropdown/Dropdown.js +49 -4
- package/lib/components/Modal/Modal.js +6 -4
- package/lib/components/MultiSelect/FilterableMultiSelect.d.ts +20 -14
- package/lib/components/MultiSelect/FilterableMultiSelect.js +51 -2
- package/lib/components/MultiSelect/MultiSelect.d.ts +7 -38
- package/lib/components/MultiSelect/MultiSelect.js +49 -2
- package/lib/components/MultiSelect/MultiSelectPropTypes.d.ts +19 -16
- package/lib/components/OverflowMenu/OverflowMenu.js +0 -1
- package/lib/components/OverflowMenu/next/index.d.ts +4 -0
- package/lib/components/OverflowMenu/next/index.js +40 -1
- package/lib/components/UIShell/HeaderContainer.d.ts +11 -9
- package/lib/components/UIShell/HeaderContainer.js +9 -7
- package/package.json +7 -7
|
@@ -9,6 +9,7 @@ import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js
|
|
|
9
9
|
import React__default, { useRef, useState, useEffect } from 'react';
|
|
10
10
|
import { isElement } from 'react-is';
|
|
11
11
|
import PropTypes from 'prop-types';
|
|
12
|
+
import { Layer } from '../Layer/index.js';
|
|
12
13
|
import { ModalHeader } from './ModalHeader.js';
|
|
13
14
|
import { ModalFooter } from './ModalFooter.js';
|
|
14
15
|
import debounce from 'lodash.debounce';
|
|
@@ -59,7 +60,7 @@ const ModalBody = /*#__PURE__*/React__default.forwardRef(function ModalBody(_ref
|
|
|
59
60
|
tabIndex: 0,
|
|
60
61
|
role: 'region'
|
|
61
62
|
} : {};
|
|
62
|
-
return /*#__PURE__*/React__default.createElement(
|
|
63
|
+
return /*#__PURE__*/React__default.createElement(Layer, _extends({
|
|
63
64
|
className: contentClass
|
|
64
65
|
}, hasScrollingContentProps, rest, {
|
|
65
66
|
ref: mergeRefs(contentRef, ref)
|
|
@@ -249,7 +250,8 @@ const ComposedModal = /*#__PURE__*/React__default.forwardRef(function ComposedMo
|
|
|
249
250
|
size: 'sm'
|
|
250
251
|
});
|
|
251
252
|
}
|
|
252
|
-
return /*#__PURE__*/React__default.createElement(
|
|
253
|
+
return /*#__PURE__*/React__default.createElement(Layer, _extends({}, rest, {
|
|
254
|
+
level: 0,
|
|
253
255
|
role: "presentation",
|
|
254
256
|
ref: ref,
|
|
255
257
|
"aria-hidden": !open,
|
|
@@ -176,6 +176,8 @@ const DatePicker = /*#__PURE__*/React__default.forwardRef(function DatePicker(_r
|
|
|
176
176
|
|
|
177
177
|
// fix datepicker deleting the selectedDate when the calendar closes
|
|
178
178
|
const onCalendarClose = (selectedDates, dateStr) => {
|
|
179
|
+
endInputField?.current?.focus();
|
|
180
|
+
calendarRef?.current?.calendarContainer?.classList.remove('open');
|
|
179
181
|
setTimeout(() => {
|
|
180
182
|
if (lastStartValue.current && selectedDates[0] && !startInputField.current.value) {
|
|
181
183
|
startInputField.current.value = lastStartValue.current;
|
|
@@ -355,9 +357,12 @@ const DatePicker = /*#__PURE__*/React__default.forwardRef(function DatePicker(_r
|
|
|
355
357
|
calendarRef.current = calendar;
|
|
356
358
|
function handleArrowDown(event) {
|
|
357
359
|
if (match(event, Escape)) {
|
|
358
|
-
calendar
|
|
360
|
+
calendar?.calendarContainer?.classList.remove('open');
|
|
359
361
|
}
|
|
360
362
|
if (match(event, ArrowDown)) {
|
|
363
|
+
if (event.target == endInputField.current) {
|
|
364
|
+
calendar?.calendarContainer?.classList.add('open');
|
|
365
|
+
}
|
|
361
366
|
const {
|
|
362
367
|
calendarContainer,
|
|
363
368
|
selectedDateElem: fpSelectedDateElem,
|
|
@@ -23,6 +23,10 @@ export interface DropdownProps<ItemType> extends Omit<ReactAttr<HTMLDivElement>,
|
|
|
23
23
|
* Specify a label to be read by screen readers on the container note.
|
|
24
24
|
*/
|
|
25
25
|
ariaLabel?: string;
|
|
26
|
+
/**
|
|
27
|
+
* **Experimental**: Will attempt to automatically align the floating element to avoid collisions with the viewport and being clipped by ancestor elements.
|
|
28
|
+
*/
|
|
29
|
+
autoAlign?: boolean;
|
|
26
30
|
/**
|
|
27
31
|
* Specify the direction of the dropdown. Can be either top or bottom.
|
|
28
32
|
*/
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
|
|
9
|
-
import React__default, { useContext, useRef, useState } from 'react';
|
|
9
|
+
import React__default, { useEffect, useContext, useRef, useState } from 'react';
|
|
10
10
|
import { useSelect } from 'downshift';
|
|
11
11
|
import cx from 'classnames';
|
|
12
12
|
import PropTypes from 'prop-types';
|
|
@@ -18,6 +18,7 @@ import { usePrefix } from '../../internal/usePrefix.js';
|
|
|
18
18
|
import '../FluidForm/FluidForm.js';
|
|
19
19
|
import { FormContext } from '../FluidForm/FormContext.js';
|
|
20
20
|
import setupGetInstanceId from '../../tools/setupGetInstanceId.js';
|
|
21
|
+
import { useFloating, size, flip, autoUpdate } from '@floating-ui/react';
|
|
21
22
|
import { ListBoxSize, ListBoxType } from '../ListBox/ListBoxPropTypes.js';
|
|
22
23
|
|
|
23
24
|
const getInstanceId = setupGetInstanceId();
|
|
@@ -43,6 +44,7 @@ const defaultItemToString = item => {
|
|
|
43
44
|
};
|
|
44
45
|
const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
|
|
45
46
|
let {
|
|
47
|
+
autoAlign = false,
|
|
46
48
|
className: containerClassName,
|
|
47
49
|
disabled = false,
|
|
48
50
|
direction = 'bottom',
|
|
@@ -54,7 +56,7 @@ const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
|
|
|
54
56
|
itemToElement = null,
|
|
55
57
|
renderSelectedItem,
|
|
56
58
|
type = 'default',
|
|
57
|
-
size,
|
|
59
|
+
size: size$1,
|
|
58
60
|
onChange,
|
|
59
61
|
id,
|
|
60
62
|
titleText = '',
|
|
@@ -73,6 +75,40 @@ const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
|
|
|
73
75
|
slug,
|
|
74
76
|
...other
|
|
75
77
|
} = _ref;
|
|
78
|
+
const {
|
|
79
|
+
refs,
|
|
80
|
+
floatingStyles
|
|
81
|
+
} = useFloating(autoAlign ? {
|
|
82
|
+
placement: direction,
|
|
83
|
+
// The floating element is positioned relative to its nearest
|
|
84
|
+
// containing block (usually the viewport). It will in many cases also
|
|
85
|
+
// “break” the floating element out of a clipping ancestor.
|
|
86
|
+
// https://floating-ui.com/docs/misc#clipping
|
|
87
|
+
strategy: 'fixed',
|
|
88
|
+
// Middleware order matters, arrow should be last
|
|
89
|
+
middleware: [size({
|
|
90
|
+
apply(_ref2) {
|
|
91
|
+
let {
|
|
92
|
+
rects,
|
|
93
|
+
elements
|
|
94
|
+
} = _ref2;
|
|
95
|
+
Object.assign(elements.floating.style, {
|
|
96
|
+
width: `${rects.reference.width}px`
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}), flip()],
|
|
100
|
+
whileElementsMounted: autoUpdate
|
|
101
|
+
} : {} // When autoAlign is turned off, floating-ui will not be used
|
|
102
|
+
);
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
if (autoAlign) {
|
|
105
|
+
Object.keys(floatingStyles).forEach(style => {
|
|
106
|
+
if (refs.floating.current) {
|
|
107
|
+
refs.floating.current.style[style] = floatingStyles[style];
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}, [floatingStyles, autoAlign, refs.floating]);
|
|
76
112
|
const prefix = usePrefix();
|
|
77
113
|
const {
|
|
78
114
|
isFluid
|
|
@@ -146,8 +182,9 @@ const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
|
|
|
146
182
|
[`${prefix}--dropdown--disabled`]: disabled,
|
|
147
183
|
[`${prefix}--dropdown--light`]: light,
|
|
148
184
|
[`${prefix}--dropdown--readonly`]: readOnly,
|
|
149
|
-
[`${prefix}--dropdown--${size}`]: size,
|
|
150
|
-
[`${prefix}--list-box--up`]: direction === 'top'
|
|
185
|
+
[`${prefix}--dropdown--${size$1}`]: size$1,
|
|
186
|
+
[`${prefix}--list-box--up`]: direction === 'top',
|
|
187
|
+
[`${prefix}--dropdown--autoalign`]: autoAlign
|
|
151
188
|
});
|
|
152
189
|
const titleClasses = cx(`${prefix}--label`, {
|
|
153
190
|
[`${prefix}--label--disabled`]: disabled,
|
|
@@ -174,10 +211,10 @@ const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
|
|
|
174
211
|
id: helperId,
|
|
175
212
|
className: helperClasses
|
|
176
213
|
}, helperText) : null;
|
|
177
|
-
function onSelectedItemChange(
|
|
214
|
+
function onSelectedItemChange(_ref3) {
|
|
178
215
|
let {
|
|
179
216
|
selectedItem
|
|
180
|
-
} =
|
|
217
|
+
} = _ref3;
|
|
181
218
|
if (onChange) {
|
|
182
219
|
onChange({
|
|
183
220
|
selectedItem: selectedItem ?? null
|
|
@@ -227,6 +264,7 @@ const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
|
|
|
227
264
|
}
|
|
228
265
|
};
|
|
229
266
|
const menuProps = getMenuProps();
|
|
267
|
+
const menuRef = mergeRefs(menuProps.ref, refs.setFloating);
|
|
230
268
|
|
|
231
269
|
// Slug is always size `mini`
|
|
232
270
|
let normalizedSlug;
|
|
@@ -243,7 +281,7 @@ const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
|
|
|
243
281
|
onFocus: handleFocus,
|
|
244
282
|
onBlur: handleFocus,
|
|
245
283
|
"aria-label": deprecatedAriaLabel || ariaLabel,
|
|
246
|
-
size: size,
|
|
284
|
+
size: size$1,
|
|
247
285
|
className: className,
|
|
248
286
|
invalid: invalid,
|
|
249
287
|
invalidText: invalidText,
|
|
@@ -251,6 +289,7 @@ const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
|
|
|
251
289
|
warnText: warnText,
|
|
252
290
|
light: light,
|
|
253
291
|
isOpen: isOpen,
|
|
292
|
+
ref: refs.setReference,
|
|
254
293
|
id: id
|
|
255
294
|
}, invalid && /*#__PURE__*/React__default.createElement(WarningFilled, {
|
|
256
295
|
className: `${prefix}--list-box__invalid-icon`
|
|
@@ -273,7 +312,9 @@ const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
|
|
|
273
312
|
}, selectedItem ? renderSelectedItem ? renderSelectedItem(selectedItem) : itemToString(selectedItem) : label), /*#__PURE__*/React__default.createElement(ListBox.MenuIcon, {
|
|
274
313
|
isOpen: isOpen,
|
|
275
314
|
translateWithId: translateWithId
|
|
276
|
-
})), normalizedSlug, /*#__PURE__*/React__default.createElement(ListBox.Menu,
|
|
315
|
+
})), normalizedSlug, /*#__PURE__*/React__default.createElement(ListBox.Menu, _extends({}, menuProps, {
|
|
316
|
+
ref: menuRef
|
|
317
|
+
}), isOpen && items.map((item, index) => {
|
|
277
318
|
const isObject = item !== null && typeof item === 'object';
|
|
278
319
|
const itemProps = getItemProps({
|
|
279
320
|
item,
|
|
@@ -308,6 +349,10 @@ Dropdown.propTypes = {
|
|
|
308
349
|
* Specify a label to be read by screen readers on the container note.
|
|
309
350
|
*/
|
|
310
351
|
ariaLabel: deprecate(PropTypes.string, 'This prop syntax has been deprecated. Please use the new `aria-label`.'),
|
|
352
|
+
/**
|
|
353
|
+
* **Experimental**: Will attempt to automatically align the floating element to avoid collisions with the viewport and being clipped by ancestor elements.
|
|
354
|
+
*/
|
|
355
|
+
autoAlign: PropTypes.bool,
|
|
311
356
|
/**
|
|
312
357
|
* Provide a custom className to be applied on the cds--dropdown node
|
|
313
358
|
*/
|
|
@@ -15,6 +15,7 @@ import Button from '../Button/Button.js';
|
|
|
15
15
|
import '../Button/Button.Skeleton.js';
|
|
16
16
|
import ButtonSet from '../ButtonSet/ButtonSet.js';
|
|
17
17
|
import InlineLoading from '../InlineLoading/InlineLoading.js';
|
|
18
|
+
import { Layer } from '../Layer/index.js';
|
|
18
19
|
import requiredIfGivenPropIsTruthy from '../../prop-types/requiredIfGivenPropIsTruthy.js';
|
|
19
20
|
import wrapFocus, { wrapFocusWithoutSentinels, elementOrParentIsFloatingMenu } from '../../internal/wrapFocus.js';
|
|
20
21
|
import debounce from 'lodash.debounce';
|
|
@@ -266,7 +267,7 @@ const Modal = /*#__PURE__*/React__default.forwardRef(function Modal(_ref, ref) {
|
|
|
266
267
|
as: "h3",
|
|
267
268
|
id: modalHeadingId,
|
|
268
269
|
className: `${prefix}--modal-header__heading`
|
|
269
|
-
}, modalHeading), normalizedSlug, !passiveModal && modalButton), /*#__PURE__*/React__default.createElement(
|
|
270
|
+
}, modalHeading), normalizedSlug, !passiveModal && modalButton), /*#__PURE__*/React__default.createElement(Layer, _extends({
|
|
270
271
|
ref: contentRef,
|
|
271
272
|
id: modalBodyId,
|
|
272
273
|
className: contentClasses
|
|
@@ -301,7 +302,8 @@ const Modal = /*#__PURE__*/React__default.forwardRef(function Modal(_ref, ref) {
|
|
|
301
302
|
className: `${prefix}--inline-loading--btn`,
|
|
302
303
|
onSuccess: onLoadingSuccess
|
|
303
304
|
}))));
|
|
304
|
-
return /*#__PURE__*/React__default.createElement(
|
|
305
|
+
return /*#__PURE__*/React__default.createElement(Layer, _extends({}, rest, {
|
|
306
|
+
level: 0,
|
|
305
307
|
onKeyDown: handleKeyDown,
|
|
306
308
|
onMouseDown: handleMousedown,
|
|
307
309
|
onBlur: !focusTrapWithoutSentinels ? handleBlur : () => {},
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { type UseComboboxProps, type UseMultipleSelectionProps } from 'downshift';
|
|
8
8
|
import { ReactNode, FunctionComponent, ReactElement } from 'react';
|
|
9
|
-
import { type
|
|
10
|
-
export interface FilterableMultiSelectProps<
|
|
9
|
+
import { type MultiSelectSortingProps } from './MultiSelectPropTypes';
|
|
10
|
+
export interface FilterableMultiSelectProps<ItemType> extends MultiSelectSortingProps<ItemType> {
|
|
11
11
|
/**
|
|
12
12
|
* Specify a label to be read by screen readers on the container node
|
|
13
13
|
* @deprecated
|
|
@@ -15,6 +15,12 @@ export interface FilterableMultiSelectProps<Item extends ItemBase> extends Sorti
|
|
|
15
15
|
'aria-label'?: string;
|
|
16
16
|
/** @deprecated */
|
|
17
17
|
ariaLabel?: string;
|
|
18
|
+
/**
|
|
19
|
+
* **Experimental**: Will attempt to automatically align the floating
|
|
20
|
+
* element to avoid collisions with the viewport and being clipped by
|
|
21
|
+
* ancestor elements.
|
|
22
|
+
*/
|
|
23
|
+
autoAlign?: boolean;
|
|
18
24
|
className?: string;
|
|
19
25
|
/**
|
|
20
26
|
* Specify the text that should be read for screen readers that describes total items selected
|
|
@@ -35,14 +41,14 @@ export interface FilterableMultiSelectProps<Item extends ItemBase> extends Sorti
|
|
|
35
41
|
/**
|
|
36
42
|
* Additional props passed to Downshift
|
|
37
43
|
*/
|
|
38
|
-
downshiftProps?: UseMultipleSelectionProps<
|
|
44
|
+
downshiftProps?: UseMultipleSelectionProps<ItemType>;
|
|
39
45
|
/**
|
|
40
46
|
* Default sorter is assigned if not provided.
|
|
41
47
|
*/
|
|
42
|
-
filterItems(items: readonly
|
|
48
|
+
filterItems(items: readonly ItemType[], extra: {
|
|
43
49
|
inputValue: string | null;
|
|
44
|
-
itemToString: NonNullable<UseMultipleSelectionProps<
|
|
45
|
-
}):
|
|
50
|
+
itemToString: NonNullable<UseMultipleSelectionProps<ItemType>['itemToString']>;
|
|
51
|
+
}): ItemType[];
|
|
46
52
|
/**
|
|
47
53
|
* Specify whether the title text should be hidden or not
|
|
48
54
|
*/
|
|
@@ -60,7 +66,7 @@ export interface FilterableMultiSelectProps<Item extends ItemBase> extends Sorti
|
|
|
60
66
|
* Allow users to pass in arbitrary items from their collection that are
|
|
61
67
|
* pre-selected
|
|
62
68
|
*/
|
|
63
|
-
initialSelectedItems?:
|
|
69
|
+
initialSelectedItems?: ItemType[];
|
|
64
70
|
/**
|
|
65
71
|
* Is the current selection invalid?
|
|
66
72
|
*/
|
|
@@ -73,7 +79,7 @@ export interface FilterableMultiSelectProps<Item extends ItemBase> extends Sorti
|
|
|
73
79
|
* Function to render items as custom components instead of strings.
|
|
74
80
|
* Defaults to null and is overridden by a getter
|
|
75
81
|
*/
|
|
76
|
-
itemToElement?: FunctionComponent<
|
|
82
|
+
itemToElement?: FunctionComponent<ItemType>;
|
|
77
83
|
/**
|
|
78
84
|
* Helper function passed to downshift that allows the library to render
|
|
79
85
|
* a given item to a string label.
|
|
@@ -81,12 +87,12 @@ export interface FilterableMultiSelectProps<Item extends ItemBase> extends Sorti
|
|
|
81
87
|
* By default, it extracts the `label` field from a given item
|
|
82
88
|
* to serve as the item label in the list.
|
|
83
89
|
*/
|
|
84
|
-
itemToString?(item:
|
|
90
|
+
itemToString?(item: ItemType | null): string;
|
|
85
91
|
/**
|
|
86
92
|
* We try to stay as generic as possible here to allow individuals to pass
|
|
87
93
|
* in a collection of whatever kind of data structure they prefer
|
|
88
94
|
*/
|
|
89
|
-
items:
|
|
95
|
+
items: ItemType[];
|
|
90
96
|
/**
|
|
91
97
|
* @deprecated `true` to use the light version.
|
|
92
98
|
*/
|
|
@@ -102,13 +108,13 @@ export interface FilterableMultiSelectProps<Item extends ItemBase> extends Sorti
|
|
|
102
108
|
* consuming component what kind of internal state changes are occurring.
|
|
103
109
|
*/
|
|
104
110
|
onChange?(changes: {
|
|
105
|
-
selectedItems:
|
|
111
|
+
selectedItems: ItemType[];
|
|
106
112
|
}): void;
|
|
107
113
|
/**
|
|
108
114
|
* A utility for this controlled component
|
|
109
115
|
* to communicate to the currently typed input.
|
|
110
116
|
*/
|
|
111
|
-
onInputValueChange?: UseComboboxProps<
|
|
117
|
+
onInputValueChange?: UseComboboxProps<ItemType>['onInputValueChange'];
|
|
112
118
|
/**
|
|
113
119
|
* `onMenuChange` is a utility for this controlled component to communicate to a
|
|
114
120
|
* consuming component that the menu was opened(`true`)/closed(`false`).
|
|
@@ -133,7 +139,7 @@ export interface FilterableMultiSelectProps<Item extends ItemBase> extends Sorti
|
|
|
133
139
|
/**
|
|
134
140
|
* For full control of the selected items
|
|
135
141
|
*/
|
|
136
|
-
selectedItems?:
|
|
142
|
+
selectedItems?: ItemType[];
|
|
137
143
|
/**
|
|
138
144
|
* Specify the size of the ListBox.
|
|
139
145
|
* Currently, supports either `sm`, `md` or `lg` as an option.
|
|
@@ -167,7 +173,7 @@ export interface FilterableMultiSelectProps<Item extends ItemBase> extends Sorti
|
|
|
167
173
|
warnText?: ReactNode;
|
|
168
174
|
}
|
|
169
175
|
declare const FilterableMultiSelect: {
|
|
170
|
-
<
|
|
176
|
+
<ItemType>(props: FilterableMultiSelectProps<ItemType>): ReactElement;
|
|
171
177
|
propTypes?: any;
|
|
172
178
|
contextTypes?: any;
|
|
173
179
|
defaultProps?: any;
|
|
@@ -11,7 +11,7 @@ import cx from 'classnames';
|
|
|
11
11
|
import Downshift, { useCombobox, useMultipleSelection } from 'downshift';
|
|
12
12
|
import isEqual from 'lodash.isequal';
|
|
13
13
|
import PropTypes from 'prop-types';
|
|
14
|
-
import React__default, { useContext, useState, useRef, useEffect } from 'react';
|
|
14
|
+
import React__default, { useContext, useState, useLayoutEffect, useRef, useEffect } from 'react';
|
|
15
15
|
import { defaultFilterItems } from '../ComboBox/tools/filter.js';
|
|
16
16
|
import { sortingPropTypes } from './MultiSelectPropTypes.js';
|
|
17
17
|
import ListBox from '../ListBox/index.js';
|
|
@@ -24,6 +24,7 @@ import { usePrefix } from '../../internal/usePrefix.js';
|
|
|
24
24
|
import '../FluidForm/FluidForm.js';
|
|
25
25
|
import { FormContext } from '../FluidForm/FormContext.js';
|
|
26
26
|
import { useSelection } from '../../internal/Selection.js';
|
|
27
|
+
import { useFloating, flip, size, autoUpdate } from '@floating-ui/react';
|
|
27
28
|
import { match } from '../../internal/keyboard/match.js';
|
|
28
29
|
import ListBoxSelection from '../ListBox/next/ListBoxSelection.js';
|
|
29
30
|
import ListBoxTrigger from '../ListBox/next/ListBoxTrigger.js';
|
|
@@ -53,6 +54,7 @@ const {
|
|
|
53
54
|
} = useMultipleSelection.stateChangeTypes;
|
|
54
55
|
const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function FilterableMultiSelect(_ref, ref) {
|
|
55
56
|
let {
|
|
57
|
+
autoAlign = false,
|
|
56
58
|
className: containerClassName,
|
|
57
59
|
clearSelectionDescription = 'Total items selected: ',
|
|
58
60
|
clearSelectionText = 'To clear selection, press Delete or Backspace',
|
|
@@ -82,7 +84,7 @@ const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function Fi
|
|
|
82
84
|
type,
|
|
83
85
|
selectionFeedback = 'top-after-reopen',
|
|
84
86
|
selectedItems: selected,
|
|
85
|
-
size,
|
|
87
|
+
size: size$1,
|
|
86
88
|
sortItems = defaultSortItems,
|
|
87
89
|
translateWithId,
|
|
88
90
|
useTitleInItem,
|
|
@@ -109,6 +111,42 @@ const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function Fi
|
|
|
109
111
|
onChange,
|
|
110
112
|
selectedItems: selected
|
|
111
113
|
});
|
|
114
|
+
const {
|
|
115
|
+
refs,
|
|
116
|
+
floatingStyles,
|
|
117
|
+
middlewareData
|
|
118
|
+
} = useFloating(autoAlign ? {
|
|
119
|
+
placement: direction,
|
|
120
|
+
// The floating element is positioned relative to its nearest
|
|
121
|
+
// containing block (usually the viewport). It will in many cases also
|
|
122
|
+
// “break” the floating element out of a clipping ancestor.
|
|
123
|
+
// https://floating-ui.com/docs/misc#clipping
|
|
124
|
+
strategy: 'fixed',
|
|
125
|
+
// Middleware order matters, arrow should be last
|
|
126
|
+
middleware: [flip({
|
|
127
|
+
crossAxis: false
|
|
128
|
+
}), size({
|
|
129
|
+
apply(_ref2) {
|
|
130
|
+
let {
|
|
131
|
+
rects,
|
|
132
|
+
elements
|
|
133
|
+
} = _ref2;
|
|
134
|
+
Object.assign(elements.floating.style, {
|
|
135
|
+
width: `${rects.reference.width}px`
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
})],
|
|
139
|
+
whileElementsMounted: autoUpdate
|
|
140
|
+
} : {});
|
|
141
|
+
useLayoutEffect(() => {
|
|
142
|
+
if (autoAlign) {
|
|
143
|
+
Object.keys(floatingStyles).forEach(style => {
|
|
144
|
+
if (refs.floating.current) {
|
|
145
|
+
refs.floating.current.style[style] = floatingStyles[style];
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}, [autoAlign, floatingStyles, refs.floating, middlewareData, open]);
|
|
112
150
|
const textInput = useRef(null);
|
|
113
151
|
const filterableMultiSelectInstanceId = useId();
|
|
114
152
|
const prefix = usePrefix();
|
|
@@ -116,6 +154,8 @@ const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function Fi
|
|
|
116
154
|
setIsOpen(open);
|
|
117
155
|
setPrevOpen(open);
|
|
118
156
|
}
|
|
157
|
+
|
|
158
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
119
159
|
const sortedItems = sortItems(filterItems(items, {
|
|
120
160
|
itemToString,
|
|
121
161
|
inputValue
|
|
@@ -453,9 +493,10 @@ const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function Fi
|
|
|
453
493
|
warn: warn,
|
|
454
494
|
warnText: warnText,
|
|
455
495
|
isOpen: isOpen,
|
|
456
|
-
size: size
|
|
496
|
+
size: size$1
|
|
457
497
|
}, /*#__PURE__*/React__default.createElement("div", {
|
|
458
|
-
className: `${prefix}--list-box__field
|
|
498
|
+
className: `${prefix}--list-box__field`,
|
|
499
|
+
ref: refs.setReference
|
|
459
500
|
}, controlledSelectedItems.length > 0 &&
|
|
460
501
|
/*#__PURE__*/
|
|
461
502
|
// @ts-expect-error: It is expecting a non-required prop called: "onClearSelection"
|
|
@@ -496,7 +537,9 @@ const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function Fi
|
|
|
496
537
|
// @ts-expect-error
|
|
497
538
|
isOpen: isOpen,
|
|
498
539
|
translateWithId: translateWithId
|
|
499
|
-
}))), normalizedSlug, /*#__PURE__*/React__default.createElement(ListBox.Menu,
|
|
540
|
+
}))), normalizedSlug, /*#__PURE__*/React__default.createElement(ListBox.Menu, _extends({}, menuProps, {
|
|
541
|
+
ref: refs.setFloating
|
|
542
|
+
}), isOpen ? sortedItems.map((item, index) => {
|
|
500
543
|
const isChecked = controlledSelectedItems.filter(selected => isEqual(selected, item)).length > 0;
|
|
501
544
|
const itemProps = getItemProps({
|
|
502
545
|
item,
|
|
@@ -544,6 +587,12 @@ FilterableMultiSelect.propTypes = {
|
|
|
544
587
|
* Specify a label to be read by screen readers on the container note.
|
|
545
588
|
*/
|
|
546
589
|
ariaLabel: deprecate(PropTypes.string, 'ariaLabel / aria-label props are no longer required for FilterableMultiSelect'),
|
|
590
|
+
/**
|
|
591
|
+
* **Experimental**: Will attempt to automatically align the floating
|
|
592
|
+
* element to avoid collisions with the viewport and being clipped by
|
|
593
|
+
* ancestor elements.
|
|
594
|
+
*/
|
|
595
|
+
autoAlign: PropTypes.bool,
|
|
547
596
|
/**
|
|
548
597
|
* Specify the text that should be read for screen readers that describes total items selected
|
|
549
598
|
*/
|
|
@@ -7,50 +7,19 @@
|
|
|
7
7
|
import { UseSelectProps } from 'downshift';
|
|
8
8
|
import React, { ReactNode } from 'react';
|
|
9
9
|
import { ListBoxSize, ListBoxType } from '../ListBox';
|
|
10
|
+
import { MultiSelectSortingProps } from './MultiSelectPropTypes';
|
|
10
11
|
import { ListBoxProps } from '../ListBox/ListBox';
|
|
11
12
|
import type { InternationalProps } from '../../types/common';
|
|
12
|
-
interface SharedOptions {
|
|
13
|
-
locale: string;
|
|
14
|
-
}
|
|
15
|
-
interface DownshiftTypedProps<ItemType> {
|
|
16
|
-
itemToString?(item: ItemType): string;
|
|
17
|
-
}
|
|
18
|
-
interface SortItemsOptions<ItemType> extends SharedOptions, DownshiftTypedProps<ItemType> {
|
|
19
|
-
compareItems(item1: ItemType, item2: ItemType, options: SharedOptions): number;
|
|
20
|
-
selectedItems: ItemType[];
|
|
21
|
-
}
|
|
22
|
-
interface MultiSelectSortingProps<ItemType> {
|
|
23
|
-
/**
|
|
24
|
-
* Provide a compare function that is used to determine the ordering of
|
|
25
|
-
* options. See 'sortItems' for more control.
|
|
26
|
-
*/
|
|
27
|
-
compareItems?(item1: ItemType, item2: ItemType, options: SharedOptions): number;
|
|
28
|
-
/**
|
|
29
|
-
* Provide a method that sorts all options in the control. Overriding this
|
|
30
|
-
* prop means that you also have to handle the sort logic for selected versus
|
|
31
|
-
* un-selected items. If you just want to control ordering, consider the
|
|
32
|
-
* `compareItems` prop instead.
|
|
33
|
-
*
|
|
34
|
-
* The return value should be a number whose sign indicates the relative order
|
|
35
|
-
* of the two elements: negative if a is less than b, positive if a is greater
|
|
36
|
-
* than b, and zero if they are equal.
|
|
37
|
-
*
|
|
38
|
-
* sortItems :
|
|
39
|
-
* (items: Array<Item>, {
|
|
40
|
-
* selectedItems: Array<Item>,
|
|
41
|
-
* itemToString: Item => string,
|
|
42
|
-
* compareItems: (itemA: string, itemB: string, {
|
|
43
|
-
* locale: string
|
|
44
|
-
* }) => number,
|
|
45
|
-
* locale: string,
|
|
46
|
-
* }) => Array<Item>
|
|
47
|
-
*/
|
|
48
|
-
sortItems?(items: ReadonlyArray<ItemType>, options: SortItemsOptions<ItemType>): ItemType[];
|
|
49
|
-
}
|
|
50
13
|
interface OnChangeData<ItemType> {
|
|
51
14
|
selectedItems: ItemType[] | null;
|
|
52
15
|
}
|
|
53
16
|
export interface MultiSelectProps<ItemType> extends MultiSelectSortingProps<ItemType>, InternationalProps<'close.menu' | 'open.menu' | 'clear.all' | 'clear.selection'> {
|
|
17
|
+
/**
|
|
18
|
+
* **Experimental**: Will attempt to automatically align the floating
|
|
19
|
+
* element to avoid collisions with the viewport and being clipped by
|
|
20
|
+
* ancestor elements.
|
|
21
|
+
*/
|
|
22
|
+
autoAlign?: boolean;
|
|
54
23
|
className?: string;
|
|
55
24
|
/**
|
|
56
25
|
* Specify the text that should be read for screen readers that describes that all items are deleted
|
|
@@ -11,7 +11,7 @@ import cx from 'classnames';
|
|
|
11
11
|
import { useSelect } from 'downshift';
|
|
12
12
|
import isEqual from 'lodash.isequal';
|
|
13
13
|
import PropTypes from 'prop-types';
|
|
14
|
-
import React__default, { useContext, useRef, useState, useMemo } from 'react';
|
|
14
|
+
import React__default, { useContext, useRef, useState, useLayoutEffect, useMemo } from 'react';
|
|
15
15
|
import ListBox from '../ListBox/index.js';
|
|
16
16
|
import { sortingPropTypes } from './MultiSelectPropTypes.js';
|
|
17
17
|
import { defaultSortItems, defaultCompareItems } from './tools/sorting.js';
|
|
@@ -23,6 +23,7 @@ import { usePrefix } from '../../internal/usePrefix.js';
|
|
|
23
23
|
import '../FluidForm/FluidForm.js';
|
|
24
24
|
import { FormContext } from '../FluidForm/FormContext.js';
|
|
25
25
|
import { noopFn } from '../../internal/noopFn.js';
|
|
26
|
+
import { useFloating, flip, size, autoUpdate } from '@floating-ui/react';
|
|
26
27
|
import { match } from '../../internal/keyboard/match.js';
|
|
27
28
|
import { ListBoxSize } from '../ListBox/ListBoxPropTypes.js';
|
|
28
29
|
import { Delete, Escape, Space, ArrowDown, Enter } from '../../internal/keyboard/keys.js';
|
|
@@ -57,6 +58,7 @@ const defaultItemToString = item => {
|
|
|
57
58
|
};
|
|
58
59
|
const MultiSelect = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
|
|
59
60
|
let {
|
|
61
|
+
autoAlign = false,
|
|
60
62
|
className: containerClassName,
|
|
61
63
|
id,
|
|
62
64
|
items,
|
|
@@ -67,7 +69,7 @@ const MultiSelect = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
|
|
|
67
69
|
helperText,
|
|
68
70
|
label,
|
|
69
71
|
type = 'default',
|
|
70
|
-
size,
|
|
72
|
+
size: size$1,
|
|
71
73
|
disabled = false,
|
|
72
74
|
initialSelectedItems = [],
|
|
73
75
|
sortItems = defaultSortItems,
|
|
@@ -116,6 +118,42 @@ const MultiSelect = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
|
|
|
116
118
|
onChange,
|
|
117
119
|
selectedItems: selected
|
|
118
120
|
});
|
|
121
|
+
const {
|
|
122
|
+
refs,
|
|
123
|
+
floatingStyles,
|
|
124
|
+
middlewareData
|
|
125
|
+
} = useFloating(autoAlign ? {
|
|
126
|
+
placement: direction,
|
|
127
|
+
// The floating element is positioned relative to its nearest
|
|
128
|
+
// containing block (usually the viewport). It will in many cases also
|
|
129
|
+
// “break” the floating element out of a clipping ancestor.
|
|
130
|
+
// https://floating-ui.com/docs/misc#clipping
|
|
131
|
+
strategy: 'fixed',
|
|
132
|
+
// Middleware order matters, arrow should be last
|
|
133
|
+
middleware: [flip({
|
|
134
|
+
crossAxis: false
|
|
135
|
+
}), size({
|
|
136
|
+
apply(_ref2) {
|
|
137
|
+
let {
|
|
138
|
+
rects,
|
|
139
|
+
elements
|
|
140
|
+
} = _ref2;
|
|
141
|
+
Object.assign(elements.floating.style, {
|
|
142
|
+
width: `${rects.reference.width}px`
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
})],
|
|
146
|
+
whileElementsMounted: autoUpdate
|
|
147
|
+
} : {});
|
|
148
|
+
useLayoutEffect(() => {
|
|
149
|
+
if (autoAlign) {
|
|
150
|
+
Object.keys(floatingStyles).forEach(style => {
|
|
151
|
+
if (refs.floating.current) {
|
|
152
|
+
refs.floating.current.style[style] = floatingStyles[style];
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}, [autoAlign, floatingStyles, refs.floating, middlewareData, open]);
|
|
119
157
|
|
|
120
158
|
// Filter out items with an object having undefined values
|
|
121
159
|
const filteredItems = useMemo(() => {
|
|
@@ -367,7 +405,7 @@ const MultiSelect = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
|
|
|
367
405
|
onFocus: isFluid ? handleFocus : undefined,
|
|
368
406
|
onBlur: isFluid ? handleFocus : undefined,
|
|
369
407
|
type: type,
|
|
370
|
-
size: size,
|
|
408
|
+
size: size$1,
|
|
371
409
|
className: className,
|
|
372
410
|
disabled: disabled,
|
|
373
411
|
light: light,
|
|
@@ -382,7 +420,8 @@ const MultiSelect = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
|
|
|
382
420
|
}), showWarning && /*#__PURE__*/React__default.createElement(WarningAltFilled, {
|
|
383
421
|
className: `${prefix}--list-box__invalid-icon ${prefix}--list-box__invalid-icon--warning`
|
|
384
422
|
}), /*#__PURE__*/React__default.createElement("div", {
|
|
385
|
-
className: multiSelectFieldWrapperClasses
|
|
423
|
+
className: multiSelectFieldWrapperClasses,
|
|
424
|
+
ref: refs.setReference
|
|
386
425
|
}, selectedItems.length > 0 && /*#__PURE__*/React__default.createElement(ListBox.Selection, {
|
|
387
426
|
readOnly: readOnly,
|
|
388
427
|
clearSelection: !disabled && !readOnly ? clearSelection : noopFn,
|
|
@@ -405,7 +444,9 @@ const MultiSelect = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
|
|
|
405
444
|
}, label), /*#__PURE__*/React__default.createElement(ListBox.MenuIcon, {
|
|
406
445
|
isOpen: isOpen,
|
|
407
446
|
translateWithId: translateWithId
|
|
408
|
-
})), normalizedSlug), /*#__PURE__*/React__default.createElement(ListBox.Menu, getMenuProps(
|
|
447
|
+
})), normalizedSlug), /*#__PURE__*/React__default.createElement(ListBox.Menu, getMenuProps({
|
|
448
|
+
ref: refs.setFloating
|
|
449
|
+
}), isOpen &&
|
|
409
450
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
410
451
|
sortItems(filteredItems, sortOptions).map((item, index) => {
|
|
411
452
|
const isChecked = selectedItems.filter(selected => isEqual(selected, item)).length > 0;
|
|
@@ -444,6 +485,12 @@ const MultiSelect = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
|
|
|
444
485
|
MultiSelect.displayName = 'MultiSelect';
|
|
445
486
|
MultiSelect.propTypes = {
|
|
446
487
|
...sortingPropTypes,
|
|
488
|
+
/**
|
|
489
|
+
* **Experimental**: Will attempt to automatically align the floating
|
|
490
|
+
* element to avoid collisions with the viewport and being clipped by
|
|
491
|
+
* ancestor elements.
|
|
492
|
+
*/
|
|
493
|
+
autoAlign: PropTypes.bool,
|
|
447
494
|
/**
|
|
448
495
|
* Provide a custom class name to be added to the outermost node in the
|
|
449
496
|
* component
|