133 lines
5.5 KiB
JavaScript
133 lines
5.5 KiB
JavaScript
'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;
|
|
}; |