UNPKG

chakra-ui

Version:

Responsive and accessible React UI components built with React and Emotion

312 lines (271 loc) 9.2 kB
import _slicedToArray from "@babel/runtime/helpers/slicedToArray"; import _extends from "@babel/runtime/helpers/extends"; import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; /** @jsx jsx */ import { jsx } from "@emotion/core"; import propTypes from "prop-types"; import { Children, cloneElement, createContext, forwardRef, useContext, useRef, useState } from "react"; import { useId } from "@reach/auto-id"; import { makeId } from "../utils"; import { useTabStyle, useTabListStyle } from "./styles"; import PseudoBox from "../PseudoBox"; import Flex from "../Flex"; import Box from "../Box"; var Tab = forwardRef(function (props, ref) { var isSelected = props.isSelected, isDisabled = props.isDisabled, id = props.id, size = props.size, rest = _objectWithoutProperties(props, ["isSelected", "isDisabled", "id", "size"]); var tabStyleProps = useTabStyle(); return jsx(PseudoBox, _extends({ ref: ref, role: "tab", tabIndex: isSelected ? 0 : -1, id: "tab:".concat(id), as: "button", type: "button", disabled: isDisabled, "aria-selected": isSelected, "aria-disabled": isDisabled, "aria-controls": "panel:".concat(id) }, tabStyleProps, rest)); }); //////////////////////////////////////////////////////////////////////// var TabList = forwardRef(function (props, ref) { var children = props.children, rest = _objectWithoutProperties(props, ["children"]); var _useContext = useContext(TabContext), id = _useContext.id, selectedIndex = _useContext.index, manualIndex = _useContext.manualIndex, onManualTabChange = _useContext.onManualTabChange, isManual = _useContext.isManual, onChangeTab = _useContext.onChangeTab, onFocusPanel = _useContext.onFocusPanel, orientation = _useContext.orientation; var tabListStyleProps = useTabListStyle(); var allNodes = useRef([]); var focusableIndexes = Children.map(children, function (child, index) { return child.props.isDisabled === true ? null : index; }).filter(function (index) { return index != null; }); var enabledSelectedIndex = focusableIndexes.indexOf(selectedIndex); var count = focusableIndexes.length; var updateIndex = function updateIndex(index) { var childIndex = focusableIndexes[index]; allNodes.current[childIndex].focus(); onChangeTab && onChangeTab(childIndex); }; var handleKeyDown = function handleKeyDown(event) { if (event.key === "ArrowRight") { var nextIndex = (enabledSelectedIndex + 1) % count; updateIndex(nextIndex); } if (event.key === "ArrowLeft") { var _nextIndex = (enabledSelectedIndex - 1 + count) % count; updateIndex(_nextIndex); } if (event.key === "Home") { updateIndex(0); } if (event.key === "End") { updateIndex(count - 1); } if (event.key === "ArrowDown") { event.preventDefault(); onFocusPanel && onFocusPanel(); } }; var clones = Children.map(children, function (child, index) { var isSelected = isManual ? index === manualIndex : index === selectedIndex; var handleClick = function handleClick() { // Hack for Safari. Buttons don't receive focus on click on Safari // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus allNodes.current[index].focus(); onManualTabChange(index); onChangeTab(index); }; return cloneElement(child, { ref: function ref(node) { return allNodes.current[index] = node; }, isSelected: isSelected, onClick: handleClick, id: makeId(id, index) }); }); return jsx(Flex, _extends({ onKeyDown: handleKeyDown, ref: ref, role: "tablist", "aria-orientation": orientation }, tabListStyleProps, rest), clones); }); //////////////////////////////////////////////////////////////////////// var TabPanel = function TabPanel(_ref) { var children = _ref.children, isSelected = _ref.isSelected, selectedPanelRef = _ref.selectedPanelRef, id = _ref.id, rest = _objectWithoutProperties(_ref, ["children", "isSelected", "selectedPanelRef", "id"]); return jsx(Box, _extends({ ref: isSelected ? selectedPanelRef : undefined, role: "tabpanel", tabIndex: -1, "aria-labelledby": "tab:".concat(id), hidden: !isSelected, id: "panel:".concat(id), css: { outline: "none" } }, rest), children); }; //////////////////////////////////////////////////////////////////////// var TabPanels = forwardRef(function (_ref2, ref) { var children = _ref2.children, rest = _objectWithoutProperties(_ref2, ["children"]); var _useContext2 = useContext(TabContext), selectedIndex = _useContext2.index, selectedPanelRef = _useContext2.selectedPanelRef, id = _useContext2.id, isManual = _useContext2.isManual, manualIndex = _useContext2.manualIndex; var clones = Children.map(children, function (child, index) { return cloneElement(child, { isSelected: isManual ? index === manualIndex : index === selectedIndex, selectedPanelRef: selectedPanelRef, id: makeId(id, index) }); }); return jsx(Box, _extends({ tabIndex: "-1", ref: ref }, rest), clones); }); //////////////////////////////////////////////////////////////////////// export var TabContext = createContext(); var Tabs = forwardRef(function (_ref3, ref) { var children = _ref3.children, onChange = _ref3.onChange, controlledIndex = _ref3.index, defaultIndex = _ref3.defaultIndex, isManual = _ref3.isManual, color = _ref3.color, align = _ref3.align, size = _ref3.size, orientation = _ref3.orientation, variant = _ref3.variant, isFitted = _ref3.isFitted, props = _objectWithoutProperties(_ref3, ["children", "onChange", "index", "defaultIndex", "isManual", "color", "align", "size", "orientation", "variant", "isFitted"]); var isControlled = controlledIndex != null; var selectedPanelRef = useRef(null); var getInitialIndex = function getInitialIndex() { if (!isManual) { return defaultIndex || 0; } else { return controlledIndex || defaultIndex || 0; } }; var getActualIdx = function getActualIdx() { if (isManual) { return selectedIndex; } else { return isControlled ? controlledIndex : selectedIndex; } }; var _useState = useState(getInitialIndex), _useState2 = _slicedToArray(_useState, 2), selectedIndex = _useState2[0], setSelectedIndex = _useState2[1]; var _useState3 = useState(controlledIndex || defaultIndex || 0), _useState4 = _slicedToArray(_useState3, 2), manualIndex = _useState4[0], setManualIndex = _useState4[1]; var actualIdx = getActualIdx(); var manualIdx = isControlled ? controlledIndex : manualIndex; var onChangeTab = function onChangeTab(index) { if (!isControlled) { setSelectedIndex(index); } if (isControlled && isManual) { setSelectedIndex(index); } if (!isManual) { onChange && onChange(index); } }; var onManualTabChange = function onManualTabChange(index) { if (!isControlled) { setManualIndex(index); } if (isManual) { onChange && onChange(index); } }; var onFocusPanel = function onFocusPanel() { if (selectedPanelRef.current) selectedPanelRef.current.focus(); }; var id = useId(); var context = { id: id, index: actualIdx, manualIndex: manualIdx, onManualTabChange: onManualTabChange, isManual: isManual, onChangeTab: onChangeTab, selectedPanelRef: selectedPanelRef, onFocusPanel: onFocusPanel, color: color, size: size, align: align, variant: variant, isFitted: isFitted, orientation: orientation }; return jsx(TabContext.Provider, { value: context }, jsx(Box, _extends({ ref: ref }, props), children)); }); Tabs.defaultProps = { size: "sm", variant: "line", align: "center", orientation: "horizontal", color: "blue" }; process.env.NODE_ENV !== "production" ? Tabs.propTypes = { /** * The alignment of the tabs * */ align: propTypes.oneOf(["start", "center", "end"]), /** * If `true`, tabs will stretch to width of the tablist * */ isFitted: propTypes.bool, /** * The orientation of the <TabList/> * */ orientation: propTypes.oneOf(["vertical", "horizontal"]), /** * The size of the tab (affects the font-size and padding) * */ size: propTypes.oneOf(["sm", "md", "lg"]), /** * If `true`, the tabs will be manually activated and * display its panel by pressing Space or Enter. * * If `false`, the tabs will be automatically activated * and their panel is displayed when they receive focus. */ isManual: propTypes.bool, /** * The children of the tabs should be `TabPanel` and `TabList` */ children: propTypes.node.isRequired, /** * Callback when the index (controlled or un-controlled) changes */ onChange: propTypes.func } : void 0; export default Tabs; export { TabList, Tab, TabPanel, TabPanels };