1 | import { __rest } from "tslib";
|
2 | import * as React from 'react';
|
3 | import styles from '@patternfly/react-styles/css/components/Tabs/tabs';
|
4 | import buttonStyles from '@patternfly/react-styles/css/components/Button/button';
|
5 | import { css } from '@patternfly/react-styles';
|
6 | import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon';
|
7 | import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';
|
8 | import PlusIcon from '@patternfly/react-icons/dist/esm/icons/plus-icon';
|
9 | import { getUniqueId, isElementInView, formatBreakpointMods } from '../../helpers/util';
|
10 | import { TabContent } from './TabContent';
|
11 | import { TabsContextProvider } from './TabsContext';
|
12 | import { Button } from '../Button';
|
13 | import { getOUIAProps, getDefaultOUIAId, canUseDOM } from '../../helpers';
|
14 | import { GenerateId } from '../../helpers/GenerateId/GenerateId';
|
15 | export var TabsComponent;
|
16 | (function (TabsComponent) {
|
17 | TabsComponent["div"] = "div";
|
18 | TabsComponent["nav"] = "nav";
|
19 | })(TabsComponent || (TabsComponent = {}));
|
20 | const variantStyle = {
|
21 | default: '',
|
22 | light300: styles.modifiers.colorSchemeLight_300
|
23 | };
|
24 | export class Tabs extends React.Component {
|
25 | constructor(props) {
|
26 | super(props);
|
27 | this.tabList = React.createRef();
|
28 | this.scrollTimeout = null;
|
29 | this.handleScrollButtons = () => {
|
30 |
|
31 | clearTimeout(this.scrollTimeout);
|
32 | this.scrollTimeout = setTimeout(() => {
|
33 | const container = this.tabList.current;
|
34 | let disableLeftScrollButton = true;
|
35 | let disableRightScrollButton = true;
|
36 | let showScrollButtons = false;
|
37 | if (container && !this.props.isVertical) {
|
38 |
|
39 | const overflowOnLeft = !isElementInView(container, container.firstChild, false);
|
40 |
|
41 | const overflowOnRight = !isElementInView(container, container.lastChild, false);
|
42 | showScrollButtons = overflowOnLeft || overflowOnRight;
|
43 | disableLeftScrollButton = !overflowOnLeft;
|
44 | disableRightScrollButton = !overflowOnRight;
|
45 | }
|
46 | this.setState({
|
47 | showScrollButtons,
|
48 | disableLeftScrollButton,
|
49 | disableRightScrollButton
|
50 | });
|
51 | }, 100);
|
52 | };
|
53 | this.scrollLeft = () => {
|
54 |
|
55 | if (this.tabList.current) {
|
56 | const container = this.tabList.current;
|
57 | const childrenArr = Array.from(container.children);
|
58 | let firstElementInView;
|
59 | let lastElementOutOfView;
|
60 | let i;
|
61 | for (i = 0; i < childrenArr.length && !firstElementInView; i++) {
|
62 | if (isElementInView(container, childrenArr[i], false)) {
|
63 | firstElementInView = childrenArr[i];
|
64 | lastElementOutOfView = childrenArr[i - 1];
|
65 | }
|
66 | }
|
67 | if (lastElementOutOfView) {
|
68 | container.scrollLeft -= lastElementOutOfView.scrollWidth;
|
69 | }
|
70 | }
|
71 | };
|
72 | this.scrollRight = () => {
|
73 |
|
74 | if (this.tabList.current) {
|
75 | const container = this.tabList.current;
|
76 | const childrenArr = Array.from(container.children);
|
77 | let lastElementInView;
|
78 | let firstElementOutOfView;
|
79 | for (let i = childrenArr.length - 1; i >= 0 && !lastElementInView; i--) {
|
80 | if (isElementInView(container, childrenArr[i], false)) {
|
81 | lastElementInView = childrenArr[i];
|
82 | firstElementOutOfView = childrenArr[i + 1];
|
83 | }
|
84 | }
|
85 | if (firstElementOutOfView) {
|
86 | container.scrollLeft += firstElementOutOfView.scrollWidth;
|
87 | }
|
88 | }
|
89 | };
|
90 | this.state = {
|
91 | showScrollButtons: false,
|
92 | disableLeftScrollButton: true,
|
93 | disableRightScrollButton: true,
|
94 | shownKeys: this.props.defaultActiveKey !== undefined ? [this.props.defaultActiveKey] : [this.props.activeKey],
|
95 | uncontrolledActiveKey: this.props.defaultActiveKey,
|
96 | uncontrolledIsExpandedLocal: this.props.defaultIsExpanded,
|
97 | ouiaStateId: getDefaultOUIAId(Tabs.displayName)
|
98 | };
|
99 | if (this.props.isVertical && this.props.expandable !== undefined) {
|
100 | if (!this.props.toggleAriaLabel && !this.props.toggleText) {
|
101 |
|
102 | console.error('Tabs:', 'toggleAriaLabel or the toggleText prop is required to make the toggle button accessible');
|
103 | }
|
104 | }
|
105 | }
|
106 | handleTabClick(event, eventKey, tabContentRef) {
|
107 | const { shownKeys } = this.state;
|
108 | const { onSelect, defaultActiveKey } = this.props;
|
109 |
|
110 | if (defaultActiveKey !== undefined) {
|
111 | this.setState({
|
112 | uncontrolledActiveKey: eventKey
|
113 | });
|
114 | }
|
115 | else {
|
116 | onSelect(event, eventKey);
|
117 | }
|
118 |
|
119 | if (tabContentRef) {
|
120 | React.Children.toArray(this.props.children)
|
121 | .map(child => child)
|
122 | .filter(child => child.props && child.props.tabContentRef && child.props.tabContentRef.current)
|
123 | .forEach(child => (child.props.tabContentRef.current.hidden = true));
|
124 |
|
125 | if (tabContentRef.current) {
|
126 | tabContentRef.current.hidden = false;
|
127 | }
|
128 | }
|
129 | if (this.props.mountOnEnter) {
|
130 | this.setState({
|
131 | shownKeys: shownKeys.concat(eventKey)
|
132 | });
|
133 | }
|
134 | }
|
135 | componentDidMount() {
|
136 | if (!this.props.isVertical) {
|
137 | if (canUseDOM) {
|
138 | window.addEventListener('resize', this.handleScrollButtons, false);
|
139 | }
|
140 |
|
141 | this.handleScrollButtons();
|
142 | }
|
143 | }
|
144 | componentWillUnmount() {
|
145 | if (!this.props.isVertical) {
|
146 | if (canUseDOM) {
|
147 | window.removeEventListener('resize', this.handleScrollButtons, false);
|
148 | }
|
149 | }
|
150 | clearTimeout(this.scrollTimeout);
|
151 | }
|
152 | componentDidUpdate(prevProps) {
|
153 | const { activeKey, mountOnEnter, children } = this.props;
|
154 | const { shownKeys } = this.state;
|
155 | if (prevProps.activeKey !== activeKey && mountOnEnter && shownKeys.indexOf(activeKey) < 0) {
|
156 | this.setState({
|
157 | shownKeys: shownKeys.concat(activeKey)
|
158 | });
|
159 | }
|
160 | if (prevProps.children &&
|
161 | children &&
|
162 | React.Children.toArray(prevProps.children).length !== React.Children.toArray(children).length) {
|
163 | this.handleScrollButtons();
|
164 | }
|
165 | }
|
166 | render() {
|
167 | const _a = this.props, { className, children, activeKey, defaultActiveKey, id, isFilled, isSecondary, isVertical, isBox, hasBorderBottom, hasSecondaryBorderBottom, leftScrollAriaLabel, rightScrollAriaLabel, 'aria-label': ariaLabel, component, ouiaId, ouiaSafe, mountOnEnter, unmountOnExit, usePageInsets, inset, variant, expandable, isExpanded, defaultIsExpanded, toggleText, toggleAriaLabel, addButtonAriaLabel, onToggle, onClose, onAdd } = _a, props = __rest(_a, ["className", "children", "activeKey", "defaultActiveKey", "id", "isFilled", "isSecondary", "isVertical", "isBox", "hasBorderBottom", "hasSecondaryBorderBottom", "leftScrollAriaLabel", "rightScrollAriaLabel", 'aria-label', "component", "ouiaId", "ouiaSafe", "mountOnEnter", "unmountOnExit", "usePageInsets", "inset", "variant", "expandable", "isExpanded", "defaultIsExpanded", "toggleText", "toggleAriaLabel", "addButtonAriaLabel", "onToggle", "onClose", "onAdd"]);
|
168 | const { showScrollButtons, disableLeftScrollButton, disableRightScrollButton, shownKeys, uncontrolledActiveKey, uncontrolledIsExpandedLocal } = this.state;
|
169 | const filteredChildren = React.Children.toArray(children)
|
170 | .filter(Boolean)
|
171 | .filter(child => !child.props.isHidden);
|
172 | const uniqueId = id || getUniqueId();
|
173 | const Component = component === TabsComponent.nav ? 'nav' : 'div';
|
174 | const localActiveKey = defaultActiveKey !== undefined ? uncontrolledActiveKey : activeKey;
|
175 | const isExpandedLocal = defaultIsExpanded !== undefined ? uncontrolledIsExpandedLocal : isExpanded;
|
176 |
|
177 | const toggleTabs = (newValue) => {
|
178 | if (isExpanded === undefined) {
|
179 | this.setState({ uncontrolledIsExpandedLocal: newValue });
|
180 | }
|
181 | else {
|
182 | onToggle(newValue);
|
183 | }
|
184 | };
|
185 | return (React.createElement(TabsContextProvider, { value: {
|
186 | variant,
|
187 | mountOnEnter,
|
188 | unmountOnExit,
|
189 | localActiveKey,
|
190 | uniqueId,
|
191 | handleTabClick: (...args) => this.handleTabClick(...args),
|
192 | handleTabClose: onClose
|
193 | } },
|
194 | React.createElement(Component, Object.assign({ "aria-label": ariaLabel, className: css(styles.tabs, isFilled && styles.modifiers.fill, isSecondary && styles.modifiers.secondary, isVertical && styles.modifiers.vertical, isVertical && expandable && formatBreakpointMods(expandable, styles), isVertical && expandable && isExpandedLocal && styles.modifiers.expanded, isBox && styles.modifiers.box, showScrollButtons && !isVertical && styles.modifiers.scrollable, usePageInsets && styles.modifiers.pageInsets, !hasBorderBottom && styles.modifiers.noBorderBottom, hasSecondaryBorderBottom && styles.modifiers.borderBottom, formatBreakpointMods(inset, styles), variantStyle[variant], className) }, getOUIAProps(Tabs.displayName, ouiaId !== undefined ? ouiaId : this.state.ouiaStateId, ouiaSafe), { id: id && id }, props),
|
195 | expandable && isVertical && (React.createElement(GenerateId, null, randomId => (React.createElement("div", { className: css(styles.tabsToggle) },
|
196 | React.createElement("div", { className: css(styles.tabsToggleButton) },
|
197 | React.createElement(Button, { onClick: () => toggleTabs(!isExpandedLocal), variant: "plain", "aria-label": toggleAriaLabel, "aria-expanded": isExpandedLocal, id: `${randomId}-button`, "aria-labelledby": `${randomId}-text ${randomId}-button` },
|
198 | React.createElement("span", { className: css(styles.tabsToggleIcon) },
|
199 | React.createElement(AngleRightIcon, { "arian-hidden": "true" })),
|
200 | toggleText && (React.createElement("span", { className: css('pf-c-tabs__toggle-text'), id: `${randomId}-text` }, toggleText)))))))),
|
201 | React.createElement("button", { className: css(styles.tabsScrollButton, isSecondary && buttonStyles.modifiers.secondary), "aria-label": leftScrollAriaLabel, onClick: this.scrollLeft, disabled: disableLeftScrollButton, "aria-hidden": disableLeftScrollButton },
|
202 | React.createElement(AngleLeftIcon, null)),
|
203 | React.createElement("ul", { className: css(styles.tabsList), ref: this.tabList, onScroll: this.handleScrollButtons, role: "tablist" }, filteredChildren),
|
204 | React.createElement("button", { className: css(styles.tabsScrollButton, isSecondary && buttonStyles.modifiers.secondary), "aria-label": rightScrollAriaLabel, onClick: this.scrollRight, disabled: disableRightScrollButton, "aria-hidden": disableRightScrollButton },
|
205 | React.createElement(AngleRightIcon, null)),
|
206 | onAdd !== undefined && (React.createElement("span", { className: css(styles.tabsAdd) },
|
207 | React.createElement(Button, { variant: "plain", "aria-label": addButtonAriaLabel || 'Add tab', onClick: onAdd },
|
208 | React.createElement(PlusIcon, null))))),
|
209 | filteredChildren
|
210 | .filter(child => child.props.children &&
|
211 | !(unmountOnExit && child.props.eventKey !== localActiveKey) &&
|
212 | !(mountOnEnter && shownKeys.indexOf(child.props.eventKey) === -1))
|
213 | .map(child => (React.createElement(TabContent, { key: child.props.eventKey, activeKey: localActiveKey, child: child, id: child.props.id || uniqueId, ouiaId: child.props.ouiaId })))));
|
214 | }
|
215 | }
|
216 | Tabs.displayName = 'Tabs';
|
217 | Tabs.defaultProps = {
|
218 | activeKey: 0,
|
219 | onSelect: () => undefined,
|
220 | isFilled: false,
|
221 | isSecondary: false,
|
222 | isVertical: false,
|
223 | isBox: false,
|
224 | hasBorderBottom: true,
|
225 | leftScrollAriaLabel: 'Scroll left',
|
226 | rightScrollAriaLabel: 'Scroll right',
|
227 | component: TabsComponent.div,
|
228 | mountOnEnter: false,
|
229 | unmountOnExit: false,
|
230 | ouiaSafe: true,
|
231 | variant: 'default',
|
232 |
|
233 | onToggle: (isExpanded) => undefined
|
234 | };
|
235 |
|
\ | No newline at end of file |