@ringcentral/spring-ui 1.11.2 → 1.13.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/Accordion.js +90 -34
- package/AccordionHeader.js +72 -20
- package/Alert.js +120 -28
- package/Animation.js +2 -45
- package/Announcement.js +46 -35
- package/Autocomplete.js +1011 -580
- package/Avatar.js +134 -73
- package/AvatarGroup.js +50 -7
- package/Backdrop.js +45 -7
- package/Badge.js +77 -15
- package/Block.js +34 -12
- package/BlockHeader.js +55 -11
- package/BottomNavigation.js +181 -65
- package/BottomNavigationItem.js +93 -30
- package/Button.js +160 -24
- package/CallButton.js +53 -31
- package/Checkbox.js +88 -26
- package/Chip.js +126 -26
- package/CircularProgressIndicator.js +80 -8
- package/ClickAwayListener.js +4 -1
- package/ComponentsProps.js +1 -0
- package/DatePicker.js +611 -190
- package/DateUtilsContext.js +43 -25
- package/Dialer.js +576 -250
- package/Dialog.js +100 -45
- package/Divider.js +43 -7
- package/DragHandle.js +32 -6
- package/Drawer.js +117 -19
- package/Dropdown.js +154 -74
- package/EmptyState.js +63 -11
- package/FabButton.js +51 -17
- package/FocusTrap.js +109 -12
- package/FormField.js +106 -8
- package/FormLabel.js +81 -7
- package/Icon.js +1 -8
- package/IconButton.js +142 -26
- package/IconButtonGroup.js +35 -12
- package/InlineEditable.js +98 -44
- package/LinearProgressIndicator.js +73 -24
- package/Link.js +40 -11
- package/List.js +54 -9
- package/ListItem.js +152 -23
- package/ListItemText.js +44 -16
- package/ListNavigation.js +272 -20
- package/Menu.js +139 -23
- package/MenuActions.js +37 -12
- package/MenuDivider.js +34 -8
- package/MenuFooter.js +36 -12
- package/MenuHeader.js +54 -15
- package/MenuItem.js +402 -33
- package/MenuItemText.js +51 -8
- package/MenuList.js +87 -34
- package/Modal.js +345 -16
- package/NestedMenuContext.js +183 -11
- package/Option.js +32 -34
- package/Overlap.js +54 -7
- package/PageHeader.js +62 -24
- package/PickerTextField.js +156 -33
- package/Popover.js +262 -21
- package/Popper.js +130 -77
- package/PopperPaper.js +54 -8
- package/Portal.js +6 -1
- package/Radio.js +65 -21
- package/RadioGroup.js +56 -17
- package/Rating.js +223 -87
- package/Select.js +387 -41
- package/SingleFilter.js +148 -77
- package/Skeleton.js +68 -17
- package/Slider.js +168 -69
- package/Snackbar.js +122 -59
- package/SortableList.js +32 -12
- package/SortableListDndContext.js +69 -22
- package/SortableListItem.js +92 -47
- package/SortableVirtualizedList.js +52 -28
- package/SplitButton.js +124 -86
- package/Squircle.js +4 -7
- package/StatusIndicator.js +51 -9
- package/Step.js +63 -38
- package/StepButton.js +51 -23
- package/StepConnector.js +48 -8
- package/StepContent.js +68 -25
- package/StepIcon.js +90 -9
- package/StepLabel.js +90 -14
- package/Stepper.js +65 -23
- package/SubMenu.js +203 -99
- package/SwipeableDrawer.js +439 -194
- package/Switch.js +70 -23
- package/Tab.js +120 -57
- package/TabContext.js +59 -15
- package/TabPanel.js +40 -13
- package/Table.js +60 -17
- package/TableBody.js +42 -12
- package/TableCell.js +84 -50
- package/TableContainer.js +32 -10
- package/TableFooter.js +38 -10
- package/TableHead.js +49 -15
- package/TablePagination.js +153 -77
- package/TableRow.js +60 -21
- package/Tabs.js +593 -268
- package/Tag.js +51 -16
- package/Text.js +68 -37
- package/TextField.js +209 -29
- package/Textarea.js +51 -40
- package/TimePicker.js +490 -230
- package/Tooltip.js +48 -23
- package/Tray.js +237 -13
- package/TrayMenu.js +62 -41
- package/VirtualizedList.js +208 -109
- package/VirtualizedMenu.js +359 -29
- package/VisuallyHidden.js +29 -6
- package/chunks/1250.js +98 -0
- package/chunks/1294.js +126 -0
- package/chunks/2956.js +18 -0
- package/chunks/4476.js +460 -0
- package/chunks/4491.js +10 -0
- package/chunks/4535.js +364 -0
- package/chunks/5047.js +3 -0
- package/chunks/6335.js +24 -0
- package/chunks/7279.js +10 -0
- package/chunks/8011.js +8 -0
- package/chunks/8307.js +56 -0
- package/chunks/8467.js +8 -0
- package/chunks/8809.js +8 -0
- package/chunks/{RadioGroup.context.js → 9551.js} +12 -6
- package/chunks/987.js +8 -0
- package/index.css +2624 -2416
- package/index.js +245 -193
- package/internal/utils/index.d.ts +1 -0
- package/internal/utils/setRef.d.ts +2 -0
- package/package.json +8 -8
- package/src/Alert/Alert.type.d.ts +2 -1
- package/src/Animation/components/Fade/Fade.d.ts +4 -2
- package/src/Animation/components/Grow/Grow.d.ts +4 -2
- package/src/Animation/components/ShowHideEye/ShowHideEye.d.ts +1 -1
- package/src/Animation/components/SlideIn/SlideIn.d.ts +4 -2
- package/src/Animation/components/SlideIn/getSlideIn.d.ts +1 -1
- package/src/Animation/hooks/useMotionSafeTransition/useMotionSafeTransition.d.ts +2 -0
- package/src/Autocomplete/SuggestionList/SuggestionList.d.ts +1 -1
- package/src/Autocomplete/useAutocomplete.d.ts +18 -18
- package/src/Autocomplete/useAutocompleteTag.d.ts +18 -18
- package/src/Badge/Badge.d.ts +2 -1
- package/src/Button/Button.classes.d.ts +1 -1
- package/src/ComponentsProps.d.ts +111 -110
- package/src/DatePicker/DatePickerDataAttributes.d.ts +11 -0
- package/src/DatePicker/utils/DatePickerAriaLabelUtils.d.ts +2 -1
- package/src/DatePicker/utils/DatePickerUtils.d.ts +1 -1
- package/src/Dialer/DialDelete/DialDelete.d.ts +2 -2
- package/src/Dialer/DialPad/DialPad.d.ts +2 -2
- package/src/Dialer/Dialer.d.ts +4 -2
- package/src/Dropdown/Dropdown.type.d.ts +2 -2
- package/src/InlineEditable/InlineEditable.type.d.ts +2 -1
- package/src/ListNavigation/useListNavigation.d.ts +3 -3
- package/src/MenuActions/MenuActions.d.ts +2 -2
- package/src/MenuItem/MenuItem.type.d.ts +1 -0
- package/src/MenuItem/useMenuItem.d.ts +1 -1
- package/src/MenuItem/useVirtualizedMenuItem.d.ts +1 -1
- package/src/MenuList/MenuList.type.d.ts +1 -1
- package/src/MenuList/MenuListContext.d.ts +2 -3
- package/src/Modal/Modal.type.d.ts +1 -0
- package/src/NestedMenuContext/MenuOverlayGroupContext.d.ts +2 -2
- package/src/Popover/Popover.classes.d.ts +1 -1
- package/src/Popover/Popover.internal.d.ts +8 -0
- package/src/Popover/Popover.type.d.ts +1 -0
- package/src/Popper/Popper.type.d.ts +10 -7
- package/src/Rating/utils.d.ts +3 -2
- package/src/Select/Select.type.d.ts +7 -5
- package/src/Slider/Slider.d.ts +2 -2
- package/src/Slider/SliderThumb.d.ts +2 -1
- package/src/Squircle/Squircle.d.ts +1 -1
- package/src/StatusIndicator/StatusIndicator.d.ts +2 -1
- package/src/SubMenu/PopperMenu.type.d.ts +4 -1
- package/src/Table/Table.type.d.ts +3 -0
- package/src/Table/TableContext.d.ts +3 -0
- package/src/Table/TableRowContext.d.ts +7 -0
- package/src/Table/index.d.ts +1 -0
- package/src/TableCell/TableCell.classes.d.ts +1 -1
- package/src/TableFooter/TableFooter.classes.d.ts +1 -1
- package/src/TableFooter/TableFooter.type.d.ts +1 -0
- package/src/TableHead/TableHead.classes.d.ts +1 -1
- package/src/TableHead/TableHead.type.d.ts +1 -0
- package/src/TableRow/TableRow.type.d.ts +2 -0
- package/chunks/AccordionContext.js +0 -6
- package/chunks/Alert.js +0 -48
- package/chunks/AvatarGroup.js +0 -22
- package/chunks/Backdrop.js +0 -18
- package/chunks/Badge.js +0 -32
- package/chunks/BottomNavigationItem.js +0 -36
- package/chunks/Button.js +0 -88
- package/chunks/Chip.js +0 -73
- package/chunks/CircularProgressIndicator.js +0 -36
- package/chunks/Divider.js +0 -19
- package/chunks/DragHandle.js +0 -11
- package/chunks/Drawer.js +0 -32
- package/chunks/ExpandCollapseCaret.js +0 -24
- package/chunks/FormLabel.js +0 -27
- package/chunks/Icon.js +0 -25
- package/chunks/IconButtonLabel.js +0 -68
- package/chunks/List.js +0 -22
- package/chunks/ListItem.js +0 -83
- package/chunks/Menu.js +0 -61
- package/chunks/MenuDivider.js +0 -14
- package/chunks/MenuItemText.js +0 -17
- package/chunks/MenuList.js +0 -46
- package/chunks/Modal.js +0 -212
- package/chunks/Overlap.js +0 -25
- package/chunks/PickerTextField.js +0 -85
- package/chunks/Popover.js +0 -53
- package/chunks/PopperPaper.js +0 -15
- package/chunks/Select.js +0 -220
- package/chunks/ShowHideEye.js +0 -56
- package/chunks/SlideIn.js +0 -19
- package/chunks/SortableArrow.js +0 -73
- package/chunks/Squircle.js +0 -31
- package/chunks/StatusIndicator.js +0 -20
- package/chunks/StepConnector.js +0 -21
- package/chunks/StepIcon.js +0 -43
- package/chunks/StepLabel.js +0 -29
- package/chunks/StepperContext.js +0 -9
- package/chunks/TableContext.js +0 -6
- package/chunks/Tablelvl2Context.js +0 -6
- package/chunks/TabsContext.js +0 -6
- package/chunks/TextField.js +0 -110
- package/chunks/Tooltip.js +0 -217
- package/chunks/VirtualizedMenu.js +0 -164
- package/chunks/VisuallyHidden.js +0 -12
- package/chunks/getFade.js +0 -27
- package/chunks/getGrow.js +0 -70
- package/chunks/getSlideIn.js +0 -30
- package/chunks/hasContent.js +0 -3
- package/chunks/useAnimationFrame.js +0 -15
- package/chunks/useFocusTrapStack.js +0 -36
- package/chunks/useHelperTextId.js +0 -22
- package/chunks/useListNavigation.js +0 -199
- package/chunks/useMenuItemSelect.js +0 -273
- package/chunks/useMotionSafeTransition.js +0 -9
- package/chunks/useNestedMenuItem.js +0 -27
- package/chunks/useNestedParentMenu.js +0 -113
- package/chunks/usePrefersReducedMotion.js +0 -36
- package/chunks/useTooltipForceHide.js +0 -27
- package/chunks/useTrayNavigate.js +0 -150
- package/chunks/withTooltip.js +0 -15
package/Tabs.js
CHANGED
|
@@ -1,87 +1,92 @@
|
|
|
1
|
-
import { jsx, jsxs
|
|
2
|
-
import
|
|
3
|
-
import { useTab, useTabsList
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import
|
|
17
|
-
import {
|
|
18
|
-
import
|
|
19
|
-
import
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
import 'clsx';
|
|
46
|
-
|
|
47
|
-
const TabMenuItem = forwardRef((props, ref) => {
|
|
48
|
-
const { label, component: MenuItemComponent = MenuItem, onKeyDown } = props;
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import react, { Children, cloneElement, forwardRef, isValidElement, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { TabsListProvider, useTab, useTabsList } from "@mui/base";
|
|
4
|
+
import { combineClasses, combineProps, getTitle, logInDev, useClasses, useEventCallback, useFontLoaded, useForkRef, useOnReRender, usePrevious, useWindowContext } from "@ringcentral/spring-base";
|
|
5
|
+
import { CaretLeftMd, CaretRightMd, OverflowMd } from "@ringcentral/spring-icon";
|
|
6
|
+
import { motionTransition, useClassesWithMode, useMode, useThemeProps } from "@ringcentral/spring-theme";
|
|
7
|
+
import { animate, motion, useMotionValue } from "framer-motion";
|
|
8
|
+
import isEqual from "lodash/isEqual";
|
|
9
|
+
import throttle from "lodash/throttle";
|
|
10
|
+
import { Badge as Badge_Badge } from "./Badge.js";
|
|
11
|
+
import { IconButton as IconButton_IconButton } from "./IconButton.js";
|
|
12
|
+
import { Menu as Menu_Menu } from "./Menu.js";
|
|
13
|
+
import { MenuItem as MenuItem_MenuItem } from "./MenuItem.js";
|
|
14
|
+
import { MenuItemText as MenuItemText_MenuItemText } from "./MenuItemText.js";
|
|
15
|
+
import { MenuList as MenuList_MenuList } from "./MenuList.js";
|
|
16
|
+
import "./Tooltip.js";
|
|
17
|
+
import { TabsContext, useTabsContext } from "./chunks/8809.js";
|
|
18
|
+
import "./chunks/4476.js";
|
|
19
|
+
import { useInternalTabsContext, generateTabStableIds } from "./TabContext.js";
|
|
20
|
+
import { Tooltip } from "./chunks/4535.js";
|
|
21
|
+
import { useMotionSafeTransition, usePrefersReducedMotion } from "./chunks/1294.js";
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
const TabMenuItem = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
44
|
+
const { label, component: MenuItemComponent = MenuItem_MenuItem, onKeyDown } = props;
|
|
49
45
|
const { onChange, ...rest } = props;
|
|
50
46
|
const { getRootProps } = useTab({
|
|
51
47
|
...rest,
|
|
52
|
-
rootRef: ref
|
|
48
|
+
rootRef: ref
|
|
53
49
|
});
|
|
54
|
-
return
|
|
50
|
+
return(// @ts-ignore
|
|
51
|
+
/*#__PURE__*/ jsx(MenuItemComponent, {
|
|
52
|
+
...props,
|
|
53
|
+
...getRootProps(),
|
|
54
|
+
onKeyDown: onKeyDown,
|
|
55
|
+
children: /*#__PURE__*/ jsx(MenuItemText_MenuItemText, {
|
|
56
|
+
children: label
|
|
57
|
+
})
|
|
58
|
+
}));
|
|
55
59
|
});
|
|
56
|
-
const TabMoreMenu = forwardRef((props, ref)
|
|
60
|
+
const TabMoreMenu = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
57
61
|
const { classes, className, menuItems, TooltipProps, BadgeProps, ButtonProps, MenuProps, MenuItemComponent, ...rest } = props;
|
|
58
62
|
const [anchor, setAnchor] = useState(null);
|
|
59
63
|
const { mode } = useMode();
|
|
60
64
|
const cls = useClasses('tab-more-menu', classes, className);
|
|
61
65
|
const { pill } = useTabsContext();
|
|
62
|
-
const iconButtonProps = useMemo(()
|
|
63
|
-
? {
|
|
66
|
+
const iconButtonProps = useMemo(()=>pill ? {
|
|
64
67
|
shape: 'circular',
|
|
65
68
|
variant: 'outlined',
|
|
66
69
|
size: mode === 'compact' ? 'medium' : 'large',
|
|
67
|
-
background: true
|
|
68
|
-
}
|
|
69
|
-
: {
|
|
70
|
+
background: true
|
|
71
|
+
} : {
|
|
70
72
|
shape: 'squircle',
|
|
71
73
|
variant: 'contained',
|
|
72
74
|
size: 'small',
|
|
73
|
-
background: false
|
|
74
|
-
}
|
|
75
|
+
background: false
|
|
76
|
+
}, [
|
|
77
|
+
pill,
|
|
78
|
+
mode
|
|
79
|
+
]);
|
|
75
80
|
let iconButtonClasses = {
|
|
76
|
-
root: cls('button')
|
|
81
|
+
root: cls('button')
|
|
77
82
|
};
|
|
78
83
|
if (ButtonProps?.classes) {
|
|
79
84
|
iconButtonClasses = combineClasses(iconButtonClasses, ButtonProps.classes);
|
|
80
85
|
}
|
|
81
86
|
let badgeClasses = {
|
|
82
87
|
root: cls('badge-root', {
|
|
83
|
-
'pill-badge': pill
|
|
84
|
-
})
|
|
88
|
+
'pill-badge': pill
|
|
89
|
+
})
|
|
85
90
|
};
|
|
86
91
|
if (BadgeProps?.classes) {
|
|
87
92
|
badgeClasses = combineClasses(badgeClasses, BadgeProps.classes);
|
|
@@ -90,73 +95,173 @@ const TabMoreMenu = forwardRef((props, ref) => {
|
|
|
90
95
|
color: 'secondary',
|
|
91
96
|
variant: pill ? 'outlined' : 'contained',
|
|
92
97
|
...BadgeProps,
|
|
93
|
-
classes: badgeClasses
|
|
98
|
+
classes: badgeClasses
|
|
94
99
|
};
|
|
95
|
-
const handleClose = (event, reason)
|
|
100
|
+
const handleClose = (event, reason)=>{
|
|
96
101
|
setAnchor(null);
|
|
97
102
|
};
|
|
98
|
-
const iconButton =
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
103
|
+
const iconButton = /*#__PURE__*/ jsx(IconButton_IconButton, {
|
|
104
|
+
color: 'secondary',
|
|
105
|
+
symbol: OverflowMd,
|
|
106
|
+
...iconButtonProps,
|
|
107
|
+
onClick: (e)=>{
|
|
108
|
+
setAnchor((prev)=>prev ? null : e.currentTarget);
|
|
109
|
+
},
|
|
110
|
+
"aria-expanded": Boolean(anchor),
|
|
111
|
+
"aria-label": getTitle(TooltipProps?.title),
|
|
112
|
+
...ButtonProps,
|
|
113
|
+
classes: iconButtonClasses
|
|
114
|
+
});
|
|
115
|
+
return /*#__PURE__*/ jsxs(Fragment, {
|
|
116
|
+
children: [
|
|
117
|
+
/*#__PURE__*/ jsxs("div", {
|
|
118
|
+
className: cls('root'),
|
|
119
|
+
ref: ref,
|
|
120
|
+
...rest,
|
|
121
|
+
children: [
|
|
122
|
+
TooltipProps ? /*#__PURE__*/ jsx(Tooltip, {
|
|
123
|
+
...TooltipProps,
|
|
124
|
+
children: iconButton
|
|
125
|
+
}) : iconButton,
|
|
126
|
+
Number(BadgeProps?.count) > 0 ? /*#__PURE__*/ jsx(Badge_Badge, {
|
|
127
|
+
...badgeProps
|
|
128
|
+
}) : null
|
|
129
|
+
]
|
|
130
|
+
}),
|
|
131
|
+
/*#__PURE__*/ jsx(Menu_Menu, {
|
|
132
|
+
open: Boolean(anchor),
|
|
133
|
+
anchorEl: anchor,
|
|
134
|
+
...combineProps({
|
|
135
|
+
onClose: handleClose
|
|
136
|
+
}, MenuProps),
|
|
137
|
+
children: /*#__PURE__*/ jsx(MenuList_MenuList, {
|
|
138
|
+
children: menuItems.map(({ ref, ...props }, index)=>/*#__PURE__*/ jsx(TabMenuItem, {
|
|
139
|
+
...props,
|
|
140
|
+
ref: ref,
|
|
141
|
+
component: MenuItemComponent
|
|
142
|
+
}, props.value || `TabMenuItem-${index}`))
|
|
143
|
+
})
|
|
144
|
+
})
|
|
145
|
+
]
|
|
146
|
+
});
|
|
102
147
|
});
|
|
103
148
|
TabMoreMenu.displayName = 'SuiTabMoreMenu';
|
|
104
149
|
|
|
105
|
-
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
const TabScrollButton = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
106
163
|
const { classes, className, ButtonProps, direction = 'left', onClick, ...rest } = props;
|
|
107
164
|
const { mode } = useMode();
|
|
108
165
|
const cls = useClasses('tab-scroll-button', classes, className);
|
|
109
|
-
const iconButtonClasses = useMemo(()
|
|
166
|
+
const iconButtonClasses = useMemo(()=>{
|
|
110
167
|
let combinedClasses = {
|
|
111
|
-
root: cls('button')
|
|
168
|
+
root: cls('button')
|
|
112
169
|
};
|
|
113
170
|
if (ButtonProps?.classes) {
|
|
114
171
|
combinedClasses = combineClasses(combinedClasses, ButtonProps.classes);
|
|
115
172
|
}
|
|
116
173
|
return combinedClasses;
|
|
117
|
-
}, [
|
|
174
|
+
}, [
|
|
175
|
+
ButtonProps?.classes,
|
|
176
|
+
cls
|
|
177
|
+
]);
|
|
118
178
|
const icon = direction === 'left' ? CaretLeftMd : CaretRightMd;
|
|
119
|
-
const iconButtonSize = useMemo(()
|
|
179
|
+
const iconButtonSize = useMemo(()=>{
|
|
120
180
|
return mode === 'compact' ? 'medium' : 'large';
|
|
121
|
-
}, [
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
181
|
+
}, [
|
|
182
|
+
mode
|
|
183
|
+
]);
|
|
184
|
+
return /*#__PURE__*/ jsx("div", {
|
|
185
|
+
className: cls('root', {
|
|
186
|
+
start: direction === 'left'
|
|
187
|
+
}),
|
|
188
|
+
ref: ref,
|
|
189
|
+
...rest,
|
|
190
|
+
children: /*#__PURE__*/ jsx(IconButton_IconButton, {
|
|
191
|
+
"aria-hidden": "true",
|
|
192
|
+
role: "presentation",
|
|
193
|
+
tabIndex: -1,
|
|
194
|
+
component: "div",
|
|
195
|
+
color: "secondary",
|
|
196
|
+
symbol: icon,
|
|
197
|
+
shape: "circular",
|
|
198
|
+
variant: "outlined",
|
|
199
|
+
size: iconButtonSize,
|
|
200
|
+
background: true,
|
|
201
|
+
onClick: onClick,
|
|
202
|
+
...ButtonProps,
|
|
203
|
+
classes: iconButtonClasses
|
|
204
|
+
})
|
|
205
|
+
});
|
|
125
206
|
});
|
|
126
207
|
TabScrollButton.displayName = 'SuiTabScrollButton';
|
|
127
208
|
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
/* oxlint-disable sonarjs/cognitive-complexity */
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
|
|
128
230
|
function useComputedTabs(props) {
|
|
129
|
-
const { variant, value, children, pill, pillGap, onGroupInfoUpdate, floatingUnderlineLeftMotionValue, floatingUnderlineWidthMotionValue, tabListRef, scrollerRef, moreRef
|
|
231
|
+
const { variant, value, children, pill, pillGap, onGroupInfoUpdate, floatingUnderlineLeftMotionValue, floatingUnderlineWidthMotionValue, tabListRef, scrollerRef, moreRef } = props;
|
|
130
232
|
const fontsLoaded = useFontLoaded(variant === 'moreMenu');
|
|
131
233
|
const prefersReducedMotion = usePrefersReducedMotion();
|
|
132
234
|
const { externalWindow } = useWindowContext();
|
|
133
235
|
const ResizeObserverClass = (externalWindow || window).ResizeObserver;
|
|
134
236
|
const [moreMenuItems, setMoreMenuItems] = useState([]);
|
|
237
|
+
// Render the more button initially to get the offsetWidth to compute
|
|
135
238
|
const [showMore, setShowMore] = useState(variant === 'moreMenu');
|
|
136
239
|
const moreBtnWidth = useRef(0);
|
|
137
240
|
const switchToMoreMenu = useRef(variant === 'moreMenu');
|
|
241
|
+
// Store a ref to visibleTabs and hiddenTabs for data comparison
|
|
138
242
|
const visibleTabsRef = useRef();
|
|
139
243
|
const hiddenTabsRef = useRef();
|
|
140
244
|
const shouldUpdate = useRef(false);
|
|
245
|
+
// Cache children info to reduce render times when computing more menu
|
|
141
246
|
const tabsChildrenCache = useRef();
|
|
142
247
|
const { tabsInstanceId } = useInternalTabsContext();
|
|
143
|
-
const processedChildren = useMemo(()
|
|
248
|
+
const processedChildren = useMemo(()=>{
|
|
144
249
|
let childIndex = 0;
|
|
145
250
|
const visibleTabs = new Map();
|
|
146
|
-
Children.forEach(children, (child)
|
|
251
|
+
Children.forEach(children, (child)=>{
|
|
147
252
|
if (isValidElement(child)) {
|
|
148
|
-
if (process.env.NODE_ENV !== 'production' &&
|
|
149
|
-
child.type === React.Fragment) {
|
|
253
|
+
if (process.env.NODE_ENV !== 'production' && child.type === react.Fragment) {
|
|
150
254
|
logInDev({
|
|
151
255
|
scope: 'Tabs',
|
|
152
256
|
level: 'error',
|
|
153
257
|
message: [
|
|
154
258
|
"The Tabs component doesn't accept a Fragment as a child.",
|
|
155
|
-
'Consider providing an array instead.'
|
|
156
|
-
].join('\n')
|
|
259
|
+
'Consider providing an array instead.'
|
|
260
|
+
].join('\n')
|
|
157
261
|
});
|
|
158
262
|
}
|
|
159
263
|
const childValue = child.props.value === undefined ? childIndex : child.props.value;
|
|
264
|
+
// Only clone element when necessary: no user-provided id
|
|
160
265
|
let finalChild = child;
|
|
161
266
|
if (child.props.id === undefined) {
|
|
162
267
|
if (process.env.NODE_ENV !== 'production') {
|
|
@@ -172,167 +277,214 @@ function useComputedTabs(props) {
|
|
|
172
277
|
`<Tab id="unique-tab-id" label="Your label" />`,
|
|
173
278
|
'',
|
|
174
279
|
'Unique ids are required for accessibility and correct tab-panel linking.',
|
|
175
|
-
'If no id is provided, each Tab element will be cloned to provide its auto-generated id, which may have a performance cost.'
|
|
176
|
-
].join('\n')
|
|
280
|
+
'If no id is provided, each Tab element will be cloned to provide its auto-generated id, which may have a performance cost.'
|
|
281
|
+
].join('\n')
|
|
177
282
|
});
|
|
178
283
|
}
|
|
179
284
|
const { tabId } = generateTabStableIds(tabsInstanceId, childValue);
|
|
180
285
|
finalChild = cloneElement(child, {
|
|
181
286
|
...child.props,
|
|
182
|
-
id: tabId
|
|
287
|
+
id: tabId
|
|
183
288
|
});
|
|
184
289
|
}
|
|
185
|
-
visibleTabs.set(childValue, {
|
|
290
|
+
visibleTabs.set(childValue, {
|
|
291
|
+
childIndex,
|
|
292
|
+
child: finalChild
|
|
293
|
+
});
|
|
186
294
|
childIndex += 1;
|
|
187
295
|
}
|
|
188
296
|
});
|
|
189
297
|
return visibleTabs;
|
|
190
|
-
}, [
|
|
298
|
+
}, [
|
|
299
|
+
children,
|
|
300
|
+
tabsInstanceId
|
|
301
|
+
]);
|
|
191
302
|
const [visibleTabs, setVisibleTabs] = useState(processedChildren);
|
|
192
|
-
const visibleTabsChildren = useMemo(()
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
303
|
+
const visibleTabsChildren = useMemo(()=>{
|
|
304
|
+
// Remapping Map → array makes these list children; React requires a key.
|
|
305
|
+
return Array.from(visibleTabs.entries()).map(([childValue, { child }])=>({
|
|
306
|
+
...child,
|
|
307
|
+
key: child.key ?? childValue,
|
|
308
|
+
props: {
|
|
309
|
+
...child.props,
|
|
310
|
+
value: childValue
|
|
311
|
+
}
|
|
312
|
+
}));
|
|
313
|
+
}, [
|
|
314
|
+
visibleTabs
|
|
315
|
+
]);
|
|
316
|
+
const prevChildren = usePrevious(()=>children);
|
|
317
|
+
const cacheTabsChildren = useEventCallback(()=>{
|
|
318
|
+
// If this is a full update clear the cache
|
|
319
|
+
if (shouldUpdate.current === 'full') tabsChildrenCache.current = new Map();
|
|
320
|
+
// We can't cache if the tabListRef is not available
|
|
321
|
+
if (!tabListRef.current?.children || !tabsChildrenCache.current) return;
|
|
203
322
|
const tabsChildren = tabListRef.current?.children;
|
|
204
|
-
for (const [childValue, { childIndex, child }] of visibleTabs.entries())
|
|
323
|
+
for (const [childValue, { childIndex, child }] of visibleTabs.entries()){
|
|
205
324
|
const childElement = tabsChildren[childIndex];
|
|
206
|
-
if (!child || !childElement)
|
|
207
|
-
return;
|
|
325
|
+
if (!child || !childElement) return;
|
|
208
326
|
tabsChildrenCache.current?.set(childValue, {
|
|
327
|
+
// @ts-ignore
|
|
209
328
|
child,
|
|
210
329
|
element: childElement,
|
|
211
|
-
width: childElement.offsetWidth
|
|
330
|
+
width: childElement.offsetWidth
|
|
212
331
|
});
|
|
213
332
|
}
|
|
214
333
|
});
|
|
215
|
-
const getSelectedTabElement = useEventCallback(()
|
|
334
|
+
const getSelectedTabElement = useEventCallback(()=>{
|
|
216
335
|
const children = tabListRef.current?.children;
|
|
217
|
-
if (!children || children.length === 0 || value === null)
|
|
218
|
-
return undefined;
|
|
336
|
+
if (!children || children.length === 0 || value === null) return undefined;
|
|
219
337
|
let selectedIndex = -1;
|
|
220
338
|
if (variant === 'moreMenu' && visibleTabsRef.current) {
|
|
221
|
-
|
|
339
|
+
/**
|
|
340
|
+
* When the variant is `moreMenu`, visibleTabs may have a different
|
|
341
|
+
* order than processed children as some tabs are hidden in the more menu.
|
|
342
|
+
*/ selectedIndex = Array.from(visibleTabsRef.current).findIndex(([key])=>{
|
|
222
343
|
return key === value;
|
|
223
344
|
});
|
|
224
|
-
}
|
|
225
|
-
else {
|
|
345
|
+
} else {
|
|
226
346
|
selectedIndex = visibleTabs.get(value)?.childIndex;
|
|
227
347
|
}
|
|
228
348
|
const selectedTabElement = children[selectedIndex];
|
|
229
|
-
if (process.env.NODE_ENV !== 'production' &&
|
|
230
|
-
selectedTabElement === undefined) {
|
|
349
|
+
if (process.env.NODE_ENV !== 'production' && selectedTabElement === undefined) {
|
|
231
350
|
logInDev({
|
|
232
351
|
scope: 'Tabs',
|
|
233
352
|
message: [
|
|
234
353
|
`The \`value\` provided to the Tabs component is invalid.`,
|
|
235
|
-
`None of the Tabs' children match with "${value}"
|
|
236
|
-
].join('\n')
|
|
354
|
+
`None of the Tabs' children match with "${value}".`
|
|
355
|
+
].join('\n')
|
|
237
356
|
});
|
|
238
357
|
}
|
|
358
|
+
// provide the first tab as default selected tab when value is invalid
|
|
239
359
|
return selectedTabElement || children[0];
|
|
240
360
|
});
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const width = scrollerRef.current?.offsetWidth || 0;
|
|
245
|
-
const visibleWidth = width +
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
const selectedItemWidth =
|
|
249
|
-
const spaceForMoreBtn = moreBtnWidth.current + pillGap;
|
|
361
|
+
// main logic for computing whether show more menu when variant is 'moreMenu'
|
|
362
|
+
const computeMoreMenuWithCache = useEventCallback(()=>{
|
|
363
|
+
if (variant !== 'moreMenu' || !tabsChildrenCache.current) return;
|
|
364
|
+
const width = scrollerRef.current?.offsetWidth || 0; // Includes padding
|
|
365
|
+
const visibleWidth = width + pillGap - (tabListRef.current ? getHorizontalPadding(tabListRef.current) : 0); // Excludes padding
|
|
366
|
+
// The reserved slot must include the gap, like every other tab slot below,
|
|
367
|
+
// otherwise the more button loses its left margin at the overflow threshold
|
|
368
|
+
const selectedItemWidth = value !== null ? (tabsChildrenCache.current.get(value)?.width ?? 0) + pillGap : 0;
|
|
369
|
+
const spaceForMoreBtn = moreBtnWidth.current + pillGap; // more button needs left margin
|
|
250
370
|
let consumedWidth = spaceForMoreBtn;
|
|
251
371
|
let isSelectedItemVisible = false;
|
|
252
372
|
let noWidthForUnselectedTab = false;
|
|
253
373
|
const visibleItems = new Map();
|
|
254
374
|
const hiddenItems = new Map();
|
|
255
375
|
const cache = Array.from(tabsChildrenCache.current);
|
|
256
|
-
for
|
|
376
|
+
for(let i = 0; i < cache.length; i++){
|
|
257
377
|
const [key, itemInfo] = cache[i];
|
|
258
378
|
const { child, width: itemWidth } = itemInfo;
|
|
259
379
|
const width = itemWidth + pillGap;
|
|
260
|
-
|
|
380
|
+
/**
|
|
381
|
+
* If there's not enough space to accommodate an unselected tab and
|
|
382
|
+
* the tab isn't selected we'll hide it, aka put it in the more menu.
|
|
383
|
+
*/ if (noWidthForUnselectedTab && key !== value) {
|
|
261
384
|
hiddenItems.set(key, child);
|
|
262
385
|
continue;
|
|
263
386
|
}
|
|
264
387
|
if (isSelectedItemVisible) {
|
|
388
|
+
// Check if this is the last tab in the list
|
|
265
389
|
if (cache.length === i + 1) {
|
|
390
|
+
// Check if there is enough space to accommodate the tab without the more button
|
|
266
391
|
if (visibleWidth >= consumedWidth - spaceForMoreBtn + width) {
|
|
392
|
+
// There's enough space available, show the last tab
|
|
267
393
|
visibleItems.set(key, child);
|
|
268
|
-
}
|
|
269
|
-
|
|
394
|
+
} else {
|
|
395
|
+
// There's not enough space available, put the last item inside more menu
|
|
270
396
|
hiddenItems.set(key, child);
|
|
271
397
|
}
|
|
272
|
-
}
|
|
273
|
-
|
|
398
|
+
} else {
|
|
399
|
+
// The current item is not the last tab in the list
|
|
400
|
+
// Check if there is enough space to accommodate this tab
|
|
274
401
|
if (visibleWidth >= consumedWidth + width) {
|
|
275
402
|
consumedWidth += width;
|
|
403
|
+
// There's enough space available, show this tab in the list
|
|
276
404
|
visibleItems.set(key, child);
|
|
277
|
-
}
|
|
278
|
-
|
|
405
|
+
} else {
|
|
406
|
+
// There's not enoguh space available put the tab inside the more menu (hide it)
|
|
279
407
|
hiddenItems.set(key, child);
|
|
280
408
|
noWidthForUnselectedTab = true;
|
|
281
409
|
}
|
|
282
410
|
}
|
|
283
411
|
continue;
|
|
284
412
|
}
|
|
413
|
+
// The selected tab is not visible yet. Check if the current tab is selected
|
|
285
414
|
if (key === value) {
|
|
415
|
+
// Show the selected tab, it should never be inside the more menu
|
|
286
416
|
visibleItems.set(key, child);
|
|
287
|
-
if
|
|
288
|
-
|
|
417
|
+
// Check if the current tab is not the last one in the list, if it isn't we need to account for its width
|
|
418
|
+
if (cache.length !== i + 1) consumedWidth += width;
|
|
289
419
|
isSelectedItemVisible = true;
|
|
290
420
|
continue;
|
|
291
421
|
}
|
|
422
|
+
// The current tab is not selected. Check if there is enough space for the current tab, the selected tab and more button
|
|
292
423
|
if (visibleWidth >= consumedWidth + width + selectedItemWidth) {
|
|
293
424
|
consumedWidth += width;
|
|
294
425
|
visibleItems.set(key, child);
|
|
295
426
|
continue;
|
|
296
427
|
}
|
|
428
|
+
// There's not enough space available. Check if current tab immediately precedes the last selected tab
|
|
297
429
|
const isSecondToLast = cache.length - 1 === i + 1;
|
|
298
|
-
|
|
299
|
-
|
|
430
|
+
// Check if there is enough space for the current tab and the last selected tab without the more button
|
|
431
|
+
const hasSpaceForLast2Items = visibleWidth >= consumedWidth - spaceForMoreBtn + width + selectedItemWidth;
|
|
300
432
|
if (isSecondToLast && hasSpaceForLast2Items) {
|
|
433
|
+
// If it's second to last and has enough space, show the tab
|
|
301
434
|
visibleItems.set(key, child);
|
|
302
|
-
}
|
|
303
|
-
|
|
435
|
+
} else {
|
|
436
|
+
// Otherwise hide the tab and put it in the more menu
|
|
304
437
|
hiddenItems.set(key, child);
|
|
305
438
|
noWidthForUnselectedTab = true;
|
|
306
439
|
}
|
|
307
440
|
}
|
|
308
441
|
if (hiddenItems.size) {
|
|
309
442
|
setShowMore(true);
|
|
310
|
-
setMoreMenuItems(Array.from(hiddenItems.entries()).map(([childValue, child])
|
|
311
|
-
return {
|
|
443
|
+
setMoreMenuItems(Array.from(hiddenItems.entries()).map(([childValue, child])=>{
|
|
444
|
+
return {
|
|
445
|
+
...child.props,
|
|
446
|
+
value: childValue,
|
|
447
|
+
ref: child.ref
|
|
448
|
+
};
|
|
312
449
|
}));
|
|
313
|
-
}
|
|
314
|
-
else {
|
|
450
|
+
} else {
|
|
315
451
|
setShowMore(false);
|
|
316
452
|
}
|
|
453
|
+
// @ts-ignore
|
|
317
454
|
const newVisibleTabs = new Map();
|
|
318
455
|
let childIndex = 0;
|
|
319
|
-
for (const [childValue, child] of visibleItems.entries())
|
|
320
|
-
newVisibleTabs.set(childValue, {
|
|
456
|
+
for (const [childValue, child] of visibleItems.entries()){
|
|
457
|
+
newVisibleTabs.set(childValue, {
|
|
458
|
+
childIndex,
|
|
459
|
+
child
|
|
460
|
+
});
|
|
321
461
|
childIndex++;
|
|
322
462
|
}
|
|
323
463
|
setVisibleTabs(newVisibleTabs);
|
|
324
464
|
shouldUpdate.current = false;
|
|
325
|
-
if
|
|
326
|
-
|
|
327
|
-
|
|
465
|
+
// Check if visibleTabs or hiddenTabs has changed
|
|
466
|
+
if (!isEqual([
|
|
467
|
+
...visibleTabsRef.current?.keys() || []
|
|
468
|
+
], [
|
|
469
|
+
...visibleItems.keys()
|
|
470
|
+
]) || !isEqual([
|
|
471
|
+
...hiddenTabsRef.current?.keys() || []
|
|
472
|
+
], [
|
|
473
|
+
...hiddenItems.keys()
|
|
474
|
+
])) {
|
|
475
|
+
// If it has changed, invoke onGroupInfoUpdate with the updated group data
|
|
476
|
+
onGroupInfoUpdate?.([
|
|
477
|
+
...visibleItems.keys()
|
|
478
|
+
], [
|
|
479
|
+
...hiddenItems.keys()
|
|
480
|
+
]);
|
|
328
481
|
}
|
|
329
482
|
visibleTabsRef.current = visibleItems;
|
|
330
483
|
hiddenTabsRef.current = hiddenItems;
|
|
331
484
|
});
|
|
332
|
-
const computeFloatingUnderline = useEventCallback((withoutAnimation = false)
|
|
485
|
+
const computeFloatingUnderline = useEventCallback((withoutAnimation = false)=>{
|
|
333
486
|
const selectedTabElement = getSelectedTabElement();
|
|
334
|
-
if (!selectedTabElement)
|
|
335
|
-
return;
|
|
487
|
+
if (!selectedTabElement) return;
|
|
336
488
|
const currentWidth = floatingUnderlineWidthMotionValue.get();
|
|
337
489
|
const currentLeft = floatingUnderlineLeftMotionValue.get();
|
|
338
490
|
const targetWidth = selectedTabElement.offsetWidth;
|
|
@@ -343,25 +495,28 @@ function useComputedTabs(props) {
|
|
|
343
495
|
floatingUnderlineLeftMotionValue.set(targetLeft);
|
|
344
496
|
return;
|
|
345
497
|
}
|
|
498
|
+
// Only animate if either value has changed
|
|
346
499
|
if (currentWidth !== targetWidth || currentLeft !== targetLeft) {
|
|
347
|
-
const transition = motionTransition[
|
|
500
|
+
const transition = motionTransition["spring-01-fast"];
|
|
348
501
|
animate(floatingUnderlineWidthMotionValue, targetWidth, transition);
|
|
349
502
|
animate(floatingUnderlineLeftMotionValue, targetLeft, transition);
|
|
350
503
|
}
|
|
351
504
|
});
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
505
|
+
// variant 'moreMenu', check if cache need to be updated when visibleTabs changed
|
|
506
|
+
// also need to update the cache when fonts are loaded after the initial mount
|
|
507
|
+
useEffect(()=>{
|
|
508
|
+
if (variant !== 'moreMenu') return;
|
|
355
509
|
if (moreRef.current) {
|
|
356
510
|
moreBtnWidth.current = moreRef.current.offsetWidth;
|
|
357
511
|
}
|
|
358
512
|
if (tabsChildrenCache.current) {
|
|
359
513
|
if (!shouldUpdate.current) {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
514
|
+
// Check if the rendered visibleTabs have a different width than the cached tabs
|
|
515
|
+
for (const [childValue, { childIndex: index, child }] of visibleTabs.entries()){
|
|
516
|
+
if (shouldUpdate.current || !child) continue;
|
|
363
517
|
const cachedWidth = tabsChildrenCache.current?.get(childValue)?.width;
|
|
364
518
|
const childElement = tabListRef.current?.children[index];
|
|
519
|
+
// If the cached width is different, we should perform a partial update
|
|
365
520
|
if (cachedWidth !== childElement.offsetWidth) {
|
|
366
521
|
shouldUpdate.current = 'partial';
|
|
367
522
|
}
|
|
@@ -372,82 +527,122 @@ function useComputedTabs(props) {
|
|
|
372
527
|
computeMoreMenuWithCache();
|
|
373
528
|
}
|
|
374
529
|
}
|
|
375
|
-
|
|
376
|
-
|
|
530
|
+
// oxlint-disable-next-line react-hooks/exhaustive-deps
|
|
531
|
+
}, [
|
|
532
|
+
visibleTabs,
|
|
533
|
+
fontsLoaded
|
|
534
|
+
]);
|
|
535
|
+
useEffect(()=>{
|
|
377
536
|
if (prevChildren && children !== prevChildren) {
|
|
378
537
|
setVisibleTabs(processedChildren);
|
|
379
538
|
shouldUpdate.current = 'full';
|
|
380
539
|
}
|
|
381
|
-
}, [
|
|
382
|
-
|
|
540
|
+
}, [
|
|
541
|
+
children,
|
|
542
|
+
prevChildren,
|
|
543
|
+
processedChildren
|
|
544
|
+
]);
|
|
545
|
+
// initial cache for variant 'moreMenu', used for computing more menu
|
|
546
|
+
useLayoutEffect(()=>{
|
|
383
547
|
if (switchToMoreMenu.current && tabListRef.current?.children) {
|
|
384
548
|
switchToMoreMenu.current = false;
|
|
549
|
+
// initial cache of tabsChildrenCache
|
|
385
550
|
tabsChildrenCache.current = new Map();
|
|
386
551
|
cacheTabsChildren();
|
|
552
|
+
// initial cache of moreBtnWidth
|
|
387
553
|
moreBtnWidth.current = moreRef.current?.offsetWidth || 0;
|
|
388
554
|
computeMoreMenuWithCache();
|
|
389
555
|
}
|
|
390
|
-
|
|
391
|
-
|
|
556
|
+
// oxlint-disable-next-line react-hooks/exhaustive-deps
|
|
557
|
+
}, [
|
|
558
|
+
showMore
|
|
559
|
+
]);
|
|
560
|
+
// handle switch variant situation
|
|
561
|
+
useOnReRender(()=>{
|
|
392
562
|
if (variant !== 'moreMenu') {
|
|
393
563
|
setShowMore(false);
|
|
394
564
|
setVisibleTabs(processedChildren);
|
|
395
|
-
}
|
|
396
|
-
else {
|
|
565
|
+
} else {
|
|
397
566
|
switchToMoreMenu.current = true;
|
|
398
567
|
setShowMore(true);
|
|
399
568
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
569
|
+
// oxlint-disable-next-line react-hooks/exhaustive-deps
|
|
570
|
+
}, [
|
|
571
|
+
variant
|
|
572
|
+
]);
|
|
573
|
+
// Check if we need to update the cache and update the more menu when any tab child is resized
|
|
574
|
+
useEffect(()=>{
|
|
575
|
+
if (variant !== 'moreMenu') return;
|
|
404
576
|
let initialObservation = true;
|
|
405
|
-
const handleResize = throttle((entries)
|
|
577
|
+
const handleResize = throttle((entries)=>{
|
|
406
578
|
if (initialObservation) {
|
|
407
|
-
|
|
579
|
+
/**
|
|
580
|
+
* We handle the computation of the more menu after the initial mount,
|
|
581
|
+
* or we re-compute when the `showMore` prop has updated. There's
|
|
582
|
+
* no need to compute here.
|
|
583
|
+
*/ initialObservation = false;
|
|
408
584
|
return;
|
|
409
585
|
}
|
|
410
586
|
if (entries) {
|
|
587
|
+
// Partially update the cache before computing the more menu
|
|
411
588
|
cacheTabsChildren();
|
|
412
|
-
if (moreRef.current)
|
|
413
|
-
moreBtnWidth.current = moreRef.current.offsetWidth;
|
|
589
|
+
if (moreRef.current) moreBtnWidth.current = moreRef.current.offsetWidth;
|
|
414
590
|
}
|
|
415
591
|
computeMoreMenuWithCache();
|
|
416
|
-
|
|
592
|
+
/**
|
|
593
|
+
* For variants other than moreMenu, we compute the floating underline using a dedicated
|
|
594
|
+
* ResizeObserver for the selected tab. When we're on the moreMenu we already have a resize
|
|
595
|
+
* observer for the tabList, so we don't need to create a dedicated one for the selected tab,
|
|
596
|
+
* then we compute the floating underline as part of the resize event we otherwise use for
|
|
597
|
+
* the more menu.
|
|
598
|
+
*/ computeFloatingUnderline(true);
|
|
417
599
|
}, 200);
|
|
418
600
|
let resizeObserver;
|
|
601
|
+
// Check if ResizeObserver is supported in this environment
|
|
419
602
|
if (typeof ResizeObserverClass !== 'undefined') {
|
|
420
603
|
resizeObserver = new ResizeObserverClass(handleResize);
|
|
421
|
-
if (scrollerRef.current)
|
|
422
|
-
resizeObserver.observe(scrollerRef.current);
|
|
604
|
+
if (scrollerRef.current) resizeObserver.observe(scrollerRef.current);
|
|
423
605
|
if (tabListRef && tabListRef.current) {
|
|
424
606
|
const tabsChildren = tabListRef.current.children;
|
|
425
|
-
Array.from(tabsChildren).forEach((child, index)
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
607
|
+
Array.from(tabsChildren).forEach((child, index)=>{
|
|
608
|
+
// The last child when the pill prop is enabled is the underline element, no need to observe it
|
|
609
|
+
if (pill && index === tabsChildren.length - 1) return;
|
|
610
|
+
// When the pill props is disabled, the last 2 children are the underline and floating underline elements, no need to observe them
|
|
611
|
+
if (!pill && index >= tabsChildren.length - 2) return;
|
|
430
612
|
resizeObserver?.observe(child);
|
|
431
613
|
});
|
|
432
614
|
}
|
|
433
615
|
}
|
|
434
|
-
return ()
|
|
616
|
+
return ()=>{
|
|
435
617
|
handleResize.cancel();
|
|
436
618
|
resizeObserver?.disconnect();
|
|
437
619
|
};
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
620
|
+
// oxlint-disable-next-line react-hooks/exhaustive-deps
|
|
621
|
+
}, [
|
|
622
|
+
variant,
|
|
623
|
+
pill,
|
|
624
|
+
ResizeObserverClass
|
|
625
|
+
]);
|
|
626
|
+
useEffect(()=>{
|
|
627
|
+
// The ResizeObserver for the moreMenu automatically handles floating underline computation
|
|
628
|
+
if (variant === 'moreMenu') return;
|
|
442
629
|
let initialObservation = true;
|
|
443
|
-
const handleSelectedTabResize = throttle(()
|
|
630
|
+
const handleSelectedTabResize = throttle(()=>{
|
|
444
631
|
if (initialObservation) {
|
|
445
|
-
|
|
632
|
+
/**
|
|
633
|
+
* We handle the initial computation of the floating underline in the
|
|
634
|
+
* Tabs component after the value has changed, we don't need to compute
|
|
635
|
+
* it here again. This also allows us to distinguish between when the
|
|
636
|
+
* selected tab is resized and when the user selects a different tab,
|
|
637
|
+
* meaning we can apply an instant transition during resizing, but use
|
|
638
|
+
* a spring transition when switching tabs.
|
|
639
|
+
*/ initialObservation = false;
|
|
446
640
|
return;
|
|
447
641
|
}
|
|
448
642
|
computeFloatingUnderline(true);
|
|
449
643
|
}, 16);
|
|
450
644
|
let selectedTabResizeObserver;
|
|
645
|
+
// Check if ResizeObserver is supported in this environment
|
|
451
646
|
if (typeof ResizeObserverClass !== 'undefined') {
|
|
452
647
|
selectedTabResizeObserver = new ResizeObserverClass(handleSelectedTabResize);
|
|
453
648
|
const selectedTabElement = getSelectedTabElement();
|
|
@@ -455,24 +650,35 @@ function useComputedTabs(props) {
|
|
|
455
650
|
selectedTabResizeObserver.observe(selectedTabElement);
|
|
456
651
|
}
|
|
457
652
|
}
|
|
458
|
-
return ()
|
|
653
|
+
return ()=>{
|
|
459
654
|
handleSelectedTabResize.cancel();
|
|
460
655
|
selectedTabResizeObserver?.disconnect();
|
|
461
656
|
};
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
657
|
+
// oxlint-disable-next-line react-hooks/exhaustive-deps
|
|
658
|
+
}, [
|
|
659
|
+
variant,
|
|
660
|
+
value,
|
|
661
|
+
ResizeObserverClass
|
|
662
|
+
]);
|
|
663
|
+
const prevVariant = usePrevious(()=>variant);
|
|
664
|
+
useEffect(()=>{
|
|
665
|
+
// If only the variant is changed, we need to update the floating underline without animation
|
|
465
666
|
if (prevVariant !== variant) {
|
|
466
667
|
computeFloatingUnderline(true);
|
|
467
668
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
669
|
+
// oxlint-disable-next-line react-hooks/exhaustive-deps
|
|
670
|
+
}, [
|
|
671
|
+
variant
|
|
672
|
+
]);
|
|
673
|
+
useEffect(()=>{
|
|
674
|
+
if (variant === 'moreMenu' && value !== null && hiddenTabsRef.current?.has(value)) {
|
|
675
|
+
// Re-compute when a hidden tab (i.e. tab in the more menu) is selected
|
|
473
676
|
computeMoreMenuWithCache();
|
|
474
677
|
}
|
|
475
|
-
|
|
678
|
+
// oxlint-disable-next-line react-hooks/exhaustive-deps
|
|
679
|
+
}, [
|
|
680
|
+
value
|
|
681
|
+
]);
|
|
476
682
|
return {
|
|
477
683
|
visibleTabsChildren,
|
|
478
684
|
showMore,
|
|
@@ -480,7 +686,7 @@ function useComputedTabs(props) {
|
|
|
480
686
|
processedChildren,
|
|
481
687
|
getSelectedTabElement,
|
|
482
688
|
computeMoreMenuWithCache,
|
|
483
|
-
computeFloatingUnderline
|
|
689
|
+
computeFloatingUnderline
|
|
484
690
|
};
|
|
485
691
|
}
|
|
486
692
|
function getHorizontalPadding(element) {
|
|
@@ -490,25 +696,36 @@ function getHorizontalPadding(element) {
|
|
|
490
696
|
return paddingLeft + paddingRight;
|
|
491
697
|
}
|
|
492
698
|
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
|
|
493
708
|
const THRESHOLD_FOR_SUB_PIXEL_CALCULATION = 0.5;
|
|
494
|
-
|
|
709
|
+
/**
|
|
710
|
+
* This `useTabsScroller` hook manages the state and behavior of scroll buttons at both ends of the tab list (start and end) when the tab list exceeds the visible container width
|
|
711
|
+
*/ const useTabsScroller = ({ enable, scrollerRef, tabListRef, scroll, ScrollButtonStartProps, ScrollButtonEndProps })=>{
|
|
495
712
|
const [displayStartScroll, setDisplayStartScroll] = useState(false);
|
|
496
713
|
const [displayEndScroll, setDisplayEndScroll] = useState(false);
|
|
497
714
|
const { externalWindow } = useWindowContext();
|
|
498
715
|
const ResizeObserverClass = (externalWindow || window).ResizeObserver;
|
|
499
|
-
useLayoutEffect(()
|
|
500
|
-
if (!enable)
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
if (!scrollerRef.current)
|
|
504
|
-
return;
|
|
716
|
+
useLayoutEffect(()=>{
|
|
717
|
+
if (!enable) return;
|
|
718
|
+
const checkScroll = throttle(()=>{
|
|
719
|
+
if (!scrollerRef.current) return;
|
|
505
720
|
const { scrollLeft, scrollWidth, clientWidth } = scrollerRef.current;
|
|
506
721
|
const maxScrollLeft = scrollWidth - clientWidth;
|
|
507
722
|
setDisplayStartScroll(scrollLeft > 0);
|
|
508
723
|
setDisplayEndScroll(maxScrollLeft - scrollLeft > THRESHOLD_FOR_SUB_PIXEL_CALCULATION);
|
|
509
724
|
}, 100);
|
|
725
|
+
// add scroll event listener
|
|
510
726
|
const currentTabsRef = scrollerRef.current;
|
|
511
727
|
currentTabsRef?.addEventListener('scroll', checkScroll);
|
|
728
|
+
// ResizeObserver for tabs list
|
|
512
729
|
let resizeObserver;
|
|
513
730
|
if (typeof ResizeObserverClass !== 'undefined') {
|
|
514
731
|
resizeObserver = new ResizeObserverClass(checkScroll);
|
|
@@ -516,26 +733,31 @@ const useTabsScroller = ({ enable, scrollerRef, tabListRef, scroll, ScrollButton
|
|
|
516
733
|
resizeObserver.observe(currentTabsRef);
|
|
517
734
|
}
|
|
518
735
|
}
|
|
519
|
-
checkScroll();
|
|
520
|
-
return ()
|
|
736
|
+
checkScroll(); // Initial check
|
|
737
|
+
return ()=>{
|
|
521
738
|
currentTabsRef?.removeEventListener('scroll', checkScroll);
|
|
522
739
|
checkScroll.cancel();
|
|
740
|
+
// Disconnect the ResizeObserver on cleanup
|
|
523
741
|
resizeObserver?.disconnect();
|
|
524
742
|
};
|
|
525
|
-
}, [
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
743
|
+
}, [
|
|
744
|
+
enable,
|
|
745
|
+
scrollerRef,
|
|
746
|
+
ResizeObserverClass
|
|
747
|
+
]);
|
|
748
|
+
const moveTabsScroll = useCallback((delta)=>{
|
|
749
|
+
const scrollValue = scrollerRef.current ? scrollerRef.current.scrollLeft + delta * 1 : 0;
|
|
530
750
|
scroll(scrollValue, 'neutral-01-slow');
|
|
531
|
-
}, [
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
751
|
+
}, [
|
|
752
|
+
scroll,
|
|
753
|
+
scrollerRef
|
|
754
|
+
]);
|
|
755
|
+
const getScrollSize = useCallback(()=>{
|
|
756
|
+
if (!scrollerRef.current || !tabListRef.current) return 0;
|
|
535
757
|
const containerSize = scrollerRef.current.clientWidth;
|
|
536
758
|
let totalSize = 0;
|
|
537
759
|
const children = Array.from(tabListRef.current.children);
|
|
538
|
-
for
|
|
760
|
+
for(let i = 0; i < children.length; i += 1){
|
|
539
761
|
const tab = children[i];
|
|
540
762
|
if (totalSize + tab.clientWidth > containerSize) {
|
|
541
763
|
if (i === 0) {
|
|
@@ -546,33 +768,65 @@ const useTabsScroller = ({ enable, scrollerRef, tabListRef, scroll, ScrollButton
|
|
|
546
768
|
totalSize += tab.clientWidth;
|
|
547
769
|
}
|
|
548
770
|
return totalSize;
|
|
549
|
-
}, [
|
|
550
|
-
|
|
771
|
+
}, [
|
|
772
|
+
scrollerRef,
|
|
773
|
+
tabListRef
|
|
774
|
+
]);
|
|
775
|
+
const handleStartScrollClick = useCallback(()=>{
|
|
551
776
|
moveTabsScroll(-getScrollSize());
|
|
552
|
-
}, [
|
|
553
|
-
|
|
777
|
+
}, [
|
|
778
|
+
moveTabsScroll,
|
|
779
|
+
getScrollSize
|
|
780
|
+
]);
|
|
781
|
+
const handleEndScrollClick = useCallback(()=>{
|
|
554
782
|
moveTabsScroll(getScrollSize());
|
|
555
|
-
}, [
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
783
|
+
}, [
|
|
784
|
+
moveTabsScroll,
|
|
785
|
+
getScrollSize
|
|
786
|
+
]);
|
|
787
|
+
return useMemo(()=>({
|
|
788
|
+
scrollButtonStart: enable && displayStartScroll ? /*#__PURE__*/ jsx(TabScrollButton, {
|
|
789
|
+
direction: 'left',
|
|
790
|
+
onClick: handleStartScrollClick,
|
|
791
|
+
ButtonProps: ScrollButtonStartProps
|
|
792
|
+
}) : null,
|
|
793
|
+
scrollButtonEnd: enable && displayEndScroll ? /*#__PURE__*/ jsx(TabScrollButton, {
|
|
794
|
+
direction: 'right',
|
|
795
|
+
onClick: handleEndScrollClick,
|
|
796
|
+
ButtonProps: ScrollButtonEndProps
|
|
797
|
+
}) : null
|
|
798
|
+
}), [
|
|
560
799
|
enable,
|
|
561
800
|
displayStartScroll,
|
|
562
801
|
handleStartScrollClick,
|
|
563
802
|
ScrollButtonStartProps,
|
|
564
803
|
displayEndScroll,
|
|
565
804
|
handleEndScrollClick,
|
|
566
|
-
ScrollButtonEndProps
|
|
805
|
+
ScrollButtonEndProps
|
|
567
806
|
]);
|
|
568
807
|
};
|
|
569
808
|
|
|
570
|
-
|
|
809
|
+
// extracted by css-extract-rspack-plugin
|
|
810
|
+
/* export default */ var Tabs = ({"sui-tabs-root":"sui-tabs-root","sui-tabs-large":"sui-tabs-large","sui-tabs-scroller":"sui-tabs-scroller","sui-tabs-scrollable":"sui-tabs-scrollable","sui-tab":"sui-tab","sui-tabs-tab-list":"sui-tabs-tab-list","sui-tabs-underline":"sui-tabs-underline","sui-tabs-indicator":"sui-tabs-indicator","sui-tabs-compact":"sui-tabs-compact","sui-tabs-pill":"sui-tabs-pill","sui-tab-more-menu-root":"sui-tab-more-menu-root","sui-tab-more-menu-button":"sui-tab-more-menu-button","sui-tabs-scroll-left":"sui-tabs-scroll-left","sui-tabs-scroll-right":"sui-tabs-scroll-right","sui-tabs-scroll-both":"sui-tabs-scroll-both","sui-tabs-floating-underline":"sui-tabs-floating-underline","sui-tab-more-menu-badge-root":"sui-tab-more-menu-badge-root","sui-badge-dot":"sui-badge-dot","sui-badge-content":"sui-badge-content","sui-tab-more-menu-pill-badge":"sui-tab-more-menu-pill-badge","sui-tab-scroll-button-root":"sui-tab-scroll-button-root","sui-tab-scroll-button-start":"sui-tab-scroll-button-start","sui-compact":"sui-compact"});
|
|
811
|
+
|
|
812
|
+
|
|
813
|
+
|
|
814
|
+
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
|
|
571
822
|
|
|
572
823
|
const DEFAULT_PILL_GAP = 4;
|
|
573
|
-
const
|
|
574
|
-
const props = useThemeProps({
|
|
575
|
-
|
|
824
|
+
const Tabs_Tabs = /*#__PURE__*/ forwardRef((inProps, ref)=>{
|
|
825
|
+
const props = useThemeProps({
|
|
826
|
+
props: inProps,
|
|
827
|
+
name: 'Tabs'
|
|
828
|
+
});
|
|
829
|
+
const { classes, className, children, variant = 'standard', onGroupInfoUpdate, MoreMenuProps, pill, pillGap: pillGapProp = DEFAULT_PILL_GAP, color = 'default', size = 'medium', ScrollButtonStartProps, ScrollButtonEndProps, enableScrollButtons = true, ...rest } = props;
|
|
576
830
|
const pillGap = pill ? pillGapProp : 0;
|
|
577
831
|
const tabsRef = useRef(null);
|
|
578
832
|
const scrollerRef = useRef(null);
|
|
@@ -583,15 +837,15 @@ const Tabs = forwardRef((inProps, ref) => {
|
|
|
583
837
|
const floatingUnderlineWidthMotionValue = useMotionValue(0);
|
|
584
838
|
const floatingUnderlineLeftMotionValue = useMotionValue(0);
|
|
585
839
|
const floatingUnderlineOpacityMotionValue = useMotionValue(1);
|
|
586
|
-
const floatingUnderlineTransition = useMotionSafeTransition(motionTransition[
|
|
840
|
+
const floatingUnderlineTransition = useMotionSafeTransition(motionTransition["spring-01-fast"]);
|
|
587
841
|
const prefersReducedMotion = usePrefersReducedMotion();
|
|
588
842
|
const cls = useClassesWithMode('tabs', classes, className);
|
|
589
843
|
const { onGroupInfoUpdate: groupInfoUpdateHandler, ...restProps } = props;
|
|
590
|
-
const { getRootProps, contextValue, selectedValue: value
|
|
844
|
+
const { getRootProps, contextValue, selectedValue: value } = useTabsList({
|
|
591
845
|
...restProps,
|
|
592
|
-
rootRef
|
|
846
|
+
rootRef
|
|
593
847
|
});
|
|
594
|
-
const { processedChildren, visibleTabsChildren, showMore, moreMenuItems, getSelectedTabElement, computeFloatingUnderline
|
|
848
|
+
const { processedChildren, visibleTabsChildren, showMore, moreMenuItems, getSelectedTabElement, computeFloatingUnderline } = useComputedTabs({
|
|
595
849
|
variant,
|
|
596
850
|
value,
|
|
597
851
|
children,
|
|
@@ -601,13 +855,14 @@ const Tabs = forwardRef((inProps, ref) => {
|
|
|
601
855
|
floatingUnderlineWidthMotionValue,
|
|
602
856
|
tabListRef,
|
|
603
857
|
scrollerRef,
|
|
604
|
-
moreRef
|
|
858
|
+
moreRef
|
|
605
859
|
});
|
|
606
|
-
const getTabsMeta = useEventCallback(()
|
|
860
|
+
const getTabsMeta = useEventCallback(()=>{
|
|
607
861
|
const tabsNode = scrollerRef.current;
|
|
608
862
|
let tabsMeta;
|
|
609
863
|
if (tabsNode) {
|
|
610
864
|
const rect = tabsNode.getBoundingClientRect();
|
|
865
|
+
// create a new object with ClientRect class props + scrollLeft
|
|
611
866
|
tabsMeta = {
|
|
612
867
|
clientWidth: tabsNode.clientWidth,
|
|
613
868
|
scrollLeft: tabsNode.scrollLeft,
|
|
@@ -616,7 +871,7 @@ const Tabs = forwardRef((inProps, ref) => {
|
|
|
616
871
|
top: rect.top,
|
|
617
872
|
bottom: rect.bottom,
|
|
618
873
|
left: rect.left,
|
|
619
|
-
right: rect.right
|
|
874
|
+
right: rect.right
|
|
620
875
|
};
|
|
621
876
|
}
|
|
622
877
|
let tabMeta;
|
|
@@ -625,6 +880,7 @@ const Tabs = forwardRef((inProps, ref) => {
|
|
|
625
880
|
if (tab) {
|
|
626
881
|
const rect = tab.getBoundingClientRect();
|
|
627
882
|
tabMeta = {
|
|
883
|
+
// Use offsetWidth/offsetHeight to negate any transforms that might be applied
|
|
628
884
|
height: tab.offsetHeight,
|
|
629
885
|
width: tab.offsetWidth,
|
|
630
886
|
x: rect.x,
|
|
@@ -632,47 +888,50 @@ const Tabs = forwardRef((inProps, ref) => {
|
|
|
632
888
|
top: rect.top,
|
|
633
889
|
right: rect.right,
|
|
634
890
|
bottom: rect.bottom,
|
|
635
|
-
left: rect.left
|
|
891
|
+
left: rect.left
|
|
636
892
|
};
|
|
637
893
|
}
|
|
638
894
|
}
|
|
639
|
-
return {
|
|
895
|
+
return {
|
|
896
|
+
tabsMeta,
|
|
897
|
+
tabMeta
|
|
898
|
+
};
|
|
640
899
|
});
|
|
641
900
|
const scrollStart = 'scrollLeft';
|
|
642
901
|
const start = 'left';
|
|
643
902
|
const end = 'right';
|
|
644
|
-
const scroll = (scrollValue, transitionType)
|
|
645
|
-
if (!scrollerRef.current)
|
|
646
|
-
return;
|
|
903
|
+
const scroll = (scrollValue, transitionType)=>{
|
|
904
|
+
if (!scrollerRef.current) return;
|
|
647
905
|
const from = scrollerRef.current[scrollStart];
|
|
648
906
|
const to = Number(Number.parseFloat(scrollValue.toString()).toFixed(2));
|
|
649
907
|
if (from.toFixed(2) === to.toFixed(2)) {
|
|
650
908
|
return;
|
|
651
909
|
}
|
|
652
910
|
if (transitionType === null || prefersReducedMotion) {
|
|
911
|
+
// if transitionType is null or prefersReducedMotion, set the scroll value without animation
|
|
653
912
|
scrollerRef.current[scrollStart] = to;
|
|
654
913
|
return;
|
|
655
914
|
}
|
|
656
915
|
const transition = motionTransition[transitionType ? transitionType : 'haste-01-fast'];
|
|
657
916
|
animate(from, to, {
|
|
658
917
|
...transition,
|
|
659
|
-
onUpdate: (value)
|
|
660
|
-
if (!scrollerRef.current)
|
|
661
|
-
return;
|
|
918
|
+
onUpdate: (value)=>{
|
|
919
|
+
if (!scrollerRef.current) return;
|
|
662
920
|
scrollerRef.current[scrollStart] = value;
|
|
663
|
-
}
|
|
921
|
+
}
|
|
664
922
|
});
|
|
665
923
|
};
|
|
666
|
-
const scrollSelectedIntoView = useEventCallback((noAnimation)
|
|
924
|
+
const scrollSelectedIntoView = useEventCallback((noAnimation)=>{
|
|
667
925
|
const { tabsMeta, tabMeta } = getTabsMeta();
|
|
668
926
|
if (!tabMeta || !tabsMeta) {
|
|
669
927
|
return;
|
|
670
928
|
}
|
|
929
|
+
// Check if the left side of the tab is out of view
|
|
671
930
|
if (tabMeta[start] < tabsMeta[start]) {
|
|
672
931
|
const nextScrollStart = tabsMeta[scrollStart] + (tabMeta[start] - tabsMeta[start]);
|
|
673
932
|
scroll(nextScrollStart, noAnimation ? null : undefined);
|
|
674
|
-
|
|
675
|
-
else if (tabMeta[end] > tabsMeta[end]) {
|
|
933
|
+
// Check if the right side of the tab is out of view
|
|
934
|
+
} else if (tabMeta[end] > tabsMeta[end]) {
|
|
676
935
|
const nextScrollStart = tabsMeta[scrollStart] + Math.abs(tabMeta[end] - tabsMeta[end]);
|
|
677
936
|
scroll(nextScrollStart, noAnimation ? null : undefined);
|
|
678
937
|
}
|
|
@@ -683,48 +942,114 @@ const Tabs = forwardRef((inProps, ref) => {
|
|
|
683
942
|
tabListRef,
|
|
684
943
|
scroll,
|
|
685
944
|
ScrollButtonStartProps,
|
|
686
|
-
ScrollButtonEndProps
|
|
945
|
+
ScrollButtonEndProps
|
|
687
946
|
});
|
|
688
947
|
const prevValueAfterChildrenUpdate = useRef(value);
|
|
689
948
|
const prevVisibleTabsChildren = useRef(visibleTabsChildren);
|
|
690
|
-
useEffect(()
|
|
949
|
+
useEffect(()=>{
|
|
691
950
|
const noAnimationWhenFirstRender = floatingUnderlineWidthMotionValue.get() === 0;
|
|
692
951
|
if (variant !== 'moreMenu') {
|
|
693
|
-
|
|
952
|
+
/**
|
|
953
|
+
* Invoke scrollSelectedIntoView when visibleTabsChildren has been updated or the selected
|
|
954
|
+
* tab has changed. It will be updated when the variant is switched from `moreMenu`
|
|
955
|
+
* to another value. If the variant is `moreMenu`, `visibleTabsChildren` will also be
|
|
956
|
+
* updated when resized.
|
|
957
|
+
*/ scrollSelectedIntoView(noAnimationWhenFirstRender);
|
|
694
958
|
}
|
|
695
|
-
|
|
696
|
-
|
|
959
|
+
// If the visibleTabsChildren has been updated but selected tab has not changed,
|
|
960
|
+
// we don't need to animate the floating underline
|
|
961
|
+
const noAnimationWhenNoValueChange = prevVisibleTabsChildren.current !== visibleTabsChildren && prevValueAfterChildrenUpdate.current === value;
|
|
697
962
|
if (prevVisibleTabsChildren.current !== visibleTabsChildren) {
|
|
698
963
|
prevVisibleTabsChildren.current = visibleTabsChildren;
|
|
699
964
|
prevValueAfterChildrenUpdate.current = value;
|
|
700
965
|
}
|
|
701
966
|
computeFloatingUnderline(noAnimationWhenFirstRender || noAnimationWhenNoValueChange);
|
|
702
|
-
|
|
703
|
-
|
|
967
|
+
// oxlint-disable-next-line react-hooks/exhaustive-deps
|
|
968
|
+
}, [
|
|
969
|
+
visibleTabsChildren,
|
|
970
|
+
value
|
|
971
|
+
]);
|
|
972
|
+
const tabsContextValue = useMemo(()=>({
|
|
973
|
+
pill,
|
|
974
|
+
pillGap,
|
|
975
|
+
color,
|
|
976
|
+
size,
|
|
977
|
+
setFloatingUnderlineVisibility: (isVisible)=>{
|
|
978
|
+
floatingUnderlineOpacityMotionValue.set(isVisible ? 1 : 0);
|
|
979
|
+
}
|
|
980
|
+
}), [
|
|
981
|
+
floatingUnderlineOpacityMotionValue,
|
|
704
982
|
pill,
|
|
705
983
|
pillGap,
|
|
706
984
|
color,
|
|
707
|
-
size
|
|
708
|
-
|
|
709
|
-
floatingUnderlineOpacityMotionValue.set(isVisible ? 1 : 0);
|
|
710
|
-
},
|
|
711
|
-
}), [floatingUnderlineOpacityMotionValue, pill, pillGap, color, size]);
|
|
985
|
+
size
|
|
986
|
+
]);
|
|
712
987
|
if (!processedChildren.size) {
|
|
713
988
|
return null;
|
|
714
989
|
}
|
|
715
|
-
return
|
|
990
|
+
return /*#__PURE__*/ jsx(TabsListProvider, {
|
|
991
|
+
value: contextValue,
|
|
992
|
+
children: /*#__PURE__*/ jsx(TabsContext.Provider, {
|
|
993
|
+
value: tabsContextValue,
|
|
994
|
+
children: /*#__PURE__*/ jsxs("div", {
|
|
995
|
+
className: cls('root', {
|
|
716
996
|
pill,
|
|
717
997
|
scrollable: variant === 'scrollable',
|
|
718
998
|
compact: variant === 'moreMenu',
|
|
719
999
|
'scroll-left': !!scrollButtonStart,
|
|
720
1000
|
'scroll-right': !!scrollButtonEnd,
|
|
721
|
-
'scroll-both': !!(scrollButtonStart && scrollButtonEnd)
|
|
722
|
-
}, size),
|
|
1001
|
+
'scroll-both': !!(scrollButtonStart && scrollButtonEnd)
|
|
1002
|
+
}, size),
|
|
1003
|
+
...getRootProps(rest),
|
|
1004
|
+
children: [
|
|
1005
|
+
scrollButtonStart,
|
|
1006
|
+
/*#__PURE__*/ jsx("div", {
|
|
1007
|
+
className: cls('scroller'),
|
|
1008
|
+
ref: scrollerRef,
|
|
1009
|
+
children: /*#__PURE__*/ jsxs("div", {
|
|
1010
|
+
className: cls('tab-list'),
|
|
1011
|
+
ref: tabListRef,
|
|
1012
|
+
children: [
|
|
1013
|
+
visibleTabsChildren,
|
|
1014
|
+
showMore && /*#__PURE__*/ jsx(TabMoreMenu, {
|
|
1015
|
+
menuItems: moreMenuItems,
|
|
1016
|
+
...MoreMenuProps,
|
|
1017
|
+
ref: tabMoreMenuRef
|
|
1018
|
+
}),
|
|
1019
|
+
/*#__PURE__*/ jsx("div", {
|
|
1020
|
+
className: cls('underline')
|
|
1021
|
+
}),
|
|
1022
|
+
!pill && /*#__PURE__*/ jsx(motion.div, {
|
|
1023
|
+
style: {
|
|
723
1024
|
left: floatingUnderlineLeftMotionValue,
|
|
724
1025
|
width: floatingUnderlineWidthMotionValue,
|
|
725
|
-
opacity: floatingUnderlineOpacityMotionValue
|
|
726
|
-
},
|
|
1026
|
+
opacity: floatingUnderlineOpacityMotionValue
|
|
1027
|
+
},
|
|
1028
|
+
transition: floatingUnderlineTransition,
|
|
1029
|
+
className: cls('floating-underline')
|
|
1030
|
+
})
|
|
1031
|
+
]
|
|
1032
|
+
})
|
|
1033
|
+
}),
|
|
1034
|
+
scrollButtonEnd
|
|
1035
|
+
]
|
|
1036
|
+
})
|
|
1037
|
+
})
|
|
1038
|
+
});
|
|
727
1039
|
});
|
|
728
|
-
|
|
1040
|
+
Tabs_Tabs.displayName = 'SuiTabs';
|
|
1041
|
+
|
|
1042
|
+
|
|
1043
|
+
|
|
1044
|
+
|
|
1045
|
+
|
|
1046
|
+
|
|
1047
|
+
|
|
1048
|
+
|
|
1049
|
+
|
|
1050
|
+
|
|
1051
|
+
|
|
1052
|
+
|
|
729
1053
|
|
|
730
|
-
export { TabMoreMenu, TabScrollButton, Tabs
|
|
1054
|
+
export { TabMoreMenu, TabScrollButton, Tabs as tabsStyle, Tabs_Tabs as Tabs, useComputedTabs, useTabsScroller };
|
|
1055
|
+
export { TabsContext, useTabsContext } from "./chunks/8809.js";
|