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 { renderRadio_unstable } from './renderRadio';
import { useRadio_unstable } from './useRadio';
import { useRadioStyles_unstable } from './useRadioStyles.styles';
import { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts';
/**
* Radio component is a wrapper for a radio button with a label.
*/ export const Radio = /*#__PURE__*/ React.forwardRef((props, ref)=>{
const state = useRadio_unstable(props, ref);
useRadioStyles_unstable(state);
useCustomStyleHook_unstable('useRadioStyles_unstable')(state);
return renderRadio_unstable(state);
});
Radio.displayName = 'Radio';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Radio/Radio.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport type { ForwardRefComponent } from '@fluentui/react-utilities';\nimport type { RadioProps } from './Radio.types';\nimport { renderRadio_unstable } from './renderRadio';\nimport { useRadio_unstable } from './useRadio';\nimport { useRadioStyles_unstable } from './useRadioStyles.styles';\nimport { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts';\n\n/**\n * Radio component is a wrapper for a radio button with a label.\n */\nexport const Radio: ForwardRefComponent<RadioProps> = React.forwardRef((props, ref) => {\n const state = useRadio_unstable(props, ref);\n\n useRadioStyles_unstable(state);\n\n useCustomStyleHook_unstable('useRadioStyles_unstable')(state);\n\n return renderRadio_unstable(state);\n});\n\nRadio.displayName = 'Radio';\n"],"names":["React","renderRadio_unstable","useRadio_unstable","useRadioStyles_unstable","useCustomStyleHook_unstable","Radio","forwardRef","props","ref","state","displayName"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAG/B,SAASC,oBAAoB,QAAQ,gBAAgB;AACrD,SAASC,iBAAiB,QAAQ,aAAa;AAC/C,SAASC,uBAAuB,QAAQ,0BAA0B;AAClE,SAASC,2BAA2B,QAAQ,kCAAkC;AAE9E;;CAEC,GACD,OAAO,MAAMC,sBAAyCL,MAAMM,UAAU,CAAC,CAACC,OAAOC;IAC7E,MAAMC,QAAQP,kBAAkBK,OAAOC;IAEvCL,wBAAwBM;IAExBL,4BAA4B,2BAA2BK;IAEvD,OAAOR,qBAAqBQ;AAC9B,GAAG;AAEHJ,MAAMK,WAAW,GAAG"}

View File

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

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Radio/Radio.types.ts"],"sourcesContent":["import * as React from 'react';\nimport { Label } from '@fluentui/react-label';\nimport type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities';\n\nexport type RadioSlots = {\n /**\n * The root element of the Radio.\n *\n * The root slot receives the `className` and `style` specified directly on the `<Radio>`.\n * All other native props will be applied to the primary slot: `input`\n */\n root: NonNullable<Slot<'span'>>;\n\n /**\n * The Radio's label.\n */\n label: Slot<typeof Label>;\n\n /**\n * Hidden input that handles the radio's functionality.\n *\n * This is the PRIMARY slot: all native properties specified directly on `<Radio>` will be applied to this slot,\n * except `className` and `style`, which remain on the root slot.\n */\n input: NonNullable<Slot<'input'>>;\n\n /**\n * A circle outline, with a filled circle icon inside when the Radio is checked.\n */\n indicator: NonNullable<Slot<'div'>>;\n};\n\n/**\n * Radio Props\n */\nexport type RadioProps = Omit<ComponentProps<Partial<RadioSlots>, 'input'>, 'onChange' | 'size'> & {\n /**\n * The value of the RadioGroup when this Radio item is selected.\n */\n value?: string;\n\n /**\n * The position of the label relative to the radio indicator.\n *\n * This defaults to `after` unless the Radio is inside a RadioGroup with `layout=\"horizontalStacked\"`,\n * in which case it defaults to `below`.\n *\n * @defaultvalue after\n */\n labelPosition?: 'after' | 'below';\n\n /**\n * Disable this Radio item.\n */\n disabled?: boolean;\n\n /**\n * Callback when this Radio is selected in its group.\n *\n * **Note:** `onChange` is NOT called when this Radio is deselected.\n * Use RadioGroup's `onChange` event to determine when the selection in the group changes.\n */\n // eslint-disable-next-line @nx/workspace-consistent-callback-type -- can't change type of existing callback\n onChange?: (ev: React.ChangeEvent<HTMLInputElement>, data: RadioOnChangeData) => void;\n};\n\nexport type RadioBaseProps = RadioProps;\n\n/**\n * Data for the onChange event for Radio.\n */\nexport type RadioOnChangeData = {\n /**\n * The value prop of this Radio item.\n */\n value: string;\n};\n\n/**\n * State used in rendering Radio\n */\nexport type RadioState = ComponentState<RadioSlots> & Required<Pick<RadioProps, 'labelPosition'>>;\n\nexport type RadioBaseState = RadioState;\n"],"names":["React"],"mappings":"AAAA,YAAYA,WAAW,QAAQ"}

View File

@@ -0,0 +1,4 @@
export { Radio } from './Radio';
export { renderRadio_unstable } from './renderRadio';
export { useRadio_unstable, useRadioBase_unstable } from './useRadio';
export { radioClassNames, useRadioStyles_unstable } from './useRadioStyles.styles';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Radio/index.ts"],"sourcesContent":["export { Radio } from './Radio';\nexport type {\n RadioOnChangeData,\n RadioProps,\n RadioSlots,\n RadioState,\n RadioBaseProps,\n RadioBaseState,\n} from './Radio.types';\nexport { renderRadio_unstable } from './renderRadio';\nexport { useRadio_unstable, useRadioBase_unstable } from './useRadio';\nexport { radioClassNames, useRadioStyles_unstable } from './useRadioStyles.styles';\n"],"names":["Radio","renderRadio_unstable","useRadio_unstable","useRadioBase_unstable","radioClassNames","useRadioStyles_unstable"],"mappings":"AAAA,SAASA,KAAK,QAAQ,UAAU;AAShC,SAASC,oBAAoB,QAAQ,gBAAgB;AACrD,SAASC,iBAAiB,EAAEC,qBAAqB,QAAQ,aAAa;AACtE,SAASC,eAAe,EAAEC,uBAAuB,QAAQ,0BAA0B"}

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 Radio
*/ export const renderRadio_unstable = (state)=>{
assertSlots(state);
return /*#__PURE__*/ _jsxs(state.root, {
children: [
/*#__PURE__*/ _jsx(state.input, {}),
/*#__PURE__*/ _jsx(state.indicator, {}),
state.label && /*#__PURE__*/ _jsx(state.label, {})
]
});
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Radio/renderRadio.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 { RadioSlots, RadioBaseState } from './Radio.types';\n\n/**\n * Render the final JSX of Radio\n */\nexport const renderRadio_unstable = (state: RadioBaseState): JSXElement => {\n assertSlots<RadioSlots>(state);\n\n return (\n <state.root>\n <state.input />\n <state.indicator />\n {state.label && <state.label />}\n </state.root>\n );\n};\n"],"names":["assertSlots","renderRadio_unstable","state","root","input","indicator","label"],"mappings":"AAAA,0BAA0B,GAC1B,iDAAiD;AAEjD,SAASA,WAAW,QAAQ,4BAA4B;AAIxD;;CAEC,GACD,OAAO,MAAMC,uBAAuB,CAACC;IACnCF,YAAwBE;IAExB,qBACE,MAACA,MAAMC,IAAI;;0BACT,KAACD,MAAME,KAAK;0BACZ,KAACF,MAAMG,SAAS;YACfH,MAAMI,KAAK,kBAAI,KAACJ,MAAMI,KAAK;;;AAGlC,EAAE"}

View File

@@ -0,0 +1,103 @@
'use client';
import * as React from 'react';
import { Label } from '@fluentui/react-label';
import { getPartitionedNativeProps, mergeCallbacks, useId, slot } from '@fluentui/react-utilities';
import { useRadioGroupContextValue_unstable } from '../../contexts/RadioGroupContext';
import { useFocusWithin } from '@fluentui/react-tabster';
/**
* Create the state required to render Radio.
*
* The returned state can be modified with hooks such as useRadioStyles_unstable,
* before being passed to renderRadio_unstable.
*
* @param props - props from this instance of Radio
* @param ref - reference to `<input>` element of Radio
*/ export const useRadio_unstable = (props, ref)=>{
const state = useRadioBase_unstable(props, ref);
return {
...state,
// eslint-disable-next-line @typescript-eslint/no-deprecated
components: {
...state.components,
label: Label
},
label: slot.optional(props.label, {
defaultProps: {
...state.label
},
elementType: Label
})
};
};
/**
* Create the state required to render Radio.
*
* The returned state can be modified with hooks such as useRadioStyles_unstable,
* before being passed to renderRadio_unstable.
*
* @param props - props from this instance of Radio
* @param ref - reference to `<input>` element of Radio
*/ export const useRadioBase_unstable = (props, ref)=>{
const group = useRadioGroupContextValue_unstable();
const { name = group.name, checked = group.value !== undefined ? group.value === props.value : undefined, defaultChecked = group.defaultValue !== undefined ? group.defaultValue === props.value : undefined, labelPosition = group.layout === 'horizontal-stacked' ? 'below' : 'after', disabled = group.disabled, required = group.required, 'aria-describedby': ariaDescribedBy = group['aria-describedby'], onChange } = props;
const nativeProps = getPartitionedNativeProps({
props,
primarySlotTagName: 'input',
excludedPropNames: [
'checked',
'defaultChecked',
'onChange'
]
});
const root = slot.always(props.root, {
defaultProps: {
ref: useFocusWithin(),
...nativeProps.root
},
elementType: 'span'
});
const input = slot.always(props.input, {
defaultProps: {
ref,
type: 'radio',
id: useId('radio-', nativeProps.primary.id),
name,
checked,
defaultChecked,
disabled,
required,
'aria-describedby': ariaDescribedBy,
...nativeProps.primary
},
elementType: 'input'
});
input.onChange = mergeCallbacks(input.onChange, (ev)=>onChange === null || onChange === void 0 ? void 0 : onChange(ev, {
value: ev.currentTarget.value
}));
const label = slot.optional(props.label, {
defaultProps: {
htmlFor: input.id,
disabled: input.disabled
},
elementType: 'label'
});
const indicator = slot.always(props.indicator, {
defaultProps: {
'aria-hidden': true
},
elementType: 'div'
});
return {
labelPosition,
components: {
root: 'span',
input: 'input',
label: 'label',
indicator: 'div'
},
root,
input,
label,
indicator
};
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,91 @@
'use client';
import { createFocusOutlineStyle } from '@fluentui/react-tabster';
import { tokens } from '@fluentui/react-theme';
import { __resetStyles, __styles, mergeClasses } from '@griffel/react';
export const radioClassNames = {
root: 'fui-Radio',
indicator: 'fui-Radio__indicator',
input: 'fui-Radio__input',
label: 'fui-Radio__label'
};
// The indicator size is used by the indicator and label styles
const indicatorSize = '16px';
const useRootBaseClassName = /*#__PURE__*/__resetStyles("r1siqwd8", "rmnplyc", {
r: [".r1siqwd8{display:inline-flex;position:relative;}", ".r1siqwd8:focus{outline-style:none;}", ".r1siqwd8:focus-visible{outline-style:none;}", ".r1siqwd8[data-fui-focus-within]:focus-within{border-top-color:transparent;border-right-color:transparent;border-bottom-color:transparent;border-left-color:transparent;}", ".r1siqwd8[data-fui-focus-within]:focus-within::after{content:\"\";position:absolute;pointer-events:none;z-index:1;border:2px solid var(--colorStrokeFocus2);border-radius:var(--borderRadiusMedium);top:calc(2px * -1);right:calc(2px * -1);bottom:calc(2px * -1);left:calc(2px * -1);}", ".rmnplyc{display:inline-flex;position:relative;}", ".rmnplyc:focus{outline-style:none;}", ".rmnplyc:focus-visible{outline-style:none;}", ".rmnplyc[data-fui-focus-within]:focus-within{border-top-color:transparent;border-left-color:transparent;border-bottom-color:transparent;border-right-color:transparent;}", ".rmnplyc[data-fui-focus-within]:focus-within::after{content:\"\";position:absolute;pointer-events:none;z-index:1;border:2px solid var(--colorStrokeFocus2);border-radius:var(--borderRadiusMedium);top:calc(2px * -1);left:calc(2px * -1);bottom:calc(2px * -1);right:calc(2px * -1);}"],
s: ["@media (forced-colors: active){.r1siqwd8[data-fui-focus-within]:focus-within::after{border-top-color:Highlight;border-right-color:Highlight;border-bottom-color:Highlight;border-left-color:Highlight;}}", "@media (forced-colors: active){.rmnplyc[data-fui-focus-within]:focus-within::after{border-top-color:Highlight;border-left-color:Highlight;border-bottom-color:Highlight;border-right-color:Highlight;}}"]
});
const useRootStyles = /*#__PURE__*/__styles({
vertical: {
Beiy3e4: "f1vx9l62",
Bt984gj: "f122n59"
}
}, {
d: [".f1vx9l62{flex-direction:column;}", ".f122n59{align-items:center;}"]
});
const useInputBaseClassName = /*#__PURE__*/__resetStyles("rg1upok", "rzwdzb4", {
r: [".rg1upok{position:absolute;left:0;top:0;width:calc(16px + 2 * var(--spacingHorizontalS));height:100%;box-sizing:border-box;margin:0;opacity:0;}", ".rg1upok:enabled{cursor:pointer;}", ".rg1upok:enabled~.fui-Radio__label{cursor:pointer;}", ".rg1upok:enabled:not(:checked)~.fui-Radio__label{color:var(--colorNeutralForeground3);}", ".rg1upok:enabled:not(:checked)~.fui-Radio__indicator{border-color:var(--colorNeutralStrokeAccessible);}", ".rg1upok:enabled:not(:checked):hover~.fui-Radio__label{color:var(--colorNeutralForeground2);}", ".rg1upok:enabled:not(:checked):hover~.fui-Radio__indicator{border-color:var(--colorNeutralStrokeAccessibleHover);}", ".rg1upok:enabled:not(:checked):hover:active~.fui-Radio__label{color:var(--colorNeutralForeground1);}", ".rg1upok:enabled:not(:checked):hover:active~.fui-Radio__indicator{border-color:var(--colorNeutralStrokeAccessiblePressed);}", ".rg1upok:enabled:checked~.fui-Radio__label{color:var(--colorNeutralForeground1);}", ".rg1upok:enabled:checked~.fui-Radio__indicator{border-color:var(--colorCompoundBrandStroke);color:var(--colorCompoundBrandForeground1);}", ".rg1upok:enabled:checked:hover~.fui-Radio__indicator{border-color:var(--colorCompoundBrandStrokeHover);color:var(--colorCompoundBrandForeground1Hover);}", ".rg1upok:enabled:checked:hover:active~.fui-Radio__indicator{border-color:var(--colorCompoundBrandStrokePressed);color:var(--colorCompoundBrandForeground1Pressed);}", ".rg1upok:disabled~.fui-Radio__label{color:var(--colorNeutralForegroundDisabled);cursor:default;}", ".rg1upok:disabled~.fui-Radio__indicator{border-color:var(--colorNeutralStrokeDisabled);color:var(--colorNeutralForegroundDisabled);}", ".rzwdzb4{position:absolute;right:0;top:0;width:calc(16px + 2 * var(--spacingHorizontalS));height:100%;box-sizing:border-box;margin:0;opacity:0;}", ".rzwdzb4:enabled{cursor:pointer;}", ".rzwdzb4:enabled~.fui-Radio__label{cursor:pointer;}", ".rzwdzb4:enabled:not(:checked)~.fui-Radio__label{color:var(--colorNeutralForeground3);}", ".rzwdzb4:enabled:not(:checked)~.fui-Radio__indicator{border-color:var(--colorNeutralStrokeAccessible);}", ".rzwdzb4:enabled:not(:checked):hover~.fui-Radio__label{color:var(--colorNeutralForeground2);}", ".rzwdzb4:enabled:not(:checked):hover~.fui-Radio__indicator{border-color:var(--colorNeutralStrokeAccessibleHover);}", ".rzwdzb4:enabled:not(:checked):hover:active~.fui-Radio__label{color:var(--colorNeutralForeground1);}", ".rzwdzb4:enabled:not(:checked):hover:active~.fui-Radio__indicator{border-color:var(--colorNeutralStrokeAccessiblePressed);}", ".rzwdzb4:enabled:checked~.fui-Radio__label{color:var(--colorNeutralForeground1);}", ".rzwdzb4:enabled:checked~.fui-Radio__indicator{border-color:var(--colorCompoundBrandStroke);color:var(--colorCompoundBrandForeground1);}", ".rzwdzb4:enabled:checked:hover~.fui-Radio__indicator{border-color:var(--colorCompoundBrandStrokeHover);color:var(--colorCompoundBrandForeground1Hover);}", ".rzwdzb4:enabled:checked:hover:active~.fui-Radio__indicator{border-color:var(--colorCompoundBrandStrokePressed);color:var(--colorCompoundBrandForeground1Pressed);}", ".rzwdzb4:disabled~.fui-Radio__label{color:var(--colorNeutralForegroundDisabled);cursor:default;}", ".rzwdzb4:disabled~.fui-Radio__indicator{border-color:var(--colorNeutralStrokeDisabled);color:var(--colorNeutralForegroundDisabled);}"],
s: ["@media (forced-colors: active){.rg1upok:enabled:not(:checked)~.fui-Radio__indicator{border-color:ButtonBorder;}}", "@media (forced-colors: active){.rg1upok:enabled:checked~.fui-Radio__indicator{border-color:Highlight;color:Highlight;}.rg1upok:enabled:checked~.fui-Radio__indicator::after{background-color:Highlight;}}", "@media (forced-colors: active){.rg1upok:disabled~.fui-Radio__label{color:GrayText;}}", "@media (forced-colors: active){.rg1upok:disabled~.fui-Radio__indicator{border-color:GrayText;color:GrayText;}.rg1upok:disabled~.fui-Radio__indicator::after{background-color:GrayText;}}", "@media (forced-colors: active){.rzwdzb4:enabled:not(:checked)~.fui-Radio__indicator{border-color:ButtonBorder;}}", "@media (forced-colors: active){.rzwdzb4:enabled:checked~.fui-Radio__indicator{border-color:Highlight;color:Highlight;}.rzwdzb4:enabled:checked~.fui-Radio__indicator::after{background-color:Highlight;}}", "@media (forced-colors: active){.rzwdzb4:disabled~.fui-Radio__label{color:GrayText;}}", "@media (forced-colors: active){.rzwdzb4:disabled~.fui-Radio__indicator{border-color:GrayText;color:GrayText;}.rzwdzb4:disabled~.fui-Radio__indicator::after{background-color:GrayText;}}"]
});
const useInputStyles = /*#__PURE__*/__styles({
below: {
a9b677: "fly5x3f",
Bqenvij: "f1je6zif"
},
defaultIndicator: {
Blbys7f: "f9ma1gx"
},
customIndicator: {
Bj53wkj: "f12zxao0"
}
}, {
d: [".fly5x3f{width:100%;}", ".f1je6zif{height:calc(16px + 2 * var(--spacingVerticalS));}", ".f9ma1gx:checked~.fui-Radio__indicator::after{content:\"\";}", ".f12zxao0:not(:checked)~.fui-Radio__indicator>*{opacity:0;}"]
});
const useIndicatorBaseClassName = /*#__PURE__*/__resetStyles("rwtekvw", null, [".rwtekvw{position:relative;width:16px;height:16px;font-size:12px;box-sizing:border-box;flex-shrink:0;display:flex;align-items:center;justify-content:center;overflow:hidden;border:var(--strokeWidthThin) solid;border-radius:var(--borderRadiusCircular);margin:var(--spacingVerticalS) var(--spacingHorizontalS);fill:currentColor;pointer-events:none;}", ".rwtekvw::after{position:absolute;width:16px;height:16px;border-radius:var(--borderRadiusCircular);transform:scale(0.625);background-color:currentColor;}"]);
// Can't use makeResetStyles here because Label is a component that may itself use makeResetStyles.
const useLabelStyles = /*#__PURE__*/__styles({
base: {
qb2dma: "f7nlbp4",
Byoj8tv: 0,
uwmqm3: 0,
z189sj: 0,
z8tnut: 0,
B0ocmuz: "f1f5q0n8"
},
after: {
uwmqm3: ["fruq291", "f7x41pl"],
B6of3ja: "fjzwpt6",
jrapky: "fh6j2fo"
},
below: {
z8tnut: "f1ywm7hm",
fsow6f: "f17mccla"
}
}, {
d: [".f7nlbp4{align-self:center;}", [".f1f5q0n8{padding:var(--spacingVerticalS) var(--spacingHorizontalS);}", {
p: -1
}], ".fruq291{padding-left:var(--spacingHorizontalXS);}", ".f7x41pl{padding-right:var(--spacingHorizontalXS);}", ".fjzwpt6{margin-top:calc((16px - var(--lineHeightBase300)) / 2);}", ".fh6j2fo{margin-bottom:calc((16px - var(--lineHeightBase300)) / 2);}", ".f1ywm7hm{padding-top:var(--spacingVerticalXS);}", ".f17mccla{text-align:center;}"]
});
/**
* Apply styling to the Radio slots based on the state
*/
export const useRadioStyles_unstable = state => {
'use no memo';
const {
labelPosition
} = state;
const rootBaseClassName = useRootBaseClassName();
const rootStyles = useRootStyles();
state.root.className = mergeClasses(radioClassNames.root, rootBaseClassName, labelPosition === 'below' && rootStyles.vertical, state.root.className);
const inputBaseClassName = useInputBaseClassName();
const inputStyles = useInputStyles();
state.input.className = mergeClasses(radioClassNames.input, inputBaseClassName, labelPosition === 'below' && inputStyles.below, state.indicator.children ? inputStyles.customIndicator : inputStyles.defaultIndicator, state.input.className);
const indicatorBaseClassName = useIndicatorBaseClassName();
state.indicator.className = mergeClasses(radioClassNames.indicator, indicatorBaseClassName, state.indicator.className);
const labelStyles = useLabelStyles();
if (state.label) {
state.label.className = mergeClasses(radioClassNames.label, labelStyles.base, labelStyles[labelPosition], state.label.className);
}
return state;
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,202 @@
'use client';
import { createFocusOutlineStyle } from '@fluentui/react-tabster';
import { tokens } from '@fluentui/react-theme';
import { makeResetStyles, makeStyles, mergeClasses } from '@griffel/react';
export const radioClassNames = {
root: 'fui-Radio',
indicator: 'fui-Radio__indicator',
input: 'fui-Radio__input',
label: 'fui-Radio__label'
};
// The indicator size is used by the indicator and label styles
const indicatorSize = '16px';
const useRootBaseClassName = makeResetStyles({
display: 'inline-flex',
position: 'relative',
...createFocusOutlineStyle({
style: {},
selector: 'focus-within'
})
});
const useRootStyles = makeStyles({
vertical: {
flexDirection: 'column',
alignItems: 'center'
}
});
const useInputBaseClassName = makeResetStyles({
position: 'absolute',
left: 0,
top: 0,
width: `calc(${indicatorSize} + 2 * ${tokens.spacingHorizontalS})`,
height: '100%',
boxSizing: 'border-box',
margin: 0,
opacity: 0,
':enabled': {
cursor: 'pointer',
[`& ~ .${radioClassNames.label}`]: {
cursor: 'pointer'
}
},
// Colors for the unchecked state
':enabled:not(:checked)': {
[`& ~ .${radioClassNames.label}`]: {
color: tokens.colorNeutralForeground3
},
[`& ~ .${radioClassNames.indicator}`]: {
borderColor: tokens.colorNeutralStrokeAccessible,
'@media (forced-colors: active)': {
borderColor: 'ButtonBorder'
}
},
':hover': {
[`& ~ .${radioClassNames.label}`]: {
color: tokens.colorNeutralForeground2
},
[`& ~ .${radioClassNames.indicator}`]: {
borderColor: tokens.colorNeutralStrokeAccessibleHover
}
},
':hover:active': {
[`& ~ .${radioClassNames.label}`]: {
color: tokens.colorNeutralForeground1
},
[`& ~ .${radioClassNames.indicator}`]: {
borderColor: tokens.colorNeutralStrokeAccessiblePressed
}
}
},
// Colors for the checked state
':enabled:checked': {
[`& ~ .${radioClassNames.label}`]: {
color: tokens.colorNeutralForeground1
},
[`& ~ .${radioClassNames.indicator}`]: {
borderColor: tokens.colorCompoundBrandStroke,
color: tokens.colorCompoundBrandForeground1,
'@media (forced-colors: active)': {
borderColor: 'Highlight',
color: 'Highlight',
'::after': {
backgroundColor: 'Highlight'
}
}
},
':hover': {
[`& ~ .${radioClassNames.indicator}`]: {
borderColor: tokens.colorCompoundBrandStrokeHover,
color: tokens.colorCompoundBrandForeground1Hover
}
},
':hover:active': {
[`& ~ .${radioClassNames.indicator}`]: {
borderColor: tokens.colorCompoundBrandStrokePressed,
color: tokens.colorCompoundBrandForeground1Pressed
}
}
},
// Colors for the disabled state
':disabled': {
[`& ~ .${radioClassNames.label}`]: {
color: tokens.colorNeutralForegroundDisabled,
cursor: 'default',
'@media (forced-colors: active)': {
color: 'GrayText'
}
},
[`& ~ .${radioClassNames.indicator}`]: {
borderColor: tokens.colorNeutralStrokeDisabled,
color: tokens.colorNeutralForegroundDisabled,
'@media (forced-colors: active)': {
borderColor: 'GrayText',
color: 'GrayText',
'::after': {
backgroundColor: 'GrayText'
}
}
}
}
});
const useInputStyles = makeStyles({
below: {
width: '100%',
height: `calc(${indicatorSize} + 2 * ${tokens.spacingVerticalS})`
},
// If the indicator has no children, use the ::after pseudo-element for the checked state
defaultIndicator: {
[`:checked ~ .${radioClassNames.indicator}::after`]: {
content: '""'
}
},
// If the indicator has a child, hide it until the radio is checked
customIndicator: {
[`:not(:checked) ~ .${radioClassNames.indicator} > *`]: {
opacity: '0'
}
}
});
const useIndicatorBaseClassName = makeResetStyles({
position: 'relative',
width: indicatorSize,
height: indicatorSize,
fontSize: '12px',
boxSizing: 'border-box',
flexShrink: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
border: tokens.strokeWidthThin + ' solid',
borderRadius: tokens.borderRadiusCircular,
margin: tokens.spacingVerticalS + ' ' + tokens.spacingHorizontalS,
fill: 'currentColor',
pointerEvents: 'none',
'::after': {
position: 'absolute',
width: indicatorSize,
height: indicatorSize,
borderRadius: tokens.borderRadiusCircular,
// Use a transform to avoid pixel rounding errors at 125% DPI
// https://github.com/microsoft/fluentui/issues/30025
transform: 'scale(0.625)',
backgroundColor: 'currentColor'
}
});
// Can't use makeResetStyles here because Label is a component that may itself use makeResetStyles.
const useLabelStyles = makeStyles({
base: {
alignSelf: 'center',
padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalS}`
},
after: {
paddingLeft: tokens.spacingHorizontalXS,
// Use a (negative) margin to account for the difference between the indicator's height and the label's line height.
// This prevents the label from expanding the height of the Radio, but preserves line height if the label wraps.
marginTop: `calc((${indicatorSize} - ${tokens.lineHeightBase300}) / 2)`,
marginBottom: `calc((${indicatorSize} - ${tokens.lineHeightBase300}) / 2)`
},
below: {
paddingTop: tokens.spacingVerticalXS,
textAlign: 'center'
}
});
/**
* Apply styling to the Radio slots based on the state
*/ export const useRadioStyles_unstable = (state)=>{
'use no memo';
const { labelPosition } = state;
const rootBaseClassName = useRootBaseClassName();
const rootStyles = useRootStyles();
state.root.className = mergeClasses(radioClassNames.root, rootBaseClassName, labelPosition === 'below' && rootStyles.vertical, state.root.className);
const inputBaseClassName = useInputBaseClassName();
const inputStyles = useInputStyles();
state.input.className = mergeClasses(radioClassNames.input, inputBaseClassName, labelPosition === 'below' && inputStyles.below, state.indicator.children ? inputStyles.customIndicator : inputStyles.defaultIndicator, state.input.className);
const indicatorBaseClassName = useIndicatorBaseClassName();
state.indicator.className = mergeClasses(radioClassNames.indicator, indicatorBaseClassName, state.indicator.className);
const labelStyles = useLabelStyles();
if (state.label) {
state.label.className = mergeClasses(radioClassNames.label, labelStyles.base, labelStyles[labelPosition], state.label.className);
}
return state;
};

File diff suppressed because one or more lines are too long