UNPKG

@wordpress/components

Version:
150 lines (141 loc) 6.1 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.TabList = void 0; var Ariakit = _interopRequireWildcard(require("@ariakit/react")); var _clsx = _interopRequireDefault(require("clsx")); var _warning = _interopRequireDefault(require("@wordpress/warning")); var _element = require("@wordpress/element"); var _compose = require("@wordpress/compose"); var _context = require("./context"); var _styles = require("./styles"); var _elementRect = require("../utils/element-rect"); var _useTrackOverflow = require("./use-track-overflow"); var _useAnimatedOffsetRect = require("../utils/hooks/use-animated-offset-rect"); var _jsxRuntime = require("react/jsx-runtime"); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } /** * External dependencies */ /** * WordPress dependencies */ /** * Internal dependencies */ const DEFAULT_SCROLL_MARGIN = 24; /** * Scrolls a given parent element so that a given rect is visible. * * The scroll is updated initially and whenever the rect changes. */ function useScrollRectIntoView(parent, rect, { margin = DEFAULT_SCROLL_MARGIN } = {}) { (0, _element.useLayoutEffect)(() => { if (!parent || !rect) { return; } const { scrollLeft: parentScroll } = parent; const parentWidth = parent.getBoundingClientRect().width; const { left: childLeft, width: childWidth } = rect; const parentRightEdge = parentScroll + parentWidth; const childRightEdge = childLeft + childWidth; const rightOverflow = childRightEdge + margin - parentRightEdge; const leftOverflow = parentScroll - (childLeft - margin); let scrollLeft = null; if (leftOverflow > 0) { scrollLeft = parentScroll - leftOverflow; } else if (rightOverflow > 0) { scrollLeft = parentScroll + rightOverflow; } if (scrollLeft !== null) { /** * The optional chaining is used here to avoid unit test failures. * It can be removed when JSDOM supports `Element` scroll methods. * See: https://github.com/WordPress/gutenberg/pull/66498#issuecomment-2441146096 */ parent.scroll?.({ left: scrollLeft }); } }, [margin, parent, rect]); } const TabList = exports.TabList = (0, _element.forwardRef)(function TabList({ children, ...otherProps }, ref) { var _useTabsContext; const { store } = (_useTabsContext = (0, _context.useTabsContext)()) !== null && _useTabsContext !== void 0 ? _useTabsContext : {}; const selectedId = Ariakit.useStoreState(store, 'selectedId'); const activeId = Ariakit.useStoreState(store, 'activeId'); const selectOnMove = Ariakit.useStoreState(store, 'selectOnMove'); const items = Ariakit.useStoreState(store, 'items'); const [parent, setParent] = (0, _element.useState)(); const refs = (0, _compose.useMergeRefs)([ref, setParent]); const selectedItem = store?.item(selectedId); const renderedItems = Ariakit.useStoreState(store, 'renderedItems'); const selectedItemIndex = renderedItems && selectedItem ? renderedItems.indexOf(selectedItem) : -1; // Use selectedItemIndex as a dependency to force recalculation when the // selected item index changes (elements are swapped / added / removed). const selectedRect = (0, _elementRect.useTrackElementOffsetRect)(selectedItem?.element, [selectedItemIndex]); // Track overflow to show scroll hints. const overflow = (0, _useTrackOverflow.useTrackOverflow)(parent, { first: items?.at(0)?.element, last: items?.at(-1)?.element }); // Size, position, and animate the indicator. (0, _useAnimatedOffsetRect.useAnimatedOffsetRect)(parent, selectedRect, { prefix: 'selected', dataAttribute: 'indicator-animated', transitionEndFilter: event => event.pseudoElement === '::before', roundRect: true }); // Make sure selected tab is scrolled into view. useScrollRectIntoView(parent, selectedRect); const onBlur = () => { if (!selectOnMove) { return; } // When automatic tab selection is on, make sure that the active tab is up // to date with the selected tab when leaving the tablist. This makes sure // that the selected tab will receive keyboard focus when tabbing back into // the tablist. if (selectedId !== activeId) { store?.setActiveId(selectedId); } }; if (!store) { globalThis.SCRIPT_DEBUG === true ? (0, _warning.default)('`Tabs.TabList` must be wrapped in a `Tabs` component.') : void 0; return null; } return /*#__PURE__*/(0, _jsxRuntime.jsx)(_styles.StyledTabList, { ref: refs, store: store, render: props => { var _props$tabIndex; return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { ...props, // Fallback to -1 to prevent browsers from making the tablist // tabbable when it is a scrolling container. tabIndex: (_props$tabIndex = props.tabIndex) !== null && _props$tabIndex !== void 0 ? _props$tabIndex : -1 }); }, onBlur: onBlur, "data-select-on-move": selectOnMove ? 'true' : 'false', ...otherProps, className: (0, _clsx.default)(overflow.first && 'is-overflowing-first', overflow.last && 'is-overflowing-last', otherProps.className), children: children }); }); //# sourceMappingURL=tablist.js.map