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 { useCarouselNavButton_unstable } from './useCarouselNavButton';
import { renderCarouselNavButton_unstable } from './renderCarouselNavButton';
import { useCarouselNavButtonStyles_unstable } from './useCarouselNavButtonStyles.styles';
import { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts';
/**
* The child element of CarouselNav, a singular button that will set the carousels active value on click.
*/ export const CarouselNavButton = /*#__PURE__*/ React.forwardRef((props, ref)=>{
const state = useCarouselNavButton_unstable(props, ref);
useCarouselNavButtonStyles_unstable(state);
useCustomStyleHook_unstable('useCarouselNavButtonStyles_unstable')(state);
return renderCarouselNavButton_unstable(state);
});
CarouselNavButton.displayName = 'CarouselNavButton';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/CarouselNavButton/CarouselNavButton.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport type { ForwardRefComponent } from '@fluentui/react-utilities';\nimport { useCarouselNavButton_unstable } from './useCarouselNavButton';\nimport { renderCarouselNavButton_unstable } from './renderCarouselNavButton';\nimport { useCarouselNavButtonStyles_unstable } from './useCarouselNavButtonStyles.styles';\nimport type { CarouselNavButtonProps } from './CarouselNavButton.types';\nimport { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts';\n\n/**\n * The child element of CarouselNav, a singular button that will set the carousels active value on click.\n */\nexport const CarouselNavButton: ForwardRefComponent<CarouselNavButtonProps> = React.forwardRef((props, ref) => {\n const state = useCarouselNavButton_unstable(props, ref);\n\n useCarouselNavButtonStyles_unstable(state);\n useCustomStyleHook_unstable('useCarouselNavButtonStyles_unstable')(state);\n\n return renderCarouselNavButton_unstable(state);\n});\n\nCarouselNavButton.displayName = 'CarouselNavButton';\n"],"names":["React","useCarouselNavButton_unstable","renderCarouselNavButton_unstable","useCarouselNavButtonStyles_unstable","useCustomStyleHook_unstable","CarouselNavButton","forwardRef","props","ref","state","displayName"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAE/B,SAASC,6BAA6B,QAAQ,yBAAyB;AACvE,SAASC,gCAAgC,QAAQ,4BAA4B;AAC7E,SAASC,mCAAmC,QAAQ,sCAAsC;AAE1F,SAASC,2BAA2B,QAAQ,kCAAkC;AAE9E;;CAEC,GACD,OAAO,MAAMC,kCAAiEL,MAAMM,UAAU,CAAC,CAACC,OAAOC;IACrG,MAAMC,QAAQR,8BAA8BM,OAAOC;IAEnDL,oCAAoCM;IACpCL,4BAA4B,uCAAuCK;IAEnE,OAAOP,iCAAiCO;AAC1C,GAAG;AAEHJ,kBAAkBK,WAAW,GAAG"}

View File

@@ -0,0 +1,3 @@
/**
* State used in rendering CarouselNavButton
*/ export { };

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/CarouselNavButton/CarouselNavButton.types.ts"],"sourcesContent":["import { ARIAButtonSlotProps } from '@fluentui/react-aria';\nimport type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities';\nimport { CarouselNavState } from '../CarouselNav/CarouselNav.types';\n\nexport type CarouselNavButtonSlots = {\n /**\n * ARIA compliant nav buttons used to jump to pages\n */\n root: NonNullable<Slot<ARIAButtonSlotProps>>;\n};\n\n/**\n * CarouselNavButton Props\n */\nexport type CarouselNavButtonProps = ComponentProps<CarouselNavButtonSlots> & {};\n\n/**\n * State used in rendering CarouselNavButton\n */\nexport type CarouselNavButtonState = ComponentState<CarouselNavButtonSlots> & {\n /**\n * Enables selection state control\n */\n selected?: boolean;\n} & Pick<CarouselNavState, 'appearance'>;\n"],"names":[],"mappings":"AAgBA;;CAEC,GACD,WAKyC"}

View File

@@ -0,0 +1,4 @@
export { CarouselNavButton } from './CarouselNavButton';
export { renderCarouselNavButton_unstable } from './renderCarouselNavButton';
export { useCarouselNavButton_unstable } from './useCarouselNavButton';
export { carouselNavButtonClassNames, useCarouselNavButtonStyles_unstable } from './useCarouselNavButtonStyles.styles';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/CarouselNavButton/index.ts"],"sourcesContent":["export { CarouselNavButton } from './CarouselNavButton';\nexport type { CarouselNavButtonProps, CarouselNavButtonSlots, CarouselNavButtonState } from './CarouselNavButton.types';\nexport { renderCarouselNavButton_unstable } from './renderCarouselNavButton';\nexport { useCarouselNavButton_unstable } from './useCarouselNavButton';\nexport { carouselNavButtonClassNames, useCarouselNavButtonStyles_unstable } from './useCarouselNavButtonStyles.styles';\n"],"names":["CarouselNavButton","renderCarouselNavButton_unstable","useCarouselNavButton_unstable","carouselNavButtonClassNames","useCarouselNavButtonStyles_unstable"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,sBAAsB;AAExD,SAASC,gCAAgC,QAAQ,4BAA4B;AAC7E,SAASC,6BAA6B,QAAQ,yBAAyB;AACvE,SAASC,2BAA2B,EAAEC,mCAAmC,QAAQ,sCAAsC"}

View File

@@ -0,0 +1,9 @@
import { jsx as _jsx } from "@fluentui/react-jsx-runtime/jsx-runtime";
import { assertSlots } from '@fluentui/react-utilities';
/**
* Render the final JSX of CarouselNavButton
*/ export const renderCarouselNavButton_unstable = (state)=>{
assertSlots(state);
// TODO Add additional slots in the appropriate place
return /*#__PURE__*/ _jsx(state.root, {});
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/CarouselNavButton/renderCarouselNavButton.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 { CarouselNavButtonState, CarouselNavButtonSlots } from './CarouselNavButton.types';\n\n/**\n * Render the final JSX of CarouselNavButton\n */\nexport const renderCarouselNavButton_unstable = (state: CarouselNavButtonState): JSXElement => {\n assertSlots<CarouselNavButtonSlots>(state);\n\n // TODO Add additional slots in the appropriate place\n return <state.root />;\n};\n"],"names":["assertSlots","renderCarouselNavButton_unstable","state","root"],"mappings":"AAAA,0BAA0B,GAC1B,iDAAiD;AAEjD,SAASA,WAAW,QAAQ,4BAA4B;AAKxD;;CAEC,GACD,OAAO,MAAMC,mCAAmC,CAACC;IAC/CF,YAAoCE;IAEpC,qDAAqD;IACrD,qBAAO,KAACA,MAAMC,IAAI;AACpB,EAAE"}

View File

@@ -0,0 +1,76 @@
'use client';
import { useARIAButtonProps } from '@fluentui/react-aria';
import { useTabsterAttributes } from '@fluentui/react-tabster';
import { getIntrinsicElementProps, isHTMLElement, slot, useEventCallback, useIsomorphicLayoutEffect, useMergedRefs } from '@fluentui/react-utilities';
import * as React from 'react';
import { useCarouselContext_unstable as useCarouselContext } from '../CarouselContext';
import { useCarouselNavContext } from '../CarouselNav/CarouselNavContext';
import { useCarouselNavIndexContext } from '../CarouselNav/CarouselNavIndexContext';
/**
* Create the state required to render CarouselNavButton.
*
* The returned state can be modified with hooks such as useCarouselNavButtonStyles_unstable,
* before being passed to renderCarouselNavButton_unstable.
*
* @param props - props from this instance of CarouselNavButton
* @param ref - reference to root HTMLDivElement of CarouselNavButton
*/ export const useCarouselNavButton_unstable = (props, ref)=>{
const { onClick, as = 'button' } = props;
const { appearance } = useCarouselNavContext();
const index = useCarouselNavIndexContext();
const selectPageByIndex = useCarouselContext((ctx)=>ctx.selectPageByIndex);
const selected = useCarouselContext((ctx)=>ctx.activeIndex === index);
const subscribeForValues = useCarouselContext((ctx)=>ctx.subscribeForValues);
const resetAutoplay = useCarouselContext((ctx)=>ctx.resetAutoplay);
const handleClick = useEventCallback((event)=>{
onClick === null || onClick === void 0 ? void 0 : onClick(event);
if (!event.defaultPrevented && isHTMLElement(event.target)) {
selectPageByIndex(event, index);
}
// Ensure any autoplay timers are extended/reset
resetAutoplay();
});
const defaultTabProps = useTabsterAttributes({
focusable: {
isDefault: selected
}
});
const buttonRef = React.useRef(undefined);
const _carouselButton = slot.always(getIntrinsicElementProps(as, useARIAButtonProps(props.as, props)), {
elementType: 'button',
defaultProps: {
ref: useMergedRefs(ref, buttonRef),
role: 'tab',
type: 'button',
'aria-selected': selected,
...defaultTabProps
}
});
useIsomorphicLayoutEffect(()=>{
return subscribeForValues((data)=>{
var _data_groupIndexList;
var _data_groupIndexList_index;
const controlList = (_data_groupIndexList_index = (_data_groupIndexList = data.groupIndexList) === null || _data_groupIndexList === void 0 ? void 0 : _data_groupIndexList[index]) !== null && _data_groupIndexList_index !== void 0 ? _data_groupIndexList_index : [];
const _controlledSlideIds = controlList.map((slideIndex)=>{
return data.slideNodes[slideIndex].id;
}).join(' ');
if (buttonRef.current) {
buttonRef.current.setAttribute('aria-controls', _controlledSlideIds);
}
});
}, [
index,
subscribeForValues
]);
// Override onClick
_carouselButton.onClick = handleClick;
const state = {
selected,
appearance,
components: {
root: 'button'
},
root: _carouselButton
};
return state;
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,210 @@
'use client';
import { __styles, mergeClasses, shorthands } from '@griffel/react';
import { createCustomFocusIndicatorStyle } from '@fluentui/react-tabster';
import { tokens } from '@fluentui/react-theme';
export const carouselNavButtonClassNames = {
root: 'fui-CarouselNavButton'
};
/**
* Styles for the root slot
*/
const useStyles = /*#__PURE__*/__styles({
root: {
Bceei9c: "f1k6fduh",
Bkecrkj: "fc5wo7j",
a9b677: "f1van5z7",
Bqenvij: "f1fkmctz",
Byoj8tv: 0,
uwmqm3: 0,
z189sj: 0,
z8tnut: 0,
B0ocmuz: "f1f5q0n8",
B7ck84d: "f1e4lqlz",
De3pzq: "f1c21dwh",
B4j52fo: "fre7gi1",
Bekrc4i: ["f1358rze", "f1rvrf73"],
Bn0qgzm: "fqdk4by",
ibv6hh: ["f1rvrf73", "f1358rze"],
Bsft5z2: "f13zj6fq",
ap17g6: "f2gz7yw",
li1rpt: "f1gw3sf2",
d9w3h3: 0,
B3778ie: 0,
B4j8arr: 0,
Bl18szs: 0,
Blrzh8d: "f1x820d0",
Bjuhk93: 0,
B1q35kw: 0,
Bw17bha: 0,
Bcgy8vk: 0,
Du69r6: 0,
Gp14am: 0,
vfts7: 0,
Bhxzhr1: 0,
G63luc: 0,
s924m2: 0,
Barhvk9: 0,
Ihftqj: 0,
wywymt: 0,
B0n5ga8: 0,
Bm2nyyq: 0,
xrcqlc: 0,
e1d83w: "fnwf5yv",
Dlnsje: "foue38v",
a2br6o: "fi4ui2s",
Bjyk6c5: "f1w4p7kh",
go7t6h: "fo5b2b9",
xdqbwx: "f16vizm6",
Hwb57: "fqolsir",
umgawz: "fim7wbh"
},
rootUnselected: {
Bw0xxkn: 0,
oeaueh: 0,
Bpd4iqm: 0,
Befb4lg: "f71xx7",
Byu6kyc: 0,
n8qw10: 0,
Bbjhlyh: 0,
i2cumq: 0,
Bunx835: 0,
Bdrgwmp: 0,
mqozju: 0,
lbo84a: 0,
Bksnhdo: 0,
Bci5o5g: 0,
u5e7qz: 0,
Bn40d3w: 0,
B7b6zxw: 0,
B8q5s1w: 0,
B5gfjzb: 0,
Bbcte9g: 0,
Bqz3imu: "f1j9b7x8",
Bj9ihqo: 0,
Bl51kww: 0,
B3bvztg: 0,
Btyt4dx: 0,
Brhw1f9: "f1tdm9ui",
Bw81rd7: 0,
kdpuga: 0,
dm238s: 0,
B6xbmo0: 0,
B3whbx2: "f2krc9w",
Bp15pi3: "f7x02et",
Bay5ve9: "f1ry2q4s",
Bni0232: "f1e9f9ku"
},
rootSelected: {
a9b677: "f1eh74fx",
Byoj8tv: 0,
uwmqm3: 0,
z189sj: 0,
z8tnut: 0,
B0ocmuz: "fwku66v",
Bw0xxkn: 0,
oeaueh: 0,
Bpd4iqm: 0,
Befb4lg: "f71xx7",
Byu6kyc: 0,
n8qw10: 0,
Bbjhlyh: 0,
i2cumq: 0,
Bunx835: 0,
Bdrgwmp: 0,
mqozju: 0,
lbo84a: 0,
Bksnhdo: 0,
Bci5o5g: 0,
u5e7qz: 0,
Bn40d3w: 0,
B7b6zxw: 0,
B8q5s1w: 0,
B5gfjzb: 0,
Bbcte9g: 0,
Bqz3imu: "f1j9b7x8",
Bj9ihqo: 0,
Bl51kww: 0,
B3bvztg: 0,
Btyt4dx: 0,
Brhw1f9: "f1tdm9ui",
Bw81rd7: 0,
kdpuga: 0,
dm238s: 0,
B6xbmo0: 0,
B3whbx2: "f2krc9w",
a2br6o: "f1v6lwa2",
d9w3h3: 0,
B3778ie: 0,
B4j8arr: 0,
Bl18szs: 0,
Blrzh8d: "fgm6wgx",
Bay5ve9: "f1ry2q4s",
Bni0232: "f1gxfet"
},
brand: {
Bjyk6c5: "fnrv5e1",
Bp15pi3: "fjsqi2x",
Glksuk: "frrwqtn",
Bay5ve9: "f9atwx8",
Blzl0y7: "fmmpig5",
Bni0232: "f1e9f9ku"
},
unselectedBrand: {
Bp15pi3: "f7x02et",
Bjyk6c5: "f1w4p7kh",
Bay5ve9: "f1ry2q4s",
Bni0232: "f1e9f9ku"
}
}, {
d: [".f1k6fduh{cursor:pointer;}", ".fc5wo7j{pointer-events:all;}", ".f1van5z7{width:var(--spacingHorizontalS);}", ".f1fkmctz{height:var(--spacingVerticalS);}", [".f1f5q0n8{padding:var(--spacingVerticalS) var(--spacingHorizontalS);}", {
p: -1
}], ".f1e4lqlz{box-sizing:content-box;}", ".f1c21dwh{background-color:var(--colorTransparentBackground);}", ".fre7gi1{border-top-width:0;}", ".f1358rze{border-right-width:0;}", ".f1rvrf73{border-left-width:0;}", ".fqdk4by{border-bottom-width:0;}", ".f13zj6fq::after{content:\"\";}", ".f2gz7yw::after{display:block;}", ".f1gw3sf2::after{box-sizing:border-box;}", [".f1x820d0::after{border-radius:50%;}", {
p: -1
}], [".fnwf5yv::after{border:none;}", {
p: -2
}], ".foue38v::after{height:var(--spacingVerticalS);}", ".fi4ui2s::after{width:var(--spacingHorizontalS);}", ".f1w4p7kh::after{background-color:var(--colorNeutralForeground1);}", ".fo5b2b9::after{color:var(--colorNeutralForeground1);}", [".f71xx7{outline:var(--strokeWidthThin) solid transparent;}", {
p: -1
}], [".f1j9b7x8[data-fui-focus-visible]{border:var(--strokeWidthThick) solid var(--colorStrokeFocus2);}", {
p: -2
}], [".f1tdm9ui[data-fui-focus-visible]{margin:calc(-1 * var(--strokeWidthThick));}", {
p: -1
}], [".f2krc9w[data-fui-focus-visible]{border-radius:var(--borderRadiusMedium);}", {
p: -1
}], ".f7x02et::after{opacity:0.6;}", ".f1eh74fx{width:var(--spacingHorizontalL);}", [".fwku66v{padding:var(--spacingVerticalS) var(--spacingHorizontalXS);}", {
p: -1
}], [".f71xx7{outline:var(--strokeWidthThin) solid transparent;}", {
p: -1
}], [".f1j9b7x8[data-fui-focus-visible]{border:var(--strokeWidthThick) solid var(--colorStrokeFocus2);}", {
p: -2
}], [".f1tdm9ui[data-fui-focus-visible]{margin:calc(-1 * var(--strokeWidthThick));}", {
p: -1
}], [".f2krc9w[data-fui-focus-visible]{border-radius:var(--borderRadiusMedium);}", {
p: -1
}], ".f1v6lwa2::after{width:var(--spacingHorizontalL);}", [".fgm6wgx::after{border-radius:4px;}", {
p: -1
}], ".fnrv5e1::after{background-color:var(--colorCompoundBrandBackground);}", ".fjsqi2x::after{opacity:1;}"],
m: [["@media (forced-colors: active){.f16vizm6::after{forced-color-adjust:none;}}", {
m: "(forced-colors: active)"
}], ["@media (forced-colors: active){.fqolsir::after{background-color:white;}}", {
m: "(forced-colors: active)"
}], ["@media (forced-colors: active){.fim7wbh::after{mix-blend-mode:difference;}}", {
m: "(forced-colors: active)"
}]],
h: [".f1ry2q4s:hover::after{opacity:0.75;}", ".frrwqtn:hover::after{background-color:var(--colorCompoundBrandBackgroundHover);}", ".f9atwx8:hover::after{opacity:1;}"],
a: [".f1e9f9ku:active::after{opacity:1;}", ".f1gxfet:active::after{opacity:0.65;}", ".fmmpig5:active::after{background-color:var(--colorCompoundBrandBackgroundPressed);}"]
});
/**
* Apply styling to the CarouselNavButton slots based on the state
*/
export const useCarouselNavButtonStyles_unstable = state => {
'use no memo';
const styles = useStyles();
const {
selected,
appearance
} = state;
state.root.className = mergeClasses(carouselNavButtonClassNames.root, styles.root, selected ? styles.rootSelected : styles.rootUnselected, appearance === 'brand' && styles.brand, !selected && appearance === 'brand' && styles.unselectedBrand, state.root.className);
return state;
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,126 @@
'use client';
import { makeStyles, mergeClasses, shorthands } from '@griffel/react';
import { createCustomFocusIndicatorStyle } from '@fluentui/react-tabster';
import { tokens } from '@fluentui/react-theme';
export const carouselNavButtonClassNames = {
root: 'fui-CarouselNavButton'
};
/**
* Styles for the root slot
*/ const useStyles = makeStyles({
root: {
cursor: 'pointer',
pointerEvents: 'all',
width: tokens.spacingHorizontalS,
height: tokens.spacingVerticalS,
padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalS}`,
boxSizing: 'content-box',
backgroundColor: tokens.colorTransparentBackground,
...shorthands.borderWidth(0),
'::after': {
content: '""',
display: 'block',
boxSizing: 'border-box',
borderRadius: '50%',
border: 'none',
height: tokens.spacingVerticalS,
width: tokens.spacingHorizontalS,
backgroundColor: tokens.colorNeutralForeground1,
color: tokens.colorNeutralForeground1,
'@media (forced-colors: active)': {
// Bypass OS high contrast with inverted blend mode (otherwise icon is invisible)
forcedColorAdjust: 'none',
backgroundColor: 'white',
mixBlendMode: 'difference'
}
}
},
rootUnselected: {
outline: `${tokens.strokeWidthThin} solid transparent`,
...createCustomFocusIndicatorStyle({
border: `${tokens.strokeWidthThick} solid ${tokens.colorStrokeFocus2}`,
margin: `calc(-1 * ${tokens.strokeWidthThick})`,
borderRadius: tokens.borderRadiusMedium
}),
'::after': {
opacity: 0.6
},
':hover': {
'::after': {
opacity: 0.75
}
},
':active': {
'::after': {
opacity: 1
}
}
},
rootSelected: {
width: tokens.spacingHorizontalL,
padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalXS}`,
outline: `${tokens.strokeWidthThin} solid transparent`,
...createCustomFocusIndicatorStyle({
border: `${tokens.strokeWidthThick} solid ${tokens.colorStrokeFocus2}`,
margin: `calc(-1 * ${tokens.strokeWidthThick})`,
borderRadius: tokens.borderRadiusMedium
}),
'::after': {
width: tokens.spacingHorizontalL,
borderRadius: '4px'
},
':hover': {
'::after': {
opacity: 0.75
}
},
':active': {
'::after': {
opacity: 0.65
}
}
},
brand: {
'::after': {
backgroundColor: tokens.colorCompoundBrandBackground,
opacity: 1
},
':hover': {
'::after': {
backgroundColor: tokens.colorCompoundBrandBackgroundHover,
opacity: 1
}
},
':active': {
'::after': {
backgroundColor: tokens.colorCompoundBrandBackgroundPressed,
opacity: 1
}
}
},
unselectedBrand: {
'::after': {
opacity: 0.6,
backgroundColor: tokens.colorNeutralForeground1
},
':hover': {
'::after': {
opacity: 0.75
}
},
':active': {
'::after': {
opacity: 1
}
}
}
});
/**
* Apply styling to the CarouselNavButton slots based on the state
*/ export const useCarouselNavButtonStyles_unstable = (state)=>{
'use no memo';
const styles = useStyles();
const { selected, appearance } = state;
state.root.className = mergeClasses(carouselNavButtonClassNames.root, styles.root, selected ? styles.rootSelected : styles.rootUnselected, appearance === 'brand' && styles.brand, !selected && appearance === 'brand' && styles.unselectedBrand, state.root.className);
return state;
};

File diff suppressed because one or more lines are too long