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,17 @@
'use client';
import * as React from 'react';
import { renderMenuButton_unstable } from './renderMenuButton';
import { useMenuButton_unstable } from './useMenuButton';
import { useMenuButtonStyles_unstable } from './useMenuButtonStyles.styles';
import { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts';
/**
* MenuButtons are buttons that have a chevron icon after the button contents and are usually clicked to open/close
* menus.
*/ export const MenuButton = /*#__PURE__*/ React.forwardRef((props, ref)=>{
const state = useMenuButton_unstable(props, ref);
useMenuButtonStyles_unstable(state);
useCustomStyleHook_unstable('useMenuButtonStyles_unstable')(state);
return renderMenuButton_unstable(state);
// Casting is required due to lack of distributive union to support unions on @types/react
});
MenuButton.displayName = 'MenuButton';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/MenuButton/MenuButton.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { renderMenuButton_unstable } from './renderMenuButton';\nimport { useMenuButton_unstable } from './useMenuButton';\nimport { useMenuButtonStyles_unstable } from './useMenuButtonStyles.styles';\nimport type { MenuButtonProps } from './MenuButton.types';\nimport type { ForwardRefComponent } from '@fluentui/react-utilities';\nimport { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts';\n\n/**\n * MenuButtons are buttons that have a chevron icon after the button contents and are usually clicked to open/close\n * menus.\n */\nexport const MenuButton: ForwardRefComponent<MenuButtonProps> = React.forwardRef((props, ref) => {\n const state = useMenuButton_unstable(props, ref);\n\n useMenuButtonStyles_unstable(state);\n\n useCustomStyleHook_unstable('useMenuButtonStyles_unstable')(state);\n\n return renderMenuButton_unstable(state);\n // Casting is required due to lack of distributive union to support unions on @types/react\n}) as ForwardRefComponent<MenuButtonProps>;\n\nMenuButton.displayName = 'MenuButton';\n"],"names":["React","renderMenuButton_unstable","useMenuButton_unstable","useMenuButtonStyles_unstable","useCustomStyleHook_unstable","MenuButton","forwardRef","props","ref","state","displayName"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAC/B,SAASC,yBAAyB,QAAQ,qBAAqB;AAC/D,SAASC,sBAAsB,QAAQ,kBAAkB;AACzD,SAASC,4BAA4B,QAAQ,+BAA+B;AAG5E,SAASC,2BAA2B,QAAQ,kCAAkC;AAE9E;;;CAGC,GACD,OAAO,MAAMC,2BAAmDL,MAAMM,UAAU,CAAC,CAACC,OAAOC;IACvF,MAAMC,QAAQP,uBAAuBK,OAAOC;IAE5CL,6BAA6BM;IAE7BL,4BAA4B,gCAAgCK;IAE5D,OAAOR,0BAA0BQ;AACjC,0FAA0F;AAC5F,GAA2C;AAE3CJ,WAAWK,WAAW,GAAG"}

View File

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

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/MenuButton/MenuButton.types.ts"],"sourcesContent":["import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities';\nimport type { ButtonProps, ButtonSlots, ButtonState } from '../Button/Button.types';\n\nexport type MenuButtonSlots = ButtonSlots & {\n /**\n * Menu icon that indicates that this button has a menu that can be expanded.\n */\n menuIcon?: Slot<'span'>;\n};\n\nexport type MenuButtonProps = ComponentProps<MenuButtonSlots> &\n Pick<ButtonProps, 'appearance' | 'disabledFocusable' | 'disabled' | 'shape' | 'size'>;\n\nexport type MenuButtonState = ComponentState<MenuButtonSlots> &\n Omit<ButtonState, keyof ButtonSlots | 'components' | 'iconPosition'>;\n"],"names":[],"mappings":"AAaA,WACuE"}

View File

@@ -0,0 +1,4 @@
export { MenuButton } from './MenuButton';
export { renderMenuButton_unstable } from './renderMenuButton';
export { useMenuButton_unstable } from './useMenuButton';
export { menuButtonClassNames, useMenuButtonStyles_unstable } from './useMenuButtonStyles.styles';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/MenuButton/index.ts"],"sourcesContent":["export type { MenuButtonProps, MenuButtonSlots, MenuButtonState } from './MenuButton.types';\nexport { MenuButton } from './MenuButton';\nexport { renderMenuButton_unstable } from './renderMenuButton';\nexport { useMenuButton_unstable } from './useMenuButton';\nexport { menuButtonClassNames, useMenuButtonStyles_unstable } from './useMenuButtonStyles.styles';\n"],"names":["MenuButton","renderMenuButton_unstable","useMenuButton_unstable","menuButtonClassNames","useMenuButtonStyles_unstable"],"mappings":"AACA,SAASA,UAAU,QAAQ,eAAe;AAC1C,SAASC,yBAAyB,QAAQ,qBAAqB;AAC/D,SAASC,sBAAsB,QAAQ,kBAAkB;AACzD,SAASC,oBAAoB,EAAEC,4BAA4B,QAAQ,+BAA+B"}

View File

@@ -0,0 +1,15 @@
import { jsx as _jsx, jsxs as _jsxs } from "@fluentui/react-jsx-runtime/jsx-runtime";
import { assertSlots } from '@fluentui/react-utilities';
/**
* Renders a MenuButton component by passing the state defined props to the appropriate slots.
*/ export const renderMenuButton_unstable = (state)=>{
assertSlots(state);
const { icon, iconOnly } = state;
return /*#__PURE__*/ _jsxs(state.root, {
children: [
state.icon && /*#__PURE__*/ _jsx(state.icon, {}),
!iconOnly && state.root.children,
(!iconOnly || !(icon === null || icon === void 0 ? void 0 : icon.children)) && state.menuIcon && /*#__PURE__*/ _jsx(state.menuIcon, {})
]
});
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/MenuButton/renderMenuButton.tsx"],"sourcesContent":["/** @jsxRuntime automatic */\n/** @jsxImportSource @fluentui/react-jsx-runtime */\n\nimport { assertSlots } from '@fluentui/react-utilities';\nimport type { JSXElement } from '@fluentui/react-utilities';\n\nimport type { MenuButtonSlots, MenuButtonState } from './MenuButton.types';\n\n/**\n * Renders a MenuButton component by passing the state defined props to the appropriate slots.\n */\nexport const renderMenuButton_unstable = (state: MenuButtonState): JSXElement => {\n assertSlots<MenuButtonSlots>(state);\n const { icon, iconOnly } = state;\n\n return (\n <state.root>\n {state.icon && <state.icon />}\n {!iconOnly && state.root.children}\n {(!iconOnly || !icon?.children) && state.menuIcon && <state.menuIcon />}\n </state.root>\n );\n};\n"],"names":["assertSlots","renderMenuButton_unstable","state","icon","iconOnly","root","children","menuIcon"],"mappings":"AAAA,0BAA0B,GAC1B,iDAAiD;AAEjD,SAASA,WAAW,QAAQ,4BAA4B;AAKxD;;CAEC,GACD,OAAO,MAAMC,4BAA4B,CAACC;IACxCF,YAA6BE;IAC7B,MAAM,EAAEC,IAAI,EAAEC,QAAQ,EAAE,GAAGF;IAE3B,qBACE,MAACA,MAAMG,IAAI;;YACRH,MAAMC,IAAI,kBAAI,KAACD,MAAMC,IAAI;YACzB,CAACC,YAAYF,MAAMG,IAAI,CAACC,QAAQ;YAC/B,CAAA,CAACF,YAAY,EAACD,iBAAAA,2BAAAA,KAAMG,QAAQ,CAAD,KAAMJ,MAAMK,QAAQ,kBAAI,KAACL,MAAMK,QAAQ;;;AAG1E,EAAE"}

View File

@@ -0,0 +1,33 @@
'use client';
import * as React from 'react';
import { ChevronDownRegular } from '@fluentui/react-icons';
import { slot } from '@fluentui/react-utilities';
import { useButton_unstable } from '../Button/index';
/**
* Given user props, returns the final state for a MenuButton.
*/ export const useMenuButton_unstable = (props, ref)=>{
const { menuIcon, ...buttonProps } = props;
const buttonState = useButton_unstable(buttonProps, ref);
return {
...buttonState,
iconOnly: Boolean(!props.children),
// Slots definition
components: {
// eslint-disable-next-line @typescript-eslint/no-deprecated
...buttonState.components,
menuIcon: 'span'
},
root: {
...buttonState.root,
// force aria-expanded to be a boolean, not a string
'aria-expanded': props['aria-expanded'] ? props['aria-expanded'] === 'true' || props['aria-expanded'] === true : false
},
menuIcon: slot.optional(menuIcon, {
defaultProps: {
children: /*#__PURE__*/ React.createElement(ChevronDownRegular, null)
},
renderByDefault: true,
elementType: 'span'
})
};
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/MenuButton/useMenuButton.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { ChevronDownRegular } from '@fluentui/react-icons';\nimport { slot } from '@fluentui/react-utilities';\nimport { useButton_unstable } from '../Button/index';\nimport type { MenuButtonProps, MenuButtonState } from './MenuButton.types';\n\n/**\n * Given user props, returns the final state for a MenuButton.\n */\nexport const useMenuButton_unstable = (\n props: MenuButtonProps,\n ref: React.Ref<HTMLButtonElement | HTMLAnchorElement>,\n): MenuButtonState => {\n const { menuIcon, ...buttonProps } = props;\n const buttonState = useButton_unstable(buttonProps, ref);\n\n return {\n ...buttonState,\n iconOnly: Boolean(!props.children),\n\n // Slots definition\n components: {\n // eslint-disable-next-line @typescript-eslint/no-deprecated\n ...buttonState.components,\n menuIcon: 'span',\n },\n\n root: {\n ...buttonState.root,\n // force aria-expanded to be a boolean, not a string\n 'aria-expanded': props['aria-expanded']\n ? props['aria-expanded'] === 'true' || props['aria-expanded'] === true\n : false,\n },\n\n menuIcon: slot.optional(menuIcon, {\n defaultProps: {\n children: <ChevronDownRegular />,\n },\n renderByDefault: true,\n elementType: 'span',\n }),\n };\n};\n"],"names":["React","ChevronDownRegular","slot","useButton_unstable","useMenuButton_unstable","props","ref","menuIcon","buttonProps","buttonState","iconOnly","Boolean","children","components","root","optional","defaultProps","renderByDefault","elementType"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAC/B,SAASC,kBAAkB,QAAQ,wBAAwB;AAC3D,SAASC,IAAI,QAAQ,4BAA4B;AACjD,SAASC,kBAAkB,QAAQ,kBAAkB;AAGrD;;CAEC,GACD,OAAO,MAAMC,yBAAyB,CACpCC,OACAC;IAEA,MAAM,EAAEC,QAAQ,EAAE,GAAGC,aAAa,GAAGH;IACrC,MAAMI,cAAcN,mBAAmBK,aAAaF;IAEpD,OAAO;QACL,GAAGG,WAAW;QACdC,UAAUC,QAAQ,CAACN,MAAMO,QAAQ;QAEjC,mBAAmB;QACnBC,YAAY;YACV,4DAA4D;YAC5D,GAAGJ,YAAYI,UAAU;YACzBN,UAAU;QACZ;QAEAO,MAAM;YACJ,GAAGL,YAAYK,IAAI;YACnB,oDAAoD;YACpD,iBAAiBT,KAAK,CAAC,gBAAgB,GACnCA,KAAK,CAAC,gBAAgB,KAAK,UAAUA,KAAK,CAAC,gBAAgB,KAAK,OAChE;QACN;QAEAE,UAAUL,KAAKa,QAAQ,CAACR,UAAU;YAChCS,cAAc;gBACZJ,wBAAU,oBAACX;YACb;YACAgB,iBAAiB;YACjBC,aAAa;QACf;IACF;AACF,EAAE"}

View File

@@ -0,0 +1,119 @@
'use client';
import { iconFilledClassName, iconRegularClassName } from '@fluentui/react-icons';
import { tokens } from '@fluentui/react-theme';
import { mergeClasses, __styles, shorthands } from '@griffel/react';
import { useButtonStyles_unstable } from '../Button/useButtonStyles.styles';
export const menuButtonClassNames = {
root: 'fui-MenuButton',
icon: 'fui-MenuButton__icon',
menuIcon: 'fui-MenuButton__menuIcon'
};
const useRootExpandedStyles = /*#__PURE__*/__styles({
base: {
D0sxk3: "fxoiby5",
t6yez3: "f15q0o9g"
},
outline: {
g2u3we: "f1ly1fcm",
h3c5rm: ["fi8bssc", "fj6btzu"],
B9xav0g: "f1s9tnsa",
zhjwy3: ["fj6btzu", "fi8bssc"],
B4j52fo: "fgx37oo",
Bekrc4i: ["f130t4y6", "f1efpmoh"],
Bn0qgzm: "fv51ejd",
ibv6hh: ["f1efpmoh", "f130t4y6"],
sj55zd: "f14nttnl"
},
primary: {
De3pzq: "f8w4g0q"
},
secondary: {
De3pzq: "f1nfm20t",
g2u3we: "f1ly1fcm",
h3c5rm: ["fi8bssc", "fj6btzu"],
B9xav0g: "f1s9tnsa",
zhjwy3: ["fj6btzu", "fi8bssc"],
sj55zd: "f14nttnl"
},
subtle: {
De3pzq: "fq5gl1p",
sj55zd: "f1eryozh"
},
transparent: {
De3pzq: "f1q9pm1r",
sj55zd: "f1qj7y59"
}
}, {
d: [".fxoiby5 .fui-Icon-filled{display:inline;}", ".f15q0o9g .fui-Icon-regular{display:none;}", ".f1ly1fcm{border-top-color:var(--colorNeutralStroke1Selected);}", ".fi8bssc{border-right-color:var(--colorNeutralStroke1Selected);}", ".fj6btzu{border-left-color:var(--colorNeutralStroke1Selected);}", ".f1s9tnsa{border-bottom-color:var(--colorNeutralStroke1Selected);}", ".fgx37oo{border-top-width:var(--strokeWidthThicker);}", ".f130t4y6{border-right-width:var(--strokeWidthThicker);}", ".f1efpmoh{border-left-width:var(--strokeWidthThicker);}", ".fv51ejd{border-bottom-width:var(--strokeWidthThicker);}", ".f14nttnl{color:var(--colorNeutralForeground1Selected);}", ".f8w4g0q{background-color:var(--colorBrandBackgroundSelected);}", ".f1nfm20t{background-color:var(--colorNeutralBackground1Selected);}", ".fq5gl1p{background-color:var(--colorSubtleBackgroundSelected);}", ".f1eryozh{color:var(--colorNeutralForeground2Selected);}", ".f1q9pm1r{background-color:var(--colorTransparentBackgroundSelected);}", ".f1qj7y59{color:var(--colorNeutralForeground2BrandSelected);}"]
});
const useIconExpandedStyles = /*#__PURE__*/__styles({
outline: {
sj55zd: "f14nttnl"
},
primary: {},
secondary: {
sj55zd: "f14nttnl"
},
subtle: {
sj55zd: "f1qj7y59"
},
transparent: {
sj55zd: "f1qj7y59"
},
highContrast: {
Bahaeuw: "f1v3eptx"
}
}, {
d: [".f14nttnl{color:var(--colorNeutralForeground1Selected);}", ".f1qj7y59{color:var(--colorNeutralForeground2BrandSelected);}"],
m: [["@media (forced-colors: active){.f1v3eptx:hover{color:Highlight;}}", {
m: "(forced-colors: active)"
}]]
});
const useMenuIconStyles = /*#__PURE__*/__styles({
base: {
Bg96gwp: "fez10in"
},
small: {
Be2twd7: "f1ugzwwg",
Bqenvij: "fvblgha",
Bg96gwp: "fwrc4pm",
a9b677: "frx94fk"
},
medium: {
Be2twd7: "f1ugzwwg",
Bqenvij: "fvblgha",
Bg96gwp: "fwrc4pm",
a9b677: "frx94fk"
},
large: {
Be2twd7: "f4ybsrx",
Bqenvij: "fd461yt",
Bg96gwp: "faaz57k",
a9b677: "fjw5fx7"
},
notIconOnly: {
Frg6f3: ["fbyavb5", "fm0x6gh"]
}
}, {
d: [".fez10in{line-height:0;}", ".f1ugzwwg{font-size:12px;}", ".fvblgha{height:12px;}", ".fwrc4pm{line-height:var(--lineHeightBase200);}", ".frx94fk{width:12px;}", ".f4ybsrx{font-size:16px;}", ".fd461yt{height:16px;}", ".faaz57k{line-height:var(--lineHeightBase400);}", ".fjw5fx7{width:16px;}", ".fbyavb5{margin-left:var(--spacingHorizontalXS);}", ".fm0x6gh{margin-right:var(--spacingHorizontalXS);}"]
});
export const useMenuButtonStyles_unstable = state => {
'use no memo';
const rootExpandedStyles = useRootExpandedStyles();
const iconExpandedStyles = useIconExpandedStyles();
const menuIconStyles = useMenuIconStyles();
state.root.className = mergeClasses(menuButtonClassNames.root, state.root['aria-expanded'] && rootExpandedStyles.base, state.root['aria-expanded'] && rootExpandedStyles[state.appearance], state.root.className);
if (state.icon) {
state.icon.className = mergeClasses(menuButtonClassNames.icon, state.root['aria-expanded'] && iconExpandedStyles[state.appearance] && iconExpandedStyles.highContrast, state.icon.className);
}
if (state.menuIcon) {
state.menuIcon.className = mergeClasses(menuButtonClassNames.menuIcon, menuIconStyles.base, menuIconStyles[state.size], !state.iconOnly && menuIconStyles.notIconOnly, state.menuIcon.className);
}
useButtonStyles_unstable({
...state,
iconPosition: 'before'
});
return state;
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,113 @@
'use client';
import { iconFilledClassName, iconRegularClassName } from '@fluentui/react-icons';
import { tokens } from '@fluentui/react-theme';
import { mergeClasses, makeStyles, shorthands } from '@griffel/react';
import { useButtonStyles_unstable } from '../Button/useButtonStyles.styles';
export const menuButtonClassNames = {
root: 'fui-MenuButton',
icon: 'fui-MenuButton__icon',
menuIcon: 'fui-MenuButton__menuIcon'
};
const useRootExpandedStyles = makeStyles({
base: {
[`& .${iconFilledClassName}`]: {
display: 'inline'
},
[`& .${iconRegularClassName}`]: {
display: 'none'
}
},
// Appearance variations
outline: {
...shorthands.borderColor(tokens.colorNeutralStroke1Selected),
...shorthands.borderWidth(tokens.strokeWidthThicker),
color: tokens.colorNeutralForeground1Selected
},
primary: {
backgroundColor: tokens.colorBrandBackgroundSelected
},
secondary: {
backgroundColor: tokens.colorNeutralBackground1Selected,
...shorthands.borderColor(tokens.colorNeutralStroke1Selected),
color: tokens.colorNeutralForeground1Selected
},
subtle: {
backgroundColor: tokens.colorSubtleBackgroundSelected,
color: tokens.colorNeutralForeground2Selected
},
transparent: {
backgroundColor: tokens.colorTransparentBackgroundSelected,
color: tokens.colorNeutralForeground2BrandSelected
}
});
const useIconExpandedStyles = makeStyles({
// Appearance variations
outline: {
color: tokens.colorNeutralForeground1Selected
},
primary: {
},
secondary: {
color: tokens.colorNeutralForeground1Selected
},
subtle: {
color: tokens.colorNeutralForeground2BrandSelected
},
transparent: {
color: tokens.colorNeutralForeground2BrandSelected
},
highContrast: {
// High contrast styles
'@media (forced-colors: active)': {
':hover': {
color: 'Highlight'
}
}
}
});
const useMenuIconStyles = makeStyles({
base: {
lineHeight: 0
},
// Size appearance
small: {
fontSize: '12px',
height: '12px',
lineHeight: tokens.lineHeightBase200,
width: '12px'
},
medium: {
fontSize: '12px',
height: '12px',
lineHeight: tokens.lineHeightBase200,
width: '12px'
},
large: {
fontSize: '16px',
height: '16px',
lineHeight: tokens.lineHeightBase400,
width: '16px'
},
// Not-icon only
notIconOnly: {
marginLeft: tokens.spacingHorizontalXS
}
});
export const useMenuButtonStyles_unstable = (state)=>{
'use no memo';
const rootExpandedStyles = useRootExpandedStyles();
const iconExpandedStyles = useIconExpandedStyles();
const menuIconStyles = useMenuIconStyles();
state.root.className = mergeClasses(menuButtonClassNames.root, state.root['aria-expanded'] && rootExpandedStyles.base, state.root['aria-expanded'] && rootExpandedStyles[state.appearance], state.root.className);
if (state.icon) {
state.icon.className = mergeClasses(menuButtonClassNames.icon, state.root['aria-expanded'] && iconExpandedStyles[state.appearance] && iconExpandedStyles.highContrast, state.icon.className);
}
if (state.menuIcon) {
state.menuIcon.className = mergeClasses(menuButtonClassNames.menuIcon, menuIconStyles.base, menuIconStyles[state.size], !state.iconOnly && menuIconStyles.notIconOnly, state.menuIcon.className);
}
useButtonStyles_unstable({
...state,
iconPosition: 'before'
});
return state;
};

File diff suppressed because one or more lines are too long