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 { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts';
import { useList_unstable } from './useList';
import { renderList_unstable } from './renderList';
import { useListStyles_unstable } from './useListStyles.styles';
import { useListContextValues_unstable } from './useListContextValues';
export const List = /*#__PURE__*/ React.forwardRef((props, ref)=>{
const state = useList_unstable(props, ref);
const contextValues = useListContextValues_unstable(state);
useListStyles_unstable(state);
useCustomStyleHook_unstable('useListStyles_unstable')(state);
return renderList_unstable(state, contextValues);
});
List.displayName = 'List';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/List/List.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';\nimport { useList_unstable } from './useList';\nimport { renderList_unstable } from './renderList';\nimport { useListStyles_unstable } from './useListStyles.styles';\nimport type { ListProps } from './List.types';\nimport { useListContextValues_unstable } from './useListContextValues';\n\nexport const List: ForwardRefComponent<ListProps> = React.forwardRef((props, ref) => {\n const state = useList_unstable(props, ref);\n const contextValues = useListContextValues_unstable(state);\n\n useListStyles_unstable(state);\n useCustomStyleHook_unstable('useListStyles_unstable')(state);\n\n return renderList_unstable(state, contextValues);\n});\n\nList.displayName = 'List';\n"],"names":["React","useCustomStyleHook_unstable","useList_unstable","renderList_unstable","useListStyles_unstable","useListContextValues_unstable","List","forwardRef","props","ref","state","contextValues","displayName"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAE/B,SAASC,2BAA2B,QAAQ,kCAAkC;AAC9E,SAASC,gBAAgB,QAAQ,YAAY;AAC7C,SAASC,mBAAmB,QAAQ,eAAe;AACnD,SAASC,sBAAsB,QAAQ,yBAAyB;AAEhE,SAASC,6BAA6B,QAAQ,yBAAyB;AAEvE,OAAO,MAAMC,qBAAuCN,MAAMO,UAAU,CAAC,CAACC,OAAOC;IAC3E,MAAMC,QAAQR,iBAAiBM,OAAOC;IACtC,MAAME,gBAAgBN,8BAA8BK;IAEpDN,uBAAuBM;IACvBT,4BAA4B,0BAA0BS;IAEtD,OAAOP,oBAAoBO,OAAOC;AACpC,GAAG;AAEHL,KAAKM,WAAW,GAAG"}

View File

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

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/List/List.types.ts"],"sourcesContent":["import * as React from 'react';\n\nimport type {\n ComponentProps,\n ComponentState,\n Slot,\n SelectionMode,\n SelectionItemId,\n EventHandler,\n EventData,\n} from '@fluentui/react-utilities';\nimport type { ListSelectionState } from '../../hooks/types';\n\nexport type ListSlots = {\n root: NonNullable<Slot<'ul', 'div' | 'ol'>>;\n};\n\nexport type OnListSelectionChangeData = EventData<'change', React.SyntheticEvent> & {\n selectedItems: SelectionItemId[];\n};\n\nexport type ListNavigationMode = 'items' | 'composite';\n\n/**\n * List Props\n */\nexport type ListProps = ComponentProps<ListSlots> & {\n navigationMode?: ListNavigationMode;\n selectionMode?: SelectionMode;\n selectedItems?: SelectionItemId[];\n defaultSelectedItems?: SelectionItemId[];\n onSelectionChange?: EventHandler<OnListSelectionChangeData>;\n};\n\nexport type ListContextValue = {\n selection?: ListSelectionState;\n validateListItem: (listItemElement: HTMLElement) => void;\n};\n\nexport type ListSynchronousContextValue = {\n navigationMode: ListNavigationMode | undefined;\n listItemRole: string;\n};\n\nexport type ListContextValues = {\n listContext: ListContextValue;\n synchronousContext: ListSynchronousContextValue;\n};\n\n/**\n * State used in rendering List\n */\nexport type ListState = ComponentState<ListSlots> & ListContextValue & ListSynchronousContextValue;\n"],"names":["React"],"mappings":"AAAA,YAAYA,WAAW,QAAQ"}

View File

@@ -0,0 +1,4 @@
export { List } from './List';
export { renderList_unstable } from './renderList';
export { useList_unstable } from './useList';
export { listClassNames, useListStyles_unstable } from './useListStyles.styles';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/List/index.ts"],"sourcesContent":["export { List } from './List';\nexport type {\n ListContextValue,\n ListContextValues,\n ListNavigationMode,\n ListProps,\n ListSlots,\n ListState,\n OnListSelectionChangeData,\n} from './List.types';\nexport { renderList_unstable } from './renderList';\nexport { useList_unstable } from './useList';\nexport { listClassNames, useListStyles_unstable } from './useListStyles.styles';\n"],"names":["List","renderList_unstable","useList_unstable","listClassNames","useListStyles_unstable"],"mappings":"AAAA,SAASA,IAAI,QAAQ,SAAS;AAU9B,SAASC,mBAAmB,QAAQ,eAAe;AACnD,SAASC,gBAAgB,QAAQ,YAAY;AAC7C,SAASC,cAAc,EAAEC,sBAAsB,QAAQ,yBAAyB"}

View File

@@ -0,0 +1,23 @@
'use client';
import { createContext, useContextSelector } from '@fluentui/react-context-selector';
import * as React from 'react';
export const listContextDefaultValue = {
selection: undefined,
validateListItem: ()=>{
/* noop */ }
};
const listContext = createContext(undefined);
export const ListContextProvider = listContext.Provider;
export const useListContext_unstable = (selector)=>useContextSelector(listContext, (ctx = listContextDefaultValue)=>selector(ctx));
// This is a context that uses the standard, React Context API.
// The reason why this exists is that the Fluent UI Context Provider replaces the
// React Context Provider with a custom one that needs a layout effect to update the context value.
// This results in issues with element/role validation, as the ListItem component has not been updated yet
// when the validation happens.
// https://github.com/microsoft/fluentui/issues/34467
const ListSynchronousContext = React.createContext(undefined);
export const ListSynchronousContextProvider = ListSynchronousContext.Provider;
export const useListSynchronousContext = ()=>React.useContext(ListSynchronousContext) || {
navigationMode: undefined,
listItemRole: 'listitem'
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/List/listContext.ts"],"sourcesContent":["'use client';\n\nimport { createContext, useContextSelector } from '@fluentui/react-context-selector';\nimport type { ContextSelector } from '@fluentui/react-context-selector';\nimport { ListSynchronousContextValue, ListContextValue } from './List.types';\nimport * as React from 'react';\n\nexport const listContextDefaultValue: ListContextValue = {\n selection: undefined,\n validateListItem: () => {\n /* noop */\n },\n};\n\nconst listContext = createContext<ListContextValue | undefined>(undefined);\n\nexport const ListContextProvider = listContext.Provider;\n\nexport const useListContext_unstable = <T>(selector: ContextSelector<ListContextValue, T>): T =>\n useContextSelector(listContext, (ctx = listContextDefaultValue) => selector(ctx));\n\n// This is a context that uses the standard, React Context API.\n// The reason why this exists is that the Fluent UI Context Provider replaces the\n// React Context Provider with a custom one that needs a layout effect to update the context value.\n// This results in issues with element/role validation, as the ListItem component has not been updated yet\n// when the validation happens.\n// https://github.com/microsoft/fluentui/issues/34467\nconst ListSynchronousContext = React.createContext<ListSynchronousContextValue | undefined>(undefined);\n\nexport const ListSynchronousContextProvider = ListSynchronousContext.Provider;\nexport const useListSynchronousContext = (): ListSynchronousContextValue =>\n React.useContext(ListSynchronousContext) || {\n navigationMode: undefined,\n listItemRole: 'listitem',\n };\n"],"names":["createContext","useContextSelector","React","listContextDefaultValue","selection","undefined","validateListItem","listContext","ListContextProvider","Provider","useListContext_unstable","selector","ctx","ListSynchronousContext","ListSynchronousContextProvider","useListSynchronousContext","useContext","navigationMode","listItemRole"],"mappings":"AAAA;AAEA,SAASA,aAAa,EAAEC,kBAAkB,QAAQ,mCAAmC;AAGrF,YAAYC,WAAW,QAAQ;AAE/B,OAAO,MAAMC,0BAA4C;IACvDC,WAAWC;IACXC,kBAAkB;IAChB,QAAQ,GACV;AACF,EAAE;AAEF,MAAMC,cAAcP,cAA4CK;AAEhE,OAAO,MAAMG,sBAAsBD,YAAYE,QAAQ,CAAC;AAExD,OAAO,MAAMC,0BAA0B,CAAIC,WACzCV,mBAAmBM,aAAa,CAACK,MAAMT,uBAAuB,GAAKQ,SAASC,MAAM;AAEpF,+DAA+D;AAC/D,iFAAiF;AACjF,mGAAmG;AACnG,0GAA0G;AAC1G,+BAA+B;AAC/B,qDAAqD;AACrD,MAAMC,yBAAyBX,MAAMF,aAAa,CAA0CK;AAE5F,OAAO,MAAMS,iCAAiCD,uBAAuBJ,QAAQ,CAAC;AAC9E,OAAO,MAAMM,4BAA4B,IACvCb,MAAMc,UAAU,CAACH,2BAA2B;QAC1CI,gBAAgBZ;QAChBa,cAAc;IAChB,EAAE"}

View File

@@ -0,0 +1,15 @@
import { jsx as _jsx } from "@fluentui/react-jsx-runtime/jsx-runtime";
import { assertSlots } from '@fluentui/react-utilities';
import { ListContextProvider, ListSynchronousContextProvider } from './listContext';
/**
* Render the final JSX of List
*/ export const renderList_unstable = (state, contextValues)=>{
assertSlots(state);
return /*#__PURE__*/ _jsx(ListContextProvider, {
value: contextValues.listContext,
children: /*#__PURE__*/ _jsx(ListSynchronousContextProvider, {
value: contextValues.synchronousContext,
children: /*#__PURE__*/ _jsx(state.root, {})
})
});
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/List/renderList.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 { ListState, ListSlots, ListContextValues } from './List.types';\nimport { ListContextProvider, ListSynchronousContextProvider } from './listContext';\n\n/**\n * Render the final JSX of List\n */\nexport const renderList_unstable = (state: ListState, contextValues: ListContextValues): JSXElement => {\n assertSlots<ListSlots>(state);\n\n return (\n <ListContextProvider value={contextValues.listContext}>\n <ListSynchronousContextProvider value={contextValues.synchronousContext}>\n <state.root />\n </ListSynchronousContextProvider>\n </ListContextProvider>\n );\n};\n"],"names":["assertSlots","ListContextProvider","ListSynchronousContextProvider","renderList_unstable","state","contextValues","value","listContext","synchronousContext","root"],"mappings":"AAAA,0BAA0B,GAC1B,iDAAiD;AAEjD,SAASA,WAAW,QAAQ,4BAA4B;AAGxD,SAASC,mBAAmB,EAAEC,8BAA8B,QAAQ,gBAAgB;AAEpF;;CAEC,GACD,OAAO,MAAMC,sBAAsB,CAACC,OAAkBC;IACpDL,YAAuBI;IAEvB,qBACE,KAACH;QAAoBK,OAAOD,cAAcE,WAAW;kBACnD,cAAA,KAACL;YAA+BI,OAAOD,cAAcG,kBAAkB;sBACrE,cAAA,KAACJ,MAAMK,IAAI;;;AAInB,EAAE"}

View File

@@ -0,0 +1,76 @@
'use client';
import * as React from 'react';
import { getIntrinsicElementProps, slot, useControllableState, useEventCallback } from '@fluentui/react-utilities';
import { useArrowNavigationGroup, useFocusFinders } from '@fluentui/react-tabster';
import { useListSelection } from '../../hooks/useListSelection';
import { calculateListItemRoleForListRole, calculateListRole, validateGridCellsArePresent, validateProperElementTypes, validateProperRolesAreUsed } from '../../utils';
const DEFAULT_ROOT_EL_TYPE = 'ul';
/**
* Create the state required to render List.
*
* The returned state can be modified with hooks such as useListStyles_unstable,
* before being passed to renderList_unstable.
*
* @param props - props from this instance of List
* @param ref - reference to root HTMLElement of List
*/ export const useList_unstable = (props, ref)=>{
const { navigationMode, selectionMode, selectedItems, defaultSelectedItems, onSelectionChange } = props;
const as = props.as || navigationMode === 'composite' ? 'div' : DEFAULT_ROOT_EL_TYPE;
const arrowNavigationAttributes = useArrowNavigationGroup({
axis: 'vertical',
memorizeCurrent: true
});
const [selectionState, setSelectionState] = useControllableState({
state: selectedItems,
defaultState: defaultSelectedItems,
initialState: []
});
const onChange = useEventCallback((e, data)=>{
const selectedItemsAsArray = Array.from(data.selectedItems);
setSelectionState(selectedItemsAsArray);
onSelectionChange === null || onSelectionChange === void 0 ? void 0 : onSelectionChange(e, {
event: e,
type: 'change',
selectedItems: selectedItemsAsArray
});
});
const selection = useListSelection({
onSelectionChange: onChange,
selectionMode: selectionMode || 'multiselect',
selectedItems: selectionState
});
const listRole = props.role || calculateListRole(navigationMode, !!selectionMode);
const listItemRole = calculateListItemRoleForListRole(listRole);
const { findAllFocusable } = useFocusFinders();
const validateListItem = useEventCallback((listItemEl)=>{
if (process.env.NODE_ENV === 'production') {
return;
}
const itemRole = listItemEl.getAttribute('role') || '';
const focusable = findAllFocusable(listItemEl);
validateProperElementTypes(as, listItemEl.tagName.toLocaleLowerCase());
validateProperRolesAreUsed(listRole, itemRole, !!selectionMode, focusable.length > 0);
validateGridCellsArePresent(listRole, listItemEl);
});
return {
components: {
root: as
},
root: slot.always(getIntrinsicElementProps(as, {
ref,
role: listRole,
...selectionMode && {
'aria-multiselectable': selectionMode === 'multiselect' ? true : undefined
},
...arrowNavigationAttributes,
...props
}), {
elementType: as
}),
listItemRole,
validateListItem,
navigationMode,
// only pass down selection state if its handled internally, otherwise just report the events
selection: selectionMode ? selection : undefined
};
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,21 @@
'use client';
import * as React from 'react';
export function useListContextValues_unstable(state) {
const { selection, navigationMode, listItemRole, validateListItem } = state;
const listContext = {
selection,
navigationMode,
validateListItem
};
const synchronousContext = React.useMemo(()=>({
listItemRole,
navigationMode
}), [
listItemRole,
navigationMode
]);
return {
listContext,
synchronousContext
};
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/List/useListContextValues.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { ListContextValues, ListState } from './List.types';\n\nexport function useListContextValues_unstable(state: ListState): ListContextValues {\n const { selection, navigationMode, listItemRole, validateListItem } = state;\n\n const listContext = {\n selection,\n navigationMode,\n validateListItem,\n };\n\n const synchronousContext = React.useMemo(\n () => ({\n listItemRole,\n navigationMode,\n }),\n [listItemRole, navigationMode],\n );\n\n return {\n listContext,\n synchronousContext,\n };\n}\n"],"names":["React","useListContextValues_unstable","state","selection","navigationMode","listItemRole","validateListItem","listContext","synchronousContext","useMemo"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAG/B,OAAO,SAASC,8BAA8BC,KAAgB;IAC5D,MAAM,EAAEC,SAAS,EAAEC,cAAc,EAAEC,YAAY,EAAEC,gBAAgB,EAAE,GAAGJ;IAEtE,MAAMK,cAAc;QAClBJ;QACAC;QACAE;IACF;IAEA,MAAME,qBAAqBR,MAAMS,OAAO,CACtC,IAAO,CAAA;YACLJ;YACAD;QACF,CAAA,GACA;QAACC;QAAcD;KAAe;IAGhC,OAAO;QACLG;QACAC;IACF;AACF"}

View File

@@ -0,0 +1,17 @@
'use client';
import { __resetStyles, mergeClasses } from '@griffel/react';
export const listClassNames = {
root: 'fui-List'
};
const useRootBaseStyles = /*#__PURE__*/__resetStyles("r1m6yby2", null, [".r1m6yby2{padding:0;margin:0;text-indent:0;list-style-type:none;}"]);
/**
* Apply styling to the List slots based on the state
*/
export const useListStyles_unstable = state => {
'use no memo';
const rootStyles = useRootBaseStyles();
state.root.className = mergeClasses(listClassNames.root, rootStyles, state.root.className);
return state;
};

View File

@@ -0,0 +1 @@
{"version":3,"names":["__resetStyles","mergeClasses","listClassNames","root","useRootBaseStyles","useListStyles_unstable","state","rootStyles","className"],"sources":["useListStyles.styles.js"],"sourcesContent":["'use client';\nimport { makeResetStyles, mergeClasses } from '@griffel/react';\nexport const listClassNames = {\n root: 'fui-List'\n};\nconst useRootBaseStyles = makeResetStyles({\n padding: 0,\n margin: 0,\n textIndent: 0,\n listStyleType: 'none'\n});\n/**\n * Apply styling to the List slots based on the state\n */ export const useListStyles_unstable = (state)=>{\n 'use no memo';\n const rootStyles = useRootBaseStyles();\n state.root.className = mergeClasses(listClassNames.root, rootStyles, state.root.className);\n return state;\n};\n"],"mappings":"AAAA,YAAY;;AACZ,SAAAA,aAAA,EAA0BC,YAAY,QAAQ,gBAAgB;AAC9D,OAAO,MAAMC,cAAc,GAAG;EAC1BC,IAAI,EAAE;AACV,CAAC;AACD,MAAMC,iBAAiB,gBAAGJ,aAAA,wFAKzB,CAAC;AACF;AACA;AACA;AAAI,OAAO,MAAMK,sBAAsB,GAAIC,KAAK,IAAG;EAC/C,aAAa;;EACb,MAAMC,UAAU,GAAGH,iBAAiB,CAAC,CAAC;EACtCE,KAAK,CAACH,IAAI,CAACK,SAAS,GAAGP,YAAY,CAACC,cAAc,CAACC,IAAI,EAAEI,UAAU,EAAED,KAAK,CAACH,IAAI,CAACK,SAAS,CAAC;EAC1F,OAAOF,KAAK;AAChB,CAAC","ignoreList":[]}

View File

@@ -0,0 +1,19 @@
'use client';
import { makeResetStyles, mergeClasses } from '@griffel/react';
export const listClassNames = {
root: 'fui-List'
};
const useRootBaseStyles = makeResetStyles({
padding: 0,
margin: 0,
textIndent: 0,
listStyleType: 'none'
});
/**
* Apply styling to the List slots based on the state
*/ export const useListStyles_unstable = (state)=>{
'use no memo';
const rootStyles = useRootBaseStyles();
state.root.className = mergeClasses(listClassNames.root, rootStyles, state.root.className);
return state;
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/List/useListStyles.styles.ts"],"sourcesContent":["'use client';\n\nimport type { SlotClassNames } from '@fluentui/react-utilities';\nimport { makeResetStyles, mergeClasses } from '@griffel/react';\nimport type { ListSlots, ListState } from './List.types';\n\nexport const listClassNames: SlotClassNames<ListSlots> = {\n root: 'fui-List',\n};\n\nconst useRootBaseStyles = makeResetStyles({\n padding: 0,\n margin: 0,\n textIndent: 0,\n listStyleType: 'none',\n});\n\n/**\n * Apply styling to the List slots based on the state\n */\nexport const useListStyles_unstable = (state: ListState): ListState => {\n 'use no memo';\n\n const rootStyles = useRootBaseStyles();\n\n state.root.className = mergeClasses(listClassNames.root, rootStyles, state.root.className);\n\n return state;\n};\n"],"names":["makeResetStyles","mergeClasses","listClassNames","root","useRootBaseStyles","padding","margin","textIndent","listStyleType","useListStyles_unstable","state","rootStyles","className"],"mappings":"AAAA;AAGA,SAASA,eAAe,EAAEC,YAAY,QAAQ,iBAAiB;AAG/D,OAAO,MAAMC,iBAA4C;IACvDC,MAAM;AACR,EAAE;AAEF,MAAMC,oBAAoBJ,gBAAgB;IACxCK,SAAS;IACTC,QAAQ;IACRC,YAAY;IACZC,eAAe;AACjB;AAEA;;CAEC,GACD,OAAO,MAAMC,yBAAyB,CAACC;IACrC;IAEA,MAAMC,aAAaP;IAEnBM,MAAMP,IAAI,CAACS,SAAS,GAAGX,aAAaC,eAAeC,IAAI,EAAEQ,YAAYD,MAAMP,IAAI,CAACS,SAAS;IAEzF,OAAOF;AACT,EAAE"}