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 { useSpinner_unstable } from './useSpinner';
import { renderSpinner_unstable } from './renderSpinner';
import { useSpinnerStyles_unstable } from './useSpinnerStyles.styles';
import { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts';
/**
* Converged Spinner component for the fluentui repo
*/ export const Spinner = /*#__PURE__*/ React.forwardRef((props, ref)=>{
const state = useSpinner_unstable(props, ref);
useSpinnerStyles_unstable(state);
useCustomStyleHook_unstable('useSpinnerStyles_unstable')(state);
return renderSpinner_unstable(state);
});
Spinner.displayName = 'Spinner';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Spinner/Spinner.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { useSpinner_unstable } from './useSpinner';\nimport { renderSpinner_unstable } from './renderSpinner';\nimport { useSpinnerStyles_unstable } from './useSpinnerStyles.styles';\nimport type { SpinnerProps } from './Spinner.types';\nimport type { ForwardRefComponent } from '@fluentui/react-utilities';\nimport { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts';\n\n/**\n * Converged Spinner component for the fluentui repo\n */\nexport const Spinner: ForwardRefComponent<SpinnerProps> = React.forwardRef((props, ref) => {\n const state = useSpinner_unstable(props, ref);\n\n useSpinnerStyles_unstable(state);\n\n useCustomStyleHook_unstable('useSpinnerStyles_unstable')(state);\n\n return renderSpinner_unstable(state);\n});\n\nSpinner.displayName = 'Spinner';\n"],"names":["React","useSpinner_unstable","renderSpinner_unstable","useSpinnerStyles_unstable","useCustomStyleHook_unstable","Spinner","forwardRef","props","ref","state","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,2BAA2B,QAAQ,kCAAkC;AAE9E;;CAEC,GACD,OAAO,MAAMC,wBAA6CL,MAAMM,UAAU,CAAC,CAACC,OAAOC;IACjF,MAAMC,QAAQR,oBAAoBM,OAAOC;IAEzCL,0BAA0BM;IAE1BL,4BAA4B,6BAA6BK;IAEzD,OAAOP,uBAAuBO;AAChC,GAAG;AAEHJ,QAAQK,WAAW,GAAG"}

View File

@@ -0,0 +1,3 @@
/**
* Spinner base state, excluding design-related state like appearance and size.
*/ export { };

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Spinner/Spinner.types.ts"],"sourcesContent":["import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities';\nimport { Label } from '@fluentui/react-label';\n\nexport type SpinnerSlots = {\n /**\n * The root of the Spinner.\n * The root slot receives the `className` and `style` specified directly on the `<Spinner>`.\n */\n root: NonNullable<Slot<'div', 'span'>>;\n\n /**\n * The animated spinning ring.\n */\n spinner?: Slot<'span'>;\n\n /**\n * The part of the spinner that rotates.\n */\n spinnerTail?: NonNullable<Slot<'span'>>;\n\n /**\n * An optional label for the Spinner.\n */\n label?: Slot<typeof Label>;\n};\n\n/**\n * Spinner Props\n */\nexport type SpinnerProps = Omit<ComponentProps<SpinnerSlots>, 'size'> & {\n /**\n * The appearance of the Spinner.\n * @default 'primary'\n */\n appearance?: 'primary' | 'inverted';\n\n /**\n * Time in milliseconds after component mount before spinner is visible.\n * @default 0\n */\n delay?: number;\n\n /**\n * Where the label is positioned relative to the Spinner\n * @default 'after'\n */\n labelPosition?: 'above' | 'below' | 'before' | 'after';\n\n /**\n * The size of the spinner.\n * @default 'medium'\n */\n size?: 'extra-tiny' | 'tiny' | 'extra-small' | 'small' | 'medium' | 'large' | 'extra-large' | 'huge';\n};\n\n/**\n * Spinner base props, excluding design-related props like appearance and size.\n */\nexport type SpinnerBaseProps = Omit<SpinnerProps, 'appearance' | 'size'>;\n\n/**\n * State used in rendering Spinner\n */\nexport type SpinnerState = ComponentState<SpinnerSlots> &\n Required<Pick<SpinnerProps, 'appearance' | 'delay' | 'labelPosition' | 'size'>> & {\n /**\n * Should the spinner be rendered in the DOM\n */\n shouldRenderSpinner: boolean;\n };\n\n/**\n * Spinner base state, excluding design-related state like appearance and size.\n */\nexport type SpinnerBaseState = Omit<SpinnerState, 'appearance' | 'size'>;\n"],"names":[],"mappings":"AAuEA;;CAEC,GACD,WAAyE"}

View File

@@ -0,0 +1,4 @@
export { Spinner } from './Spinner';
export { renderSpinner_unstable } from './renderSpinner';
export { useSpinner_unstable, useSpinnerBase_unstable } from './useSpinner';
export { spinnerClassNames, useSpinnerStyles_unstable } from './useSpinnerStyles.styles';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Spinner/index.ts"],"sourcesContent":["export { Spinner } from './Spinner';\nexport type { SpinnerBaseProps, SpinnerBaseState, SpinnerProps, SpinnerSlots, SpinnerState } from './Spinner.types';\nexport { renderSpinner_unstable } from './renderSpinner';\nexport { useSpinner_unstable, useSpinnerBase_unstable } from './useSpinner';\nexport { spinnerClassNames, useSpinnerStyles_unstable } from './useSpinnerStyles.styles';\n"],"names":["Spinner","renderSpinner_unstable","useSpinner_unstable","useSpinnerBase_unstable","spinnerClassNames","useSpinnerStyles_unstable"],"mappings":"AAAA,SAASA,OAAO,QAAQ,YAAY;AAEpC,SAASC,sBAAsB,QAAQ,kBAAkB;AACzD,SAASC,mBAAmB,EAAEC,uBAAuB,QAAQ,eAAe;AAC5E,SAASC,iBAAiB,EAAEC,yBAAyB,QAAQ,4BAA4B"}

View File

@@ -0,0 +1,17 @@
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 Spinner
*/ export const renderSpinner_unstable = (state)=>{
assertSlots(state);
const { labelPosition, shouldRenderSpinner } = state;
return /*#__PURE__*/ _jsxs(state.root, {
children: [
state.label && shouldRenderSpinner && (labelPosition === 'above' || labelPosition === 'before') && /*#__PURE__*/ _jsx(state.label, {}),
state.spinner && shouldRenderSpinner && /*#__PURE__*/ _jsx(state.spinner, {
children: state.spinnerTail && /*#__PURE__*/ _jsx(state.spinnerTail, {})
}),
state.label && shouldRenderSpinner && (labelPosition === 'below' || labelPosition === 'after') && /*#__PURE__*/ _jsx(state.label, {})
]
});
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Spinner/renderSpinner.tsx"],"sourcesContent":["/** @jsxRuntime automatic */\n/** @jsxImportSource @fluentui/react-jsx-runtime */\n\nimport { assertSlots } from '@fluentui/react-utilities';\nimport type { JSXElement } from '@fluentui/react-utilities';\nimport type { SpinnerBaseState, SpinnerSlots } from './Spinner.types';\n\n/**\n * Render the final JSX of Spinner\n */\nexport const renderSpinner_unstable = (state: SpinnerBaseState): JSXElement => {\n assertSlots<SpinnerSlots>(state);\n const { labelPosition, shouldRenderSpinner } = state;\n return (\n <state.root>\n {state.label && shouldRenderSpinner && (labelPosition === 'above' || labelPosition === 'before') && (\n <state.label />\n )}\n {state.spinner && shouldRenderSpinner && (\n <state.spinner>{state.spinnerTail && <state.spinnerTail />}</state.spinner>\n )}\n {state.label && shouldRenderSpinner && (labelPosition === 'below' || labelPosition === 'after') && (\n <state.label />\n )}\n </state.root>\n );\n};\n"],"names":["assertSlots","renderSpinner_unstable","state","labelPosition","shouldRenderSpinner","root","label","spinner","spinnerTail"],"mappings":"AAAA,0BAA0B,GAC1B,iDAAiD;AAEjD,SAASA,WAAW,QAAQ,4BAA4B;AAIxD;;CAEC,GACD,OAAO,MAAMC,yBAAyB,CAACC;IACrCF,YAA0BE;IAC1B,MAAM,EAAEC,aAAa,EAAEC,mBAAmB,EAAE,GAAGF;IAC/C,qBACE,MAACA,MAAMG,IAAI;;YACRH,MAAMI,KAAK,IAAIF,uBAAwBD,CAAAA,kBAAkB,WAAWA,kBAAkB,QAAO,mBAC5F,KAACD,MAAMI,KAAK;YAEbJ,MAAMK,OAAO,IAAIH,qCAChB,KAACF,MAAMK,OAAO;0BAAEL,MAAMM,WAAW,kBAAI,KAACN,MAAMM,WAAW;;YAExDN,MAAMI,KAAK,IAAIF,uBAAwBD,CAAAA,kBAAkB,WAAWA,kBAAkB,OAAM,mBAC3F,KAACD,MAAMI,KAAK;;;AAIpB,EAAE"}

View File

@@ -0,0 +1,93 @@
'use client';
import * as React from 'react';
import { getIntrinsicElementProps, useId, useTimeout, slot } from '@fluentui/react-utilities';
import { Label } from '@fluentui/react-label';
import { useSpinnerContext } from '../../contexts/SpinnerContext';
/**
* Create the state required to render Spinner.
*
* The returned state can be modified with hooks such as useSpinnerStyles_unstable,
* before being passed to renderSpinner_unstable.
*
* @param props - props from this instance of Spinner
* @param ref - reference to root HTMLElement of Spinner
*/ export const useSpinner_unstable = (props, ref)=>{
const { size: contextSize } = useSpinnerContext();
const { appearance = 'primary', size = contextSize !== null && contextSize !== void 0 ? contextSize : 'medium', ...baseProps } = props;
const baseState = useSpinnerBase_unstable(baseProps, ref);
return {
...baseState,
appearance,
size
};
};
/**
* Base hook for Spinner component, which manages state related to slots structure, ARIA attributes,
* and delay-based visibility logic.
*
* @param props - User provided props to the Spinner component.
* @param ref - User provided ref to be passed to the Spinner component.
*/ export const useSpinnerBase_unstable = (props, ref)=>{
const { delay = 0, labelPosition = 'after' } = props;
const baseId = useId('spinner');
const { role = 'progressbar', ...rest } = props;
const nativeRoot = slot.always(getIntrinsicElementProps('div', {
// FIXME:
// `ref` is wrongly assigned to be `HTMLElement` instead of `HTMLDivElement`
// but since it would be a breaking change to fix it, we are casting ref to it's proper type
ref: ref,
role,
...rest
}), {
elementType: 'div'
});
const [isShownAfterDelay, setIsShownAfterDelay] = React.useState(false);
const [setDelayTimeout, clearDelayTimeout] = useTimeout();
React.useEffect(()=>{
if (delay <= 0) {
return;
}
setDelayTimeout(()=>{
setIsShownAfterDelay(true);
}, delay);
return ()=>{
clearDelayTimeout();
};
}, [
setDelayTimeout,
clearDelayTimeout,
delay
]);
const labelShorthand = slot.optional(props.label, {
defaultProps: {
id: baseId
},
renderByDefault: false,
elementType: Label
});
const spinnerShortHand = slot.optional(props.spinner, {
renderByDefault: true,
elementType: 'span'
});
if (labelShorthand && nativeRoot && !nativeRoot['aria-labelledby']) {
nativeRoot['aria-labelledby'] = labelShorthand.id;
}
const state = {
delay,
labelPosition,
shouldRenderSpinner: !delay || isShownAfterDelay,
components: {
root: 'div',
spinner: 'span',
spinnerTail: 'span',
label: Label
},
root: nativeRoot,
spinner: spinnerShortHand,
spinnerTail: slot.always(props.spinnerTail, {
elementType: 'span'
}),
label: labelShorthand
};
return state;
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,180 @@
'use client';
import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';
import { tokens, typographyStyles } from '@fluentui/react-theme';
import { __resetStyles, __styles, mergeClasses } from '@griffel/react';
export const spinnerClassNames = {
root: 'fui-Spinner',
spinner: 'fui-Spinner__spinner',
spinnerTail: 'fui-Spinner__spinnerTail',
label: 'fui-Spinner__label'
};
/**
* CSS variables used internally by Spinner
*/
const vars = {
strokeWidth: '--fui-Spinner--strokeWidth'
};
const useRootBaseClassName = /*#__PURE__*/__resetStyles("rpp59a7", null, [".rpp59a7{display:flex;align-items:center;justify-content:center;line-height:0;gap:8px;overflow:hidden;min-width:min-content;}"]);
const useRootStyles = /*#__PURE__*/__styles({
vertical: {
Beiy3e4: "f1vx9l62"
}
}, {
d: [".f1vx9l62{flex-direction:column;}"]
});
const useSpinnerBaseClassName = /*#__PURE__*/__resetStyles("rvgcg50", "r15nd2jo", {
r: [".rvgcg50{position:relative;flex-shrink:0;-webkit-mask-image:radial-gradient(closest-side, transparent calc(100% - var(--fui-Spinner--strokeWidth) - 1px), white calc(100% - var(--fui-Spinner--strokeWidth)) calc(100% - 1px), transparent 100%);mask-image:radial-gradient(closest-side, transparent calc(100% - var(--fui-Spinner--strokeWidth) - 1px), white calc(100% - var(--fui-Spinner--strokeWidth)) calc(100% - 1px), transparent 100%);background-color:var(--colorBrandStroke2Contrast);color:var(--colorBrandStroke1);animation-duration:1.5s;animation-iteration-count:infinite;animation-timing-function:linear;animation-name:rb7n1on;}", "@keyframes rb7n1on{0%{transform:rotate(0deg);}100%{transform:rotate(360deg);}}", ".r15nd2jo{position:relative;flex-shrink:0;-webkit-mask-image:radial-gradient(closest-side, transparent calc(100% - var(--fui-Spinner--strokeWidth) - 1px), white calc(100% - var(--fui-Spinner--strokeWidth)) calc(100% - 1px), transparent 100%);mask-image:radial-gradient(closest-side, transparent calc(100% - var(--fui-Spinner--strokeWidth) - 1px), white calc(100% - var(--fui-Spinner--strokeWidth)) calc(100% - 1px), transparent 100%);background-color:var(--colorBrandStroke2Contrast);color:var(--colorBrandStroke1);animation-duration:1.5s;animation-iteration-count:infinite;animation-timing-function:linear;animation-name:r1gx3jof;}", "@keyframes r1gx3jof{0%{transform:rotate(0deg);}100%{transform:rotate(-360deg);}}"],
s: ["@media screen and (forced-colors: active){.rvgcg50{background-color:HighlightText;color:Highlight;forced-color-adjust:none;}}", "@media screen and (prefers-reduced-motion: reduce){.rvgcg50{animation-duration:1.8s;}}", "@media screen and (forced-colors: active){.r15nd2jo{background-color:HighlightText;color:Highlight;forced-color-adjust:none;}}", "@media screen and (prefers-reduced-motion: reduce){.r15nd2jo{animation-duration:1.8s;}}"]
});
// The spinner tail is rendered using two 135deg arc segments, behind a 105deg arc mask.
// The segments are rotated out from behind the mask to expand the visible arc from
// 30deg (min) to 255deg (max), and then back behind the mask again to shrink the arc.
// The tail and spinner itself also have 360deg rotation animations for the spin.
const useSpinnerTailBaseClassName = /*#__PURE__*/__resetStyles("rxov3xa", "r1o544mv", {
r: [".rxov3xa{position:absolute;display:block;width:100%;height:100%;-webkit-mask-image:conic-gradient(transparent 105deg, white 105deg);mask-image:conic-gradient(transparent 105deg, white 105deg);animation-duration:1.5s;animation-iteration-count:infinite;animation-timing-function:var(--curveEasyEase);animation-name:r15mim6k;}", ".rxov3xa::before,.rxov3xa::after{content:\"\";position:absolute;display:block;width:100%;height:100%;animation:inherit;background-image:conic-gradient(currentcolor 135deg, transparent 135deg);}", "@keyframes r15mim6k{0%{transform:rotate(-135deg);}50%{transform:rotate(0deg);}100%{transform:rotate(225deg);}}", ".rxov3xa::before{animation-name:r18vhmn8;}", "@keyframes r18vhmn8{0%{transform:rotate(0deg);}50%{transform:rotate(105deg);}100%{transform:rotate(0deg);}}", ".rxov3xa::after{animation-name:rkgrvoi;}", "@keyframes rkgrvoi{0%{transform:rotate(0deg);}50%{transform:rotate(225deg);}100%{transform:rotate(0deg);}}", ".r1o544mv{position:absolute;display:block;width:100%;height:100%;-webkit-mask-image:conic-gradient(transparent 105deg, white 105deg);mask-image:conic-gradient(transparent 105deg, white 105deg);animation-duration:1.5s;animation-iteration-count:infinite;animation-timing-function:var(--curveEasyEase);animation-name:r109gmi5;}", ".r1o544mv::before,.r1o544mv::after{content:\"\";position:absolute;display:block;width:100%;height:100%;animation:inherit;background-image:conic-gradient(currentcolor 135deg, transparent 135deg);}", "@keyframes r109gmi5{0%{transform:rotate(135deg);}50%{transform:rotate(0deg);}100%{transform:rotate(-225deg);}}", ".r1o544mv::before{animation-name:r17whflh;}", "@keyframes r17whflh{0%{transform:rotate(0deg);}50%{transform:rotate(-105deg);}100%{transform:rotate(0deg);}}", ".r1o544mv::after{animation-name:re4odhl;}", "@keyframes re4odhl{0%{transform:rotate(0deg);}50%{transform:rotate(-225deg);}100%{transform:rotate(0deg);}}"],
s: ["@media screen and (prefers-reduced-motion: reduce){.rxov3xa{animation-iteration-count:0;background-image:conic-gradient(transparent 120deg, currentcolor 360deg);}.rxov3xa::before,.rxov3xa::after{content:none;}}", "@media screen and (prefers-reduced-motion: reduce){.r1o544mv{animation-iteration-count:0;background-image:conic-gradient(transparent 120deg, currentcolor 360deg);}.r1o544mv::before,.r1o544mv::after{content:none;}}"]
});
const useSpinnerStyles = /*#__PURE__*/__styles({
inverted: {
De3pzq: "fr407j0",
sj55zd: "f1f7voed"
},
rtlTail: {
btxmck: "f179dep3",
gb5jj2: "fbz9ihp",
Bdya8wy: "f1pme1qz"
},
"extra-tiny": {
Bqenvij: "fd461yt",
a9b677: "fjw5fx7",
qmp6fs: "f1v3ph3m"
},
tiny: {
Bqenvij: "fjamq6b",
a9b677: "f64fuq3",
qmp6fs: "f1v3ph3m"
},
"extra-small": {
Bqenvij: "frvgh55",
a9b677: "fq4mcun",
qmp6fs: "f1v3ph3m"
},
small: {
Bqenvij: "fxldao9",
a9b677: "f1w9dchk",
qmp6fs: "f1v3ph3m"
},
medium: {
Bqenvij: "f1d2rq10",
a9b677: "f1szoe96",
qmp6fs: "fb52u90"
},
large: {
Bqenvij: "f8ljn23",
a9b677: "fpdz1er",
qmp6fs: "fb52u90"
},
"extra-large": {
Bqenvij: "fbhnoac",
a9b677: "feqmc2u",
qmp6fs: "fb52u90"
},
huge: {
Bqenvij: "f1ft4266",
a9b677: "fksc0bp",
qmp6fs: "fa3u9ii"
}
}, {
d: [".fr407j0{background-color:var(--colorNeutralStrokeAlpha2);}", ".f1f7voed{color:var(--colorNeutralStrokeOnBrand2);}", ".f179dep3{-webkit-mask-image:conic-gradient(white 255deg, transparent 255deg);mask-image:conic-gradient(white 255deg, transparent 255deg);}", ".fbz9ihp::before,.fbz9ihp::after{background-image:conic-gradient(transparent 225deg, currentcolor 225deg);}", ".fd461yt{height:16px;}", ".fjw5fx7{width:16px;}", ".f1v3ph3m{--fui-Spinner--strokeWidth:var(--strokeWidthThick);}", ".fjamq6b{height:20px;}", ".f64fuq3{width:20px;}", ".frvgh55{height:24px;}", ".fq4mcun{width:24px;}", ".fxldao9{height:28px;}", ".f1w9dchk{width:28px;}", ".f1d2rq10{height:32px;}", ".f1szoe96{width:32px;}", ".fb52u90{--fui-Spinner--strokeWidth:var(--strokeWidthThicker);}", ".f8ljn23{height:36px;}", ".fpdz1er{width:36px;}", ".fbhnoac{height:40px;}", ".feqmc2u{width:40px;}", ".f1ft4266{height:44px;}", ".fksc0bp{width:44px;}", ".fa3u9ii{--fui-Spinner--strokeWidth:var(--strokeWidthThickest);}"],
m: [["@media screen and (prefers-reduced-motion: reduce){.f1pme1qz{background-image:conic-gradient(currentcolor 0deg, transparent 240deg);}}", {
m: "screen and (prefers-reduced-motion: reduce)"
}]]
});
const useLabelStyles = /*#__PURE__*/__styles({
inverted: {
sj55zd: "fonrgv7"
},
"extra-tiny": {
Bahqtrf: "fk6fouc",
Be2twd7: "fkhj508",
Bhrd7zp: "figsok6",
Bg96gwp: "f1i3iumi"
},
tiny: {
Bahqtrf: "fk6fouc",
Be2twd7: "fkhj508",
Bhrd7zp: "figsok6",
Bg96gwp: "f1i3iumi"
},
"extra-small": {
Bahqtrf: "fk6fouc",
Be2twd7: "fkhj508",
Bhrd7zp: "figsok6",
Bg96gwp: "f1i3iumi"
},
small: {
Bahqtrf: "fk6fouc",
Be2twd7: "fkhj508",
Bhrd7zp: "figsok6",
Bg96gwp: "f1i3iumi"
},
medium: {
Bahqtrf: "fk6fouc",
Be2twd7: "fod5ikn",
Bhrd7zp: "fl43uef",
Bg96gwp: "faaz57k"
},
large: {
Bahqtrf: "fk6fouc",
Be2twd7: "fod5ikn",
Bhrd7zp: "fl43uef",
Bg96gwp: "faaz57k"
},
"extra-large": {
Bahqtrf: "fk6fouc",
Be2twd7: "fod5ikn",
Bhrd7zp: "fl43uef",
Bg96gwp: "faaz57k"
},
huge: {
Bahqtrf: "fk6fouc",
Be2twd7: "f1pp30po",
Bhrd7zp: "fl43uef",
Bg96gwp: "f106mvju"
}
}, {
d: [".fonrgv7{color:var(--colorNeutralForegroundStaticInverted);}", ".fk6fouc{font-family:var(--fontFamilyBase);}", ".fkhj508{font-size:var(--fontSizeBase300);}", ".figsok6{font-weight:var(--fontWeightRegular);}", ".f1i3iumi{line-height:var(--lineHeightBase300);}", ".fod5ikn{font-size:var(--fontSizeBase400);}", ".fl43uef{font-weight:var(--fontWeightSemibold);}", ".faaz57k{line-height:var(--lineHeightBase400);}", ".f1pp30po{font-size:var(--fontSizeBase500);}", ".f106mvju{line-height:var(--lineHeightBase500);}"]
});
/**
* Apply styling to the Spinner slots based on the state
*/
export const useSpinnerStyles_unstable = state => {
'use no memo';
const {
labelPosition,
size,
appearance
} = state;
const {
dir
} = useFluent();
const rootBaseClassName = useRootBaseClassName();
const rootStyles = useRootStyles();
const spinnerBaseClassName = useSpinnerBaseClassName();
const spinnerStyles = useSpinnerStyles();
const spinnerTailBaseClassName = useSpinnerTailBaseClassName();
const labelStyles = useLabelStyles();
state.root.className = mergeClasses(spinnerClassNames.root, rootBaseClassName, (labelPosition === 'above' || labelPosition === 'below') && rootStyles.vertical, state.root.className);
if (state.spinner) {
state.spinner.className = mergeClasses(spinnerClassNames.spinner, spinnerBaseClassName, spinnerStyles[size], appearance === 'inverted' && spinnerStyles.inverted, state.spinner.className);
}
if (state.spinnerTail) {
state.spinnerTail.className = mergeClasses(spinnerClassNames.spinnerTail, spinnerTailBaseClassName, dir === 'rtl' && spinnerStyles.rtlTail, state.spinnerTail.className);
}
if (state.label) {
state.label.className = mergeClasses(spinnerClassNames.label, labelStyles[size], appearance === 'inverted' && labelStyles.inverted, state.label.className);
}
return state;
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,231 @@
'use client';
import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';
import { tokens, typographyStyles } from '@fluentui/react-theme';
import { makeResetStyles, makeStyles, mergeClasses } from '@griffel/react';
export const spinnerClassNames = {
root: 'fui-Spinner',
spinner: 'fui-Spinner__spinner',
spinnerTail: 'fui-Spinner__spinnerTail',
label: 'fui-Spinner__label'
};
/**
* CSS variables used internally by Spinner
*/ const vars = {
strokeWidth: '--fui-Spinner--strokeWidth'
};
const useRootBaseClassName = makeResetStyles({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
lineHeight: '0',
gap: '8px',
overflow: 'hidden',
minWidth: 'min-content'
});
const useRootStyles = makeStyles({
vertical: {
flexDirection: 'column'
}
});
const useSpinnerBaseClassName = makeResetStyles({
position: 'relative',
flexShrink: 0,
// Use a mask to create the ring shape of the spinner.
maskImage: `radial-gradient(closest-side, ` + `transparent calc(100% - var(${vars.strokeWidth}) - 1px), ` + `white calc(100% - var(${vars.strokeWidth})) calc(100% - 1px), ` + `transparent 100%)`,
backgroundColor: tokens.colorBrandStroke2Contrast,
color: tokens.colorBrandStroke1,
'@media screen and (forced-colors: active)': {
backgroundColor: 'HighlightText',
color: 'Highlight',
forcedColorAdjust: 'none'
},
animationDuration: '1.5s',
animationIterationCount: 'infinite',
animationTimingFunction: 'linear',
animationName: {
'0%': {
transform: 'rotate(0deg)'
},
'100%': {
transform: 'rotate(360deg)'
}
},
'@media screen and (prefers-reduced-motion: reduce)': {
animationDuration: '1.8s'
}
});
// The spinner tail is rendered using two 135deg arc segments, behind a 105deg arc mask.
// The segments are rotated out from behind the mask to expand the visible arc from
// 30deg (min) to 255deg (max), and then back behind the mask again to shrink the arc.
// The tail and spinner itself also have 360deg rotation animations for the spin.
const useSpinnerTailBaseClassName = makeResetStyles({
position: 'absolute',
display: 'block',
width: '100%',
height: '100%',
maskImage: 'conic-gradient(transparent 105deg, white 105deg)',
'&::before, &::after': {
content: '""',
position: 'absolute',
display: 'block',
width: '100%',
height: '100%',
animation: 'inherit',
backgroundImage: 'conic-gradient(currentcolor 135deg, transparent 135deg)'
},
animationDuration: '1.5s',
animationIterationCount: 'infinite',
animationTimingFunction: tokens.curveEasyEase,
animationName: {
'0%': {
transform: 'rotate(-135deg)'
},
'50%': {
transform: 'rotate(0deg)'
},
'100%': {
transform: 'rotate(225deg)'
}
},
'&::before': {
animationName: {
'0%': {
transform: 'rotate(0deg)'
},
'50%': {
transform: 'rotate(105deg)'
},
'100%': {
transform: 'rotate(0deg)'
}
}
},
'&::after': {
animationName: {
'0%': {
transform: 'rotate(0deg)'
},
'50%': {
transform: 'rotate(225deg)'
},
'100%': {
transform: 'rotate(0deg)'
}
}
},
'@media screen and (prefers-reduced-motion: reduce)': {
animationIterationCount: '0',
backgroundImage: 'conic-gradient(transparent 120deg, currentcolor 360deg)',
'&::before, &::after': {
content: 'none'
}
}
});
const useSpinnerStyles = makeStyles({
inverted: {
backgroundColor: tokens.colorNeutralStrokeAlpha2,
color: tokens.colorNeutralStrokeOnBrand2
},
rtlTail: {
maskImage: 'conic-gradient(white 255deg, transparent 255deg)',
'&::before, &::after': {
backgroundImage: 'conic-gradient(transparent 225deg, currentcolor 225deg)'
},
'@media screen and (prefers-reduced-motion: reduce)': {
backgroundImage: 'conic-gradient(currentcolor 0deg, transparent 240deg)'
}
},
'extra-tiny': {
height: '16px',
width: '16px',
[vars.strokeWidth]: tokens.strokeWidthThick
},
tiny: {
height: '20px',
width: '20px',
[vars.strokeWidth]: tokens.strokeWidthThick
},
'extra-small': {
height: '24px',
width: '24px',
[vars.strokeWidth]: tokens.strokeWidthThick
},
small: {
height: '28px',
width: '28px',
[vars.strokeWidth]: tokens.strokeWidthThick
},
medium: {
height: '32px',
width: '32px',
[vars.strokeWidth]: tokens.strokeWidthThicker
},
large: {
height: '36px',
width: '36px',
[vars.strokeWidth]: tokens.strokeWidthThicker
},
'extra-large': {
height: '40px',
width: '40px',
[vars.strokeWidth]: tokens.strokeWidthThicker
},
huge: {
height: '44px',
width: '44px',
[vars.strokeWidth]: tokens.strokeWidthThickest
}
});
const useLabelStyles = makeStyles({
inverted: {
color: tokens.colorNeutralForegroundStaticInverted
},
'extra-tiny': {
...typographyStyles.body1
},
tiny: {
...typographyStyles.body1
},
'extra-small': {
...typographyStyles.body1
},
small: {
...typographyStyles.body1
},
medium: {
...typographyStyles.subtitle2
},
large: {
...typographyStyles.subtitle2
},
'extra-large': {
...typographyStyles.subtitle2
},
huge: {
...typographyStyles.subtitle1
}
});
/**
* Apply styling to the Spinner slots based on the state
*/ export const useSpinnerStyles_unstable = (state)=>{
'use no memo';
const { labelPosition, size, appearance } = state;
const { dir } = useFluent();
const rootBaseClassName = useRootBaseClassName();
const rootStyles = useRootStyles();
const spinnerBaseClassName = useSpinnerBaseClassName();
const spinnerStyles = useSpinnerStyles();
const spinnerTailBaseClassName = useSpinnerTailBaseClassName();
const labelStyles = useLabelStyles();
state.root.className = mergeClasses(spinnerClassNames.root, rootBaseClassName, (labelPosition === 'above' || labelPosition === 'below') && rootStyles.vertical, state.root.className);
if (state.spinner) {
state.spinner.className = mergeClasses(spinnerClassNames.spinner, spinnerBaseClassName, spinnerStyles[size], appearance === 'inverted' && spinnerStyles.inverted, state.spinner.className);
}
if (state.spinnerTail) {
state.spinnerTail.className = mergeClasses(spinnerClassNames.spinnerTail, spinnerTailBaseClassName, dir === 'rtl' && spinnerStyles.rtlTail, state.spinnerTail.className);
}
if (state.label) {
state.label.className = mergeClasses(spinnerClassNames.label, labelStyles[size], appearance === 'inverted' && labelStyles.inverted, state.label.className);
}
return state;
};

File diff suppressed because one or more lines are too long