UNPKG

@primer/react

Version:

An implementation of GitHub's Primer Design System using React

189 lines (180 loc) • 8.69 kB
'use strict'; var React = require('react'); var tabContainerElement = require('../../node_modules/@github/tab-container-element/dist/tab-container-element.js'); require('../../node_modules/@github/tab-container-element/dist/tab-container-element-define.js'); var createComponent = require('../../utils/create-component.js'); var UnderlineTabbedInterface = require('../../internal/components/UnderlineTabbedInterface.js'); var Box = require('../../Box/Box.js'); require('@primer/behaviors/utils'); require('@primer/behaviors'); var useResizeObserver = require('../../hooks/useResizeObserver.js'); var useIsomorphicLayoutEffect = require('../../utils/useIsomorphicLayoutEffect.js'); var useId = require('../../hooks/useId.js'); var invariant = require('../../utils/invariant.js'); require('@styled-system/css'); var merge = require('deepmerge'); var defaultSxProp = require('../../utils/defaultSxProp.js'); require('../../FeatureFlags/FeatureFlags.js'); var useFeatureFlag = require('../../FeatureFlags/useFeatureFlag.js'); require('../../FeatureFlags/DefaultFeatureFlags.js'); var UnderlinePanels_module = require('./UnderlinePanels.module.css.js'); var toggleStyledComponent = require('../../internal/utils/toggleStyledComponent.js'); var clsx = require('clsx'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var React__default = /*#__PURE__*/_interopDefault(React); var merge__default = /*#__PURE__*/_interopDefault(merge); function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_staff'; const TabContainerComponent = createComponent.createComponent(tabContainerElement.TabContainerElement, 'tab-container'); const StyledTabContainerComponent = toggleStyledComponent.toggleStyledComponent(CSS_MODULES_FEATURE_FLAG, 'tab-container', TabContainerComponent); const UnderlinePanels = ({ 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, children, loadingCounters, sx: sxProp = defaultSxProp.defaultSxProp, className, ...props }) => { const [iconsVisible, setIconsVisible] = React.useState(true); const wrapperRef = React.useRef(null); const listRef = React.useRef(null); // We need to always call useId() because React Hooks must be // called in the exact same order in every component render const parentId = useId.useId(props.id); const [tabs, setTabs] = React.useState([]); const [tabPanels, setTabPanels] = React.useState([]); // Make sure we have fresh prop data whenever the tabs or panels are updated (keep aria-selected current) React.useEffect(() => { // Loop through the chidren, if it's a tab, then add id="{id}-tab-{index}" // If it's a panel, then add aria-labelledby="{id}-tab-{index}" let tabIndex = 0; let panelIndex = 0; const childrenWithProps = React.Children.map(children, child => { if (/*#__PURE__*/React.isValidElement(child) && child.type === Tab) { return /*#__PURE__*/React.cloneElement(child, { id: `${parentId}-tab-${tabIndex++}`, loadingCounters, iconsVisible }); } if (/*#__PURE__*/React.isValidElement(child) && child.type === Panel) { return /*#__PURE__*/React.cloneElement(child, { 'aria-labelledby': `${parentId}-tab-${panelIndex++}` }); } return child; }); const newTabs = React.Children.toArray(childrenWithProps).filter(child => { return /*#__PURE__*/React.isValidElement(child) && child.type === Tab; }); const newTabPanels = React.Children.toArray(childrenWithProps).filter(child => /*#__PURE__*/React.isValidElement(child) && child.type === Panel); setTabs(newTabs); setTabPanels(newTabPanels); }, [children, parentId, loadingCounters, iconsVisible]); const tabsHaveIcons = tabs.some(tab => /*#__PURE__*/React__default.default.isValidElement(tab) && tab.props.icon); const enabled = useFeatureFlag.useFeatureFlag(CSS_MODULES_FEATURE_FLAG); // this is a workaround to get the list's width on the first render const [listWidth, setListWidth] = React.useState(0); useIsomorphicLayoutEffect(() => { var _listRef$current$getB, _listRef$current; if (!tabsHaveIcons) { return; } setListWidth((_listRef$current$getB = (_listRef$current = listRef.current) === null || _listRef$current === undefined ? undefined : _listRef$current.getBoundingClientRect().width) !== null && _listRef$current$getB !== undefined ? _listRef$current$getB : 0); }, [tabsHaveIcons]); // when the wrapper resizes, check if the icons should be visible // by comparing the wrapper width to the list width useResizeObserver.useResizeObserver(resizeObserverEntries => { if (!tabsHaveIcons) { return; } const wrapperWidth = resizeObserverEntries[0].contentRect.width; setIconsVisible(wrapperWidth > listWidth); }, wrapperRef, [enabled]); if (process.env.NODE_ENV !== "production") { const selectedTabs = tabs.filter(tab => { const ariaSelected = /*#__PURE__*/React__default.default.isValidElement(tab) && tab.props['aria-selected']; return ariaSelected === true || ariaSelected === 'true'; }); !(selectedTabs.length <= 1) ? process.env.NODE_ENV !== "production" ? invariant.invariant(false, 'Only one tab can be selected at a time.') : invariant.invariant(false) : undefined; !(tabs.length === tabPanels.length) ? process.env.NODE_ENV !== "production" ? invariant.invariant(false, `The number of tabs and panels must be equal. Counted ${tabs.length} tabs and ${tabPanels.length} panels.`) : invariant.invariant(false) : undefined; } if (enabled) { return /*#__PURE__*/React__default.default.createElement(StyledTabContainerComponent, null, /*#__PURE__*/React__default.default.createElement(UnderlineTabbedInterface.StyledUnderlineWrapper, _extends({ ref: wrapperRef, slot: "tablist-wrapper", "data-icons-visible": iconsVisible, sx: sxProp, className: clsx.clsx(className, UnderlinePanels_module.StyledUnderlineWrapper) }, props), /*#__PURE__*/React__default.default.createElement(UnderlineTabbedInterface.StyledUnderlineItemList, { ref: listRef, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, role: "tablist" }, tabs)), tabPanels); } return /*#__PURE__*/React__default.default.createElement(StyledTabContainerComponent, null, /*#__PURE__*/React__default.default.createElement(UnderlineTabbedInterface.StyledUnderlineWrapper, _extends({ ref: wrapperRef, slot: "tablist-wrapper", "data-icons-visible": iconsVisible, sx: merge__default.default({ width: '100%', overflowX: 'auto', overflowY: 'hidden', '-webkit-overflow-scrolling': 'auto', '&[data-icons-visible="false"] [data-component="icon"]': { display: 'none' } }, sxProp), className: className }, props), /*#__PURE__*/React__default.default.createElement(UnderlineTabbedInterface.StyledUnderlineItemList, { ref: listRef, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, role: "tablist" }, tabs)), tabPanels); }; UnderlinePanels.displayName = "UnderlinePanels"; const Tab = ({ 'aria-selected': ariaSelected, sx: sxProp = defaultSxProp.defaultSxProp, onSelect, ...props }) => { const clickHandler = React__default.default.useCallback(event => { if (!event.defaultPrevented && typeof onSelect === 'function') { onSelect(event); } }, [onSelect]); const keyDownHandler = React__default.default.useCallback(event => { if ((event.key === ' ' || event.key === 'Enter') && !event.defaultPrevented && typeof onSelect === 'function') { onSelect(event); } }, [onSelect]); return /*#__PURE__*/React__default.default.createElement(UnderlineTabbedInterface.UnderlineItem, _extends({ as: "button", role: "tab", tabIndex: ariaSelected ? 0 : -1, "aria-selected": ariaSelected, sx: sxProp, type: "button", onClick: clickHandler, onKeyDown: keyDownHandler }, props)); }; Tab.displayName = "Tab"; Tab.displayName = 'UnderlinePanels.Tab'; const Panel = props => { return /*#__PURE__*/React__default.default.createElement(Box, _extends({ as: "div", role: "tabpanel" }, props)); }; Panel.displayName = "Panel"; Panel.displayName = 'UnderlinePanels.Panel'; var UnderlinePanels$1 = Object.assign(UnderlinePanels, { Panel, Tab }); module.exports = UnderlinePanels$1;