@carbon/react 1.111.1 → 1.112.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 +994 -994
- package/es/components/ComboBox/ComboBox.js +7 -7
- package/es/components/Dropdown/Dropdown.js +1 -4
- package/es/components/FeatureFlags/index.js +32 -14
- package/es/components/IconIndicator/index.d.ts +33 -1
- package/es/components/IconIndicator/index.js +55 -26
- package/es/components/MultiSelect/FilterableMultiSelect.js +6 -7
- package/es/components/MultiSelect/MultiSelect.js +3 -4
- package/es/components/OverflowMenu/OverflowMenu.js +31 -6
- package/es/components/OverflowMenu/next/index.d.ts +1 -1
- package/es/components/OverflowMenu/next/index.js +2 -2
- package/es/components/ShapeIndicator/index.d.ts +18 -0
- package/es/components/ShapeIndicator/index.js +49 -7
- package/es/components/Tabs/Tabs.js +1 -1
- package/es/components/Tooltip/DefinitionTooltip.js +5 -2
- package/es/internal/index.d.ts +2 -1
- package/es/internal/isItemDisabled.d.ts +7 -0
- package/es/internal/isItemDisabled.js +17 -0
- package/lib/components/ComboBox/ComboBox.js +7 -7
- package/lib/components/Dropdown/Dropdown.js +2 -5
- package/lib/components/FeatureFlags/index.js +32 -14
- package/lib/components/IconIndicator/index.d.ts +33 -1
- package/lib/components/IconIndicator/index.js +54 -25
- package/lib/components/MultiSelect/FilterableMultiSelect.js +6 -7
- package/lib/components/MultiSelect/MultiSelect.js +3 -4
- package/lib/components/OverflowMenu/OverflowMenu.js +30 -5
- package/lib/components/OverflowMenu/next/index.d.ts +1 -1
- package/lib/components/OverflowMenu/next/index.js +2 -2
- package/lib/components/ShapeIndicator/index.d.ts +18 -0
- package/lib/components/ShapeIndicator/index.js +48 -6
- package/lib/components/Tabs/Tabs.js +1 -1
- package/lib/components/Tooltip/DefinitionTooltip.js +4 -1
- package/lib/internal/index.d.ts +2 -1
- package/lib/internal/isItemDisabled.d.ts +7 -0
- package/lib/internal/isItemDisabled.js +17 -0
- package/package.json +9 -8
- package/telemetry.yml +2 -0
|
@@ -12,6 +12,7 @@ import { match } from "../../internal/keyboard/match.js";
|
|
|
12
12
|
import { useId } from "../../internal/useId.js";
|
|
13
13
|
import { deprecate } from "../../prop-types/deprecate.js";
|
|
14
14
|
import { defaultItemToString } from "../../internal/defaultItemToString.js";
|
|
15
|
+
import { isItemDisabled } from "../../internal/isItemDisabled.js";
|
|
15
16
|
import { isComponentElement } from "../../internal/utils.js";
|
|
16
17
|
import { useFeatureFlag } from "../FeatureFlags/index.js";
|
|
17
18
|
import { useNormalizedInputProps } from "../../internal/useNormalizedInputProps.js";
|
|
@@ -39,7 +40,6 @@ import isEqual from "react-fast-compare";
|
|
|
39
40
|
*/
|
|
40
41
|
const { InputBlur, InputKeyDownEnter, FunctionToggleMenu, ToggleButtonClick, ItemMouseMove, InputKeyDownArrowUp, InputKeyDownArrowDown, MenuMouseLeave, ItemClick, FunctionSelectItem } = useCombobox.stateChangeTypes;
|
|
41
42
|
const defaultShouldFilterItem = () => true;
|
|
42
|
-
const isDisabledItem = (item) => item !== null && typeof item === "object" && "disabled" in item && Boolean(item.disabled);
|
|
43
43
|
const autocompleteCustomFilter = ({ item, inputValue }) => {
|
|
44
44
|
if (inputValue === null || inputValue === "") return true;
|
|
45
45
|
const lowercaseItem = item.toLowerCase();
|
|
@@ -56,7 +56,7 @@ const findHighlightedIndex = ({ items, itemToString = defaultItemToString }, inp
|
|
|
56
56
|
const searchValue = inputValue.toLowerCase();
|
|
57
57
|
for (let i = 0; i < items.length; i++) {
|
|
58
58
|
const item = itemToString(items[i]).toLowerCase();
|
|
59
|
-
if (!
|
|
59
|
+
if (!isItemDisabled(items[i]) && item.indexOf(searchValue) !== -1) return i;
|
|
60
60
|
}
|
|
61
61
|
return -1;
|
|
62
62
|
};
|
|
@@ -99,7 +99,7 @@ const ComboBox = forwardRef((props, ref) => {
|
|
|
99
99
|
useEffect(() => {
|
|
100
100
|
if (typeahead) {
|
|
101
101
|
if (inputValue.length >= prevInputLengthRef.current) if (inputValue) {
|
|
102
|
-
const filteredItems = items.filter((item) => !
|
|
102
|
+
const filteredItems = items.filter((item) => !isItemDisabled(item) && autocompleteCustomFilter({
|
|
103
103
|
item: itemToString(item),
|
|
104
104
|
inputValue
|
|
105
105
|
}));
|
|
@@ -221,7 +221,7 @@ const ComboBox = forwardRef((props, ref) => {
|
|
|
221
221
|
case InputKeyDownEnter:
|
|
222
222
|
if (!allowCustomValue) if (state.highlightedIndex !== -1) {
|
|
223
223
|
const highlightedItem = filterItems(items, itemToString, inputValue)[state.highlightedIndex];
|
|
224
|
-
if (highlightedItem && !
|
|
224
|
+
if (highlightedItem && !isItemDisabled(highlightedItem)) return {
|
|
225
225
|
...changes,
|
|
226
226
|
selectedItem: highlightedItem,
|
|
227
227
|
inputValue: itemToString(highlightedItem)
|
|
@@ -230,7 +230,7 @@ const ComboBox = forwardRef((props, ref) => {
|
|
|
230
230
|
const autoIndex = indexToHighlight(inputValue);
|
|
231
231
|
if (autoIndex !== -1) {
|
|
232
232
|
const matchingItem = items[autoIndex];
|
|
233
|
-
if (matchingItem && !
|
|
233
|
+
if (matchingItem && !isItemDisabled(matchingItem)) return {
|
|
234
234
|
...changes,
|
|
235
235
|
selectedItem: matchingItem,
|
|
236
236
|
inputValue: itemToString(matchingItem)
|
|
@@ -352,7 +352,7 @@ const ComboBox = forwardRef((props, ref) => {
|
|
|
352
352
|
initialSelectedItem,
|
|
353
353
|
inputId: id,
|
|
354
354
|
stateReducer,
|
|
355
|
-
isItemDisabled
|
|
355
|
+
isItemDisabled,
|
|
356
356
|
...downshiftProps,
|
|
357
357
|
onStateChange: ({ type, selectedItem: newSelectedItem }) => {
|
|
358
358
|
if (isManualClearingRef.current || isSyncingControlledSelectionRef.current) {
|
|
@@ -539,7 +539,7 @@ const ComboBox = forwardRef((props, ref) => {
|
|
|
539
539
|
}
|
|
540
540
|
if (typeahead && event.key === "Tab") {
|
|
541
541
|
if (!isOpen) return;
|
|
542
|
-
const matchingItem = items.find((item) => !
|
|
542
|
+
const matchingItem = items.find((item) => !isItemDisabled(item) && itemToString(item).toLowerCase().startsWith(inputValue.toLowerCase()));
|
|
543
543
|
if (matchingItem) {
|
|
544
544
|
downshiftSetInputValue(itemToString(matchingItem));
|
|
545
545
|
selectItem(matchingItem);
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { usePrefix } from "../../internal/usePrefix.js";
|
|
9
9
|
import { deprecate } from "../../prop-types/deprecate.js";
|
|
10
10
|
import { defaultItemToString } from "../../internal/defaultItemToString.js";
|
|
11
|
+
import { isItemDisabled } from "../../internal/isItemDisabled.js";
|
|
11
12
|
import { isComponentElement } from "../../internal/utils.js";
|
|
12
13
|
import { useFeatureFlag } from "../FeatureFlags/index.js";
|
|
13
14
|
import { useNormalizedInputProps } from "../../internal/useNormalizedInputProps.js";
|
|
@@ -99,9 +100,6 @@ const Dropdown = React.forwardRef(({ autoAlign = false, className: containerClas
|
|
|
99
100
|
const onSelectedItemChange = useCallback(({ selectedItem }) => {
|
|
100
101
|
if (onChange) onChange({ selectedItem: selectedItem ?? null });
|
|
101
102
|
}, [onChange]);
|
|
102
|
-
const isItemDisabled = useCallback((item) => {
|
|
103
|
-
return item !== null && typeof item === "object" && "disabled" in item && item.disabled === true;
|
|
104
|
-
}, []);
|
|
105
103
|
const onHighlightedIndexChange = useCallback((changes) => {
|
|
106
104
|
const { highlightedIndex } = changes;
|
|
107
105
|
if (highlightedIndex !== void 0 && highlightedIndex > -1) {
|
|
@@ -128,7 +126,6 @@ const Dropdown = React.forwardRef(({ autoAlign = false, className: containerClas
|
|
|
128
126
|
initialSelectedItem,
|
|
129
127
|
onSelectedItemChange,
|
|
130
128
|
stateReducer,
|
|
131
|
-
isItemDisabled,
|
|
132
129
|
onHighlightedIndexChange,
|
|
133
130
|
downshiftProps
|
|
134
131
|
]);
|
|
@@ -22,27 +22,45 @@ import { jsx } from "react/jsx-runtime";
|
|
|
22
22
|
* or disable feature flags in a given React tree
|
|
23
23
|
*/
|
|
24
24
|
const FeatureFlagContext = createContext(FeatureFlags);
|
|
25
|
+
const PROP_TO_FLAG = {
|
|
26
|
+
enableV12TileDefaultIcons: "enable-v12-tile-default-icons",
|
|
27
|
+
enableV12TileRadioIcons: "enable-v12-tile-radio-icons",
|
|
28
|
+
enableV12Overflowmenu: "enable-v12-overflowmenu",
|
|
29
|
+
enableTreeviewControllable: "enable-treeview-controllable",
|
|
30
|
+
enableExperimentalFocusWrapWithoutSentinels: "enable-experimental-focus-wrap-without-sentinels",
|
|
31
|
+
enableFocusWrapWithoutSentinels: "enable-focus-wrap-without-sentinels",
|
|
32
|
+
enableDialogElement: "enable-dialog-element",
|
|
33
|
+
enableV12DynamicFloatingStyles: "enable-v12-dynamic-floating-styles",
|
|
34
|
+
enableEnhancedFileUploader: "enable-enhanced-file-uploader",
|
|
35
|
+
enablePresence: "enable-presence"
|
|
36
|
+
};
|
|
25
37
|
/**
|
|
26
38
|
* Supports an object of feature flag values with the `flags` prop, merging them
|
|
27
39
|
* along with the current `FeatureFlagContext` to provide consumers to check if
|
|
28
40
|
* a feature flag is enabled or disabled in a given React tree
|
|
29
41
|
*/
|
|
30
|
-
const FeatureFlags$1 = ({ children, flags
|
|
42
|
+
const FeatureFlags$1 = ({ children, flags, enableV12TileDefaultIcons, enableV12TileRadioIcons, enableV12Overflowmenu, enableTreeviewControllable, enableExperimentalFocusWrapWithoutSentinels, enableFocusWrapWithoutSentinels, enableDialogElement, enableV12DynamicFloatingStyles, enableEnhancedFileUploader, enablePresence }) => {
|
|
31
43
|
const parentScope = useContext(FeatureFlagContext);
|
|
32
44
|
const scope = useMemo(() => {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
45
|
+
const flagProps = {
|
|
46
|
+
enableV12TileDefaultIcons,
|
|
47
|
+
enableV12TileRadioIcons,
|
|
48
|
+
enableV12Overflowmenu,
|
|
49
|
+
enableTreeviewControllable,
|
|
50
|
+
enableExperimentalFocusWrapWithoutSentinels,
|
|
51
|
+
enableFocusWrapWithoutSentinels,
|
|
52
|
+
enableDialogElement,
|
|
53
|
+
enableV12DynamicFloatingStyles,
|
|
54
|
+
enableEnhancedFileUploader,
|
|
55
|
+
enablePresence
|
|
56
|
+
};
|
|
57
|
+
const explicitFlags = {};
|
|
58
|
+
for (const [prop, flagKey] of Object.entries(PROP_TO_FLAG)) {
|
|
59
|
+
const value = flagProps[prop];
|
|
60
|
+
if (value !== void 0) explicitFlags[flagKey] = value;
|
|
61
|
+
}
|
|
62
|
+
if (flags) Object.assign(explicitFlags, flags);
|
|
63
|
+
const scope = createScope(explicitFlags);
|
|
46
64
|
scope.mergeWithScope(parentScope);
|
|
47
65
|
return scope;
|
|
48
66
|
}, [
|
|
@@ -5,13 +5,45 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
import React from 'react';
|
|
8
|
-
|
|
8
|
+
import { PopoverAlignment } from '../Popover';
|
|
9
|
+
declare const iconTypes: {
|
|
10
|
+
readonly failed: import("@carbon/icons-react").CarbonIconType;
|
|
11
|
+
readonly 'caution-major': import("@carbon/icons-react").CarbonIconType;
|
|
12
|
+
readonly 'caution-minor': import("@carbon/icons-react").CarbonIconType;
|
|
13
|
+
readonly undefined: import("@carbon/icons-react").CarbonIconType;
|
|
14
|
+
readonly succeeded: import("@carbon/icons-react").CarbonIconType;
|
|
15
|
+
readonly normal: import("@carbon/icons-react").CarbonIconType;
|
|
16
|
+
readonly 'in-progress': import("@carbon/icons-react").CarbonIconType;
|
|
17
|
+
readonly incomplete: import("@carbon/icons-react").CarbonIconType;
|
|
18
|
+
readonly 'not-started': import("@carbon/icons-react").CarbonIconType;
|
|
19
|
+
readonly pending: import("@carbon/icons-react").CarbonIconType;
|
|
20
|
+
readonly unknown: import("@carbon/icons-react").CarbonIconType;
|
|
21
|
+
readonly informative: import("@carbon/icons-react").CarbonIconType;
|
|
22
|
+
};
|
|
23
|
+
export declare const IconIndicatorKinds: (keyof typeof iconTypes)[];
|
|
9
24
|
export type IconIndicatorKind = (typeof IconIndicatorKinds)[number];
|
|
10
25
|
export interface IconIndicatorProps {
|
|
26
|
+
/**
|
|
27
|
+
* Specify how the tooltip should align with the icon in compact mode
|
|
28
|
+
*/
|
|
29
|
+
align?: PopoverAlignment;
|
|
30
|
+
/**
|
|
31
|
+
* Will auto-align the tooltip in compact mode.
|
|
32
|
+
*/
|
|
33
|
+
autoAlign?: boolean;
|
|
11
34
|
/**
|
|
12
35
|
* Specify an optional className to add.
|
|
13
36
|
*/
|
|
14
37
|
className?: string;
|
|
38
|
+
/**
|
|
39
|
+
* When true, displays only the icon with the label in a tooltip
|
|
40
|
+
*/
|
|
41
|
+
compact?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Description for the icon announced to screen readers in compact mode.
|
|
44
|
+
* Defaults to `label` when not provided.
|
|
45
|
+
*/
|
|
46
|
+
iconDescription?: string;
|
|
15
47
|
/**
|
|
16
48
|
* Specify the kind of icon to be used
|
|
17
49
|
*/
|
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { usePrefix } from "../../internal/usePrefix.js";
|
|
9
|
+
import { DefinitionTooltip } from "../Tooltip/DefinitionTooltip.js";
|
|
9
10
|
import classNames from "classnames";
|
|
10
11
|
import React from "react";
|
|
11
12
|
import PropTypes from "prop-types";
|
|
12
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
13
14
|
import { CheckmarkFilled, CheckmarkOutline, CircleDash, ErrorFilled, InProgress, Incomplete, PendingFilled, UndefinedFilled, UnknownFilled, WarningAltFilled, WarningAltInvertedFilled, WarningSquareFilled } from "@carbon/icons-react";
|
|
14
15
|
//#region src/components/IconIndicator/index.tsx
|
|
15
16
|
/**
|
|
@@ -18,54 +19,82 @@ import { CheckmarkFilled, CheckmarkOutline, CircleDash, ErrorFilled, InProgress,
|
|
|
18
19
|
* This source code is licensed under the Apache-2.0 license found in the
|
|
19
20
|
* LICENSE file in the root directory of this source tree.
|
|
20
21
|
*/
|
|
21
|
-
const IconIndicatorKinds = [
|
|
22
|
-
"failed",
|
|
23
|
-
"caution-major",
|
|
24
|
-
"caution-minor",
|
|
25
|
-
"undefined",
|
|
26
|
-
"succeeded",
|
|
27
|
-
"normal",
|
|
28
|
-
"in-progress",
|
|
29
|
-
"incomplete",
|
|
30
|
-
"not-started",
|
|
31
|
-
"pending",
|
|
32
|
-
"unknown",
|
|
33
|
-
"informative"
|
|
34
|
-
];
|
|
35
22
|
const iconTypes = {
|
|
36
23
|
failed: ErrorFilled,
|
|
37
|
-
|
|
38
|
-
|
|
24
|
+
"caution-major": WarningAltInvertedFilled,
|
|
25
|
+
"caution-minor": WarningAltFilled,
|
|
39
26
|
undefined: UndefinedFilled,
|
|
40
27
|
succeeded: CheckmarkFilled,
|
|
41
28
|
normal: CheckmarkOutline,
|
|
42
|
-
|
|
29
|
+
"in-progress": InProgress,
|
|
43
30
|
incomplete: Incomplete,
|
|
44
|
-
|
|
31
|
+
"not-started": CircleDash,
|
|
45
32
|
pending: PendingFilled,
|
|
46
33
|
unknown: UnknownFilled,
|
|
47
34
|
informative: WarningSquareFilled
|
|
48
35
|
};
|
|
49
|
-
const
|
|
36
|
+
const IconIndicatorKinds = Object.keys(iconTypes);
|
|
37
|
+
const IconIndicator = React.forwardRef(({ align = "right", autoAlign = false, className: customClassName, compact = false, iconDescription, kind, label, size = 16 }, ref) => {
|
|
50
38
|
const prefix = usePrefix();
|
|
51
|
-
const classNames$1 = classNames(`${prefix}--icon-indicator`, customClassName, { [`${prefix}--icon-indicator--20`]: size
|
|
39
|
+
const classNames$1 = classNames(`${prefix}--icon-indicator`, customClassName, { [`${prefix}--icon-indicator--20`]: size === 20 });
|
|
52
40
|
const IconForKind = iconTypes[kind];
|
|
53
41
|
if (!IconForKind) return null;
|
|
54
|
-
|
|
42
|
+
const iconElement = /* @__PURE__ */ jsx(IconForKind, {
|
|
43
|
+
size,
|
|
44
|
+
className: `${prefix}--icon-indicator--${kind}`
|
|
45
|
+
});
|
|
46
|
+
return /* @__PURE__ */ jsx("div", {
|
|
55
47
|
className: classNames$1,
|
|
56
48
|
ref,
|
|
57
|
-
children:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
49
|
+
children: compact ? /* @__PURE__ */ jsxs(DefinitionTooltip, {
|
|
50
|
+
align,
|
|
51
|
+
autoAlign,
|
|
52
|
+
openOnHover: true,
|
|
53
|
+
definition: label,
|
|
54
|
+
triggerClassName: `${prefix}--icon-indicator__button`,
|
|
55
|
+
children: [iconElement, /* @__PURE__ */ jsx("span", {
|
|
56
|
+
className: `${prefix}--visually-hidden`,
|
|
57
|
+
children: iconDescription ?? label
|
|
58
|
+
})]
|
|
59
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [iconElement, label] })
|
|
61
60
|
});
|
|
62
61
|
});
|
|
63
62
|
IconIndicator.propTypes = {
|
|
63
|
+
/**
|
|
64
|
+
* Specify how the tooltip should align with the icon in compact mode
|
|
65
|
+
*/
|
|
66
|
+
align: PropTypes.oneOf([
|
|
67
|
+
"top",
|
|
68
|
+
"top-start",
|
|
69
|
+
"top-end",
|
|
70
|
+
"bottom",
|
|
71
|
+
"bottom-start",
|
|
72
|
+
"bottom-end",
|
|
73
|
+
"left",
|
|
74
|
+
"left-start",
|
|
75
|
+
"left-end",
|
|
76
|
+
"right",
|
|
77
|
+
"right-start",
|
|
78
|
+
"right-end"
|
|
79
|
+
]),
|
|
80
|
+
/**
|
|
81
|
+
* Will auto-align the tooltip in compact mode
|
|
82
|
+
*/
|
|
83
|
+
autoAlign: PropTypes.bool,
|
|
64
84
|
/**
|
|
65
85
|
* Specify an optional className to add.
|
|
66
86
|
*/
|
|
67
87
|
className: PropTypes.string,
|
|
68
88
|
/**
|
|
89
|
+
* When true, displays only the icon with the label in a tooltip
|
|
90
|
+
*/
|
|
91
|
+
compact: PropTypes.bool,
|
|
92
|
+
/**
|
|
93
|
+
* Description for the icon announced to screen readers in compact mode.
|
|
94
|
+
* Defaults to `label` when not provided.
|
|
95
|
+
*/
|
|
96
|
+
iconDescription: PropTypes.string,
|
|
97
|
+
/**
|
|
69
98
|
* Specify the kind of the Icon Indicator
|
|
70
99
|
*/
|
|
71
100
|
kind: PropTypes.oneOf(IconIndicatorKinds).isRequired,
|
|
@@ -12,6 +12,7 @@ import useIsomorphicEffect from "../../internal/useIsomorphicEffect.js";
|
|
|
12
12
|
import { useId } from "../../internal/useId.js";
|
|
13
13
|
import { deprecate } from "../../prop-types/deprecate.js";
|
|
14
14
|
import { defaultItemToString } from "../../internal/defaultItemToString.js";
|
|
15
|
+
import { isItemDisabled } from "../../internal/isItemDisabled.js";
|
|
15
16
|
import { isComponentElement } from "../../internal/utils.js";
|
|
16
17
|
import { hasHelperText } from "../../internal/hasHelperText.js";
|
|
17
18
|
import { useNormalizedInputProps } from "../../internal/useNormalizedInputProps.js";
|
|
@@ -72,7 +73,7 @@ const FilterableMultiSelect = forwardRef(function FilterableMultiSelect({ autoAl
|
|
|
72
73
|
filteredItems
|
|
73
74
|
});
|
|
74
75
|
const selectAllStatus = useMemo(() => {
|
|
75
|
-
const selectable = nonSelectAllItems.filter((item) => !item
|
|
76
|
+
const selectable = nonSelectAllItems.filter((item) => !isItemDisabled(item));
|
|
76
77
|
const nonSelectedCount = selectable.filter((item) => !controlledSelectedItems.some((sel) => isEqual(sel, item))).length;
|
|
77
78
|
const totalCount = selectable.length;
|
|
78
79
|
return {
|
|
@@ -81,7 +82,7 @@ const FilterableMultiSelect = forwardRef(function FilterableMultiSelect({ autoAl
|
|
|
81
82
|
};
|
|
82
83
|
}, [controlledSelectedItems, nonSelectAllItems]);
|
|
83
84
|
const handleSelectAllClick = useCallback(() => {
|
|
84
|
-
const selectable = nonSelectAllItems.filter((
|
|
85
|
+
const selectable = nonSelectAllItems.filter((item) => !isItemDisabled(item));
|
|
85
86
|
const { checked, indeterminate } = selectAllStatus;
|
|
86
87
|
if (checked || indeterminate) toggleAll(controlledSelectedItems.filter((sel) => !filteredItems.some((e) => isEqual(e, sel))));
|
|
87
88
|
else {
|
|
@@ -131,7 +132,7 @@ const FilterableMultiSelect = forwardRef(function FilterableMultiSelect({ autoAl
|
|
|
131
132
|
}, [open]);
|
|
132
133
|
const sortedItems = useMemo(() => {
|
|
133
134
|
const selectAllItem = items.find(isSelectAllItem);
|
|
134
|
-
const selectableRealItems = nonSelectAllItems.filter((item) => !item
|
|
135
|
+
const selectableRealItems = nonSelectAllItems.filter((item) => !isItemDisabled(item));
|
|
135
136
|
const sortedReal = sortItems(nonSelectAllItems, {
|
|
136
137
|
selectedItems: {
|
|
137
138
|
top: controlledSelectedItems,
|
|
@@ -255,9 +256,7 @@ const FilterableMultiSelect = forwardRef(function FilterableMultiSelect({ autoAl
|
|
|
255
256
|
inputId,
|
|
256
257
|
inputValue,
|
|
257
258
|
stateReducer,
|
|
258
|
-
isItemDisabled
|
|
259
|
-
return item?.disabled;
|
|
260
|
-
}
|
|
259
|
+
isItemDisabled
|
|
261
260
|
});
|
|
262
261
|
function stateReducer(state, actionAndChanges) {
|
|
263
262
|
const { type, props, changes } = actionAndChanges;
|
|
@@ -266,7 +265,7 @@ const FilterableMultiSelect = forwardRef(function FilterableMultiSelect({ autoAl
|
|
|
266
265
|
switch (type) {
|
|
267
266
|
case InputKeyDownEnter:
|
|
268
267
|
if (sortedItems.length === 0) return changes;
|
|
269
|
-
if (changes.selectedItem && changes.selectedItem
|
|
268
|
+
if (changes.selectedItem && !isItemDisabled(changes.selectedItem)) if (isSelectAllItem(changes.selectedItem)) handleSelectAllClick();
|
|
270
269
|
else onItemChange(changes.selectedItem);
|
|
271
270
|
setHighlightedIndex(changes.selectedItem);
|
|
272
271
|
return {
|
|
@@ -13,6 +13,7 @@ import { useId } from "../../internal/useId.js";
|
|
|
13
13
|
import { noopFn } from "../../internal/noopFn.js";
|
|
14
14
|
import { deprecate } from "../../prop-types/deprecate.js";
|
|
15
15
|
import { defaultItemToString } from "../../internal/defaultItemToString.js";
|
|
16
|
+
import { isItemDisabled } from "../../internal/isItemDisabled.js";
|
|
16
17
|
import { isComponentElement } from "../../internal/utils.js";
|
|
17
18
|
import { useFeatureFlag } from "../FeatureFlags/index.js";
|
|
18
19
|
import { useNormalizedInputProps } from "../../internal/useNormalizedInputProps.js";
|
|
@@ -114,9 +115,7 @@ const MultiSelect = React.forwardRef(({ autoAlign = false, className: containerC
|
|
|
114
115
|
},
|
|
115
116
|
selectedItem: controlledSelectedItems,
|
|
116
117
|
items: filteredItems,
|
|
117
|
-
isItemDisabled
|
|
118
|
-
return item?.disabled;
|
|
119
|
-
},
|
|
118
|
+
isItemDisabled,
|
|
120
119
|
...downshiftProps
|
|
121
120
|
});
|
|
122
121
|
const toggleButtonProps = getToggleButtonProps({
|
|
@@ -298,7 +297,7 @@ const MultiSelect = React.forwardRef(({ autoAlign = false, className: containerC
|
|
|
298
297
|
return {
|
|
299
298
|
hasIndividualSelections: selectedItems.some((selected) => !isSelectAllItem(selected)),
|
|
300
299
|
nonSelectAllSelectedCount: selectedItems.filter((selected) => !isSelectAllItem(selected)).length,
|
|
301
|
-
totalSelectableCount: filteredItems.filter((item) => !isSelectAllItem(item) && !item
|
|
300
|
+
totalSelectableCount: filteredItems.filter((item) => !isSelectAllItem(item) && !isItemDisabled(item)).length
|
|
302
301
|
};
|
|
303
302
|
}, [selectedItems, filteredItems]);
|
|
304
303
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -18,7 +18,7 @@ import { mergeRefs } from "../../tools/mergeRefs.js";
|
|
|
18
18
|
import { DIRECTION_BOTTOM, FloatingMenu } from "../../internal/FloatingMenu.js";
|
|
19
19
|
import { useOutsideClick } from "../../internal/useOutsideClick.js";
|
|
20
20
|
import classNames from "classnames";
|
|
21
|
-
import React, { Children, cloneElement, forwardRef, isValidElement, useCallback, useContext, useEffect, useRef, useState } from "react";
|
|
21
|
+
import React, { Children, cloneElement, forwardRef, isValidElement, useCallback, useContext, useEffect, useReducer, useRef, useState } from "react";
|
|
22
22
|
import PropTypes from "prop-types";
|
|
23
23
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
24
24
|
import { OverflowMenuVertical } from "@carbon/icons-react";
|
|
@@ -81,7 +81,7 @@ const getMenuOffset = (menuBody, direction, trigger, flip) => {
|
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
83
|
};
|
|
84
|
-
const OverflowMenu = forwardRef(({ align, ["aria-label"]: ariaLabel = null, ariaLabel: deprecatedAriaLabel, children, className, direction = DIRECTION_BOTTOM, flipped = false, focusTrap = false, iconClass, iconDescription = "Options", id, light, menuOffset = getMenuOffset, menuOffsetFlip = getMenuOffset, menuOptionsClass, onClick = noopFn, onClose = noopFn, onOpen = noopFn, open: openProp, renderIcon: IconElement = OverflowMenuVertical, selectorPrimaryFocus = "[data-floating-menu-primary-focus]", size
|
|
84
|
+
const OverflowMenu = forwardRef(({ align, ["aria-label"]: ariaLabel = null, ariaLabel: deprecatedAriaLabel, children, className, direction = DIRECTION_BOTTOM, flipped = false, focusTrap = false, iconClass, iconDescription = "Options", id, light, menuOffset = getMenuOffset, menuOffsetFlip = getMenuOffset, menuOptionsClass, onClick = noopFn, onClose = noopFn, onOpen = noopFn, open: openProp, renderIcon: IconElement = OverflowMenuVertical, selectorPrimaryFocus = "[data-floating-menu-primary-focus]", size, innerRef, ...other }, ref) => {
|
|
85
85
|
const prefix = useContext(PrefixContext);
|
|
86
86
|
const [open, setOpen] = useState(openProp ?? false);
|
|
87
87
|
const [click, setClick] = useState(false);
|
|
@@ -96,6 +96,8 @@ const OverflowMenu = forwardRef(({ align, ["aria-label"]: ariaLabel = null, aria
|
|
|
96
96
|
/** The element ref of the tooltip's trigger button. */
|
|
97
97
|
const triggerRef = useRef(null);
|
|
98
98
|
const wrapperRef = useRef(null);
|
|
99
|
+
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
|
100
|
+
const resizeObserverRef = useRef(null);
|
|
99
101
|
useEffect(() => {
|
|
100
102
|
if (prevOpenProp.current !== openProp) {
|
|
101
103
|
setOpen(!!openProp);
|
|
@@ -105,6 +107,26 @@ const OverflowMenu = forwardRef(({ align, ["aria-label"]: ariaLabel = null, aria
|
|
|
105
107
|
useEffect(() => {
|
|
106
108
|
if (triggerRef.current) setHasMountedTrigger(true);
|
|
107
109
|
}, []);
|
|
110
|
+
/**
|
|
111
|
+
* When `size` is brought in from a contextual layout token, FloatingMenu can't detect size changes
|
|
112
|
+
* while the menu is open since there is no state/prop update. This ResizeObserver watches
|
|
113
|
+
* the trigger button when the menu is open and calls `forceUpdate()` on resize to force a re-render if its
|
|
114
|
+
* size changes while the menu is open
|
|
115
|
+
*/
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
resizeObserverRef.current = new ResizeObserver(() => {
|
|
118
|
+
forceUpdate();
|
|
119
|
+
});
|
|
120
|
+
return () => {
|
|
121
|
+
resizeObserverRef.current?.disconnect();
|
|
122
|
+
resizeObserverRef.current = null;
|
|
123
|
+
};
|
|
124
|
+
}, []);
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
if (!triggerRef.current || !resizeObserverRef.current) return;
|
|
127
|
+
if (open) resizeObserverRef.current.observe(triggerRef.current);
|
|
128
|
+
else resizeObserverRef.current.disconnect();
|
|
129
|
+
}, [open]);
|
|
108
130
|
useEffect(() => {
|
|
109
131
|
if (open && !prevOpenState.current) onOpen();
|
|
110
132
|
else if (!open && prevOpenState.current) onClose();
|
|
@@ -205,22 +227,25 @@ const OverflowMenu = forwardRef(({ align, ["aria-label"]: ariaLabel = null, aria
|
|
|
205
227
|
};
|
|
206
228
|
const getTarget = () => {
|
|
207
229
|
const triggerEl = triggerRef.current;
|
|
208
|
-
if (triggerEl instanceof Element) return triggerEl.closest("[data-floating-menu-container]") || document.body;
|
|
230
|
+
if (triggerEl instanceof Element) return triggerEl.closest("[data-floating-menu-container]") || triggerEl.closest(`.${prefix}--layout`) || document.body;
|
|
209
231
|
return document.body;
|
|
210
232
|
};
|
|
211
233
|
const menuBodyId = `overflow-menu-${instanceId.current}__menu-body`;
|
|
212
234
|
const overflowMenuClasses = classNames(className, `${prefix}--overflow-menu`, {
|
|
213
235
|
[`${prefix}--overflow-menu--open`]: open,
|
|
214
236
|
[`${prefix}--overflow-menu--light`]: light,
|
|
215
|
-
[`${prefix}--overflow-menu--${size}`]: size
|
|
237
|
+
[`${prefix}--overflow-menu--${size}`]: size,
|
|
238
|
+
[`${prefix}--layout--size-${size}`]: size
|
|
216
239
|
});
|
|
217
240
|
const overflowMenuOptionsClasses = classNames(menuOptionsClass, `${prefix}--overflow-menu-options`, {
|
|
218
241
|
[`${prefix}--overflow-menu--flip`]: flipped,
|
|
219
242
|
[`${prefix}--overflow-menu-options--open`]: open,
|
|
220
243
|
[`${prefix}--overflow-menu-options--light`]: light,
|
|
221
|
-
[`${prefix}--overflow-menu-options--${size}`]: size
|
|
244
|
+
[`${prefix}--overflow-menu-options--${size}`]: size,
|
|
245
|
+
[`${prefix}--layout--size-${size}`]: size
|
|
222
246
|
});
|
|
223
247
|
const overflowMenuIconClasses = classNames(`${prefix}--overflow-menu__icon`, iconClass);
|
|
248
|
+
const overflowMenuWrapperClasses = classNames(`${prefix}--overflow-menu__wrapper`);
|
|
224
249
|
const childrenWithProps = Children.toArray(children).map((child, index) => {
|
|
225
250
|
if (isValidElement(child)) {
|
|
226
251
|
const childElement = child;
|
|
@@ -257,7 +282,7 @@ const OverflowMenu = forwardRef(({ align, ["aria-label"]: ariaLabel = null, aria
|
|
|
257
282
|
});
|
|
258
283
|
const combinedRef = innerRef ? mergeRefs(triggerRef, innerRef, ref) : mergeRefs(triggerRef, ref);
|
|
259
284
|
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("span", {
|
|
260
|
-
className:
|
|
285
|
+
className: overflowMenuWrapperClasses,
|
|
261
286
|
"aria-owns": open ? menuBodyId : void 0,
|
|
262
287
|
ref: wrapperRef,
|
|
263
288
|
children: [/* @__PURE__ */ jsx(IconButton, {
|
|
@@ -22,7 +22,7 @@ import { OverflowMenuVertical } from "@carbon/icons-react";
|
|
|
22
22
|
import { autoUpdate, flip, useFloating } from "@floating-ui/react";
|
|
23
23
|
//#region src/components/OverflowMenu/next/index.tsx
|
|
24
24
|
/**
|
|
25
|
-
* Copyright IBM Corp. 2020,
|
|
25
|
+
* Copyright IBM Corp. 2020, 2026
|
|
26
26
|
*
|
|
27
27
|
* This source code is licensed under the Apache-2.0 license found in the
|
|
28
28
|
* LICENSE file in the root directory of this source tree.
|
|
@@ -67,7 +67,7 @@ const OverflowMenu = React.forwardRef(({ autoAlign = false, children, className,
|
|
|
67
67
|
}
|
|
68
68
|
const containerClasses = classNames(className, `${prefix}--overflow-menu__container`, { [`${prefix}--autoalign`]: enableFloatingStyles });
|
|
69
69
|
const menuClasses = classNames(`${prefix}--overflow-menu__${menuAlignment}`);
|
|
70
|
-
const triggerClasses = classNames(`${prefix}--overflow-menu`, { [`${prefix}--overflow-menu--open`]: open }, size !== defaultSize && `${prefix}--overflow-menu--${size}`);
|
|
70
|
+
const triggerClasses = classNames(`${prefix}--overflow-menu`, { [`${prefix}--overflow-menu--open`]: open }, size !== defaultSize && `${prefix}--overflow-menu--${size}`, size !== defaultSize && `${prefix}--layout--size-${size}`);
|
|
71
71
|
const floatingRef = mergeRefs(triggerRef, refs.setReference);
|
|
72
72
|
return /* @__PURE__ */ jsxs("div", {
|
|
73
73
|
...rest,
|
|
@@ -5,13 +5,26 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
import React from 'react';
|
|
8
|
+
import { PopoverAlignment } from '../Popover';
|
|
8
9
|
export declare const ShapeIndicatorKinds: string[];
|
|
9
10
|
export type ShapeIndicatorKind = (typeof ShapeIndicatorKinds)[number];
|
|
10
11
|
export interface ShapeIndicatorProps {
|
|
12
|
+
/**
|
|
13
|
+
* Specify how the tooltip should align with the shape in compact mode
|
|
14
|
+
*/
|
|
15
|
+
align?: PopoverAlignment;
|
|
16
|
+
/**
|
|
17
|
+
* Will auto-align the tooltip in compact mode.
|
|
18
|
+
*/
|
|
19
|
+
autoAlign?: boolean;
|
|
11
20
|
/**
|
|
12
21
|
* Specify an optional className to add.
|
|
13
22
|
*/
|
|
14
23
|
className?: string;
|
|
24
|
+
/**
|
|
25
|
+
* When true, displays only the shape with the label in a tooltip
|
|
26
|
+
*/
|
|
27
|
+
compact?: boolean;
|
|
15
28
|
/**
|
|
16
29
|
* Specify the kind of shape to be used
|
|
17
30
|
*/
|
|
@@ -20,6 +33,11 @@ export interface ShapeIndicatorProps {
|
|
|
20
33
|
* Label next to the shape
|
|
21
34
|
*/
|
|
22
35
|
label: string;
|
|
36
|
+
/**
|
|
37
|
+
* Description for the shape announced to screen readers in compact mode.
|
|
38
|
+
* Defaults to `label` when not provided.
|
|
39
|
+
*/
|
|
40
|
+
shapeDescription?: string;
|
|
23
41
|
/**
|
|
24
42
|
* Specify the text size of the Shape Indicator. Defaults to 12.
|
|
25
43
|
*/
|
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { usePrefix } from "../../internal/usePrefix.js";
|
|
9
|
+
import { DefinitionTooltip } from "../Tooltip/DefinitionTooltip.js";
|
|
9
10
|
import classNames from "classnames";
|
|
10
11
|
import React from "react";
|
|
11
12
|
import PropTypes from "prop-types";
|
|
12
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
13
14
|
import { Caution, CircleFill, CircleStroke, Critical, CriticalSeverity, DiamondFill, LowSeverity } from "@carbon/icons-react";
|
|
14
15
|
//#region src/components/ShapeIndicator/index.tsx
|
|
15
16
|
/**
|
|
@@ -61,26 +62,62 @@ const shapeTypes = {
|
|
|
61
62
|
incomplete: incompleteIcon,
|
|
62
63
|
draft: CircleStroke
|
|
63
64
|
};
|
|
64
|
-
const ShapeIndicator = React.forwardRef(({ className: customClassName, kind, label, textSize = 12 }, ref) => {
|
|
65
|
+
const ShapeIndicator = React.forwardRef(({ align = "right", autoAlign = false, className: customClassName, compact = false, kind, label, shapeDescription, textSize = 12 }, ref) => {
|
|
65
66
|
const prefix = usePrefix();
|
|
66
67
|
const classNames$1 = classNames(`${prefix}--shape-indicator`, customClassName, { [`${prefix}--shape-indicator--14`]: textSize == 14 });
|
|
67
68
|
const ShapeForKind = shapeTypes[kind];
|
|
68
69
|
if (!ShapeForKind) return null;
|
|
69
|
-
|
|
70
|
+
const shapeElement = /* @__PURE__ */ jsx(ShapeForKind, {
|
|
71
|
+
size: 16,
|
|
72
|
+
className: `${prefix}--shape-indicator--${kind}`
|
|
73
|
+
});
|
|
74
|
+
return /* @__PURE__ */ jsx("div", {
|
|
70
75
|
className: classNames$1,
|
|
71
76
|
ref,
|
|
72
|
-
children:
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
children: compact ? /* @__PURE__ */ jsxs(DefinitionTooltip, {
|
|
78
|
+
align,
|
|
79
|
+
autoAlign,
|
|
80
|
+
openOnHover: true,
|
|
81
|
+
definition: label,
|
|
82
|
+
triggerClassName: `${prefix}--shape-indicator__button`,
|
|
83
|
+
children: [shapeElement, /* @__PURE__ */ jsx("span", {
|
|
84
|
+
className: `${prefix}--visually-hidden`,
|
|
85
|
+
children: shapeDescription ?? label
|
|
86
|
+
})]
|
|
87
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [shapeElement, label] })
|
|
76
88
|
});
|
|
77
89
|
});
|
|
78
90
|
ShapeIndicator.propTypes = {
|
|
91
|
+
/**
|
|
92
|
+
* Specify how the tooltip should align with the shape in compact mode
|
|
93
|
+
*/
|
|
94
|
+
align: PropTypes.oneOf([
|
|
95
|
+
"top",
|
|
96
|
+
"top-start",
|
|
97
|
+
"top-end",
|
|
98
|
+
"bottom",
|
|
99
|
+
"bottom-start",
|
|
100
|
+
"bottom-end",
|
|
101
|
+
"left",
|
|
102
|
+
"left-start",
|
|
103
|
+
"left-end",
|
|
104
|
+
"right",
|
|
105
|
+
"right-start",
|
|
106
|
+
"right-end"
|
|
107
|
+
]),
|
|
108
|
+
/**
|
|
109
|
+
* Will auto-align the tooltip in compact mode
|
|
110
|
+
*/
|
|
111
|
+
autoAlign: PropTypes.bool,
|
|
79
112
|
/**
|
|
80
113
|
* Specify an optional className to add.
|
|
81
114
|
*/
|
|
82
115
|
className: PropTypes.string,
|
|
83
116
|
/**
|
|
117
|
+
* When true, displays only the shape with the label in a tooltip
|
|
118
|
+
*/
|
|
119
|
+
compact: PropTypes.bool,
|
|
120
|
+
/**
|
|
84
121
|
* Specify the kind of the Shape Indicator
|
|
85
122
|
*/
|
|
86
123
|
kind: PropTypes.oneOf(ShapeIndicatorKinds).isRequired,
|
|
@@ -89,6 +126,11 @@ ShapeIndicator.propTypes = {
|
|
|
89
126
|
*/
|
|
90
127
|
label: PropTypes.string.isRequired,
|
|
91
128
|
/**
|
|
129
|
+
* Description for the shape announced to screen readers in compact mode.
|
|
130
|
+
* Defaults to `label` when not provided.
|
|
131
|
+
*/
|
|
132
|
+
shapeDescription: PropTypes.string,
|
|
133
|
+
/**
|
|
92
134
|
* Specify the text size of the Shape Indicator. Defaults to 12.
|
|
93
135
|
*/
|
|
94
136
|
textSize: PropTypes.oneOf([12, 14])
|