@wordpress/components
Version:
UI components for WordPress.
150 lines (141 loc) • 6.1 kB
JavaScript
;
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