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 { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts';
import { useNav_unstable } from './useNav';
import { renderNav_unstable } from './renderNav';
import { useNavStyles_unstable } from './useNavStyles.styles';
import { useNavContextValues_unstable } from '../useNavContextValues';
/**
* Nav - A component that provides up to two levels of nesting for navigation.
*/ export const Nav = /*#__PURE__*/ React.forwardRef((props, ref)=>{
const state = useNav_unstable(props, ref);
const contextValues = useNavContextValues_unstable(state);
useNavStyles_unstable(state);
useCustomStyleHook_unstable('useHamburgerStyles_unstable')(state);
return renderNav_unstable(state, contextValues);
});
Nav.displayName = 'Nav';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Nav/Nav.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport type { ForwardRefComponent } from '@fluentui/react-utilities';\nimport { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts';\n\nimport { useNav_unstable } from './useNav';\nimport { renderNav_unstable } from './renderNav';\nimport { useNavStyles_unstable } from './useNavStyles.styles';\nimport { useNavContextValues_unstable } from '../useNavContextValues';\nimport type { NavProps } from './Nav.types';\n\n/**\n * Nav - A component that provides up to two levels of nesting for navigation.\n */\nexport const Nav: ForwardRefComponent<NavProps> = React.forwardRef((props, ref) => {\n const state = useNav_unstable(props, ref);\n\n const contextValues = useNavContextValues_unstable(state);\n\n useNavStyles_unstable(state);\n useCustomStyleHook_unstable('useHamburgerStyles_unstable')(state);\n\n return renderNav_unstable(state, contextValues);\n});\n\nNav.displayName = 'Nav';\n"],"names":["React","useCustomStyleHook_unstable","useNav_unstable","renderNav_unstable","useNavStyles_unstable","useNavContextValues_unstable","Nav","forwardRef","props","ref","state","contextValues","displayName"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAE/B,SAASC,2BAA2B,QAAQ,kCAAkC;AAE9E,SAASC,eAAe,QAAQ,WAAW;AAC3C,SAASC,kBAAkB,QAAQ,cAAc;AACjD,SAASC,qBAAqB,QAAQ,wBAAwB;AAC9D,SAASC,4BAA4B,QAAQ,yBAAyB;AAGtE;;CAEC,GACD,OAAO,MAAMC,oBAAqCN,MAAMO,UAAU,CAAC,CAACC,OAAOC;IACzE,MAAMC,QAAQR,gBAAgBM,OAAOC;IAErC,MAAME,gBAAgBN,6BAA6BK;IAEnDN,sBAAsBM;IACtBT,4BAA4B,+BAA+BS;IAE3D,OAAOP,mBAAmBO,OAAOC;AACnC,GAAG;AAEHL,IAAIM,WAAW,GAAG"}

View File

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

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Nav/Nav.types.ts"],"sourcesContent":["import * as React from 'react';\n\nimport type { ComponentProps, ComponentState, EventData, EventHandler, Slot } from '@fluentui/react-utilities';\nimport type { NavContextValue, NavItemValue } from '../NavContext.types';\n\nexport type NavSlots = {\n root: NonNullable<Slot<'div'>>;\n};\n\n/***\n * Indicates the vertical density of the Nav content.\n * This does not affect horizontal spacing.\n */\nexport type NavDensity = 'small' | 'medium';\n\n/**\n * Nav Props\n */\nexport type NavProps = ComponentProps<NavSlots> & {\n /**\n * The value of the navItem to be selected by default.\n * Typically useful when the selectedValue is uncontrolled.\n * Mutually exclusive with selectedValue.\n * Empty string indicates no selection.\n */\n defaultSelectedValue?: NavItemValue;\n\n /**\n * The value of the navCategory to be selected by default.\n * Typically useful when the selectedValue is uncontrolled.\n * Mutually exclusive with selectedValue.\n * Empty string indicates no selection.\n */\n defaultSelectedCategoryValue?: NavItemValue;\n\n /**\n * Set of categories that are opened by default.\n * Typically useful when the openCategories is uncontrolled.\n */\n defaultOpenCategories?: NavItemValue[];\n\n /**\n * Controls the open categories.\n * For use in controlled scenarios.\n */\n openCategories?: NavItemValue[];\n\n /**\n * Raised when a navItem is selected.\n * If the navItem is child of a category, the categoryValue will be provided\n */\n onNavItemSelect?: EventHandler<OnNavItemSelectData>;\n\n /**\n * The value of the currently selected navItem.\n * Mutually exclusive with defaultSelectedValue.\n * @default undefined\n */\n selectedValue?: NavItemValue;\n\n /**\n * Indicates a category that has a selected child\n * Will show the category as selected if it is closed.\n * @default undefined\n */\n selectedCategoryValue?: NavItemValue;\n\n /**\n * Indicates if Nav supports multiple open Categories at the same time.\n * @default true, indicating that multiple categories can be open at the same time.\n */\n multiple?: boolean;\n\n /**\n * Callback raised when a NavCategoryItem is toggled.\n */\n onNavCategoryItemToggle?: EventHandler<OnNavItemSelectData>;\n\n /**\n * The vertical density of the Nav and it's children\n * @default 'medium'\n */\n density?: NavDensity;\n};\n\nexport type OnNavItemSelectData = EventData<'click', React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>> & {\n /**\n * The value of the selected navItem.\n * In the case of a category selection, this will be the value of the selected category.\n */\n value: NavItemValue;\n\n /**\n * The parent value of the selected navSubItem\n * Null if not a child of a category\n */\n categoryValue?: NavItemValue;\n};\n\n/**\n * State used in rendering Nav\n */\nexport type NavState = ComponentState<NavSlots> & NavContextValue;\n"],"names":["React"],"mappings":"AAAA,YAAYA,WAAW,QAAQ"}

View File

@@ -0,0 +1,4 @@
export { Nav } from './Nav';
export { renderNav_unstable } from './renderNav';
export { useNav_unstable } from './useNav';
export { useNavStyles_unstable, navClassNames } from './useNavStyles.styles';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Nav/index.ts"],"sourcesContent":["export { Nav } from './Nav';\nexport type { NavSlots, NavProps, OnNavItemSelectData, NavState, NavDensity } from './Nav.types';\nexport { renderNav_unstable } from './renderNav';\nexport { useNav_unstable } from './useNav';\nexport { useNavStyles_unstable, navClassNames } from './useNavStyles.styles';\n"],"names":["Nav","renderNav_unstable","useNav_unstable","useNavStyles_unstable","navClassNames"],"mappings":"AAAA,SAASA,GAAG,QAAQ,QAAQ;AAE5B,SAASC,kBAAkB,QAAQ,cAAc;AACjD,SAASC,eAAe,QAAQ,WAAW;AAC3C,SAASC,qBAAqB,EAAEC,aAAa,QAAQ,wBAAwB"}

View File

@@ -0,0 +1,10 @@
import { jsx as _jsx } from "@fluentui/react-jsx-runtime/jsx-runtime";
import { assertSlots } from '@fluentui/react-utilities';
import { NavProvider } from '../NavContext';
export const renderNav_unstable = (state, contextValues)=>{
assertSlots(state);
return /*#__PURE__*/ _jsx(NavProvider, {
value: contextValues.nav,
children: /*#__PURE__*/ _jsx(state.root, {})
});
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Nav/renderNav.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 { NavProvider } from '../NavContext';\nimport type { NavState, NavSlots } from './Nav.types';\nimport type { NavContextValues } from '../NavContext.types';\n\nexport const renderNav_unstable = (state: NavState, contextValues: NavContextValues): JSXElement => {\n assertSlots<NavSlots>(state);\n\n return (\n <NavProvider value={contextValues.nav}>\n <state.root />\n </NavProvider>\n );\n};\n"],"names":["assertSlots","NavProvider","renderNav_unstable","state","contextValues","value","nav","root"],"mappings":"AAAA,0BAA0B,GAC1B,iDAAiD;AAEjD,SAASA,WAAW,QAAQ,4BAA4B;AAExD,SAASC,WAAW,QAAQ,gBAAgB;AAI5C,OAAO,MAAMC,qBAAqB,CAACC,OAAiBC;IAClDJ,YAAsBG;IAEtB,qBACE,KAACF;QAAYI,OAAOD,cAAcE,GAAG;kBACnC,cAAA,KAACH,MAAMI,IAAI;;AAGjB,EAAE"}

View File

@@ -0,0 +1,136 @@
'use client';
import * as React from 'react';
import { useControllableState, useEventCallback, useMergedRefs, slot, getIntrinsicElementProps } from '@fluentui/react-utilities';
/**
* Initial value for the uncontrolled case of the list of open indexes
*/ function initializeUncontrolledOpenCategories({ defaultOpenCategories, multiple }) {
if (defaultOpenCategories !== undefined) {
if (Array.isArray(defaultOpenCategories)) {
return multiple ? defaultOpenCategories : [
defaultOpenCategories[0]
];
}
return [
defaultOpenCategories
];
}
return undefined;
}
/**
* Updates the list of open indexes based on an index that changes
* @param value - the index that will change
* @param previousOpenItems - list of current open indexes
* @param multiple - if Nav supports open categories at the same time
*/ const updateOpenCategories = (value, previousOpenItems, multiple)=>{
if (multiple) {
if (previousOpenItems.includes(value)) {
return previousOpenItems.filter((i)=>i !== value);
} else {
return [
...previousOpenItems,
value
];
}
}
return previousOpenItems[0] === value ? [] : [
value
];
};
/**
* Create the state required to render Nav.
*
* The returned state can be modified with hooks such as useNavStyles,
* before being passed to renderNav.
*
* @param props - props from this instance of Nav
* @param ref - reference to root HTMLDivElement of Nav
*/ export const useNav_unstable = (props, ref)=>{
const { onNavItemSelect, onNavCategoryItemToggle, multiple = true, density = 'medium', openCategories: controlledOpenCategoryItems, selectedCategoryValue: controlledSelectedCategoryValue, selectedValue: controlledSelectedValue, defaultOpenCategories, defaultSelectedValue, defaultSelectedCategoryValue } = props;
const innerRef = React.useRef(null);
const [openCategories, setOpenCategories] = useControllableState({
state: controlledOpenCategoryItems,
defaultState: initializeUncontrolledOpenCategories({
defaultOpenCategories,
multiple
}),
initialState: []
});
const [selectedCategoryValue, setSelectedCategoryValue] = useControllableState({
state: controlledSelectedCategoryValue,
defaultState: defaultSelectedCategoryValue,
initialState: undefined
});
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 navItem list re-renders.
// these refs avoid getRegisteredNavItems changing when selectedValue changes and causing
// renders for navItems that have not changed.
const currentSelectedValue = React.useRef(undefined);
const previousSelectedValue = React.useRef(undefined);
const currentSelectedCategoryValue = React.useRef(undefined);
const previousSelectedCategoryValue = React.useRef(undefined);
if (currentSelectedValue.current !== selectedValue) {
previousSelectedValue.current = currentSelectedValue.current;
currentSelectedValue.current = selectedValue;
}
if (currentSelectedCategoryValue.current !== selectedCategoryValue) {
previousSelectedCategoryValue.current = currentSelectedCategoryValue.current;
currentSelectedCategoryValue.current = selectedCategoryValue;
}
// used for NavItems and NavSubItems
const onSelect = useEventCallback((event, data)=>{
setSelectedValue(data.value);
setSelectedCategoryValue(data.categoryValue ? data.categoryValue : '');
onNavItemSelect === null || onNavItemSelect === void 0 ? void 0 : onNavItemSelect(event, data);
});
// used for NavCategoryItems
const onRequestNavCategoryItemToggle = useEventCallback((event, data)=>{
if (data.categoryValue !== undefined) {
const nextOpenCategories = updateOpenCategories(data.categoryValue, openCategories !== null && openCategories !== void 0 ? openCategories : [], multiple);
onNavCategoryItemToggle === null || onNavCategoryItemToggle === void 0 ? void 0 : onNavCategoryItemToggle(event, data);
setOpenCategories(nextOpenCategories);
}
});
const registeredNavItems = React.useRef({});
const onRegister = React.useCallback((data)=>{
registeredNavItems.current[JSON.stringify(data.value)] = data;
}, []);
const onUnregister = React.useCallback((data)=>{
delete registeredNavItems.current[JSON.stringify(data.value)];
}, []);
const getRegisteredNavItems = React.useCallback(()=>{
return {
selectedValue: currentSelectedValue.current,
previousSelectedValue: previousSelectedValue.current,
selectedCategoryValue: currentSelectedCategoryValue.current,
previousSelectedCategoryValue: previousSelectedCategoryValue.current,
registeredNavItems: registeredNavItems.current
};
}, []);
return {
components: {
root: 'div'
},
root: slot.always(getIntrinsicElementProps('div', {
ref: useMergedRefs(ref, innerRef),
...props
}), {
elementType: 'div'
}),
openCategories,
selectedValue,
selectedCategoryValue,
onRegister,
onUnregister,
onSelect,
getRegisteredNavItems,
onRequestNavCategoryItemToggle,
multiple,
density,
tabbable: false
};
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,29 @@
'use client';
import { __styles, mergeClasses } from '@griffel/react';
export const navClassNames = {
root: 'fui-Nav'
};
/**
* Styles for the root slot
*/
const useStyles = /*#__PURE__*/__styles({
root: {
mc9l5x: "f22iagw",
Beiy3e4: "f1vx9l62"
}
}, {
d: [".f22iagw{display:flex;}", ".f1vx9l62{flex-direction:column;}"]
});
/**
* Apply styling to the Nav slots based on the state
*/
export const useNavStyles_unstable = state => {
'use no memo';
const styles = useStyles();
state.root.className = mergeClasses(navClassNames.root, styles.root, state.root.className);
// TODO Add class names to slots, for example:
// state.mySlot.className = mergeClasses(styles.mySlot, state.mySlot.className);
return state;
};

View File

@@ -0,0 +1 @@
{"version":3,"names":["__styles","mergeClasses","navClassNames","root","useStyles","mc9l5x","Beiy3e4","d","useNavStyles_unstable","state","styles","className"],"sources":["useNavStyles.styles.js"],"sourcesContent":["'use client';\nimport { makeStyles, mergeClasses } from '@griffel/react';\nexport const navClassNames = {\n root: 'fui-Nav'\n};\n/**\n * Styles for the root slot\n */ const useStyles = makeStyles({\n root: {\n display: 'flex',\n flexDirection: 'column'\n }\n});\n/**\n * Apply styling to the Nav slots based on the state\n */ export const useNavStyles_unstable = (state)=>{\n 'use no memo';\n const styles = useStyles();\n state.root.className = mergeClasses(navClassNames.root, styles.root, state.root.className);\n // TODO Add class names to slots, for example:\n // state.mySlot.className = mergeClasses(styles.mySlot, state.mySlot.className);\n return state;\n};\n"],"mappings":"AAAA,YAAY;;AACZ,SAAAA,QAAA,EAAqBC,YAAY,QAAQ,gBAAgB;AACzD,OAAO,MAAMC,aAAa,GAAG;EACzBC,IAAI,EAAE;AACV,CAAC;AACD;AACA;AACA;AAAI,MAAMC,SAAS,gBAAGJ,QAAA;EAAAG,IAAA;IAAAE,MAAA;IAAAC,OAAA;EAAA;AAAA;EAAAC,CAAA;AAAA,CAKrB,CAAC;AACF;AACA;AACA;AAAI,OAAO,MAAMC,qBAAqB,GAAIC,KAAK,IAAG;EAC9C,aAAa;;EACb,MAAMC,MAAM,GAAGN,SAAS,CAAC,CAAC;EAC1BK,KAAK,CAACN,IAAI,CAACQ,SAAS,GAAGV,YAAY,CAACC,aAAa,CAACC,IAAI,EAAEO,MAAM,CAACP,IAAI,EAAEM,KAAK,CAACN,IAAI,CAACQ,SAAS,CAAC;EAC1F;EACA;EACA,OAAOF,KAAK;AAChB,CAAC","ignoreList":[]}

View File

@@ -0,0 +1,23 @@
'use client';
import { makeStyles, mergeClasses } from '@griffel/react';
export const navClassNames = {
root: 'fui-Nav'
};
/**
* Styles for the root slot
*/ const useStyles = makeStyles({
root: {
display: 'flex',
flexDirection: 'column'
}
});
/**
* Apply styling to the Nav slots based on the state
*/ export const useNavStyles_unstable = (state)=>{
'use no memo';
const styles = useStyles();
state.root.className = mergeClasses(navClassNames.root, styles.root, state.root.className);
// TODO Add class names to slots, for example:
// state.mySlot.className = mergeClasses(styles.mySlot, state.mySlot.className);
return state;
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Nav/useNavStyles.styles.ts"],"sourcesContent":["'use client';\n\nimport { makeStyles, mergeClasses } from '@griffel/react';\nimport type { SlotClassNames } from '@fluentui/react-utilities';\nimport type { NavSlots, NavState } from './Nav.types';\n\nexport const navClassNames: SlotClassNames<NavSlots> = {\n root: 'fui-Nav',\n // TODO: add class names for all slots on NavSlots.\n // Should be of the form `<slotName>: 'fui-Nav__<slotName>`\n};\n\n/**\n * Styles for the root slot\n */\nconst useStyles = makeStyles({\n root: {\n display: 'flex',\n flexDirection: 'column',\n },\n\n // TODO add additional classes for different states and/or slots\n});\n\n/**\n * Apply styling to the Nav slots based on the state\n */\nexport const useNavStyles_unstable = (state: NavState): NavState => {\n 'use no memo';\n\n const styles = useStyles();\n state.root.className = mergeClasses(navClassNames.root, styles.root, state.root.className);\n\n // TODO Add class names to slots, for example:\n // state.mySlot.className = mergeClasses(styles.mySlot, state.mySlot.className);\n\n return state;\n};\n"],"names":["makeStyles","mergeClasses","navClassNames","root","useStyles","display","flexDirection","useNavStyles_unstable","state","styles","className"],"mappings":"AAAA;AAEA,SAASA,UAAU,EAAEC,YAAY,QAAQ,iBAAiB;AAI1D,OAAO,MAAMC,gBAA0C;IACrDC,MAAM;AAGR,EAAE;AAEF;;CAEC,GACD,MAAMC,YAAYJ,WAAW;IAC3BG,MAAM;QACJE,SAAS;QACTC,eAAe;IACjB;AAGF;AAEA;;CAEC,GACD,OAAO,MAAMC,wBAAwB,CAACC;IACpC;IAEA,MAAMC,SAASL;IACfI,MAAML,IAAI,CAACO,SAAS,GAAGT,aAAaC,cAAcC,IAAI,EAAEM,OAAON,IAAI,EAAEK,MAAML,IAAI,CAACO,SAAS;IAEzF,8CAA8C;IAC9C,gFAAgF;IAEhF,OAAOF;AACT,EAAE"}