@shopify/polaris
Version:
Shopify’s product component library
337 lines (301 loc) • 10.5 kB
JavaScript
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 };