UNPKG

@spark-web/tabs

Version:

--- title: Tabs storybookPath: page-layout-tabs--default isExperimentalPackage: true ---

300 lines (278 loc) 9.1 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var react = require('react'); var _slicedToArray = require('@babel/runtime/helpers/slicedToArray'); var _objectSpread = require('@babel/runtime/helpers/objectSpread2'); var css = require('@emotion/css'); var reactTabs = require('@radix-ui/react-tabs'); var button = require('@spark-web/button'); var divider = require('@spark-web/divider'); var inline = require('@spark-web/inline'); var stack = require('@spark-web/stack'); var text = require('@spark-web/text'); var theme = require('@spark-web/theme'); var utils = require('@spark-web/utils'); var internal = require('@spark-web/utils/internal'); var useMeasure = require('react-use-measure'); var jsxRuntime = require('react/jsx-runtime'); function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; } var useMeasure__default = /*#__PURE__*/_interopDefault(useMeasure); /** @todo handle fragments */ var IndexContext = /*#__PURE__*/react.createContext(0); var IndexProvider = IndexContext.Provider; function useIndexContext() { var index = react.useContext(IndexContext); // Radix UI requires that the `value` prop for Tab and TabPanel be a string return String(index); } var Tabs = /*#__PURE__*/react.forwardRef(function (_ref, forwardedRef) { var activationMode = _ref.activationMode, data = _ref.data, children = _ref.children, defaultIndex = _ref.defaultIndex; var defaultValue = typeof defaultIndex === 'undefined' ? String(0) : String(defaultIndex); return /*#__PURE__*/jsxRuntime.jsx(reactTabs.Root, _objectSpread(_objectSpread({}, data ? internal.buildDataAttributes(data) : undefined), {}, { activationMode: activationMode, defaultValue: defaultValue, ref: forwardedRef, children: children })); }); Tabs.displayName = 'Tabs'; //////////////////////////////////////////////////////////////////////////////// /** * TabList * * The parent component of the tabs. */ function TabList(_ref2) { var children = _ref2.children, data = _ref2.data; return /*#__PURE__*/jsxRuntime.jsx(reactTabs.List, { asChild: true, children: /*#__PURE__*/jsxRuntime.jsx(inline.Inline, { data: data, gap: "small", children: resolveTabListChildren(children) }) }); } //////////////////////////////////////////////////////////////////////////////// /** * Tab * * The interactive element that changes the selected panel. */ var GAP = 'small'; // Space between the interactive element and the pseudo border var Tab = /*#__PURE__*/react.forwardRef(function (_ref3, forwardedRef) { var children = _ref3.children, data = _ref3.data, disabled = _ref3.disabled; var _useTabStyles = useTabStyles(), _useTabStyles2 = _slicedToArray(_useTabStyles, 2), boxProps = _useTabStyles2[0], tabStyles = _useTabStyles2[1]; /** * The font-weight of the tab changes when the button is active. * This causes the button to get slightly wider (which we don't want). * To avoid this, we measure the initial width of the button (after the * first paint) and give it fixed width. * We're using the style prop for this so that Emotion doesn't need to * generate a completely new hash for each tab (as the width will vary * for each tab based on the length of the text). */ var _useMeasure = useMeasure__default["default"](), _useMeasure2 = _slicedToArray(_useMeasure, 2), internalRef = _useMeasure2[0], bounds = _useMeasure2[1]; var composedRef = utils.useComposedRefs(internalRef, forwardedRef); var widthRef = react.useRef(undefined); react.useEffect(function () { if (bounds.width && !widthRef.current) { widthRef.current = bounds.width; } }, [bounds.width]); var index = useIndexContext(); return /*#__PURE__*/jsxRuntime.jsx(stack.Stack, { position: "relative", paddingY: GAP, children: /*#__PURE__*/jsxRuntime.jsx(reactTabs.Trigger, { asChild: true, disabled: disabled, value: index, children: /*#__PURE__*/jsxRuntime.jsx(button.BaseButton, _objectSpread(_objectSpread({}, boxProps), {}, { ref: composedRef, data: data, className: css.css(tabStyles), style: { width: widthRef.current }, children: /*#__PURE__*/jsxRuntime.jsx(text.DefaultTextPropsProvider, { size: "small", tone: "muted", children: /*#__PURE__*/jsxRuntime.jsx(Content, { children: children }) }) })) }) }); }); var TAB_NAME = 'Tab'; Tab.displayName = TAB_NAME; //////////////////////////////////////////////////////////////////////////////// /** * TabPanels * * The parent component of the panels. */ function TabPanels(_ref4) { var children = _ref4.children, data = _ref4.data; return /*#__PURE__*/jsxRuntime.jsxs(stack.Stack, { data: data, children: [/*#__PURE__*/jsxRuntime.jsx(divider.Divider, { width: "large" }), resolveTabPanelsChildren(children)] }); } //////////////////////////////////////////////////////////////////////////////// /** * TabPanel * * The panel that displays when it's corresponding tab is active. */ var TabPanel = /*#__PURE__*/react.forwardRef(function (_ref5, forwardedRef) { var children = _ref5.children, data = _ref5.data; var index = useIndexContext(); return /*#__PURE__*/jsxRuntime.jsx(reactTabs.Content, _objectSpread(_objectSpread({}, data ? internal.buildDataAttributes(data) : undefined), {}, { forceMount: true, ref: forwardedRef, value: index, className: css.css({ '&[data-state=inactive]': { display: 'none' } }), children: children })); }); var TAB_PANEL_NAME = 'TabPanel'; TabPanel.displayName = TAB_PANEL_NAME; //////////////////////////////////////////////////////////////////////////////// /** * Helpers */ /** * Custom hook to encapsulate styles for the Tab component */ function useTabStyles() { var theme$1 = theme.useTheme(); var _useButtonStyles = button.useButtonStyles({ iconOnly: false, prominence: 'none', size: 'medium', tone: 'primary' }), _useButtonStyles2 = _slicedToArray(_useButtonStyles, 2), boxProps = _useButtonStyles2[0], buttonStyles = _useButtonStyles2[1]; return [_objectSpread(_objectSpread({}, boxProps), {}, { paddingX: 'xlarge' }), _objectSpread(_objectSpread({}, buttonStyles), {}, { '&[data-state=active]': { '&:not([aria-disabled=true])': { ':hover': { background: theme$1.color.background.primaryMuted }, '*': { color: theme$1.color.foreground.primaryActive, fontWeight: theme$1.typography.fontWeight.semibold } }, ':active': { transform: 'none' }, // Pseudo border '::after': { content: '""', position: 'absolute', background: theme$1.color.foreground.primaryActive, bottom: -theme$1.spacing[GAP], left: 0, right: 0, height: theme$1.border.width.large, width: '100%', transform: 'translateY(100%)' } } })]; } /** * Provides base typographic styles when the type of children is `string` or * `number`. * Otherwise children are wrapped in a `Fragment` in order to provide custom * styles. */ function Content(_ref6) { var children = _ref6.children; if (typeof children === 'string' || typeof children === 'number') { return /*#__PURE__*/jsxRuntime.jsx(text.Text, { as: "span", baseline: false, overflowStrategy: "nowrap", children: children }); } return /*#__PURE__*/jsxRuntime.jsx(react.Fragment, { children: children }); } /** * Throws an error if children are not `Tab` components */ function resolveTabListChildren(children) { return react.Children.map(children, function (child, index) { if (child.type.displayName !== TAB_NAME) { throw new Error('All children of `TabList` must be `Tab` components'); } return childWithIndexProvider({ child: child, index: index }); }); } /** * Throws an error if children are not `TabPanel` components */ function resolveTabPanelsChildren(children) { return react.Children.map(children, function (child, index) { if (child.type.displayName !== TAB_PANEL_NAME) { throw new Error('All children of `TabPanels` must be `TabPanel` components'); } return childWithIndexProvider({ child: child, index: index }); }); } /** * Ensures that the children are valid `ReactElements` before wrapping them with * the IndexProvider. */ function childWithIndexProvider(_ref7) { var child = _ref7.child, index = _ref7.index; return /*#__PURE__*/react.isValidElement(child) && /*#__PURE__*/jsxRuntime.jsx(IndexProvider, { value: index, children: child }); } exports.IndexProvider = IndexProvider; exports.Tab = Tab; exports.TabList = TabList; exports.TabPanel = TabPanel; exports.TabPanels = TabPanels; exports.Tabs = Tabs; exports.useIndexContext = useIndexContext;