Private
Public Access
1
0

feat: Fluent UI Outlook Lite + connections mockup

This commit is contained in:
2026-04-14 18:52:25 +00:00
parent 1199eff6c3
commit dfa4010406
34820 changed files with 1003813 additions and 205 deletions

View File

@@ -0,0 +1,15 @@
'use client';
import * as React from 'react';
import { useTab_unstable } from './useTab';
import { renderTab_unstable } from './renderTab';
import { useTabStyles_unstable } from './useTabStyles.styles';
import { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts';
/**
* A tab provides a selectable item in a tab list.
*/ export const Tab = /*#__PURE__*/ React.forwardRef((props, ref)=>{
const state = useTab_unstable(props, ref);
useTabStyles_unstable(state);
useCustomStyleHook_unstable('useTabStyles_unstable')(state);
return renderTab_unstable(state);
});
Tab.displayName = 'Tab';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Tab/Tab.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { useTab_unstable } from './useTab';\nimport { renderTab_unstable } from './renderTab';\nimport { useTabStyles_unstable } from './useTabStyles.styles';\nimport type { TabProps } from './Tab.types';\nimport type { ForwardRefComponent } from '@fluentui/react-utilities';\nimport { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts';\n\n/**\n * A tab provides a selectable item in a tab list.\n */\nexport const Tab: ForwardRefComponent<TabProps> = React.forwardRef((props, ref) => {\n const state = useTab_unstable(props, ref);\n\n useTabStyles_unstable(state);\n\n useCustomStyleHook_unstable('useTabStyles_unstable')(state);\n\n return renderTab_unstable(state);\n});\n\nTab.displayName = 'Tab';\n"],"names":["React","useTab_unstable","renderTab_unstable","useTabStyles_unstable","useCustomStyleHook_unstable","Tab","forwardRef","props","ref","state","displayName"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAC/B,SAASC,eAAe,QAAQ,WAAW;AAC3C,SAASC,kBAAkB,QAAQ,cAAc;AACjD,SAASC,qBAAqB,QAAQ,wBAAwB;AAG9D,SAASC,2BAA2B,QAAQ,kCAAkC;AAE9E;;CAEC,GACD,OAAO,MAAMC,oBAAqCL,MAAMM,UAAU,CAAC,CAACC,OAAOC;IACzE,MAAMC,QAAQR,gBAAgBM,OAAOC;IAErCL,sBAAsBM;IAEtBL,4BAA4B,yBAAyBK;IAErD,OAAOP,mBAAmBO;AAC5B,GAAG;AAEHJ,IAAIK,WAAW,GAAG"}

View File

@@ -0,0 +1 @@
export { };

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Tab/Tab.types.ts"],"sourcesContent":["import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities';\n\n/**\n * Any value that identifies a specific tab.\n */\nexport type TabValue = unknown;\n\nexport type TabSlots = {\n /**\n * Root of the component.\n */\n root: Slot<'button'>;\n\n /**\n * Icon that renders before the content.\n */\n icon?: Slot<'span'>;\n\n /**\n * Component children are placed in this slot\n * Avoid using the `children` property in this slot in favour of Component children whenever possible.\n */\n content: NonNullable<Slot<'span'>>;\n};\n\nexport type TabInternalSlots = TabSlots & {\n contentReservedSpace?: Slot<'span'>;\n};\n\n/**\n * Tab Props\n */\nexport type TabProps = Omit<ComponentProps<Partial<TabSlots>>, 'content' | 'value'> &\n Pick<Partial<TabSlots>, 'content'> & {\n /**\n * A tab can be set to disable interaction.\n * @default false\n */\n disabled?: boolean;\n /**\n * The value that identifies this tab when selected.\n */\n value: TabValue;\n };\n\nexport type TabBaseProps = Omit<TabProps, 'contentReservedSpace'>;\n\n/**\n * State used in rendering Tab\n */\nexport type TabState = ComponentState<TabInternalSlots> &\n Pick<TabProps, 'value'> &\n Required<Pick<TabProps, 'disabled'>> & {\n /**\n * A tab supports 'transparent', 'subtle', `subtle-circular` and `filled-circular` appearance.\n */\n appearance?: 'transparent' | 'subtle' | 'subtle-circular' | 'filled-circular';\n\n /**\n * A tab can have only an icon slot filled and no content.\n */\n iconOnly: boolean;\n /**\n * If this tab is selected\n */\n selected: boolean;\n /**\n * When defined, tab content with selected style is rendered hidden to reserve space.\n * This keeps consistent content size between unselected and selected states.\n *\n * @deprecated - use `contentReservedSpace` internal slot instead.\n */\n contentReservedSpaceClassName?: string;\n /**\n * A tab can be either 'small', 'medium', or 'large' size.\n */\n size: 'small' | 'medium' | 'large';\n /**\n * A tab can arrange its content based on if the tabs in the list are arranged vertically.\n */\n vertical: boolean;\n };\n\nexport type TabBaseState = Omit<TabState, 'appearance' | 'size' | 'contentReservedSpace'>;\n"],"names":[],"mappings":"AAmFA,WAA0F"}

View File

@@ -0,0 +1,5 @@
export { Tab } from './Tab';
export { renderTab_unstable } from './renderTab';
export { useTab_unstable, useTabBase_unstable, useTabA11yBehavior_unstable } from './useTab';
export { tabClassNames, tabReservedSpaceClassNames, useTabButtonStyles_unstable, useTabContentStyles_unstable, useTabIndicatorStyles_unstable, useTabStyles_unstable } from './useTabStyles.styles';
export { useTabAnimatedIndicatorStyles_unstable } from './useTabAnimatedIndicator.styles';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Tab/index.ts"],"sourcesContent":["export { Tab } from './Tab';\nexport type { TabInternalSlots, TabBaseProps, TabProps, TabSlots, TabBaseState, TabState, TabValue } from './Tab.types';\nexport { renderTab_unstable } from './renderTab';\nexport { useTab_unstable, useTabBase_unstable, useTabA11yBehavior_unstable } from './useTab';\nexport {\n tabClassNames,\n tabReservedSpaceClassNames,\n useTabButtonStyles_unstable,\n useTabContentStyles_unstable,\n useTabIndicatorStyles_unstable,\n useTabStyles_unstable,\n} from './useTabStyles.styles';\nexport { useTabAnimatedIndicatorStyles_unstable } from './useTabAnimatedIndicator.styles';\n"],"names":["Tab","renderTab_unstable","useTab_unstable","useTabBase_unstable","useTabA11yBehavior_unstable","tabClassNames","tabReservedSpaceClassNames","useTabButtonStyles_unstable","useTabContentStyles_unstable","useTabIndicatorStyles_unstable","useTabStyles_unstable","useTabAnimatedIndicatorStyles_unstable"],"mappings":"AAAA,SAASA,GAAG,QAAQ,QAAQ;AAE5B,SAASC,kBAAkB,QAAQ,cAAc;AACjD,SAASC,eAAe,EAAEC,mBAAmB,EAAEC,2BAA2B,QAAQ,WAAW;AAC7F,SACEC,aAAa,EACbC,0BAA0B,EAC1BC,2BAA2B,EAC3BC,4BAA4B,EAC5BC,8BAA8B,EAC9BC,qBAAqB,QAChB,wBAAwB;AAC/B,SAASC,sCAAsC,QAAQ,mCAAmC"}

View File

@@ -0,0 +1,14 @@
import { jsx as _jsx, jsxs as _jsxs } from "@fluentui/react-jsx-runtime/jsx-runtime";
import { assertSlots } from '@fluentui/react-utilities';
/**
* Render the final JSX of Tab
*/ export const renderTab_unstable = (state)=>{
assertSlots(state);
return /*#__PURE__*/ _jsxs(state.root, {
children: [
state.icon && /*#__PURE__*/ _jsx(state.icon, {}),
!state.iconOnly && /*#__PURE__*/ _jsx(state.content, {}),
state.contentReservedSpace && /*#__PURE__*/ _jsx(state.contentReservedSpace, {})
]
});
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Tab/renderTab.tsx"],"sourcesContent":["/** @jsxRuntime automatic */\n/** @jsxImportSource @fluentui/react-jsx-runtime */\nimport { assertSlots } from '@fluentui/react-utilities';\nimport type { JSXElement } from '@fluentui/react-utilities';\nimport type { TabBaseState, TabInternalSlots } from './Tab.types';\n\n/**\n * Render the final JSX of Tab\n */\nexport const renderTab_unstable = (state: TabBaseState): JSXElement => {\n assertSlots<TabInternalSlots>(state);\n\n return (\n <state.root>\n {state.icon && <state.icon />}\n {!state.iconOnly && <state.content />}\n {state.contentReservedSpace && <state.contentReservedSpace />}\n </state.root>\n );\n};\n"],"names":["assertSlots","renderTab_unstable","state","root","icon","iconOnly","content","contentReservedSpace"],"mappings":"AAAA,0BAA0B,GAC1B,iDAAiD;AACjD,SAASA,WAAW,QAAQ,4BAA4B;AAIxD;;CAEC,GACD,OAAO,MAAMC,qBAAqB,CAACC;IACjCF,YAA8BE;IAE9B,qBACE,MAACA,MAAMC,IAAI;;YACRD,MAAME,IAAI,kBAAI,KAACF,MAAME,IAAI;YACzB,CAACF,MAAMG,QAAQ,kBAAI,KAACH,MAAMI,OAAO;YACjCJ,MAAMK,oBAAoB,kBAAI,KAACL,MAAMK,oBAAoB;;;AAGhE,EAAE"}

View File

@@ -0,0 +1,140 @@
'use client';
import * as React from 'react';
import { useTabsterAttributes } from '@fluentui/react-tabster';
import { mergeCallbacks, useEventCallback, useMergedRefs, slot, omit } from '@fluentui/react-utilities';
import { useTabListContext_unstable } from '../TabList';
/**
* Create the state required to render Tab.
*
* The returned state can be modified with hooks such as useTabStyles_unstable,
* before being passed to renderTab_unstable.
*
* @param props - props from this instance of Tab
* @param ref - reference to root HTMLElement of Tab
*/ export const useTab_unstable = (props, ref)=>{
const { content } = props;
const state = useTabBase_unstable(props, ref);
const focusAttributes = useTabA11yBehavior_unstable(state);
const appearance = useTabListContext_unstable((ctx)=>ctx.appearance);
const reserveSelectedTabSpace = useTabListContext_unstable((ctx)=>ctx.reserveSelectedTabSpace);
const size = useTabListContext_unstable((ctx)=>{
var _ctx_size;
return (_ctx_size = ctx.size) !== null && _ctx_size !== void 0 ? _ctx_size : 'medium';
});
const contentReservedSpace = content && typeof content === 'object' ? omit(content, [
'ref'
]) : content;
return {
...state,
// eslint-disable-next-line @typescript-eslint/no-deprecated
components: {
...state.components,
contentReservedSpace: 'span'
},
root: {
...focusAttributes,
...state.root
},
contentReservedSpace: slot.optional(contentReservedSpace, {
renderByDefault: !state.selected && !state.iconOnly && reserveSelectedTabSpace,
defaultProps: {
children: props.children
},
elementType: 'span'
}),
appearance,
size
};
};
/**
* Create the based state required to render Tab without design specifics and focus attributes.
*
* @param props - props from this instance of Tab
* @param ref - reference to root HTMLElement of Tab
*/ export const useTabBase_unstable = (props, ref)=>{
const { content, disabled: tabDisabled = false, icon, onClick, onFocus, value, ...rest } = props;
const selectTabOnFocus = useTabListContext_unstable((ctx)=>ctx.selectTabOnFocus);
const listDisabled = useTabListContext_unstable((ctx)=>ctx.disabled);
const selected = useTabListContext_unstable((ctx)=>ctx.selectedValue === value);
const onRegister = useTabListContext_unstable((ctx)=>ctx.onRegister);
const onUnregister = useTabListContext_unstable((ctx)=>ctx.onUnregister);
const onSelect = useTabListContext_unstable((ctx)=>ctx.onSelect);
const vertical = useTabListContext_unstable((ctx)=>!!ctx.vertical);
const disabled = listDisabled || tabDisabled;
const innerRef = React.useRef(null);
const onSelectCallback = (event)=>onSelect(event, {
value
});
const onTabClick = useEventCallback(mergeCallbacks(onClick, onSelectCallback));
const onTabFocus = useEventCallback(mergeCallbacks(onFocus, onSelectCallback));
React.useEffect(()=>{
onRegister({
value,
ref: innerRef
});
return ()=>{
onUnregister({
value,
ref: innerRef
});
};
}, [
onRegister,
onUnregister,
innerRef,
value
]);
const iconSlot = slot.optional(icon, {
elementType: 'span'
});
const contentSlot = slot.always(content, {
defaultProps: {
children: props.children
},
elementType: 'span'
});
const iconOnly = Boolean((iconSlot === null || iconSlot === void 0 ? void 0 : iconSlot.children) && !contentSlot.children);
return {
components: {
root: 'button',
icon: 'span',
content: 'span',
contentReservedSpace: 'span'
},
root: slot.always({
ref: useMergedRefs(ref, innerRef),
role: 'tab',
type: 'button',
// aria-selected undefined indicates it is not selectable
// according to https://www.w3.org/TR/wai-aria-1.1/#aria-selected
'aria-selected': disabled ? undefined : `${selected}`,
value,
...rest,
disabled,
onClick: onTabClick,
onFocus: selectTabOnFocus ? onTabFocus : onFocus
}, {
elementType: 'button'
}),
icon: iconSlot,
iconOnly,
content: contentSlot,
disabled,
selected,
value,
vertical
};
};
/**
* Hook to return a11y attributes to a Tab based on selected state.
* Should be applied on the button with role="tab".
*
* @param selected - whether the Tab is selected
* @returns Tabster DOM attributes
*/ export const useTabA11yBehavior_unstable = ({ selected })=>{
return useTabsterAttributes({
focusable: {
isDefault: selected
}
});
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,133 @@
'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;
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,125 @@
'use client';
import * as React from 'react';
import { makeStyles, 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 = makeStyles({
base: {
// overflow is required to allow the selection indicator to animate outside the tab area.
overflow: 'visible'
},
animated: {
'::after': {
transitionProperty: 'transform',
transitionDuration: `${tokens.durationSlow}`,
transitionTimingFunction: `${tokens.curveDecelerateMax}`
},
'@media (prefers-reduced-motion: reduce)': {
'::after': {
transitionProperty: 'none',
transitionDuration: '0.01ms'
}
}
},
horizontal: {
'::after': {
transformOrigin: 'left',
transform: `translateX(var(${tabIndicatorCssVars_unstable.offsetVar}))
scaleX(var(${tabIndicatorCssVars_unstable.scaleVar}))`
}
},
vertical: {
'::after': {
transformOrigin: 'top',
transform: `translateY(var(${tabIndicatorCssVars_unstable.offsetVar}))
scaleY(var(${tabIndicatorCssVars_unstable.scaleVar}))`
}
}
});
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;
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,883 @@
'use client';
import { __styles, mergeClasses, shorthands } from '@griffel/react';
import { createCustomFocusIndicatorStyle } from '@fluentui/react-tabster';
import { tokens, typographyStyles } from '@fluentui/react-theme';
import { useTabAnimatedIndicatorStyles_unstable } from './useTabAnimatedIndicator.styles';
export const tabClassNames = {
root: 'fui-Tab',
icon: 'fui-Tab__icon',
content: 'fui-Tab__content'
};
export const tabReservedSpaceClassNames = {
content: 'fui-Tab__content--reserved-space'
};
// These should match the constants defined in @fluentui/react-icons
// This package avoids taking a dependency on the icons package for only the constants.
const iconClassNames = {
filled: 'fui-Icon-filled',
regular: 'fui-Icon-regular'
};
/**
* Styles for the root slot
*/
const useRootStyles = /*#__PURE__*/__styles({
root: {
Bt984gj: "f122n59",
mc9l5x: "f13qh94s",
Bnnss6s: "fi64zpg",
Bxotwcr: "f1u07yai",
Budl1dq: "frn2hmy",
wkccdc: "f1olsevy",
oeaueh: "f1s6fcnf",
qhf8xq: "f10pi13n"
},
button: {
Bt984gj: "f122n59",
Bgfg5da: 0,
B9xav0g: 0,
oivjwe: 0,
Bn0qgzm: 0,
B4g9neb: 0,
zhjwy3: 0,
wvpqe5: 0,
ibv6hh: 0,
u1mtju: 0,
h3c5rm: 0,
vrafjx: 0,
Bekrc4i: 0,
i8vvqc: 0,
g2u3we: 0,
icvyot: 0,
B4j52fo: 0,
irswps: "f3bhgqh",
Beyfa6y: 0,
Bbmb7ep: 0,
Btl43ni: 0,
B7oj6ja: 0,
Dimara: "ft85np5",
Bceei9c: "f1k6fduh",
mc9l5x: "f13qh94s",
Bnnss6s: "fi64zpg",
Bxotwcr: "f1u07yai",
Budl1dq: "frn2hmy",
wkccdc: "f1olsevy",
Bahqtrf: "fk6fouc",
Bg96gwp: "f1i3iumi",
oeaueh: "f1s6fcnf",
qhf8xq: "f10pi13n",
B68tc82: 0,
Bmxbyg5: 0,
Bpg54ce: "f1a3p1vp",
B9bfxx9: "f1cxpek8"
},
horizontal: {
Brf1p80: "f4d9j23"
},
vertical: {
Brf1p80: "f1s9ku6b"
},
smallHorizontal: {
i8kkvl: "f14mj54c",
Byoj8tv: 0,
uwmqm3: 0,
z189sj: 0,
z8tnut: 0,
B0ocmuz: "f1wmopi4"
},
smallVertical: {
i8kkvl: "f14mj54c",
Byoj8tv: 0,
uwmqm3: 0,
z189sj: 0,
z8tnut: 0,
B0ocmuz: "f12or63q"
},
mediumHorizontal: {
i8kkvl: "f1rjii52",
Byoj8tv: 0,
uwmqm3: 0,
z189sj: 0,
z8tnut: 0,
B0ocmuz: "f1w08f2p"
},
mediumVertical: {
i8kkvl: "f1rjii52",
Byoj8tv: 0,
uwmqm3: 0,
z189sj: 0,
z8tnut: 0,
B0ocmuz: "fymxs25"
},
largeHorizontal: {
i8kkvl: "f1rjii52",
Byoj8tv: 0,
uwmqm3: 0,
z189sj: 0,
z8tnut: 0,
B0ocmuz: "f1ssfvub"
},
largeVertical: {
i8kkvl: "f1rjii52",
Byoj8tv: 0,
uwmqm3: 0,
z189sj: 0,
z8tnut: 0,
B0ocmuz: "fwkd1rq"
},
transparent: {
De3pzq: "f1c21dwh",
B95qlz1: "f9rvdkv",
B7xitij: "f1051ucx",
Bptxc3x: "fmmjozx",
Bwqhzpy: "fqhzt5g",
iyk698: "f7l5cgy",
cl4aha: "fpkze5g",
B0q3jbp: "f1iywnoi",
Be9ayug: "f9n45c4"
},
subtle: {
De3pzq: "fhovq9v",
B95qlz1: "f1bifk9c",
B7xitij: "fo6hitd",
Bptxc3x: "fmmjozx",
Bwqhzpy: "fqhzt5g",
iyk698: "f7l5cgy",
cl4aha: "fpkze5g",
B0q3jbp: "f1iywnoi",
Be9ayug: "f9n45c4"
},
disabledCursor: {
Bceei9c: "fdrzuqr"
},
disabled: {
De3pzq: "f1c21dwh",
Bptxc3x: "fato7r6",
cl4aha: "fao1bnu"
},
selected: {
Bptxc3x: "f1cadz5z",
Bwqhzpy: "fwhdxxj",
iyk698: "fintccb",
cl4aha: "ffplhdr",
B0q3jbp: "fjo17wb",
Be9ayug: "f148789c"
}
}, {
d: [".f122n59{align-items:center;}", ".f13qh94s{display:grid;}", ".fi64zpg{flex-shrink:0;}", ".f1u07yai{grid-auto-flow:column;}", ".frn2hmy{grid-template-columns:auto;}", ".f1olsevy{grid-template-rows:auto;}", ".f1s6fcnf{outline-style:none;}", ".f10pi13n{position:relative;}", [".f3bhgqh{border:none;}", {
p: -2
}], [".ft85np5{border-radius:var(--borderRadiusMedium);}", {
p: -1
}], ".f1k6fduh{cursor:pointer;}", ".fk6fouc{font-family:var(--fontFamilyBase);}", ".f1i3iumi{line-height:var(--lineHeightBase300);}", [".f1a3p1vp{overflow:hidden;}", {
p: -1
}], ".f1cxpek8{text-transform:none;}", ".f4d9j23{justify-content:center;}", ".f1s9ku6b{justify-content:start;}", ".f14mj54c{column-gap:var(--spacingHorizontalXXS);}", [".f1wmopi4{padding:var(--spacingVerticalSNudge) var(--spacingHorizontalSNudge);}", {
p: -1
}], [".f12or63q{padding:var(--spacingVerticalXXS) var(--spacingHorizontalSNudge);}", {
p: -1
}], ".f1rjii52{column-gap:var(--spacingHorizontalSNudge);}", [".f1w08f2p{padding:var(--spacingVerticalM) var(--spacingHorizontalMNudge);}", {
p: -1
}], [".fymxs25{padding:var(--spacingVerticalSNudge) var(--spacingHorizontalMNudge);}", {
p: -1
}], [".f1ssfvub{padding:var(--spacingVerticalL) var(--spacingHorizontalMNudge);}", {
p: -1
}], [".fwkd1rq{padding:var(--spacingVerticalS) var(--spacingHorizontalMNudge);}", {
p: -1
}], ".f1c21dwh{background-color:var(--colorTransparentBackground);}", ".f9rvdkv:enabled:hover{background-color:var(--colorTransparentBackgroundHover);}", ".f1051ucx:enabled:active{background-color:var(--colorTransparentBackgroundPressed);}", ".fmmjozx .fui-Tab__icon{color:var(--colorNeutralForeground2);}", ".fqhzt5g:enabled:hover .fui-Tab__icon{color:var(--colorNeutralForeground2Hover);}", ".f7l5cgy:enabled:active .fui-Tab__icon{color:var(--colorNeutralForeground2Pressed);}", ".fpkze5g .fui-Tab__content{color:var(--colorNeutralForeground2);}", ".f1iywnoi:enabled:hover .fui-Tab__content{color:var(--colorNeutralForeground2Hover);}", ".f9n45c4:enabled:active .fui-Tab__content{color:var(--colorNeutralForeground2Pressed);}", ".fhovq9v{background-color:var(--colorSubtleBackground);}", ".f1bifk9c:enabled:hover{background-color:var(--colorSubtleBackgroundHover);}", ".fo6hitd:enabled:active{background-color:var(--colorSubtleBackgroundPressed);}", ".fdrzuqr{cursor:not-allowed;}", ".fato7r6 .fui-Tab__icon{color:var(--colorNeutralForegroundDisabled);}", ".fao1bnu .fui-Tab__content{color:var(--colorNeutralForegroundDisabled);}", ".f1cadz5z .fui-Tab__icon{color:var(--colorCompoundBrandForeground1);}", ".fwhdxxj:enabled:hover .fui-Tab__icon{color:var(--colorCompoundBrandForeground1Hover);}", ".fintccb:enabled:active .fui-Tab__icon{color:var(--colorCompoundBrandForeground1Pressed);}", ".ffplhdr .fui-Tab__content{color:var(--colorNeutralForeground1);}", ".fjo17wb:enabled:hover .fui-Tab__content{color:var(--colorNeutralForeground1Hover);}", ".f148789c:enabled:active .fui-Tab__content{color:var(--colorNeutralForeground1Pressed);}"]
});
const useCircularAppearanceStyles = /*#__PURE__*/__styles({
base: {
Beyfa6y: 0,
Bbmb7ep: 0,
Btl43ni: 0,
B7oj6ja: 0,
Dimara: "f44lkw9",
Bgfg5da: 0,
B9xav0g: 0,
oivjwe: 0,
Bn0qgzm: 0,
B4g9neb: 0,
zhjwy3: 0,
wvpqe5: 0,
ibv6hh: 0,
u1mtju: 0,
h3c5rm: 0,
vrafjx: 0,
Bekrc4i: 0,
i8vvqc: 0,
g2u3we: 0,
icvyot: 0,
B4j52fo: 0,
irswps: "fp7rvkm",
Bptxc3x: "ftorr8m",
cl4aha: "f16lqpmv"
},
small: {
Dbcxam: 0,
rjzwhg: 0,
Bblux5w: "fzklhed"
},
medium: {
Dbcxam: 0,
rjzwhg: 0,
Bblux5w: "f1j721cc"
},
large: {
Dbcxam: 0,
rjzwhg: 0,
Bblux5w: "frx9knr"
},
subtle: {
De3pzq: "fhovq9v",
sj55zd: "fkfq4zb",
B95qlz1: "f1bifk9c",
Eo63ln: 0,
r9osk6: 0,
Itrz8y: 0,
zeg6vx: 0,
l65xgk: 0,
Bw4olcx: 0,
Folb0i: 0,
I2h8y4: 0,
Bgxgoyi: 0,
Bvlkotb: 0,
Fwyncl: 0,
Byh5edv: 0,
Becqvjq: 0,
uumbiq: 0,
B73q3dg: 0,
Bblwbaf: 0,
B0ezav: "ft57sj0",
r4wkhp: "f1fcoy83",
B7xitij: "fo6hitd",
d3wsvi: 0,
Hdqn7s: 0,
zu5y1p: 0,
owqphb: 0,
g9c53k: 0,
Btmu08z: 0,
Bthxvy6: 0,
gluvuq: 0,
tb88gp: 0,
wns6jk: 0,
kdfdk4: 0,
Bbw008l: 0,
Bayi1ib: 0,
B1kkfu3: 0,
J1oqyp: 0,
kem6az: 0,
goa3yj: "fhn220o",
p743kt: "f15qf7sh",
wmvzou: 0,
sc4o1m: 0,
wymq9i: 0,
u9orzk: 0,
puiv5t: 0,
Bosien3: 0,
b2z72d: 0,
Beulxaw: 0,
B57pkaw: 0,
Jcjdmf: 0,
B8qgbzl: 0,
Bbmb0sr: 0,
B14q8qp: 0,
Bcq6wej: 0,
Byz1pjr: 0,
kr9cjb: 0,
Ff9ifp: "f1mljprk"
},
subtleSelected: {
De3pzq: "f16xkysk",
Bgfg5da: 0,
B9xav0g: 0,
oivjwe: 0,
Bn0qgzm: 0,
B4g9neb: 0,
zhjwy3: 0,
wvpqe5: 0,
ibv6hh: 0,
u1mtju: 0,
h3c5rm: 0,
vrafjx: 0,
Bekrc4i: 0,
i8vvqc: 0,
g2u3we: 0,
icvyot: 0,
B4j52fo: 0,
irswps: "f1c2pc3t",
sj55zd: "faj9fo0",
B95qlz1: "fsm7zmf",
Eo63ln: 0,
r9osk6: 0,
Itrz8y: 0,
zeg6vx: 0,
l65xgk: 0,
Bw4olcx: 0,
Folb0i: 0,
I2h8y4: 0,
Bgxgoyi: 0,
Bvlkotb: 0,
Fwyncl: 0,
Byh5edv: 0,
Becqvjq: 0,
uumbiq: 0,
B73q3dg: 0,
Bblwbaf: 0,
B0ezav: "f1wo0sfq",
r4wkhp: "f1afuynh",
B7xitij: "f94ddyl",
d3wsvi: 0,
Hdqn7s: 0,
zu5y1p: 0,
owqphb: 0,
g9c53k: 0,
Btmu08z: 0,
Bthxvy6: 0,
gluvuq: 0,
tb88gp: 0,
wns6jk: 0,
kdfdk4: 0,
Bbw008l: 0,
Bayi1ib: 0,
B1kkfu3: 0,
J1oqyp: 0,
kem6az: 0,
goa3yj: "fmle6oo",
p743kt: "f1d3itm4",
wmvzou: 0,
sc4o1m: 0,
wymq9i: 0,
u9orzk: 0,
puiv5t: 0,
Bosien3: 0,
b2z72d: 0,
Beulxaw: 0,
B57pkaw: 0,
Jcjdmf: 0,
B8qgbzl: 0,
Bbmb0sr: 0,
B14q8qp: 0,
Bcq6wej: 0,
Byz1pjr: 0,
kr9cjb: 0,
Ff9ifp: "f1e9l2u"
},
subtleDisabled: {
De3pzq: "fhovq9v",
sj55zd: "f1s2aq7o"
},
subtleDisabledSelected: {
De3pzq: "f1bg9a2p",
Bgfg5da: 0,
B9xav0g: 0,
oivjwe: 0,
Bn0qgzm: 0,
B4g9neb: 0,
zhjwy3: 0,
wvpqe5: 0,
ibv6hh: 0,
u1mtju: 0,
h3c5rm: 0,
vrafjx: 0,
Bekrc4i: 0,
i8vvqc: 0,
g2u3we: 0,
icvyot: 0,
B4j52fo: 0,
irswps: "fegtqic",
sj55zd: "f1s2aq7o"
},
filled: {
De3pzq: "f16xq7d1",
sj55zd: "fkfq4zb",
B95qlz1: "fwwxidx",
r4wkhp: "f1fcoy83",
B7xitij: "f14i52sd",
p743kt: "f15qf7sh",
Hhssn: "f1cpi5f4",
uaurb9: "f6pj1s0",
B401qx7: "f12sz5kj",
Buyp1jb: "f17588c3",
gsp74p: "fnthgdt"
},
filledSelected: {
De3pzq: "ffp7eso",
sj55zd: "f1phragk",
B95qlz1: "f1lm9dni",
r4wkhp: "f1mn5ei1",
B7xitij: "f1g6ncd0",
p743kt: "fl71aob",
Gedr8k: "f14zl92z",
Bm69vlq: "f16zy3eu",
Bodipah: "f1gdgb7p",
B5kpen0: "f1tua9ng"
},
filledDisabled: {
De3pzq: "f1bg9a2p",
sj55zd: "f1s2aq7o"
},
filledDisabledSelected: {
De3pzq: "f1bg9a2p",
Bgfg5da: 0,
B9xav0g: 0,
oivjwe: 0,
Bn0qgzm: 0,
B4g9neb: 0,
zhjwy3: 0,
wvpqe5: 0,
ibv6hh: 0,
u1mtju: 0,
h3c5rm: 0,
vrafjx: 0,
Bekrc4i: 0,
i8vvqc: 0,
g2u3we: 0,
icvyot: 0,
B4j52fo: 0,
irswps: "fegtqic",
sj55zd: "f1s2aq7o"
}
}, {
d: [[".f44lkw9{border-radius:var(--borderRadiusCircular);}", {
p: -1
}], [".fp7rvkm{border:solid var(--strokeWidthThin) var(--colorTransparentStroke);}", {
p: -2
}], ".ftorr8m .fui-Tab__icon{color:inherit;}", ".f16lqpmv .fui-Tab__content{color:inherit;}", [".fzklhed{padding-block:calc(var(--spacingVerticalXXS) - var(--strokeWidthThin));}", {
p: -1
}], [".f1j721cc{padding-block:calc(var(--spacingVerticalSNudge) - var(--strokeWidthThin));}", {
p: -1
}], [".frx9knr{padding-block:calc(var(--spacingVerticalS) - var(--strokeWidthThin));}", {
p: -1
}], ".fhovq9v{background-color:var(--colorSubtleBackground);}", ".fkfq4zb{color:var(--colorNeutralForeground2);}", ".f1bifk9c:enabled:hover{background-color:var(--colorSubtleBackgroundHover);}", [".ft57sj0:enabled:hover{border:solid var(--strokeWidthThin) var(--colorNeutralStroke1Hover);}", {
p: -2
}], ".f1fcoy83:enabled:hover{color:var(--colorNeutralForeground2Hover);}", ".fo6hitd:enabled:active{background-color:var(--colorSubtleBackgroundPressed);}", [".fhn220o:enabled:active{border:solid var(--strokeWidthThin) var(--colorNeutralStroke1Pressed);}", {
p: -2
}], ".f15qf7sh:enabled:active{color:var(--colorNeutralForeground2Pressed);}", ".f16xkysk{background-color:var(--colorBrandBackground2);}", [".f1c2pc3t{border:solid var(--strokeWidthThin) var(--colorCompoundBrandStroke);}", {
p: -2
}], ".faj9fo0{color:var(--colorBrandForeground2);}", ".fsm7zmf:enabled:hover{background-color:var(--colorBrandBackground2Hover);}", [".f1wo0sfq:enabled:hover{border:solid var(--strokeWidthThin) var(--colorCompoundBrandStrokeHover);}", {
p: -2
}], ".f1afuynh:enabled:hover{color:var(--colorBrandForeground2Hover);}", ".f94ddyl:enabled:active{background-color:var(--colorBrandBackground2Pressed);}", [".fmle6oo:enabled:active{border:solid var(--strokeWidthThin) var(--colorCompoundBrandStrokePressed);}", {
p: -2
}], ".f1d3itm4:enabled:active{color:var(--colorBrandForeground2Pressed);}", ".f1s2aq7o{color:var(--colorNeutralForegroundDisabled);}", ".f1bg9a2p{background-color:var(--colorNeutralBackgroundDisabled);}", [".fegtqic{border:solid var(--strokeWidthThin) var(--colorNeutralStrokeDisabled);}", {
p: -2
}], ".f16xq7d1{background-color:var(--colorNeutralBackground3);}", ".fwwxidx:enabled:hover{background-color:var(--colorNeutralBackground3Hover);}", ".f14i52sd:enabled:active{background-color:var(--colorNeutralBackground3Pressed);}", ".ffp7eso{background-color:var(--colorBrandBackground);}", ".f1phragk{color:var(--colorNeutralForegroundOnBrand);}", ".f1lm9dni:enabled:hover{background-color:var(--colorBrandBackgroundHover);}", ".f1mn5ei1:enabled:hover{color:var(--colorNeutralForegroundOnBrand);}", ".f1g6ncd0:enabled:active{background-color:var(--colorBrandBackgroundPressed);}", ".fl71aob:enabled:active{color:var(--colorNeutralForegroundOnBrand);}", [".fegtqic{border:solid var(--strokeWidthThin) var(--colorNeutralStrokeDisabled);}", {
p: -2
}]],
m: [["@media (forced-colors: active){.f1mljprk{border:solid var(--strokeWidthThin) Canvas;}}", {
p: -2,
m: "(forced-colors: active)"
}], ["@media (forced-colors: active){.f1e9l2u{border:solid var(--strokeWidthThin) Highlight;}}", {
p: -2,
m: "(forced-colors: active)"
}], ["@media (forced-colors: active){.f1cpi5f4:enabled:hover{background-color:Highlight;}}", {
m: "(forced-colors: active)"
}], ["@media (forced-colors: active){.f6pj1s0:enabled:hover{forced-color-adjust:none;}}", {
m: "(forced-colors: active)"
}], ["@media (forced-colors: active){.f12sz5kj:enabled:hover .fui-Tab__content{color:HighlightText;}}", {
m: "(forced-colors: active)"
}], ["@media (forced-colors: active){.f17588c3:enabled:hover .fui-Icon-filled{color:HighlightText;}}", {
m: "(forced-colors: active)"
}], ["@media (forced-colors: active){.fnthgdt:enabled:hover .fui-Icon-regular{color:HighlightText;}}", {
m: "(forced-colors: active)"
}], ["@media (forced-colors: active){.f14zl92z:enabled{background-color:ButtonText;}}", {
m: "(forced-colors: active)"
}], ["@media (forced-colors: active){.f16zy3eu:enabled .fui-Tab__content{color:ButtonFace;}}", {
m: "(forced-colors: active)"
}], ["@media (forced-colors: active){.f1gdgb7p:enabled .fui-Tab__content{forced-color-adjust:none;}}", {
m: "(forced-colors: active)"
}], ["@media (forced-colors: active){.f1tua9ng:enabled .fui-Tab__icon{color:ButtonFace;}}", {
m: "(forced-colors: active)"
}]]
});
/**
* Focus styles for the root slot
*/
const useFocusStyles = /*#__PURE__*/__styles({
base: {
B8q5s1w: "f8hki3x",
Bci5o5g: ["f1d2448m", "ffh67wi"],
n8qw10: "f1bjia2o",
Bdrgwmp: ["ffh67wi", "f1d2448m"],
Bn4voq9: "f1p7hgxw",
Bfpq7zp: "f1way5bb",
g9k6zt: "f9znhxp",
j6ew2k: ["fqa318h", "fqa318h"],
Bhxq17a: "f1vjpng2"
},
circular: {
B8q5s1w: "f8hki3x",
Bci5o5g: ["f1d2448m", "ffh67wi"],
n8qw10: "f1bjia2o",
Bdrgwmp: ["ffh67wi", "f1d2448m"],
Bn4voq9: "f1p7hgxw",
Bfpq7zp: "f1way5bb",
g9k6zt: "f9znhxp",
j6ew2k: ["fzgyhws", "fqxug60"],
Bhxq17a: "f1vjpng2"
}
}, {
d: [".f8hki3x[data-fui-focus-visible]{border-top-color:transparent;}", ".f1d2448m[data-fui-focus-visible]{border-right-color:transparent;}", ".ffh67wi[data-fui-focus-visible]{border-left-color:transparent;}", ".f1bjia2o[data-fui-focus-visible]{border-bottom-color:transparent;}", ".f1p7hgxw[data-fui-focus-visible]{outline-width:var(--strokeWidthThick);}", ".f1way5bb[data-fui-focus-visible]{outline-color:transparent;}", ".f9znhxp[data-fui-focus-visible]{outline-style:solid;}", ".fqa318h[data-fui-focus-visible]{box-shadow:var(--shadow4),0 0 0 var(--strokeWidthThick) var(--colorStrokeFocus2);}", ".f1vjpng2[data-fui-focus-visible]{z-index:1;}", ".fzgyhws[data-fui-focus-visible]{box-shadow:var(--shadow4),0 0 0 var(--strokeWidthThick) var(--colorStrokeFocus2),0 0 0 var(--strokeWidthThin) var(--colorNeutralStrokeOnBrand) inset;}", ".fqxug60[data-fui-focus-visible]{box-shadow:var(--shadow4),0 0 0 var(--strokeWidthThick) var(--colorStrokeFocus2),0 0 0 var(--strokeWidthThin) var(--colorNeutralStrokeOnBrand) inset;}"]
});
/** Indicator styles for when pending selection */
const usePendingIndicatorStyles = /*#__PURE__*/__styles({
base: {
az7l2e: "fhw179n",
vqofr: 0,
Bv4n3vi: 0,
Bgqb9hq: 0,
B0uxbk8: 0,
Bf3jju6: "fg9j5n4",
amg5m6: "f1kmhr4c",
zkfqfm: "fl1ydde",
Bkydozb: "f1y7maxz",
Bka2azo: 0,
vzq8l0: 0,
csmgbd: 0,
Br4ovkg: 0,
aelrif: "fceyvr4",
y36c18: "f16cxu0",
B1ctymy: "f1nwgacf",
xzc5d: "fm6alxz",
h5yqg8: "fhx32z1"
},
disabled: {
az7l2e: "f1ut20fw",
Bkydozb: "fhrzcfn",
xzc5d: "fw15awd",
h5yqg8: "faqmq51"
},
smallHorizontal: {
lawp4y: "fchca7p",
Baz25je: "f1r53b5e",
Fbdkly: ["f1s6rxz5", "fo35v8s"],
mdwyqc: ["fo35v8s", "f1s6rxz5"]
},
smallVertical: {
lawp4y: "fze4zud",
Fbdkly: ["f1fzr1x6", "f1f351id"],
Bciustq: "fdp32p8",
Ccq8qp: "f1aij3q"
},
mediumHorizontal: {
lawp4y: "fchca7p",
Baz25je: "f1s2r9ax",
Fbdkly: ["f1o0nnkk", "fxb7rol"],
mdwyqc: ["fxb7rol", "f1o0nnkk"]
},
mediumVertical: {
lawp4y: "f17jracn",
Fbdkly: ["f1fzr1x6", "f1f351id"],
Bciustq: "f117lcb2",
Ccq8qp: "f1aij3q"
},
largeHorizontal: {
lawp4y: "fchca7p",
Baz25je: "f1s2r9ax",
Fbdkly: ["f1o0nnkk", "fxb7rol"],
mdwyqc: ["fxb7rol", "f1o0nnkk"]
},
largeVertical: {
lawp4y: "fel9d3z",
Fbdkly: ["f1fzr1x6", "f1f351id"],
Bciustq: "f6vqlre",
Ccq8qp: "f1aij3q"
}
}, {
h: [".fhw179n:hover::before{background-color:var(--colorNeutralStroke1Hover);}", [".fg9j5n4:hover::before{border-radius:var(--borderRadiusCircular);}", {
p: -1
}], ".f1kmhr4c:hover::before{content:\"\";}", ".fl1ydde:hover::before{position:absolute;}", ".f1ut20fw:hover::before{background-color:var(--colorTransparentStroke);}"],
a: [".f1y7maxz:active::before{background-color:var(--colorNeutralStroke1Pressed);}", [".fceyvr4:active::before{border-radius:var(--borderRadiusCircular);}", {
p: -1
}], ".f16cxu0:active::before{content:\"\";}", ".f1nwgacf:active::before{position:absolute;}", ".fhrzcfn:active::before{background-color:var(--colorTransparentStroke);}"],
m: [["@media (forced-colors: active){.fm6alxz:hover::before{background-color:Highlight;}}", {
m: "(forced-colors: active)"
}], ["@media (forced-colors: active){.fhx32z1:active::before{background-color:Highlight;}}", {
m: "(forced-colors: active)"
}], ["@media (forced-colors: active){.fw15awd:hover::before{background-color:transparent;}}", {
m: "(forced-colors: active)"
}], ["@media (forced-colors: active){.faqmq51:active::before{background-color:transparent;}}", {
m: "(forced-colors: active)"
}]],
d: [".fchca7p::before{bottom:0;}", ".f1r53b5e::before{height:var(--strokeWidthThick);}", ".f1s6rxz5::before{left:var(--spacingHorizontalSNudge);}", ".fo35v8s::before{right:var(--spacingHorizontalSNudge);}", ".fze4zud::before{bottom:var(--spacingVerticalXS);}", ".f1fzr1x6::before{left:0;}", ".f1f351id::before{right:0;}", ".fdp32p8::before{top:var(--spacingVerticalXS);}", ".f1aij3q::before{width:var(--strokeWidthThicker);}", ".f1s2r9ax::before{height:var(--strokeWidthThicker);}", ".f1o0nnkk::before{left:var(--spacingHorizontalM);}", ".fxb7rol::before{right:var(--spacingHorizontalM);}", ".f17jracn::before{bottom:var(--spacingVerticalS);}", ".f117lcb2::before{top:var(--spacingVerticalS);}", ".fel9d3z::before{bottom:var(--spacingVerticalMNudge);}", ".f6vqlre::before{top:var(--spacingVerticalMNudge);}"]
});
const useActiveIndicatorStyles = /*#__PURE__*/__styles({
base: {
Bjyk6c5: "f1rp0jgh",
d9w3h3: 0,
B3778ie: 0,
B4j8arr: 0,
Bl18szs: 0,
Blrzh8d: "f3b9emi",
Bsft5z2: "f13zj6fq",
E3zdtr: "f1mdlcz9"
},
selected: {
Bjyk6c5: "f1ksivud",
Bej4dhw: "f1476jrx",
B7wqxwa: "f18q216b",
Hwb57: "f1wf6u82",
Beg08s0: "f1xwit7m",
Bo27r1v: "f1ksfgwi"
},
disabled: {
Bjyk6c5: "f13lkzet"
},
smallHorizontal: {
By385i5: "fo72kxq",
Dlnsje: "f9bb2ob",
Eqx8gd: ["f1q70ajw", "f18rbzdx"],
B1piin3: ["f18rbzdx", "f1q70ajw"]
},
smallVertical: {
By385i5: "fqbue9b",
Eqx8gd: ["f1n6gb5g", "f15yvnhg"],
bn5sak: "fk1klkt",
a2br6o: "f1o25lip"
},
mediumHorizontal: {
By385i5: "fo72kxq",
Dlnsje: "f1vx7lu8",
Eqx8gd: ["fna7m5n", "f1oxpfwv"],
B1piin3: ["f1oxpfwv", "fna7m5n"]
},
mediumVertical: {
By385i5: "fipylg0",
Eqx8gd: ["f1n6gb5g", "f15yvnhg"],
bn5sak: "fqchiol",
a2br6o: "f1o25lip"
},
largeHorizontal: {
By385i5: "fo72kxq",
Dlnsje: "f1vx7lu8",
Eqx8gd: ["fna7m5n", "f1oxpfwv"],
B1piin3: ["f1oxpfwv", "fna7m5n"]
},
largeVertical: {
By385i5: "f1w7dm5g",
Eqx8gd: ["f1n6gb5g", "f15yvnhg"],
bn5sak: "f1p6em4m",
a2br6o: "f1o25lip"
}
}, {
d: [".f1rp0jgh::after{background-color:var(--colorTransparentStroke);}", [".f3b9emi::after{border-radius:var(--borderRadiusCircular);}", {
p: -1
}], ".f13zj6fq::after{content:\"\";}", ".f1mdlcz9::after{position:absolute;}", ".f1ksivud::after{background-color:var(--colorCompoundBrandStroke);}", ".f1476jrx:enabled:hover::after{background-color:var(--colorCompoundBrandStrokeHover);}", ".f18q216b:enabled:active::after{background-color:var(--colorCompoundBrandStrokePressed);}", ".f13lkzet::after{background-color:var(--colorNeutralForegroundDisabled);}", ".fo72kxq::after{bottom:0;}", ".f9bb2ob::after{height:var(--strokeWidthThick);}", ".f1q70ajw::after{left:var(--spacingHorizontalSNudge);}", ".f18rbzdx::after{right:var(--spacingHorizontalSNudge);}", ".fqbue9b::after{bottom:var(--spacingVerticalXS);}", ".f1n6gb5g::after{left:0;}", ".f15yvnhg::after{right:0;}", ".fk1klkt::after{top:var(--spacingVerticalXS);}", ".f1o25lip::after{width:var(--strokeWidthThicker);}", ".f1vx7lu8::after{height:var(--strokeWidthThicker);}", ".fna7m5n::after{left:var(--spacingHorizontalM);}", ".f1oxpfwv::after{right:var(--spacingHorizontalM);}", ".fipylg0::after{bottom:var(--spacingVerticalS);}", ".fqchiol::after{top:var(--spacingVerticalS);}", ".f1w7dm5g::after{bottom:var(--spacingVerticalMNudge);}", ".f1p6em4m::after{top:var(--spacingVerticalMNudge);}"],
m: [["@media (forced-colors: active){.f1wf6u82::after{background-color:ButtonText;}}", {
m: "(forced-colors: active)"
}], ["@media (forced-colors: active){.f1xwit7m:enabled:hover::after{background-color:ButtonText;}}", {
m: "(forced-colors: active)"
}], ["@media (forced-colors: active){.f1ksfgwi:enabled:active::after{background-color:ButtonText;}}", {
m: "(forced-colors: active)"
}]]
});
/**
* Styles for the icon slot.
*/
const useIconStyles = /*#__PURE__*/__styles({
base: {
Br312pm: "fwpfdsa",
Ijaq50: "f16hsg94",
Bt984gj: "f122n59",
mc9l5x: "ftuwxu6",
Brf1p80: "f4d9j23",
B68tc82: 0,
Bmxbyg5: 0,
Bpg54ce: "f1a3p1vp",
D0sxk3: "f16u1re",
t6yez3: "f8bsbmo"
},
small: {
Be2twd7: "fe5j1ua",
Bqenvij: "fjamq6b",
a9b677: "f64fuq3"
},
medium: {
Be2twd7: "fe5j1ua",
Bqenvij: "fjamq6b",
a9b677: "f64fuq3"
},
large: {
Be2twd7: "f1rt2boy",
Bqenvij: "frvgh55",
a9b677: "fq4mcun"
},
selected: {
D0sxk3: "fxoiby5",
t6yez3: "f15q0o9g"
}
}, {
d: [".fwpfdsa{grid-column-start:1;}", ".f16hsg94{grid-row-start:1;}", ".f122n59{align-items:center;}", ".ftuwxu6{display:inline-flex;}", ".f4d9j23{justify-content:center;}", [".f1a3p1vp{overflow:hidden;}", {
p: -1
}], ".f16u1re .fui-Icon-filled{display:none;}", ".f8bsbmo .fui-Icon-regular{display:inline;}", ".fe5j1ua{font-size:20px;}", ".fjamq6b{height:20px;}", ".f64fuq3{width:20px;}", ".f1rt2boy{font-size:24px;}", ".frvgh55{height:24px;}", ".fq4mcun{width:24px;}", ".fxoiby5 .fui-Icon-filled{display:inline;}", ".f15q0o9g .fui-Icon-regular{display:none;}"]
});
/**
* Styles for the content slot (children)
*/
const useContentStyles = /*#__PURE__*/__styles({
base: {
Bahqtrf: "fk6fouc",
Be2twd7: "fkhj508",
Bhrd7zp: "figsok6",
Bg96gwp: "f1i3iumi",
B68tc82: 0,
Bmxbyg5: 0,
Bpg54ce: "f1a3p1vp",
Byoj8tv: 0,
uwmqm3: 0,
z189sj: 0,
z8tnut: 0,
B0ocmuz: "f1bwptpd"
},
selected: {
Bahqtrf: "fk6fouc",
Be2twd7: "fkhj508",
Bhrd7zp: "fl43uef",
Bg96gwp: "f1i3iumi"
},
large: {
Bahqtrf: "fk6fouc",
Be2twd7: "fod5ikn",
Bhrd7zp: "figsok6",
Bg96gwp: "faaz57k"
},
largeSelected: {
Bahqtrf: "fk6fouc",
Be2twd7: "fod5ikn",
Bhrd7zp: "fl43uef",
Bg96gwp: "faaz57k"
},
noIconBefore: {
Br312pm: "fwpfdsa",
Ijaq50: "f16hsg94"
},
iconBefore: {
Br312pm: "fd46tj4",
Ijaq50: "f16hsg94"
},
placeholder: {
Bcdw1i0: "fd7fpy0"
}
}, {
d: [".fk6fouc{font-family:var(--fontFamilyBase);}", ".fkhj508{font-size:var(--fontSizeBase300);}", ".figsok6{font-weight:var(--fontWeightRegular);}", ".f1i3iumi{line-height:var(--lineHeightBase300);}", [".f1a3p1vp{overflow:hidden;}", {
p: -1
}], [".f1bwptpd{padding:var(--spacingVerticalNone) var(--spacingHorizontalXXS);}", {
p: -1
}], ".fl43uef{font-weight:var(--fontWeightSemibold);}", ".fod5ikn{font-size:var(--fontSizeBase400);}", ".faaz57k{line-height:var(--lineHeightBase400);}", ".fwpfdsa{grid-column-start:1;}", ".f16hsg94{grid-row-start:1;}", ".fd46tj4{grid-column-start:2;}", ".fd7fpy0{visibility:hidden;}"]
});
/**
* Apply styling to the Tab slots based on the state
*/
export const useTabStyles_unstable = state => {
'use no memo';
useTabIndicatorStyles_unstable(state);
useTabButtonStyles_unstable(state, state.root);
useTabContentStyles_unstable(state);
return state;
};
/**
* Applies styles for the Tab indicator based on its current state.
*
* This hook is typically used internally by `useTabStyles_unstable`. You should
* only use it directly if you're creating a custom `Tab` component.
*
* @param state - The `Tab` component's current state
* @returns The state object with updated button styles
*/
export const useTabIndicatorStyles_unstable = state => {
'use no memo';
const rootStyles = useRootStyles();
const pendingIndicatorStyles = usePendingIndicatorStyles();
const activeIndicatorStyles = useActiveIndicatorStyles();
const {
appearance,
disabled,
selected,
size,
vertical
} = state;
const classes = [tabClassNames.root, rootStyles.root];
if (appearance !== 'subtle-circular' && appearance !== 'filled-circular') {
classes.push(
// pending indicator (before pseudo element)
pendingIndicatorStyles.base, size === 'small' && (vertical ? pendingIndicatorStyles.smallVertical : pendingIndicatorStyles.smallHorizontal), size === 'medium' && (vertical ? pendingIndicatorStyles.mediumVertical : pendingIndicatorStyles.mediumHorizontal), size === 'large' && (vertical ? pendingIndicatorStyles.largeVertical : pendingIndicatorStyles.largeHorizontal), disabled && pendingIndicatorStyles.disabled,
// active indicator (after pseudo element)
selected && activeIndicatorStyles.base, selected && !disabled && activeIndicatorStyles.selected, selected && size === 'small' && (vertical ? activeIndicatorStyles.smallVertical : activeIndicatorStyles.smallHorizontal), selected && size === 'medium' && (vertical ? activeIndicatorStyles.mediumVertical : activeIndicatorStyles.mediumHorizontal), selected && size === 'large' && (vertical ? activeIndicatorStyles.largeVertical : activeIndicatorStyles.largeHorizontal), selected && disabled && activeIndicatorStyles.disabled);
}
state.root.className = mergeClasses(...classes, state.root.className);
useTabAnimatedIndicatorStyles_unstable(state);
return state;
};
/**
* Applies styles to the Tab button slot based on its current state.
*
* This hook is typically used internally by `useTabStyles_unstable`. You should
* only use it directly if you're creating a custom `Tab` component.
*
* @param state - The Tab component's current state
* @param slot - The button slot of the Tab component
* @returns The state object with updated button styles
*/
export const useTabButtonStyles_unstable = (state, slot) => {
'use no memo';
const rootStyles = useRootStyles();
const focusStyles = useFocusStyles();
const circularStyles = useCircularAppearanceStyles();
const {
appearance,
disabled,
selected,
size,
vertical
} = state;
const isSubtleCircular = appearance === 'subtle-circular';
const isFilledCircular = appearance === 'filled-circular';
const isCircular = isSubtleCircular || isFilledCircular;
const circularAppearance = [circularStyles.base, focusStyles.circular,
// sizes
size === 'small' && circularStyles.small, size === 'medium' && circularStyles.medium, size === 'large' && circularStyles.large,
// subtle-circular appearance
isSubtleCircular && circularStyles.subtle, selected && isSubtleCircular && circularStyles.subtleSelected, disabled && isSubtleCircular && circularStyles.subtleDisabled, selected && disabled && isSubtleCircular && circularStyles.subtleDisabledSelected,
// filled-circular appearance
isFilledCircular && circularStyles.filled, selected && isFilledCircular && circularStyles.filledSelected, disabled && isFilledCircular && circularStyles.filledDisabled, selected && disabled && isFilledCircular && circularStyles.filledDisabledSelected];
const regularAppearance = [focusStyles.base, !disabled && appearance === 'subtle' && rootStyles.subtle, !disabled && appearance === 'transparent' && rootStyles.transparent, !disabled && selected && rootStyles.selected, disabled && rootStyles.disabled];
slot.className = mergeClasses(rootStyles.button,
// orientation
vertical ? rootStyles.vertical : rootStyles.horizontal,
// size
size === 'small' && (vertical ? rootStyles.smallVertical : rootStyles.smallHorizontal), size === 'medium' && (vertical ? rootStyles.mediumVertical : rootStyles.mediumHorizontal), size === 'large' && (vertical ? rootStyles.largeVertical : rootStyles.largeHorizontal), ...(isCircular ? circularAppearance : regularAppearance), disabled && rootStyles.disabledCursor, slot.className);
return state;
};
/**
* Applies styles to the Tab content slot based on its current state.
*
* This hook is typically used internally by `useTabStyles_unstable`. You should
* only use it directly if you're creating a custom `Tab` component.
*
* @param state - The Tab component's current state
* @returns The state object with updated content styles
*/
export const useTabContentStyles_unstable = state => {
'use no memo';
const iconStyles = useIconStyles();
const contentStyles = useContentStyles();
const {
selected,
size
} = state;
if (state.icon) {
state.icon.className = mergeClasses(tabClassNames.icon, iconStyles.base, iconStyles[size], selected && iconStyles.selected, state.icon.className);
}
// This needs to be before state.content.className is updated
if (state.contentReservedSpace) {
state.contentReservedSpace.className = mergeClasses(tabReservedSpaceClassNames.content, contentStyles.base, size === 'large' ? contentStyles.largeSelected : contentStyles.selected, state.icon ? contentStyles.iconBefore : contentStyles.noIconBefore, contentStyles.placeholder, state.content.className);
// FIXME: this is a deprecated API
// should be removed in the next major version
// eslint-disable-next-line @typescript-eslint/no-deprecated
state.contentReservedSpaceClassName = state.contentReservedSpace.className;
}
state.content.className = mergeClasses(tabClassNames.content, contentStyles.base, size === 'large' && contentStyles.large, selected && (size === 'large' ? contentStyles.largeSelected : contentStyles.selected), state.icon ? contentStyles.iconBefore : contentStyles.noIconBefore, state.content.className);
return state;
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,685 @@
'use client';
import { makeStyles, mergeClasses, shorthands } from '@griffel/react';
import { createCustomFocusIndicatorStyle } from '@fluentui/react-tabster';
import { tokens, typographyStyles } from '@fluentui/react-theme';
import { useTabAnimatedIndicatorStyles_unstable } from './useTabAnimatedIndicator.styles';
export const tabClassNames = {
root: 'fui-Tab',
icon: 'fui-Tab__icon',
content: 'fui-Tab__content'
};
export const tabReservedSpaceClassNames = {
content: 'fui-Tab__content--reserved-space'
};
// These should match the constants defined in @fluentui/react-icons
// This package avoids taking a dependency on the icons package for only the constants.
const iconClassNames = {
filled: 'fui-Icon-filled',
regular: 'fui-Icon-regular'
};
/**
* Styles for the root slot
*/ const useRootStyles = makeStyles({
root: {
alignItems: 'center',
display: 'grid',
flexShrink: 0,
gridAutoFlow: 'column',
gridTemplateColumns: 'auto',
gridTemplateRows: 'auto',
outlineStyle: 'none',
position: 'relative'
},
button: {
alignItems: 'center',
border: 'none',
borderRadius: tokens.borderRadiusMedium,
cursor: 'pointer',
display: 'grid',
flexShrink: 0,
gridAutoFlow: 'column',
gridTemplateColumns: 'auto',
gridTemplateRows: 'auto',
fontFamily: tokens.fontFamilyBase,
lineHeight: tokens.lineHeightBase300,
outlineStyle: 'none',
position: 'relative',
overflow: 'hidden',
textTransform: 'none'
},
horizontal: {
justifyContent: 'center'
},
vertical: {
justifyContent: 'start'
},
smallHorizontal: {
columnGap: tokens.spacingHorizontalXXS,
padding: `${tokens.spacingVerticalSNudge} ${tokens.spacingHorizontalSNudge}`
},
smallVertical: {
// horizontal spacing is deliberate. This is the gap between icon and content.
columnGap: tokens.spacingHorizontalXXS,
padding: `${tokens.spacingVerticalXXS} ${tokens.spacingHorizontalSNudge}`
},
mediumHorizontal: {
columnGap: tokens.spacingHorizontalSNudge,
padding: `${tokens.spacingVerticalM} ${tokens.spacingHorizontalMNudge}`
},
mediumVertical: {
// horizontal spacing is deliberate. This is the gap between icon and content.
columnGap: tokens.spacingHorizontalSNudge,
padding: `${tokens.spacingVerticalSNudge} ${tokens.spacingHorizontalMNudge}`
},
largeHorizontal: {
columnGap: tokens.spacingHorizontalSNudge,
padding: `${tokens.spacingVerticalL} ${tokens.spacingHorizontalMNudge}`
},
largeVertical: {
// horizontal spacing is deliberate. This is the gap between icon and content.
columnGap: tokens.spacingHorizontalSNudge,
padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalMNudge}`
},
transparent: {
backgroundColor: tokens.colorTransparentBackground,
':enabled:hover': {
backgroundColor: tokens.colorTransparentBackgroundHover
},
':enabled:active': {
backgroundColor: tokens.colorTransparentBackgroundPressed
},
[`& .${tabClassNames.icon}`]: {
color: tokens.colorNeutralForeground2
},
[`:enabled:hover .${tabClassNames.icon}`]: {
color: tokens.colorNeutralForeground2Hover
},
[`:enabled:active .${tabClassNames.icon}`]: {
color: tokens.colorNeutralForeground2Pressed
},
[`& .${tabClassNames.content}`]: {
color: tokens.colorNeutralForeground2
},
[`:enabled:hover .${tabClassNames.content}`]: {
color: tokens.colorNeutralForeground2Hover
},
[`:enabled:active .${tabClassNames.content}`]: {
color: tokens.colorNeutralForeground2Pressed
}
},
subtle: {
backgroundColor: tokens.colorSubtleBackground,
':enabled:hover': {
backgroundColor: tokens.colorSubtleBackgroundHover
},
':enabled:active': {
backgroundColor: tokens.colorSubtleBackgroundPressed
},
[`& .${tabClassNames.icon}`]: {
color: tokens.colorNeutralForeground2
},
[`:enabled:hover .${tabClassNames.icon}`]: {
color: tokens.colorNeutralForeground2Hover
},
[`:enabled:active .${tabClassNames.icon}`]: {
color: tokens.colorNeutralForeground2Pressed
},
[`& .${tabClassNames.content}`]: {
color: tokens.colorNeutralForeground2
},
[`:enabled:hover .${tabClassNames.content}`]: {
color: tokens.colorNeutralForeground2Hover
},
[`:enabled:active .${tabClassNames.content}`]: {
color: tokens.colorNeutralForeground2Pressed
}
},
disabledCursor: {
cursor: 'not-allowed'
},
disabled: {
backgroundColor: tokens.colorTransparentBackground,
[`& .${tabClassNames.icon}`]: {
color: tokens.colorNeutralForegroundDisabled
},
[`& .${tabClassNames.content}`]: {
color: tokens.colorNeutralForegroundDisabled
}
},
selected: {
[`& .${tabClassNames.icon}`]: {
color: tokens.colorCompoundBrandForeground1
},
[`:enabled:hover .${tabClassNames.icon}`]: {
color: tokens.colorCompoundBrandForeground1Hover
},
[`:enabled:active .${tabClassNames.icon}`]: {
color: tokens.colorCompoundBrandForeground1Pressed
},
[`& .${tabClassNames.content}`]: {
color: tokens.colorNeutralForeground1
},
[`:enabled:hover .${tabClassNames.content}`]: {
color: tokens.colorNeutralForeground1Hover
},
[`:enabled:active .${tabClassNames.content}`]: {
color: tokens.colorNeutralForeground1Pressed
}
}
});
const useCircularAppearanceStyles = makeStyles({
base: {
borderRadius: tokens.borderRadiusCircular,
border: `solid ${tokens.strokeWidthThin} ${tokens.colorTransparentStroke}`,
[`& .${tabClassNames.icon}`]: {
color: 'inherit'
},
[`& .${tabClassNames.content}`]: {
color: 'inherit'
}
},
small: {
paddingBlock: `calc(${tokens.spacingVerticalXXS} - ${tokens.strokeWidthThin})`
},
medium: {
paddingBlock: `calc(${tokens.spacingVerticalSNudge} - ${tokens.strokeWidthThin})`
},
large: {
paddingBlock: `calc(${tokens.spacingVerticalS} - ${tokens.strokeWidthThin})`
},
subtle: {
backgroundColor: tokens.colorSubtleBackground,
color: tokens.colorNeutralForeground2,
':enabled:hover': {
backgroundColor: tokens.colorSubtleBackgroundHover,
border: `solid ${tokens.strokeWidthThin} ${tokens.colorNeutralStroke1Hover}`,
color: tokens.colorNeutralForeground2Hover
},
':enabled:active': {
backgroundColor: tokens.colorSubtleBackgroundPressed,
border: `solid ${tokens.strokeWidthThin} ${tokens.colorNeutralStroke1Pressed}`,
color: tokens.colorNeutralForeground2Pressed
},
'@media (forced-colors: active)': {
border: `solid ${tokens.strokeWidthThin} Canvas`
}
},
subtleSelected: {
backgroundColor: tokens.colorBrandBackground2,
border: `solid ${tokens.strokeWidthThin} ${tokens.colorCompoundBrandStroke}`,
color: tokens.colorBrandForeground2,
':enabled:hover': {
backgroundColor: tokens.colorBrandBackground2Hover,
border: `solid ${tokens.strokeWidthThin} ${tokens.colorCompoundBrandStrokeHover}`,
color: tokens.colorBrandForeground2Hover
},
':enabled:active': {
backgroundColor: tokens.colorBrandBackground2Pressed,
border: `solid ${tokens.strokeWidthThin} ${tokens.colorCompoundBrandStrokePressed}`,
color: tokens.colorBrandForeground2Pressed
},
'@media (forced-colors: active)': {
border: `solid ${tokens.strokeWidthThin} Highlight`
}
},
subtleDisabled: {
backgroundColor: tokens.colorSubtleBackground,
color: tokens.colorNeutralForegroundDisabled
},
subtleDisabledSelected: {
backgroundColor: tokens.colorNeutralBackgroundDisabled,
border: `solid ${tokens.strokeWidthThin} ${tokens.colorNeutralStrokeDisabled}`,
color: tokens.colorNeutralForegroundDisabled
},
filled: {
backgroundColor: tokens.colorNeutralBackground3,
color: tokens.colorNeutralForeground2,
':enabled:hover': {
backgroundColor: tokens.colorNeutralBackground3Hover,
color: tokens.colorNeutralForeground2Hover
},
':enabled:active': {
backgroundColor: tokens.colorNeutralBackground3Pressed,
color: tokens.colorNeutralForeground2Pressed
},
'@media (forced-colors: active)': {
':enabled:hover': {
backgroundColor: 'Highlight',
forcedColorAdjust: 'none',
[`& .${tabClassNames.content}`]: {
color: 'HighlightText'
},
[`& .${iconClassNames.filled}`]: {
color: 'HighlightText'
},
[`& .${iconClassNames.regular}`]: {
color: 'HighlightText'
}
}
}
},
filledSelected: {
backgroundColor: tokens.colorBrandBackground,
color: tokens.colorNeutralForegroundOnBrand,
':enabled:hover': {
backgroundColor: tokens.colorBrandBackgroundHover,
color: tokens.colorNeutralForegroundOnBrand
},
':enabled:active': {
backgroundColor: tokens.colorBrandBackgroundPressed,
color: tokens.colorNeutralForegroundOnBrand
},
'@media (forced-colors: active)': {
':enabled': {
backgroundColor: 'ButtonText',
[`& .${tabClassNames.content}`]: {
color: 'ButtonFace',
forcedColorAdjust: 'none'
}
},
[`:enabled .${tabClassNames.icon}`]: {
color: 'ButtonFace'
}
}
},
filledDisabled: {
backgroundColor: tokens.colorNeutralBackgroundDisabled,
color: tokens.colorNeutralForegroundDisabled
},
filledDisabledSelected: {
backgroundColor: tokens.colorNeutralBackgroundDisabled,
border: `solid ${tokens.strokeWidthThin} ${tokens.colorNeutralStrokeDisabled}`,
color: tokens.colorNeutralForegroundDisabled
}
});
/**
* Focus styles for the root slot
*/ const useFocusStyles = makeStyles({
// Tab creates a custom focus indicator because the default focus indicator
// is applied using an ::after pseudo-element on the root. Since the selection
// indicator uses an ::after pseudo-element on the root, there is a conflict.
base: createCustomFocusIndicatorStyle({
...shorthands.borderColor('transparent'),
outlineWidth: tokens.strokeWidthThick,
outlineColor: 'transparent',
outlineStyle: 'solid',
boxShadow: `
${tokens.shadow4},
0 0 0 ${tokens.strokeWidthThick} ${tokens.colorStrokeFocus2}
`,
zIndex: 1
}, {
enableOutline: true
}),
circular: createCustomFocusIndicatorStyle({
...shorthands.borderColor('transparent'),
outlineWidth: tokens.strokeWidthThick,
outlineColor: 'transparent',
outlineStyle: 'solid',
boxShadow: `
${tokens.shadow4},
0 0 0 ${tokens.strokeWidthThick} ${tokens.colorStrokeFocus2},
0 0 0 ${tokens.strokeWidthThin} ${tokens.colorNeutralStrokeOnBrand} inset
`,
zIndex: 1
}, {
enableOutline: true
})
});
/** Indicator styles for when pending selection */ const usePendingIndicatorStyles = makeStyles({
base: {
':hover::before': {
backgroundColor: tokens.colorNeutralStroke1Hover,
borderRadius: tokens.borderRadiusCircular,
content: '""',
position: 'absolute'
},
':active::before': {
backgroundColor: tokens.colorNeutralStroke1Pressed,
borderRadius: tokens.borderRadiusCircular,
content: '""',
position: 'absolute'
},
'@media (forced-colors: active)': {
':hover::before': {
backgroundColor: 'Highlight'
},
':active::before': {
backgroundColor: 'Highlight'
}
}
},
disabled: {
':hover::before': {
backgroundColor: tokens.colorTransparentStroke
},
':active::before': {
backgroundColor: tokens.colorTransparentStroke
},
'@media (forced-colors: active)': {
':hover::before': {
backgroundColor: 'transparent'
},
':active::before': {
backgroundColor: 'transparent'
}
}
},
smallHorizontal: {
'::before': {
bottom: 0,
height: tokens.strokeWidthThick,
left: tokens.spacingHorizontalSNudge,
right: tokens.spacingHorizontalSNudge
}
},
smallVertical: {
'::before': {
bottom: tokens.spacingVerticalXS,
left: 0,
top: tokens.spacingVerticalXS,
width: tokens.strokeWidthThicker
}
},
mediumHorizontal: {
'::before': {
bottom: 0,
height: tokens.strokeWidthThicker,
left: tokens.spacingHorizontalM,
right: tokens.spacingHorizontalM
}
},
mediumVertical: {
'::before': {
bottom: tokens.spacingVerticalS,
left: 0,
top: tokens.spacingVerticalS,
width: tokens.strokeWidthThicker
}
},
largeHorizontal: {
'::before': {
bottom: 0,
height: tokens.strokeWidthThicker,
left: tokens.spacingHorizontalM,
right: tokens.spacingHorizontalM
}
},
largeVertical: {
'::before': {
bottom: tokens.spacingVerticalMNudge,
left: 0,
top: tokens.spacingVerticalMNudge,
width: tokens.strokeWidthThicker
}
}
});
const useActiveIndicatorStyles = makeStyles({
base: {
'::after': {
backgroundColor: tokens.colorTransparentStroke,
borderRadius: tokens.borderRadiusCircular,
content: '""',
position: 'absolute'
}
},
selected: {
'::after': {
backgroundColor: tokens.colorCompoundBrandStroke
},
':enabled:hover::after': {
backgroundColor: tokens.colorCompoundBrandStrokeHover
},
':enabled:active::after': {
backgroundColor: tokens.colorCompoundBrandStrokePressed
},
'@media (forced-colors: active)': {
'::after': {
backgroundColor: 'ButtonText'
},
':enabled:hover::after': {
backgroundColor: 'ButtonText'
},
':enabled:active::after': {
backgroundColor: 'ButtonText'
}
}
},
disabled: {
'::after': {
backgroundColor: tokens.colorNeutralForegroundDisabled
}
},
smallHorizontal: {
'::after': {
bottom: 0,
height: tokens.strokeWidthThick,
left: tokens.spacingHorizontalSNudge,
right: tokens.spacingHorizontalSNudge
}
},
smallVertical: {
'::after': {
bottom: tokens.spacingVerticalXS,
left: '0',
top: tokens.spacingVerticalXS,
width: tokens.strokeWidthThicker
}
},
mediumHorizontal: {
'::after': {
bottom: '0',
height: tokens.strokeWidthThicker,
left: tokens.spacingHorizontalM,
right: tokens.spacingHorizontalM
}
},
mediumVertical: {
'::after': {
bottom: tokens.spacingVerticalS,
left: 0,
top: tokens.spacingVerticalS,
width: tokens.strokeWidthThicker
}
},
largeHorizontal: {
'::after': {
bottom: 0,
height: tokens.strokeWidthThicker,
left: tokens.spacingHorizontalM,
right: tokens.spacingHorizontalM
}
},
largeVertical: {
'::after': {
bottom: tokens.spacingVerticalMNudge,
left: 0,
top: tokens.spacingVerticalMNudge,
width: tokens.strokeWidthThicker
}
}
});
/**
* Styles for the icon slot.
*/ const useIconStyles = makeStyles({
base: {
gridColumnStart: 1,
gridRowStart: 1,
alignItems: 'center',
display: 'inline-flex',
justifyContent: 'center',
overflow: 'hidden',
[`& .${iconClassNames.filled}`]: {
display: 'none'
},
[`& .${iconClassNames.regular}`]: {
display: 'inline'
}
},
// per design, the small and medium font sizes are the same.
// the size prop only affects spacing.
small: {
fontSize: '20px',
height: '20px',
width: '20px'
},
medium: {
fontSize: '20px',
height: '20px',
width: '20px'
},
large: {
fontSize: '24px',
height: '24px',
width: '24px'
},
selected: {
[`& .${iconClassNames.filled}`]: {
display: 'inline'
},
[`& .${iconClassNames.regular}`]: {
display: 'none'
}
}
});
/**
* Styles for the content slot (children)
*/ const useContentStyles = makeStyles({
base: {
...typographyStyles.body1,
overflow: 'hidden',
// content padding is the same for medium & small, horizontal & vertical
padding: `${tokens.spacingVerticalNone} ${tokens.spacingHorizontalXXS}`
},
selected: {
...typographyStyles.body1Strong
},
large: {
...typographyStyles.body2
},
largeSelected: {
...typographyStyles.subtitle2
},
noIconBefore: {
gridColumnStart: 1,
gridRowStart: 1
},
iconBefore: {
gridColumnStart: 2,
gridRowStart: 1
},
placeholder: {
visibility: 'hidden'
}
});
/**
* Apply styling to the Tab slots based on the state
*/ export const useTabStyles_unstable = (state)=>{
'use no memo';
useTabIndicatorStyles_unstable(state);
useTabButtonStyles_unstable(state, state.root);
useTabContentStyles_unstable(state);
return state;
};
/**
* Applies styles for the Tab indicator based on its current state.
*
* This hook is typically used internally by `useTabStyles_unstable`. You should
* only use it directly if you're creating a custom `Tab` component.
*
* @param state - The `Tab` component's current state
* @returns The state object with updated button styles
*/ export const useTabIndicatorStyles_unstable = (state)=>{
'use no memo';
const rootStyles = useRootStyles();
const pendingIndicatorStyles = usePendingIndicatorStyles();
const activeIndicatorStyles = useActiveIndicatorStyles();
const { appearance, disabled, selected, size, vertical } = state;
const classes = [
tabClassNames.root,
rootStyles.root
];
if (appearance !== 'subtle-circular' && appearance !== 'filled-circular') {
classes.push(// pending indicator (before pseudo element)
pendingIndicatorStyles.base, size === 'small' && (vertical ? pendingIndicatorStyles.smallVertical : pendingIndicatorStyles.smallHorizontal), size === 'medium' && (vertical ? pendingIndicatorStyles.mediumVertical : pendingIndicatorStyles.mediumHorizontal), size === 'large' && (vertical ? pendingIndicatorStyles.largeVertical : pendingIndicatorStyles.largeHorizontal), disabled && pendingIndicatorStyles.disabled, // active indicator (after pseudo element)
selected && activeIndicatorStyles.base, selected && !disabled && activeIndicatorStyles.selected, selected && size === 'small' && (vertical ? activeIndicatorStyles.smallVertical : activeIndicatorStyles.smallHorizontal), selected && size === 'medium' && (vertical ? activeIndicatorStyles.mediumVertical : activeIndicatorStyles.mediumHorizontal), selected && size === 'large' && (vertical ? activeIndicatorStyles.largeVertical : activeIndicatorStyles.largeHorizontal), selected && disabled && activeIndicatorStyles.disabled);
}
state.root.className = mergeClasses(...classes, state.root.className);
useTabAnimatedIndicatorStyles_unstable(state);
return state;
};
/**
* Applies styles to the Tab button slot based on its current state.
*
* This hook is typically used internally by `useTabStyles_unstable`. You should
* only use it directly if you're creating a custom `Tab` component.
*
* @param state - The Tab component's current state
* @param slot - The button slot of the Tab component
* @returns The state object with updated button styles
*/ export const useTabButtonStyles_unstable = (state, slot)=>{
'use no memo';
const rootStyles = useRootStyles();
const focusStyles = useFocusStyles();
const circularStyles = useCircularAppearanceStyles();
const { appearance, disabled, selected, size, vertical } = state;
const isSubtleCircular = appearance === 'subtle-circular';
const isFilledCircular = appearance === 'filled-circular';
const isCircular = isSubtleCircular || isFilledCircular;
const circularAppearance = [
circularStyles.base,
focusStyles.circular,
// sizes
size === 'small' && circularStyles.small,
size === 'medium' && circularStyles.medium,
size === 'large' && circularStyles.large,
// subtle-circular appearance
isSubtleCircular && circularStyles.subtle,
selected && isSubtleCircular && circularStyles.subtleSelected,
disabled && isSubtleCircular && circularStyles.subtleDisabled,
selected && disabled && isSubtleCircular && circularStyles.subtleDisabledSelected,
// filled-circular appearance
isFilledCircular && circularStyles.filled,
selected && isFilledCircular && circularStyles.filledSelected,
disabled && isFilledCircular && circularStyles.filledDisabled,
selected && disabled && isFilledCircular && circularStyles.filledDisabledSelected
];
const regularAppearance = [
focusStyles.base,
!disabled && appearance === 'subtle' && rootStyles.subtle,
!disabled && appearance === 'transparent' && rootStyles.transparent,
!disabled && selected && rootStyles.selected,
disabled && rootStyles.disabled
];
slot.className = mergeClasses(rootStyles.button, // orientation
vertical ? rootStyles.vertical : rootStyles.horizontal, // size
size === 'small' && (vertical ? rootStyles.smallVertical : rootStyles.smallHorizontal), size === 'medium' && (vertical ? rootStyles.mediumVertical : rootStyles.mediumHorizontal), size === 'large' && (vertical ? rootStyles.largeVertical : rootStyles.largeHorizontal), ...isCircular ? circularAppearance : regularAppearance, disabled && rootStyles.disabledCursor, slot.className);
return state;
};
/**
* Applies styles to the Tab content slot based on its current state.
*
* This hook is typically used internally by `useTabStyles_unstable`. You should
* only use it directly if you're creating a custom `Tab` component.
*
* @param state - The Tab component's current state
* @returns The state object with updated content styles
*/ export const useTabContentStyles_unstable = (state)=>{
'use no memo';
const iconStyles = useIconStyles();
const contentStyles = useContentStyles();
const { selected, size } = state;
if (state.icon) {
state.icon.className = mergeClasses(tabClassNames.icon, iconStyles.base, iconStyles[size], selected && iconStyles.selected, state.icon.className);
}
// This needs to be before state.content.className is updated
if (state.contentReservedSpace) {
state.contentReservedSpace.className = mergeClasses(tabReservedSpaceClassNames.content, contentStyles.base, size === 'large' ? contentStyles.largeSelected : contentStyles.selected, state.icon ? contentStyles.iconBefore : contentStyles.noIconBefore, contentStyles.placeholder, state.content.className);
// FIXME: this is a deprecated API
// should be removed in the next major version
// eslint-disable-next-line @typescript-eslint/no-deprecated
state.contentReservedSpaceClassName = state.contentReservedSpace.className;
}
state.content.className = mergeClasses(tabClassNames.content, contentStyles.base, size === 'large' && contentStyles.large, selected && (size === 'large' ? contentStyles.largeSelected : contentStyles.selected), state.icon ? contentStyles.iconBefore : contentStyles.noIconBefore, state.content.className);
return state;
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,17 @@
'use client';
import * as React from 'react';
import { useTabList_unstable } from './useTabList';
import { renderTabList_unstable } from './renderTabList';
import { useTabListStyles_unstable } from './useTabListStyles.styles';
import { useTabListContextValues_unstable } from './useTabListContextValues';
import { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts';
/**
* A tab list provides single selection from a set of tabs.
*/ export const TabList = /*#__PURE__*/ React.forwardRef((props, ref)=>{
const state = useTabList_unstable(props, ref);
const contextValues = useTabListContextValues_unstable(state);
useTabListStyles_unstable(state);
useCustomStyleHook_unstable('useTabListStyles_unstable')(state);
return renderTabList_unstable(state, contextValues);
});
TabList.displayName = 'TabList';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/TabList/TabList.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { useTabList_unstable } from './useTabList';\nimport { renderTabList_unstable } from './renderTabList';\nimport { useTabListStyles_unstable } from './useTabListStyles.styles';\nimport type { TabListProps } from './TabList.types';\nimport type { ForwardRefComponent } from '@fluentui/react-utilities';\nimport { useTabListContextValues_unstable } from './useTabListContextValues';\nimport { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts';\n\n/**\n * A tab list provides single selection from a set of tabs.\n */\nexport const TabList: ForwardRefComponent<TabListProps> = React.forwardRef((props, ref) => {\n const state = useTabList_unstable(props, ref);\n const contextValues = useTabListContextValues_unstable(state);\n\n useTabListStyles_unstable(state);\n\n useCustomStyleHook_unstable('useTabListStyles_unstable')(state);\n\n return renderTabList_unstable(state, contextValues);\n});\n\nTabList.displayName = 'TabList';\n"],"names":["React","useTabList_unstable","renderTabList_unstable","useTabListStyles_unstable","useTabListContextValues_unstable","useCustomStyleHook_unstable","TabList","forwardRef","props","ref","state","contextValues","displayName"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAC/B,SAASC,mBAAmB,QAAQ,eAAe;AACnD,SAASC,sBAAsB,QAAQ,kBAAkB;AACzD,SAASC,yBAAyB,QAAQ,4BAA4B;AAGtE,SAASC,gCAAgC,QAAQ,4BAA4B;AAC7E,SAASC,2BAA2B,QAAQ,kCAAkC;AAE9E;;CAEC,GACD,OAAO,MAAMC,wBAA6CN,MAAMO,UAAU,CAAC,CAACC,OAAOC;IACjF,MAAMC,QAAQT,oBAAoBO,OAAOC;IACzC,MAAME,gBAAgBP,iCAAiCM;IAEvDP,0BAA0BO;IAE1BL,4BAA4B,6BAA6BK;IAEzD,OAAOR,uBAAuBQ,OAAOC;AACvC,GAAG;AAEHL,QAAQM,WAAW,GAAG"}

View File

@@ -0,0 +1 @@
import * as React from 'react';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/TabList/TabList.types.ts"],"sourcesContent":["import * as React from 'react';\nimport type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities';\nimport { TabValue } from '../Tab/Tab.types';\n\nexport type TabRegisterData = {\n /**\n * The value of the tab.\n */\n value: TabValue;\n\n /**\n * The reference to the tab HTML element.\n */\n ref: React.RefObject<HTMLElement | null>;\n};\n\nexport type RegisterTabEventHandler = (data: TabRegisterData) => void;\n\nexport type SelectTabData = {\n /**\n * The value of the selected tab.\n */\n value: TabValue;\n};\n\nexport type SelectTabEvent<E = HTMLElement> = React.MouseEvent<E> | React.KeyboardEvent<E> | React.FocusEvent<E>;\n\nexport type SelectTabEventHandler = (event: SelectTabEvent, data: SelectTabData) => void;\n\nexport type TabListSlots = {\n /**\n * The slot associated with the root element of this tab list.\n */\n root: Slot<'div'>;\n};\n\n/**\n * TabList Props\n */\nexport type TabListProps = ComponentProps<TabListSlots> & {\n /**\n * A tab list can supports 'transparent' and 'subtle' appearance.\n *- 'subtle': Minimizes emphasis to blend into the background until hovered or focused.\n *- 'transparent': No background and border styling\n *- 'subtle-circular': Adds background and border styling\n *- 'filled-circular': Adds background styling\n *\n * The appearance affects each of the contained tabs.\n * @default 'transparent'\n */\n appearance?: 'transparent' | 'subtle' | 'subtle-circular' | 'filled-circular';\n\n /**\n * Tab size may change between unselected and selected states.\n * The default scenario is a selected tab has bold text.\n *\n * When true, this property requests tabs be the same size whether unselected or selected.\n * @default true\n */\n reserveSelectedTabSpace?: boolean;\n\n /**\n * The value of the tab to be selected by default.\n * Typically useful when the selectedValue is uncontrolled.\n */\n defaultSelectedValue?: TabValue;\n\n /**\n * A tab list can be set to disable interaction.\n * @default false\n */\n disabled?: boolean;\n\n /**\n * Raised when a tab is selected.\n */\n // eslint-disable-next-line @nx/workspace-consistent-callback-type -- can't change type of existing callback\n onTabSelect?: SelectTabEventHandler;\n\n /**\n * When true, focusing a tab will select it.\n * @default false\n */\n selectTabOnFocus?: boolean;\n\n /**\n * The value of the currently selected tab.\n */\n selectedValue?: TabValue;\n\n /**\n * A tab list can be either 'small', 'medium', or 'large' size.\n * The size affects each of the contained tabs.\n * @default 'medium'\n */\n size?: 'small' | 'medium' | 'large';\n\n /**\n * A tab list can arrange its tabs vertically.\n * @default false\n */\n vertical?: boolean;\n};\n\nexport type TabListBaseProps = Omit<TabListProps, 'appearance' | 'size' | 'reserveSelectedTabSpace'>;\n\nexport type TabListContextValue = Pick<\n TabListProps,\n 'onTabSelect' | 'selectTabOnFocus' | 'selectedValue' | 'reserveSelectedTabSpace'\n> &\n Required<Pick<TabListProps, 'appearance' | 'disabled' | 'size' | 'vertical'>> & {\n /** A callback to allow a tab to register itself with the tab list. */\n onRegister: RegisterTabEventHandler;\n\n /** A callback to allow a tab to unregister itself with the tab list. */\n onUnregister: RegisterTabEventHandler;\n /**\n * A callback to allow a tab to select itself when pressed.\n */\n onSelect: SelectTabEventHandler;\n /**\n * Gets the registered tab data along with current and previous selected values.\n */\n getRegisteredTabs: () => {\n selectedValue?: TabValue;\n previousSelectedValue?: TabValue;\n registeredTabs: Record<string, TabRegisterData>;\n };\n };\n\n/**\n * Context values used in rendering TabList.\n */\nexport type TabListContextValues = {\n /**\n * The context of the tab list available to each tab.\n */\n tabList: TabListContextValue;\n};\n\n/**\n * State used in rendering TabList.\n */\nexport type TabListState = ComponentState<Required<TabListSlots>> & TabListContextValue;\n\nexport type TabListBaseState = Omit<TabListState, 'appearance' | 'size' | 'reserveSelectedTabSpace'>;\n"],"names":["React"],"mappings":"AAAA,YAAYA,WAAW,QAAQ"}

View File

@@ -0,0 +1,25 @@
'use client';
import { createContext, useContextSelector } from '@fluentui/react-context-selector';
const tabListContextDefaultValue = {
appearance: 'transparent',
reserveSelectedTabSpace: true,
selectTabOnFocus: false,
disabled: false,
selectedValue: undefined,
onRegister: ()=>{
/* noop */ },
onUnregister: ()=>{
/* noop */ },
onSelect: ()=>{
/* noop */ },
getRegisteredTabs: ()=>{
return {
registeredTabs: {}
};
},
size: 'medium',
vertical: false
};
export const TabListContext = createContext(undefined);
export const TabListProvider = TabListContext.Provider;
export const useTabListContext_unstable = (selector)=>useContextSelector(TabListContext, (ctx = tabListContextDefaultValue)=>selector(ctx));

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/TabList/TabListContext.ts"],"sourcesContent":["'use client';\n\nimport { createContext, useContextSelector } from '@fluentui/react-context-selector';\nimport type { Context, ContextSelector } from '@fluentui/react-context-selector';\nimport { TabListContextValue } from './TabList.types';\n\nconst tabListContextDefaultValue: TabListContextValue = {\n appearance: 'transparent',\n reserveSelectedTabSpace: true,\n selectTabOnFocus: false,\n disabled: false,\n selectedValue: undefined,\n onRegister: () => {\n /* noop */\n },\n onUnregister: () => {\n /* noop */\n },\n onSelect: () => {\n /* noop */\n },\n getRegisteredTabs: () => {\n return {\n registeredTabs: {},\n };\n },\n size: 'medium',\n vertical: false,\n};\n\nexport const TabListContext: Context<TabListContextValue> = createContext<TabListContextValue | undefined>(\n undefined,\n) as Context<TabListContextValue>;\n\nexport const TabListProvider = TabListContext.Provider;\nexport const useTabListContext_unstable = <T>(selector: ContextSelector<TabListContextValue, T>): T =>\n useContextSelector(TabListContext, (ctx = tabListContextDefaultValue) => selector(ctx));\n"],"names":["createContext","useContextSelector","tabListContextDefaultValue","appearance","reserveSelectedTabSpace","selectTabOnFocus","disabled","selectedValue","undefined","onRegister","onUnregister","onSelect","getRegisteredTabs","registeredTabs","size","vertical","TabListContext","TabListProvider","Provider","useTabListContext_unstable","selector","ctx"],"mappings":"AAAA;AAEA,SAASA,aAAa,EAAEC,kBAAkB,QAAQ,mCAAmC;AAIrF,MAAMC,6BAAkD;IACtDC,YAAY;IACZC,yBAAyB;IACzBC,kBAAkB;IAClBC,UAAU;IACVC,eAAeC;IACfC,YAAY;IACV,QAAQ,GACV;IACAC,cAAc;IACZ,QAAQ,GACV;IACAC,UAAU;IACR,QAAQ,GACV;IACAC,mBAAmB;QACjB,OAAO;YACLC,gBAAgB,CAAC;QACnB;IACF;IACAC,MAAM;IACNC,UAAU;AACZ;AAEA,OAAO,MAAMC,iBAA+ChB,cAC1DQ,WACgC;AAElC,OAAO,MAAMS,kBAAkBD,eAAeE,QAAQ,CAAC;AACvD,OAAO,MAAMC,6BAA6B,CAAIC,WAC5CnB,mBAAmBe,gBAAgB,CAACK,MAAMnB,0BAA0B,GAAKkB,SAASC,MAAM"}

View File

@@ -0,0 +1,6 @@
export { TabList } from './TabList';
export { TabListContext, TabListProvider, useTabListContext_unstable } from './TabListContext';
export { renderTabList_unstable } from './renderTabList';
export { useTabList_unstable, useTabListBase_unstable, useTabListA11yBehavior_unstable } from './useTabList';
export { useTabListContextValues_unstable } from './useTabListContextValues';
export { tabListClassNames, useTabListStyles_unstable } from './useTabListStyles.styles';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/TabList/index.ts"],"sourcesContent":["export { TabList } from './TabList';\nexport type {\n RegisterTabEventHandler,\n SelectTabData,\n SelectTabEvent,\n SelectTabEventHandler,\n TabListContextValue,\n TabListContextValues,\n TabListBaseProps,\n TabListProps,\n TabListSlots,\n TabListBaseState,\n TabListState,\n TabRegisterData,\n} from './TabList.types';\nexport { TabListContext, TabListProvider, useTabListContext_unstable } from './TabListContext';\nexport { renderTabList_unstable } from './renderTabList';\nexport { useTabList_unstable, useTabListBase_unstable, useTabListA11yBehavior_unstable } from './useTabList';\nexport { useTabListContextValues_unstable } from './useTabListContextValues';\nexport { tabListClassNames, useTabListStyles_unstable } from './useTabListStyles.styles';\n"],"names":["TabList","TabListContext","TabListProvider","useTabListContext_unstable","renderTabList_unstable","useTabList_unstable","useTabListBase_unstable","useTabListA11yBehavior_unstable","useTabListContextValues_unstable","tabListClassNames","useTabListStyles_unstable"],"mappings":"AAAA,SAASA,OAAO,QAAQ,YAAY;AAepC,SAASC,cAAc,EAAEC,eAAe,EAAEC,0BAA0B,QAAQ,mBAAmB;AAC/F,SAASC,sBAAsB,QAAQ,kBAAkB;AACzD,SAASC,mBAAmB,EAAEC,uBAAuB,EAAEC,+BAA+B,QAAQ,eAAe;AAC7G,SAASC,gCAAgC,QAAQ,4BAA4B;AAC7E,SAASC,iBAAiB,EAAEC,yBAAyB,QAAQ,4BAA4B"}

View File

@@ -0,0 +1,14 @@
import { jsx as _jsx } from "@fluentui/react-jsx-runtime/jsx-runtime";
import { assertSlots } from '@fluentui/react-utilities';
import { TabListProvider } from './TabListContext';
/**
* Render the final JSX of TabList
*/ export const renderTabList_unstable = (state, contextValues)=>{
assertSlots(state);
return /*#__PURE__*/ _jsx(state.root, {
children: /*#__PURE__*/ _jsx(TabListProvider, {
value: contextValues.tabList,
children: state.root.children
})
});
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/TabList/renderTabList.tsx"],"sourcesContent":["/** @jsxRuntime automatic */\n/** @jsxImportSource @fluentui/react-jsx-runtime */\nimport { assertSlots } from '@fluentui/react-utilities';\nimport type { JSXElement } from '@fluentui/react-utilities';\nimport type { TabListBaseState, TabListSlots, TabListContextValues } from './TabList.types';\nimport { TabListProvider } from './TabListContext';\n\n/**\n * Render the final JSX of TabList\n */\nexport const renderTabList_unstable = (state: TabListBaseState, contextValues: TabListContextValues): JSXElement => {\n assertSlots<TabListSlots>(state);\n\n return (\n <state.root>\n <TabListProvider value={contextValues.tabList}>{state.root.children}</TabListProvider>\n </state.root>\n );\n};\n"],"names":["assertSlots","TabListProvider","renderTabList_unstable","state","contextValues","root","value","tabList","children"],"mappings":"AAAA,0BAA0B,GAC1B,iDAAiD;AACjD,SAASA,WAAW,QAAQ,4BAA4B;AAGxD,SAASC,eAAe,QAAQ,mBAAmB;AAEnD;;CAEC,GACD,OAAO,MAAMC,yBAAyB,CAACC,OAAyBC;IAC9DJ,YAA0BG;IAE1B,qBACE,KAACA,MAAME,IAAI;kBACT,cAAA,KAACJ;YAAgBK,OAAOF,cAAcG,OAAO;sBAAGJ,MAAME,IAAI,CAACG,QAAQ;;;AAGzE,EAAE"}

View File

@@ -0,0 +1,122 @@
'use client';
import * as React from 'react';
import { useArrowNavigationGroup } from '@fluentui/react-tabster';
import { useControllableState, useEventCallback, useMergedRefs, slot } from '@fluentui/react-utilities';
/**
* Create the state required to render TabList.
*
* The returned state can be modified with hooks such as useTabListStyles_unstable,
* before being passed to renderTabList_unstable.
*
* @param props - props from this instance of TabList
* @param ref - reference to root HTMLElement of TabList
*/ export const useTabList_unstable = (props, ref)=>{
const { appearance = 'transparent', reserveSelectedTabSpace = true, size = 'medium' } = props;
const state = useTabListBase_unstable(props, ref);
const focusAttributes = useTabListA11yBehavior_unstable({
vertical: state.vertical
});
return {
...state,
root: {
...focusAttributes,
...state.root
},
appearance,
reserveSelectedTabSpace,
size
};
};
/**
* Create the state required to render TabList.
*
* The returned state can be modified with hooks such as useTabListStyles_unstable,
* before being passed to renderTabList_unstable.
*
* @param props - props from this instance of TabList
* @param ref - reference to root HTMLElement of TabList
*/ export const useTabListBase_unstable = (props, ref)=>{
const { disabled = false, onTabSelect, selectTabOnFocus = false, vertical = false, selectedValue: controlledSelectedValue, defaultSelectedValue, ...rest } = props;
const innerRef = React.useRef(null);
const [selectedValue, setSelectedValue] = useControllableState({
state: controlledSelectedValue,
defaultState: defaultSelectedValue,
initialState: undefined
});
// considered usePrevious, but it is sensitive to re-renders
// this could cause the previous to move to current in the case where the tab list re-renders.
// these refs avoid getRegisteredTabs changing when selectedValue changes and causing
// renders for tabs that have not changed.
const currentSelectedValue = React.useRef(undefined);
const previousSelectedValue = React.useRef(undefined);
React.useEffect(()=>{
previousSelectedValue.current = currentSelectedValue.current;
currentSelectedValue.current = selectedValue;
}, [
selectedValue
]);
const onSelect = useEventCallback((event, data)=>{
setSelectedValue(data.value);
onTabSelect === null || onTabSelect === void 0 ? void 0 : onTabSelect(event, data);
});
const registeredTabs = React.useRef({});
const onRegister = useEventCallback((data)=>{
const key = JSON.stringify(data.value);
if (!key && process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line no-console
console.error([
`[@fluentui/react-tabs] The value "${data.value}" cannot be serialized to JSON string.`,
'Tab component requires serializable values.',
'Please provide a primitive value (string, number, boolean),',
`or a plain object/array that doesn't contain functions, symbols, or circular references.`
].join(' '));
}
registeredTabs.current[key] = data;
});
const onUnregister = useEventCallback((data)=>{
delete registeredTabs.current[JSON.stringify(data.value)];
});
const getRegisteredTabs = React.useCallback(()=>{
return {
selectedValue: currentSelectedValue.current,
previousSelectedValue: previousSelectedValue.current,
registeredTabs: registeredTabs.current
};
}, []);
return {
components: {
root: 'div'
},
root: slot.always({
ref: useMergedRefs(ref, innerRef),
role: 'tablist',
'aria-orientation': vertical ? 'vertical' : 'horizontal',
...rest
}, {
elementType: 'div'
}),
disabled,
selectTabOnFocus,
selectedValue,
onRegister,
onUnregister,
onSelect,
getRegisteredTabs,
vertical
};
};
/**
* Hook to get accessibility attributes for TabList component, such as roving tab index.
* Based on Tabster's useArrowNavigationGroup.
*
* @param vertical - whether the TabList is vertical
* @returns Tabster DOM attributes
*/ export const useTabListA11yBehavior_unstable = ({ vertical })=>{
return useArrowNavigationGroup({
circular: true,
axis: vertical ? 'vertical' : 'horizontal',
memorizeCurrent: false,
// eslint-disable-next-line @typescript-eslint/naming-convention
unstable_hasDefault: true
});
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,19 @@
export function useTabListContextValues_unstable(state) {
const { appearance, reserveSelectedTabSpace, disabled, selectTabOnFocus, selectedValue: selectedKey, onRegister, onUnregister, onSelect, getRegisteredTabs, size, vertical } = state;
const tabList = {
appearance,
reserveSelectedTabSpace,
disabled,
selectTabOnFocus,
selectedValue: selectedKey,
onSelect,
onRegister,
onUnregister,
getRegisteredTabs,
size,
vertical
};
return {
tabList
};
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/TabList/useTabListContextValues.tsx"],"sourcesContent":["import { TabListContextValue, TabListContextValues, TabListState } from './TabList.types';\n\nexport function useTabListContextValues_unstable(state: TabListState): TabListContextValues {\n const {\n appearance,\n reserveSelectedTabSpace,\n disabled,\n selectTabOnFocus,\n selectedValue: selectedKey,\n onRegister,\n onUnregister,\n onSelect,\n getRegisteredTabs,\n size,\n vertical,\n } = state;\n\n const tabList: TabListContextValue = {\n appearance,\n reserveSelectedTabSpace,\n disabled,\n selectTabOnFocus,\n selectedValue: selectedKey,\n onSelect,\n onRegister,\n onUnregister,\n getRegisteredTabs,\n size,\n vertical,\n };\n\n return { tabList };\n}\n"],"names":["useTabListContextValues_unstable","state","appearance","reserveSelectedTabSpace","disabled","selectTabOnFocus","selectedValue","selectedKey","onRegister","onUnregister","onSelect","getRegisteredTabs","size","vertical","tabList"],"mappings":"AAEA,OAAO,SAASA,iCAAiCC,KAAmB;IAClE,MAAM,EACJC,UAAU,EACVC,uBAAuB,EACvBC,QAAQ,EACRC,gBAAgB,EAChBC,eAAeC,WAAW,EAC1BC,UAAU,EACVC,YAAY,EACZC,QAAQ,EACRC,iBAAiB,EACjBC,IAAI,EACJC,QAAQ,EACT,GAAGZ;IAEJ,MAAMa,UAA+B;QACnCZ;QACAC;QACAC;QACAC;QACAC,eAAeC;QACfG;QACAF;QACAC;QACAE;QACAC;QACAC;IACF;IAEA,OAAO;QAAEC;IAAQ;AACnB"}

View File

@@ -0,0 +1,59 @@
'use client';
import { __styles, mergeClasses } from '@griffel/react';
import { tokens } from '@fluentui/react-theme';
export const tabListClassNames = {
root: 'fui-TabList'
};
/**
* Styles for the root slot
*/
const useStyles = /*#__PURE__*/__styles({
root: {
mc9l5x: "f22iagw",
Beiy3e4: "f1063pyq",
Bnnss6s: "fi64zpg",
Eh141a: "flvyvdh",
qhf8xq: "f10pi13n"
},
horizontal: {
Bt984gj: "f1q9h2pe",
Beiy3e4: "f1063pyq"
},
vertical: {
Bt984gj: "f1q9h2pe",
Beiy3e4: "f1vx9l62"
},
roundedSmall: {
i8kkvl: 0,
Belr9w4: 0,
rmohyg: "f1eyhf9v"
},
rounded: {
i8kkvl: 0,
Belr9w4: 0,
rmohyg: "faqewft"
}
}, {
d: [".f22iagw{display:flex;}", ".f1063pyq{flex-direction:row;}", ".fi64zpg{flex-shrink:0;}", ".flvyvdh{flex-wrap:nowrap;}", ".f10pi13n{position:relative;}", ".f1q9h2pe{align-items:stretch;}", ".f1vx9l62{flex-direction:column;}", [".f1eyhf9v{gap:var(--spacingHorizontalSNudge);}", {
p: -1
}], [".faqewft{gap:var(--spacingHorizontalS);}", {
p: -1
}]]
});
/**
* Apply styling to the TabList slots based on the state
*/
export const useTabListStyles_unstable = state => {
'use no memo';
const {
appearance,
vertical,
size
} = state;
const styles = useStyles();
const isRounded = appearance === 'subtle-circular' || appearance === 'filled-circular';
state.root.className = mergeClasses(tabListClassNames.root, styles.root, vertical ? styles.vertical : styles.horizontal, isRounded && (size === 'small' ? styles.roundedSmall : styles.rounded), state.root.className);
return state;
};

View File

@@ -0,0 +1 @@
{"version":3,"names":["__styles","mergeClasses","tokens","tabListClassNames","root","useStyles","mc9l5x","Beiy3e4","Bnnss6s","Eh141a","qhf8xq","horizontal","Bt984gj","vertical","roundedSmall","i8kkvl","Belr9w4","rmohyg","rounded","d","p","useTabListStyles_unstable","state","appearance","size","styles","isRounded","className"],"sources":["useTabListStyles.styles.js"],"sourcesContent":["'use client';\nimport { makeStyles, mergeClasses } from '@griffel/react';\nimport { tokens } from '@fluentui/react-theme';\nexport const tabListClassNames = {\n root: 'fui-TabList'\n};\n/**\n * Styles for the root slot\n */ const useStyles = makeStyles({\n root: {\n display: 'flex',\n flexDirection: 'row',\n flexShrink: 0,\n flexWrap: 'nowrap',\n position: 'relative'\n },\n horizontal: {\n alignItems: 'stretch',\n flexDirection: 'row'\n },\n vertical: {\n alignItems: 'stretch',\n flexDirection: 'column'\n },\n roundedSmall: {\n gap: tokens.spacingHorizontalSNudge\n },\n rounded: {\n gap: tokens.spacingHorizontalS\n }\n});\n/**\n * Apply styling to the TabList slots based on the state\n */ export const useTabListStyles_unstable = (state)=>{\n 'use no memo';\n const { appearance, vertical, size } = state;\n const styles = useStyles();\n const isRounded = appearance === 'subtle-circular' || appearance === 'filled-circular';\n state.root.className = mergeClasses(tabListClassNames.root, styles.root, vertical ? styles.vertical : styles.horizontal, isRounded && (size === 'small' ? styles.roundedSmall : styles.rounded), state.root.className);\n return state;\n};\n"],"mappings":"AAAA,YAAY;;AACZ,SAAAA,QAAA,EAAqBC,YAAY,QAAQ,gBAAgB;AACzD,SAASC,MAAM,QAAQ,uBAAuB;AAC9C,OAAO,MAAMC,iBAAiB,GAAG;EAC7BC,IAAI,EAAE;AACV,CAAC;AACD;AACA;AACA;AAAI,MAAMC,SAAS,gBAAGL,QAAA;EAAAI,IAAA;IAAAE,MAAA;IAAAC,OAAA;IAAAC,OAAA;IAAAC,MAAA;IAAAC,MAAA;EAAA;EAAAC,UAAA;IAAAC,OAAA;IAAAL,OAAA;EAAA;EAAAM,QAAA;IAAAD,OAAA;IAAAL,OAAA;EAAA;EAAAO,YAAA;IAAAC,MAAA;IAAAC,OAAA;IAAAC,MAAA;EAAA;EAAAC,OAAA;IAAAH,MAAA;IAAAC,OAAA;IAAAC,MAAA;EAAA;AAAA;EAAAE,CAAA;IAAAC,CAAA;EAAA;IAAAA,CAAA;EAAA;AAAA,CAsBrB,CAAC;AACF;AACA;AACA;AAAI,OAAO,MAAMC,yBAAyB,GAAIC,KAAK,IAAG;EAClD,aAAa;;EACb,MAAM;IAAEC,UAAU;IAAEV,QAAQ;IAAEW;EAAK,CAAC,GAAGF,KAAK;EAC5C,MAAMG,MAAM,GAAGpB,SAAS,CAAC,CAAC;EAC1B,MAAMqB,SAAS,GAAGH,UAAU,KAAK,iBAAiB,IAAIA,UAAU,KAAK,iBAAiB;EACtFD,KAAK,CAAClB,IAAI,CAACuB,SAAS,GAAG1B,YAAY,CAACE,iBAAiB,CAACC,IAAI,EAAEqB,MAAM,CAACrB,IAAI,EAAES,QAAQ,GAAGY,MAAM,CAACZ,QAAQ,GAAGY,MAAM,CAACd,UAAU,EAAEe,SAAS,KAAKF,IAAI,KAAK,OAAO,GAAGC,MAAM,CAACX,YAAY,GAAGW,MAAM,CAACP,OAAO,CAAC,EAAEI,KAAK,CAAClB,IAAI,CAACuB,SAAS,CAAC;EACtN,OAAOL,KAAK;AAChB,CAAC","ignoreList":[]}

View File

@@ -0,0 +1,41 @@
'use client';
import { makeStyles, mergeClasses } from '@griffel/react';
import { tokens } from '@fluentui/react-theme';
export const tabListClassNames = {
root: 'fui-TabList'
};
/**
* Styles for the root slot
*/ const useStyles = makeStyles({
root: {
display: 'flex',
flexDirection: 'row',
flexShrink: 0,
flexWrap: 'nowrap',
position: 'relative'
},
horizontal: {
alignItems: 'stretch',
flexDirection: 'row'
},
vertical: {
alignItems: 'stretch',
flexDirection: 'column'
},
roundedSmall: {
gap: tokens.spacingHorizontalSNudge
},
rounded: {
gap: tokens.spacingHorizontalS
}
});
/**
* Apply styling to the TabList slots based on the state
*/ export const useTabListStyles_unstable = (state)=>{
'use no memo';
const { appearance, vertical, size } = state;
const styles = useStyles();
const isRounded = appearance === 'subtle-circular' || appearance === 'filled-circular';
state.root.className = mergeClasses(tabListClassNames.root, styles.root, vertical ? styles.vertical : styles.horizontal, isRounded && (size === 'small' ? styles.roundedSmall : styles.rounded), state.root.className);
return state;
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/TabList/useTabListStyles.styles.ts"],"sourcesContent":["'use client';\n\nimport { SlotClassNames } from '@fluentui/react-utilities';\nimport { makeStyles, mergeClasses } from '@griffel/react';\nimport { tokens } from '@fluentui/react-theme';\nimport type { TabListSlots, TabListState } from './TabList.types';\n\nexport const tabListClassNames: SlotClassNames<TabListSlots> = {\n root: 'fui-TabList',\n};\n\n/**\n * Styles for the root slot\n */\nconst useStyles = makeStyles({\n root: {\n display: 'flex',\n flexDirection: 'row',\n flexShrink: 0,\n flexWrap: 'nowrap',\n position: 'relative',\n },\n horizontal: {\n alignItems: 'stretch',\n flexDirection: 'row',\n },\n vertical: {\n alignItems: 'stretch',\n flexDirection: 'column',\n },\n roundedSmall: {\n gap: tokens.spacingHorizontalSNudge,\n },\n rounded: {\n gap: tokens.spacingHorizontalS,\n },\n});\n\n/**\n * Apply styling to the TabList slots based on the state\n */\nexport const useTabListStyles_unstable = (state: TabListState): TabListState => {\n 'use no memo';\n\n const { appearance, vertical, size } = state;\n\n const styles = useStyles();\n\n const isRounded = appearance === 'subtle-circular' || appearance === 'filled-circular';\n\n state.root.className = mergeClasses(\n tabListClassNames.root,\n styles.root,\n vertical ? styles.vertical : styles.horizontal,\n isRounded && (size === 'small' ? styles.roundedSmall : styles.rounded),\n state.root.className,\n );\n\n return state;\n};\n"],"names":["makeStyles","mergeClasses","tokens","tabListClassNames","root","useStyles","display","flexDirection","flexShrink","flexWrap","position","horizontal","alignItems","vertical","roundedSmall","gap","spacingHorizontalSNudge","rounded","spacingHorizontalS","useTabListStyles_unstable","state","appearance","size","styles","isRounded","className"],"mappings":"AAAA;AAGA,SAASA,UAAU,EAAEC,YAAY,QAAQ,iBAAiB;AAC1D,SAASC,MAAM,QAAQ,wBAAwB;AAG/C,OAAO,MAAMC,oBAAkD;IAC7DC,MAAM;AACR,EAAE;AAEF;;CAEC,GACD,MAAMC,YAAYL,WAAW;IAC3BI,MAAM;QACJE,SAAS;QACTC,eAAe;QACfC,YAAY;QACZC,UAAU;QACVC,UAAU;IACZ;IACAC,YAAY;QACVC,YAAY;QACZL,eAAe;IACjB;IACAM,UAAU;QACRD,YAAY;QACZL,eAAe;IACjB;IACAO,cAAc;QACZC,KAAKb,OAAOc,uBAAuB;IACrC;IACAC,SAAS;QACPF,KAAKb,OAAOgB,kBAAkB;IAChC;AACF;AAEA;;CAEC,GACD,OAAO,MAAMC,4BAA4B,CAACC;IACxC;IAEA,MAAM,EAAEC,UAAU,EAAER,QAAQ,EAAES,IAAI,EAAE,GAAGF;IAEvC,MAAMG,SAASlB;IAEf,MAAMmB,YAAYH,eAAe,qBAAqBA,eAAe;IAErED,MAAMhB,IAAI,CAACqB,SAAS,GAAGxB,aACrBE,kBAAkBC,IAAI,EACtBmB,OAAOnB,IAAI,EACXS,WAAWU,OAAOV,QAAQ,GAAGU,OAAOZ,UAAU,EAC9Ca,aAAcF,CAAAA,SAAS,UAAUC,OAAOT,YAAY,GAAGS,OAAON,OAAO,AAAD,GACpEG,MAAMhB,IAAI,CAACqB,SAAS;IAGtB,OAAOL;AACT,EAAE"}