chakra-ui
Version:
Responsive and accessible React UI components built with React and Emotion
312 lines (271 loc) • 9.21 kB
JavaScript
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import _extends from "@babel/runtime/helpers/esm/extends";
import _objectWithoutProperties from "@babel/runtime/helpers/esm/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 };