@stratakit/structures
Version:
Medium-sized component structures for the Strata design system
105 lines (104 loc) • 3.04 kB
JavaScript
import { jsx } from "react/jsx-runtime";
import { useStoreState } from "@ariakit/react/store";
import * as AkTab from "@ariakit/react/tab";
import {
forwardRef,
isBrowser,
useUnreactiveCallback
} from "@stratakit/foundations/secret-internals";
import cx from "classnames";
import { useInit } from "./~utils.useInit.js";
const supportsAnchorPositioning = isBrowser && CSS?.supports?.("anchor-name: --foo");
const prefersReducedMotion = () => isBrowser && window.matchMedia("(prefers-reduced-motion)").matches;
function TabsProvider(props) {
useInit();
const {
defaultSelectedId,
selectedId,
setSelectedId,
selectOnMove = false,
children
} = props;
const store = AkTab.useTabStore({ defaultSelectedId });
const tablist = useStoreState(store, "baseElement");
const selectedIdFromStore = useStoreState(store, "selectedId");
const flipAnimateStripe = (newSelectedId) => {
if (!supportsAnchorPositioning) return;
const rootNode = tablist?.getRootNode();
if (!rootNode || !selectedIdFromStore || !newSelectedId) return;
const previousTabRect = rootNode.getElementById?.(selectedIdFromStore)?.getBoundingClientRect();
const nextTabRect = rootNode.getElementById?.(newSelectedId)?.getBoundingClientRect();
if (!previousTabRect || !nextTabRect) return;
const deltaX = previousTabRect.left - nextTabRect.left;
const deltaWidth = previousTabRect.width / nextTabRect.width;
tablist?.animate(
[
{ transform: `translateX(${deltaX}px) scaleX(${deltaWidth})` },
{ transform: "none" }
],
{
pseudoElement: "::after",
duration: 150,
easing: "ease-in-out"
}
);
};
return /* @__PURE__ */ jsx(
AkTab.TabProvider,
{
store,
selectedId,
setSelectedId: useUnreactiveCallback(
(newSelectedId) => {
if (!prefersReducedMotion()) flipAnimateStripe(newSelectedId);
setSelectedId?.(newSelectedId);
}
),
selectOnMove,
children
}
);
}
DEV: TabsProvider.displayName = "Tabs.Provider";
const TabList = forwardRef((props, forwardedRef) => {
const { tone = "neutral", ...rest } = props;
return /* @__PURE__ */ jsx(
AkTab.TabList,
{
...rest,
"data-_sk-tone": tone,
className: cx("\u{1F95D}TabList", props.className),
ref: forwardedRef
}
);
});
DEV: TabList.displayName = "Tabs.TabList";
const Tab = forwardRef((props, forwardedRef) => {
return /* @__PURE__ */ jsx(
AkTab.Tab,
{
accessibleWhenDisabled: true,
...props,
className: cx("\u{1F95D}Tab", props.className),
ref: forwardedRef
}
);
});
DEV: Tab.displayName = "Tabs.Tab";
const TabPanel = forwardRef((props, forwardedRef) => {
return /* @__PURE__ */ jsx(
AkTab.TabPanel,
{
...props,
className: cx("\u{1F95D}TabPanel", props.className),
ref: forwardedRef
}
);
});
DEV: TabPanel.displayName = "Tabs.TabPanel";
export {
TabsProvider as Provider,
Tab,
TabList,
TabPanel
};