forked from reactjs/react-tabs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTabs.js
142 lines (126 loc) · 4.2 KB
/
Tabs.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import PropTypes from 'prop-types';
import React, { useEffect, useState } from 'react';
import {
childrenPropType,
onSelectPropType,
selectedIndexPropType,
} from '../helpers/propTypes';
import UncontrolledTabs from './UncontrolledTabs';
import { getTabsCount } from '../helpers/count';
const MODE_CONTROLLED = 0;
const MODE_UNCONTROLLED = 1;
const propTypes = {
children: childrenPropType,
className: PropTypes.oneOfType([
PropTypes.string,
PropTypes.array,
PropTypes.object,
]),
defaultFocus: PropTypes.bool,
defaultIndex: PropTypes.number,
direction: PropTypes.oneOf(['rtl', 'ltr']),
disabledTabClassName: PropTypes.string,
disableUpDownKeys: PropTypes.bool,
disableLeftRightKeys: PropTypes.bool,
domRef: PropTypes.func,
environment: PropTypes.object,
focusTabOnClick: PropTypes.bool,
forceRenderTabPanel: PropTypes.bool,
onSelect: onSelectPropType,
selectedIndex: selectedIndexPropType,
selectedTabClassName: PropTypes.string,
selectedTabPanelClassName: PropTypes.string,
};
const defaultProps = {
defaultFocus: false,
focusTabOnClick: true,
forceRenderTabPanel: false,
selectedIndex: null,
defaultIndex: null,
environment: null,
disableUpDownKeys: false,
disableLeftRightKeys: false,
};
const getModeFromProps = (props) => {
return props.selectedIndex === null ? MODE_UNCONTROLLED : MODE_CONTROLLED;
};
const checkForIllegalModeChange = (props, mode) => {
if (
process.env.NODE_ENV !== 'production' &&
mode != undefined &&
mode !== getModeFromProps(props)
) {
throw new Error(
`Switching between controlled mode (by using \`selectedIndex\`) and uncontrolled mode is not supported in \`Tabs\`.
For more information about controlled and uncontrolled mode of react-tabs see https://github.com/reactjs/react-tabs#controlled-vs-uncontrolled-mode.`,
);
}
};
/**
* State:
* mode: Initialized only once from props and never changes
* selectedIndex: null if controlled mode, otherwise initialized with prop defaultIndex, changed on selection of tabs, has effect to ensure it never gets out of bound
* focus: Because we never remove focus from the Tabs this state is only used to indicate that we should focus the current tab.
* It is initialized from the prop defaultFocus, and after the first render it is reset back to false. Later it can become true again when using keys to navigate the tabs.
*/
const Tabs = (props) => {
const {
children,
defaultFocus,
defaultIndex,
focusTabOnClick,
onSelect,
...attributes
} = {
...defaultProps,
...props,
};
const [focus, setFocus] = useState(defaultFocus);
const [mode] = useState(getModeFromProps(attributes));
const [selectedIndex, setSelectedIndex] = useState(
mode === MODE_UNCONTROLLED ? defaultIndex || 0 : null,
);
useEffect(() => {
// Reset focus after initial render, see comment above
setFocus(false);
}, []);
if (mode === MODE_UNCONTROLLED) {
// Ensure that we handle removed tabs and don't let selectedIndex get out of bounds
const tabsCount = getTabsCount(children);
useEffect(() => {
if (selectedIndex != null) {
const maxTabIndex = Math.max(0, tabsCount - 1);
setSelectedIndex(Math.min(selectedIndex, maxTabIndex));
}
}, [tabsCount]);
}
checkForIllegalModeChange(attributes, mode);
const handleSelected = (index, last, event) => {
// Call change event handler
if (typeof onSelect === 'function') {
// Check if the change event handler cancels the tab change
if (onSelect(index, last, event) === false) return;
}
// Always set focus on tabs unless it is disabled
if (focusTabOnClick) {
setFocus(true);
}
if (mode === MODE_UNCONTROLLED) {
// Update selected index
setSelectedIndex(index);
}
};
let subProps = { ...props, ...attributes };
subProps.focus = focus;
subProps.onSelect = handleSelected;
if (selectedIndex != null) {
subProps.selectedIndex = selectedIndex;
}
delete subProps.defaultFocus;
delete subProps.defaultIndex;
delete subProps.focusTabOnClick;
return <UncontrolledTabs {...subProps}>{children}</UncontrolledTabs>;
};
Tabs.propTypes = propTypes;
Tabs.tabsRole = 'Tabs';
export default Tabs;