UNPKG

@shopify/polaris

Version:

Shopify’s product component library

337 lines (301 loc) • 10.5 kB
import React$1, { PureComponent } from 'react'; import { useFeatures } from '../../utilities/features/hooks.js'; import { useI18n } from '../../utilities/i18n/hooks.js'; import { classNames } from '../../utilities/css.js'; import { CaretDownMinor, HorizontalDotsMinor } from '@shopify/polaris-icons'; import { Icon as Icon$1 } from '../Icon/Icon.js'; import { Popover as Popover$1 } from '../Popover/Popover.js'; import { getVisibleAndHiddenTabIndices } from './utilities.js'; import styles from './Tabs.scss.js'; import { List as List$1 } from './components/List/List.js'; import { Panel as Panel$1 } from './components/Panel/Panel.js'; import { Tab as Tab$1 } from './components/Tab/Tab.js'; import { TabMeasurer as TabMeasurer$1 } from './components/TabMeasurer/TabMeasurer.js'; var _ref = /*#__PURE__*/React$1.createElement(Icon$1, { source: CaretDownMinor, color: "inkLighter" }); var _ref2 = /*#__PURE__*/React$1.createElement(Icon$1, { source: HorizontalDotsMinor }); class TabsInner extends PureComponent { constructor(...args) { super(...args); this.state = { disclosureWidth: 0, containerWidth: Infinity, tabWidths: [], visibleTabs: [], hiddenTabs: [], showDisclosure: false, tabToFocus: -1 }; this.handleKeyPress = event => { var { tabToFocus, visibleTabs, hiddenTabs, showDisclosure } = this.state; var key = event.key; var tabsArrayInOrder = showDisclosure ? visibleTabs.concat(hiddenTabs) : [...visibleTabs]; var newFocus = tabsArrayInOrder.indexOf(tabToFocus); if (key === 'ArrowRight' || key === 'ArrowDown') { newFocus += 1; if (newFocus === tabsArrayInOrder.length) { newFocus = 0; } } if (key === 'ArrowLeft' || key === 'ArrowUp') { if (newFocus === -1 || newFocus === 0) { newFocus = tabsArrayInOrder.length - 1; } else { newFocus -= 1; } } this.setState({ tabToFocus: tabsArrayInOrder[newFocus] }); }; this.renderTabMarkup = (tab, index) => { var { selected } = this.props; var { tabToFocus } = this.state; return /*#__PURE__*/React$1.createElement(Tab$1, { key: "".concat(index, "-").concat(tab.id), id: tab.id, siblingTabHasFocus: tabToFocus > -1, focused: index === tabToFocus, selected: index === selected, onClick: this.handleTabClick, panelID: tab.panelID || "".concat(tab.id, "-panel"), accessibilityLabel: tab.accessibilityLabel, url: tab.url }, tab.content); }; this.handleFocus = event => { var { selected, tabs } = this.props; // If we are explicitly focusing a non-selected tab, this focuses it var target = event.target; if (target.classList.contains(styles.Tab) || target.classList.contains(styles.Item)) { var tabToFocus = -1; tabs.every((tab, index) => { if (tab.id === target.id) { tabToFocus = index; return false; } return true; }); this.setState({ tabToFocus }); return; } if (target.classList.contains(styles.DisclosureActivator)) { return; } // If we are coming in from somewhere other than another tab, focus the // selected tab, and the focus (click) is not on the disclosure activator, // focus the selected tab if (!event.relatedTarget) { this.setState({ tabToFocus: selected }); return; } var relatedTarget = event.relatedTarget; if (relatedTarget instanceof HTMLElement && !relatedTarget.classList.contains(styles.Tab) && !relatedTarget.classList.contains(styles.Item) && !relatedTarget.classList.contains(styles.DisclosureActivator)) { this.setState({ tabToFocus: selected }); } }; this.handleBlur = event => { // If we blur and the target is not another tab, forget the focus position if (event.relatedTarget == null) { this.setState({ tabToFocus: -1 }); return; } var target = event.relatedTarget; // If we are going to anywhere other than another tab, lose the last focused tab if (target instanceof HTMLElement && !target.classList.contains(styles.Tab) && !target.classList.contains(styles.Item)) { this.setState({ tabToFocus: -1 }); } }; this.handleDisclosureActivatorClick = () => { this.setState(({ showDisclosure }) => ({ showDisclosure: !showDisclosure })); }; this.handleClose = () => { this.setState({ showDisclosure: false }); }; this.handleMeasurement = measurements => { var { tabs, selected } = this.props; var { tabToFocus } = this.state; var { hiddenTabWidths: tabWidths, containerWidth, disclosureWidth } = measurements; var { visibleTabs, hiddenTabs } = getVisibleAndHiddenTabIndices(tabs, selected, disclosureWidth, tabWidths, containerWidth); this.setState({ tabToFocus: tabToFocus === -1 ? -1 : selected, visibleTabs, hiddenTabs, disclosureWidth, containerWidth, tabWidths }); }; this.handleTabClick = id => { var { tabs, onSelect = noop } = this.props; var tab = tabs.find(aTab => aTab.id === id); if (tab == null) { return; } var selectedIndex = tabs.indexOf(tab); onSelect(selectedIndex); }; } static getDerivedStateFromProps(nextProps, prevState) { var { disclosureWidth, tabWidths, containerWidth } = prevState; var { visibleTabs, hiddenTabs } = getVisibleAndHiddenTabIndices(nextProps.tabs, nextProps.selected, disclosureWidth, tabWidths, containerWidth); return { visibleTabs, hiddenTabs, selected: nextProps.selected }; } render() { var { tabs, selected, fitted, children, i18n, features, disclosureText } = this.props; var { tabToFocus, visibleTabs, hiddenTabs, showDisclosure } = this.state; var disclosureTabs = hiddenTabs.map(tabIndex => tabs[tabIndex]); var { newDesignLanguage } = features; var panelMarkup = children ? tabs.map((_tab, index) => { return selected === index ? /*#__PURE__*/React$1.createElement(Panel$1, { id: tabs[index].panelID || "".concat(tabs[index].id, "-panel"), tabID: tabs[index].id, key: tabs[index].id }, children) : /*#__PURE__*/React$1.createElement(Panel$1, { id: tabs[index].panelID || "".concat(tabs[index].id, "-panel"), tabID: tabs[index].id, key: tabs[index].id, hidden: true }); }) : null; var tabsMarkup = visibleTabs.sort((tabA, tabB) => tabA - tabB).map(tabIndex => this.renderTabMarkup(tabs[tabIndex], tabIndex)); var disclosureActivatorVisible = visibleTabs.length < tabs.length; var hasCustomDisclosure = Boolean(disclosureText); var classname = classNames(styles.Tabs, fitted && styles.fitted, disclosureActivatorVisible && styles.fillSpace, newDesignLanguage && styles.newDesignLanguage); var wrapperClassName = classNames(styles.Wrapper, newDesignLanguage && styles.newDesignLanguage); var disclosureTabClassName = classNames(styles.DisclosureTab, disclosureActivatorVisible && styles['DisclosureTab-visible']); var disclosureActivatorClassName = classNames(styles.TabContainer, newDesignLanguage && styles.newDesignLanguage); var disclosureButtonClassName = classNames(styles.DisclosureActivator, hasCustomDisclosure && styles.Tab); var disclosureButtonContentWrapperClassName = classNames(styles.Title, hasCustomDisclosure && styles.titleWithIcon); var disclosureButtonContent = hasCustomDisclosure ? /*#__PURE__*/React$1.createElement(React$1.Fragment, null, disclosureText, _ref) : _ref2; var disclosureButton = /*#__PURE__*/React$1.createElement("button", { type: "button", className: disclosureButtonClassName, onClick: this.handleDisclosureActivatorClick, "aria-label": i18n.translate('Polaris.Tabs.toggleTabsLabel') }, /*#__PURE__*/React$1.createElement("span", { className: disclosureButtonContentWrapperClassName }, disclosureButtonContent)); var activator = disclosureText ? /*#__PURE__*/React$1.createElement("div", { className: disclosureActivatorClassName }, disclosureButton) : disclosureButton; return /*#__PURE__*/React$1.createElement("div", null, /*#__PURE__*/React$1.createElement("div", { className: wrapperClassName }, /*#__PURE__*/React$1.createElement("ul", { role: "tablist", className: classname, onFocus: this.handleFocus, onBlur: this.handleBlur, onKeyDown: handleKeyDown, onKeyUp: this.handleKeyPress }, tabsMarkup, /*#__PURE__*/React$1.createElement("li", { className: disclosureTabClassName }, /*#__PURE__*/React$1.createElement(Popover$1, { preferredPosition: "below", activator: activator, active: disclosureActivatorVisible && showDisclosure, onClose: this.handleClose }, /*#__PURE__*/React$1.createElement(List$1, { focusIndex: hiddenTabs.indexOf(tabToFocus), disclosureTabs: disclosureTabs, onClick: this.handleTabClick, onKeyPress: this.handleKeyPress })))), /*#__PURE__*/React$1.createElement(TabMeasurer$1, { tabToFocus: tabToFocus, activator: activator, selected: selected, tabs: tabs, siblingTabHasFocus: tabToFocus > -1, handleMeasurement: this.handleMeasurement })), panelMarkup); } } function noop() {} function handleKeyDown(event) { var { key } = event; if (key === 'ArrowUp' || key === 'ArrowDown' || key === 'ArrowLeft' || key === 'ArrowRight') { event.preventDefault(); event.stopPropagation(); } } function Tabs(props) { var i18n = useI18n(); var features = useFeatures(); return /*#__PURE__*/React$1.createElement(TabsInner, Object.assign({}, props, { i18n: i18n, features: features })); } export { Tabs };