'use client'; import * as React from 'react'; import { __styles, mergeClasses } from '@griffel/react'; import { useTabListContext_unstable } from '../TabList/TabListContext'; import { tokens } from '@fluentui/react-theme'; import { useAnimationFrame } from '@fluentui/react-utilities'; // eslint-disable-next-line @typescript-eslint/naming-convention const tabIndicatorCssVars_unstable = { offsetVar: '--fui-Tab__indicator--offset', scaleVar: '--fui-Tab__indicator--scale' }; const useActiveIndicatorStyles = /*#__PURE__*/__styles({ base: { B68tc82: 0, Bmxbyg5: 0, Bpg54ce: "f1gl81tg" }, animated: { Ba2ppi3: "fhwpy7i", F2fol1: "f6zz20j", B1dyfl9: "f1ai4sc1", Bu93a1u: "f1rnm8gg", Bdasjez: "f1x3cmqg" }, horizontal: { sjv3b2: ["fug4aj8", "f1i5xzg7"], b1kco5: "f1q7ujh" }, vertical: { sjv3b2: "f1hqboyk", b1kco5: "f1dxupa6" } }, { d: [[".f1gl81tg{overflow:visible;}", { p: -1 }], ".fhwpy7i::after{transition-property:transform;}", ".f6zz20j::after{transition-duration:var(--durationSlow);}", ".f1ai4sc1::after{transition-timing-function:var(--curveDecelerateMax);}", ".fug4aj8::after{transform-origin:left;}", ".f1i5xzg7::after{transform-origin:right;}", ".f1q7ujh::after{transform:translateX(var(--fui-Tab__indicator--offset)) scaleX(var(--fui-Tab__indicator--scale));}", ".f1hqboyk::after{transform-origin:top;}", ".f1dxupa6::after{transform:translateY(var(--fui-Tab__indicator--offset)) scaleY(var(--fui-Tab__indicator--scale));}"], m: [["@media (prefers-reduced-motion: reduce){.f1rnm8gg::after{transition-property:none;}}", { m: "(prefers-reduced-motion: reduce)" }], ["@media (prefers-reduced-motion: reduce){.f1x3cmqg::after{transition-duration:0.01ms;}}", { m: "(prefers-reduced-motion: reduce)" }]] }); const calculateTabRect = element => { if (element) { var _element_parentElement; const parentRect = ((_element_parentElement = element.parentElement) === null || _element_parentElement === void 0 ? void 0 : _element_parentElement.getBoundingClientRect()) || { x: 0, y: 0, width: 0, height: 0 }; const tabRect = element.getBoundingClientRect(); return { x: tabRect.x - parentRect.x, y: tabRect.y - parentRect.y, width: tabRect.width, height: tabRect.height }; } return undefined; }; const getRegisteredTabRect = (registeredTabs, value) => { var _registeredTabs_JSON_stringify; const element = isValueDefined(value) ? (_registeredTabs_JSON_stringify = registeredTabs[JSON.stringify(value)]) === null || _registeredTabs_JSON_stringify === void 0 ? void 0 : _registeredTabs_JSON_stringify.ref.current : undefined; return element ? calculateTabRect(element) : undefined; }; // eslint-disable-next-line eqeqeq const isValueDefined = value => value != null; /** * Adds additional styling to the active tab selection indicator to create a sliding animation. */ export const useTabAnimatedIndicatorStyles_unstable = state => { const { disabled, selected, vertical } = state; const activeIndicatorStyles = useActiveIndicatorStyles(); const [lastAnimatedFrom, setLastAnimatedFrom] = React.useState(); const [animationValues, setAnimationValues] = React.useState({ offset: 0, scale: 1 }); const getRegisteredTabs = useTabListContext_unstable(ctx => ctx.getRegisteredTabs); const [requestAnimationFrame] = useAnimationFrame(); if (selected) { const { previousSelectedValue, selectedValue, registeredTabs } = getRegisteredTabs(); if (isValueDefined(previousSelectedValue) && lastAnimatedFrom !== previousSelectedValue) { const previousSelectedTabRect = getRegisteredTabRect(registeredTabs, previousSelectedValue); const selectedTabRect = getRegisteredTabRect(registeredTabs, selectedValue); if (selectedTabRect && previousSelectedTabRect) { const offset = vertical ? previousSelectedTabRect.y - selectedTabRect.y : previousSelectedTabRect.x - selectedTabRect.x; const scale = vertical ? previousSelectedTabRect.height / selectedTabRect.height : previousSelectedTabRect.width / selectedTabRect.width; setAnimationValues({ offset, scale }); setLastAnimatedFrom(previousSelectedValue); // Reset the animation values after the animation is complete requestAnimationFrame(() => setAnimationValues({ offset: 0, scale: 1 })); } } } else if (isValueDefined(lastAnimatedFrom)) { // need to clear the last animated from so that if this tab is selected again // from the same previous tab as last time, that animation still happens. setLastAnimatedFrom(undefined); } // do not apply any animation if the tab is disabled if (disabled) { return state; } // the animation should only happen as the selection indicator returns to its // original position and not when set at the previous tabs position. const animating = animationValues.offset === 0 && animationValues.scale === 1; state.root.className = mergeClasses(state.root.className, selected && activeIndicatorStyles.base, selected && animating && activeIndicatorStyles.animated, selected && (vertical ? activeIndicatorStyles.vertical : activeIndicatorStyles.horizontal)); const rootCssVars = { [tabIndicatorCssVars_unstable.offsetVar]: `${animationValues.offset}px`, [tabIndicatorCssVars_unstable.scaleVar]: `${animationValues.scale}` }; state.root.style = { ...rootCssVars, ...state.root.style }; return state; };