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,20 @@
'use client';
import * as React from 'react';
import { useFlatTree_unstable } from './useFlatTree';
import { useFlatTreeStyles_unstable } from './useFlatTreeStyles.styles';
import { useFlatTreeContextValues_unstable } from './useFlatTreeContextValues';
import { renderFlatTree_unstable } from './renderFlatTree';
import { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts';
/**
* The `FlatTree` component is a variation of the `Tree` component that deals with a flattened data structure.
*
* It should be used on cases where more complex interactions with a Tree is required.
* On simple scenarios it is advised to simply use a nested structure instead.
*/ export const FlatTree = /*#__PURE__*/ React.forwardRef((props, ref)=>{
const state = useFlatTree_unstable(props, ref);
const contextValues = useFlatTreeContextValues_unstable(state);
useFlatTreeStyles_unstable(state);
useCustomStyleHook_unstable('useFlatTreeStyles_unstable')(state);
return renderFlatTree_unstable(state, contextValues);
});
FlatTree.displayName = 'FlatTree';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/FlatTree/FlatTree.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport type { ForwardRefComponent } from '@fluentui/react-utilities';\nimport type { FlatTreeProps } from './FlatTree.types';\nimport { useFlatTree_unstable } from './useFlatTree';\nimport { useFlatTreeStyles_unstable } from './useFlatTreeStyles.styles';\nimport { useFlatTreeContextValues_unstable } from './useFlatTreeContextValues';\nimport { renderFlatTree_unstable } from './renderFlatTree';\nimport { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts';\n\n/**\n * The `FlatTree` component is a variation of the `Tree` component that deals with a flattened data structure.\n *\n * It should be used on cases where more complex interactions with a Tree is required.\n * On simple scenarios it is advised to simply use a nested structure instead.\n */\nexport const FlatTree: ForwardRefComponent<FlatTreeProps> = React.forwardRef((props, ref) => {\n const state = useFlatTree_unstable(props, ref);\n const contextValues = useFlatTreeContextValues_unstable(state);\n useFlatTreeStyles_unstable(state);\n useCustomStyleHook_unstable('useFlatTreeStyles_unstable')(state);\n\n return renderFlatTree_unstable(state, contextValues);\n});\n\nFlatTree.displayName = 'FlatTree';\n"],"names":["React","useFlatTree_unstable","useFlatTreeStyles_unstable","useFlatTreeContextValues_unstable","renderFlatTree_unstable","useCustomStyleHook_unstable","FlatTree","forwardRef","props","ref","state","contextValues","displayName"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAG/B,SAASC,oBAAoB,QAAQ,gBAAgB;AACrD,SAASC,0BAA0B,QAAQ,6BAA6B;AACxE,SAASC,iCAAiC,QAAQ,6BAA6B;AAC/E,SAASC,uBAAuB,QAAQ,mBAAmB;AAC3D,SAASC,2BAA2B,QAAQ,kCAAkC;AAE9E;;;;;CAKC,GACD,OAAO,MAAMC,yBAA+CN,MAAMO,UAAU,CAAC,CAACC,OAAOC;IACnF,MAAMC,QAAQT,qBAAqBO,OAAOC;IAC1C,MAAME,gBAAgBR,kCAAkCO;IACxDR,2BAA2BQ;IAC3BL,4BAA4B,8BAA8BK;IAE1D,OAAON,wBAAwBM,OAAOC;AACxC,GAAG;AAEHL,SAASM,WAAW,GAAG"}

View File

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

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/FlatTree/FlatTree.types.ts"],"sourcesContent":["import type { ComponentProps, ComponentState, SelectionMode } from '@fluentui/react-utilities';\nimport type {\n TreeSlots,\n TreeCheckedChangeData,\n TreeCheckedChangeEvent,\n TreeNavigationData_unstable,\n TreeNavigationEvent_unstable,\n TreeOpenChangeData,\n TreeOpenChangeEvent,\n TreeSelectionValue,\n} from '../Tree/index';\nimport type { TreeItemValue } from '../TreeItem/index';\nimport type { TreeContextValue } from '../../contexts';\n\nexport type FlatTreeSlots = TreeSlots;\n\nexport type FlatTreeContextValues = {\n tree: TreeContextValue;\n};\n\nexport type FlatTreeProps = ComponentProps<TreeSlots> & {\n /**\n * Indicates how navigation between a treeitem and its actions work\n * - 'tree' (default): The default navigation, pressing right arrow key navigates inward the first inner children of a branch treeitem\n * - 'treegrid': Pressing right arrow key navigate towards the actions of a treeitem\n * @default 'tree'\n */\n navigationMode?: 'tree' | 'treegrid';\n /**\n * A tree item can have various appearances:\n * - 'subtle' (default): The default tree item styles.\n * - 'subtle-alpha': Minimizes emphasis on hovered or focused states.\n * - 'transparent': Removes background color.\n * @default 'subtle'\n */\n appearance?: 'subtle' | 'subtle-alpha' | 'transparent';\n /**\n * Size of the tree item.\n * @default 'medium'\n */\n size?: 'small' | 'medium';\n /**\n * This refers to a list of ids of opened tree items.\n * Controls the state of the open tree items.\n * These property is ignored for subtrees.\n */\n openItems?: Iterable<TreeItemValue>;\n /**\n * Callback fired when the component changes value from open state.\n * These property is ignored for subtrees.\n *\n * @param event - a React's Synthetic event\n * @param data - A data object with relevant information,\n * such as open value and type of interaction that created the event.\n */\n onOpenChange?(event: TreeOpenChangeEvent, data: TreeOpenChangeData): void;\n\n /**\n * Callback fired when navigation happens inside the component.\n * These property is ignored for subtrees.\n *\n * FIXME: This method is not ideal, as navigation should be handled internally by tabster.\n *\n * @param event - a React's Synthetic event\n * @param data - A data object with relevant information,\n */\n onNavigation?(event: TreeNavigationEvent_unstable, data: TreeNavigationData_unstable): void;\n\n /**\n * This refers to the selection mode of the tree.\n * - undefined: No selection can be done.\n * - 'single': Only one tree item can be selected, radio buttons are rendered.\n * - 'multiselect': Multiple tree items can be selected, checkboxes are rendered.\n *\n * @default undefined\n */\n selectionMode?: SelectionMode;\n /**\n * This refers to a list of ids of checked tree items, or a list of tuples of ids and checked state.\n * Controls the state of the checked tree items.\n * These property is ignored for subtrees.\n */\n checkedItems?: Iterable<TreeItemValue | [TreeItemValue, TreeSelectionValue]>;\n /**\n * Callback fired when the component changes value from checked state.\n * These property is ignored for subtrees.\n *\n * @param event - a React's Synthetic event\n * @param data - A data object with relevant information,\n * such as checked value and type of interaction that created the event.\n */\n onCheckedChange?(event: TreeCheckedChangeEvent, data: TreeCheckedChangeData): void;\n};\n\nexport type FlatTreeState = ComponentState<FlatTreeSlots> &\n TreeContextValue & {\n open: boolean;\n };\n"],"names":[],"mappings":"AA8FA,WAGI"}

View File

@@ -0,0 +1,6 @@
export { FlatTree } from './FlatTree';
export { useHeadlessFlatTree_unstable } from './useHeadlessFlatTree';
export { useFlatTree_unstable } from './useFlatTree';
export { flatTreeClassNames, useFlatTreeStyles_unstable } from './useFlatTreeStyles.styles';
export { useFlatTreeContextValues_unstable } from './useFlatTreeContextValues';
export { renderFlatTree_unstable } from './renderFlatTree';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/FlatTree/index.ts"],"sourcesContent":["export { FlatTree } from './FlatTree';\nexport type { FlatTreeContextValues, FlatTreeProps, FlatTreeSlots, FlatTreeState } from './FlatTree.types';\nexport type {\n HeadlessFlatTree,\n HeadlessFlatTreeItem,\n HeadlessFlatTreeItemProps,\n HeadlessFlatTreeOptions,\n} from './useHeadlessFlatTree';\nexport { useHeadlessFlatTree_unstable } from './useHeadlessFlatTree';\nexport { useFlatTree_unstable } from './useFlatTree';\nexport { flatTreeClassNames, useFlatTreeStyles_unstable } from './useFlatTreeStyles.styles';\nexport { useFlatTreeContextValues_unstable } from './useFlatTreeContextValues';\nexport { renderFlatTree_unstable } from './renderFlatTree';\n"],"names":["FlatTree","useHeadlessFlatTree_unstable","useFlatTree_unstable","flatTreeClassNames","useFlatTreeStyles_unstable","useFlatTreeContextValues_unstable","renderFlatTree_unstable"],"mappings":"AAAA,SAASA,QAAQ,QAAQ,aAAa;AAQtC,SAASC,4BAA4B,QAAQ,wBAAwB;AACrE,SAASC,oBAAoB,QAAQ,gBAAgB;AACrD,SAASC,kBAAkB,EAAEC,0BAA0B,QAAQ,6BAA6B;AAC5F,SAASC,iCAAiC,QAAQ,6BAA6B;AAC/E,SAASC,uBAAuB,QAAQ,mBAAmB"}

View File

@@ -0,0 +1,2 @@
import { renderTree_unstable } from '../../Tree';
export const renderFlatTree_unstable = renderTree_unstable;

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/FlatTree/renderFlatTree.ts"],"sourcesContent":["import { renderTree_unstable } from '../../Tree';\nimport type { FlatTreeContextValues, FlatTreeState } from './FlatTree.types';\nimport type { JSXElement } from '@fluentui/react-utilities';\nexport const renderFlatTree_unstable: (state: FlatTreeState, contextValues: FlatTreeContextValues) => JSXElement =\n renderTree_unstable;\n"],"names":["renderTree_unstable","renderFlatTree_unstable"],"mappings":"AAAA,SAASA,mBAAmB,QAAQ,aAAa;AAGjD,OAAO,MAAMC,0BACXD,oBAAoB"}

View File

@@ -0,0 +1,88 @@
'use client';
import { useControllableState } from '@fluentui/react-utilities';
import { ImmutableMap } from '../../utils/ImmutableMap';
import * as React from 'react';
import { createCheckedItems } from '../../utils/createCheckedItems';
export function useFlatControllableCheckedItems(props, headlessTree) {
return useControllableState({
initialState: ImmutableMap.empty,
state: React.useMemo(()=>props.selectionMode ? props.checkedItems && createCheckedItems(props.checkedItems) : undefined, [
props.checkedItems,
props.selectionMode
]),
defaultState: props.defaultCheckedItems ? ()=>initializeCheckedItems(props, headlessTree) : undefined
});
}
export function createNextFlatCheckedItems(data, previousCheckedItems, headlessTree) {
if (data.selectionMode === 'single') {
return ImmutableMap.from([
[
data.value,
data.checked
]
]);
}
const treeItem = headlessTree.get(data.value);
if (!treeItem) {
if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line no-console
console.error(`@fluentui/react-tree [useHeadlessFlatTree]:
Tree item ${data.value} not found.`);
}
return previousCheckedItems;
}
// Calling `ImmutableMap.set()` creates a new ImmutableMap - avoid this in loops.
// Instead write all updates to a native Map and create a new ImmutableMap at the end.
// Note that all descendants of the toggled item are processed even if they are collapsed,
// making the choice of algorithm more important.
const nextCheckedItemsMap = new Map(ImmutableMap.dangerouslyGetInternalMap(previousCheckedItems));
// The toggled item itself
nextCheckedItemsMap.set(data.value, data.checked);
// Descendant updates
for (const children of headlessTree.subtree(data.value)){
nextCheckedItemsMap.set(children.value, data.checked);
}
// Ancestor updates - must be done after adding descendants and the toggle item.
// If any ancestor is mixed, all ancestors above it are mixed too.
let isAncestorsMixed = false;
for (const ancestor of headlessTree.ancestors(treeItem.value)){
if (isAncestorsMixed) {
nextCheckedItemsMap.set(ancestor.value, 'mixed');
continue;
}
// For each ancestor, if all of its children now have the same checked state as the toggled item,
// set the ancestor to that checked state too. Otherwise it is 'mixed'.
let childrenWithSameState = 0;
for (const child of headlessTree.children(ancestor.value)){
if ((nextCheckedItemsMap.get(child.value) || false) === data.checked) {
childrenWithSameState++;
}
}
if (childrenWithSameState === ancestor.childrenValues.length) {
nextCheckedItemsMap.set(ancestor.value, data.checked);
} else {
nextCheckedItemsMap.set(ancestor.value, 'mixed');
isAncestorsMixed = true;
}
}
const nextCheckedItems = ImmutableMap.from(nextCheckedItemsMap);
return nextCheckedItems;
}
function initializeCheckedItems(props, headlessTree) {
if (!props.selectionMode) {
return ImmutableMap.empty;
}
let state = createCheckedItems(props.defaultCheckedItems);
// if selectionMode is multiselect, we need to calculate the checked state of all children
// and ancestors of the defaultCheckedItems
if (props.selectionMode === 'multiselect') {
for (const [value, checked] of state){
state = createNextFlatCheckedItems({
value,
checked,
selectionMode: props.selectionMode
}, state, headlessTree);
}
}
return state;
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,58 @@
'use client';
import * as React from 'react';
import { useRootTree } from '../../hooks/useRootTree';
import { useEventCallback, useMergedRefs } from '@fluentui/react-utilities';
import { useFlatTreeNavigation } from '../../hooks/useFlatTreeNavigation';
import { useSubtree } from '../../hooks/useSubtree';
import { ImmutableSet } from '../../utils/ImmutableSet';
import { ImmutableMap } from '../../utils/ImmutableMap';
import { SubtreeContext } from '../../contexts/subtreeContext';
export const useFlatTree_unstable = (props, ref)=>{
'use no memo';
const isRoot = React.useContext(SubtreeContext) === undefined;
// as level is static, this doesn't break rule of hooks
// and if this becomes an issue later on, this can be easily converted
// eslint-disable-next-line react-hooks/rules-of-hooks
return isRoot ? useRootFlatTree(props, ref) : useSubFlatTree(props, ref);
};
function useRootFlatTree(props, ref) {
const navigation = useFlatTreeNavigation(props.navigationMode);
return Object.assign(useRootTree({
...props,
onNavigation: useEventCallback((event, data)=>{
var _props_onNavigation;
(_props_onNavigation = props.onNavigation) === null || _props_onNavigation === void 0 ? void 0 : _props_onNavigation.call(props, event, data);
if (!event.isDefaultPrevented()) {
navigation.navigate(data);
}
})
}, useMergedRefs(ref, navigation.rootRef)), {
treeType: 'flat',
forceUpdateRovingTabIndex: navigation.forceUpdateRovingTabIndex
});
}
function useSubFlatTree(props, ref) {
if (process.env.NODE_ENV === 'development') {
throw new Error(`@fluentui/react-tree [useFlatTree]:
Subtrees are not allowed in a FlatTree!
You cannot use a <FlatTree> component inside of another <FlatTree> nor a <Tree> component!`);
}
return {
...useSubtree(props, ref),
// ------ defaultTreeContextValue
level: 0,
contextType: 'root',
treeType: 'nested',
selectionMode: 'none',
openItems: ImmutableSet.empty,
checkedItems: ImmutableMap.empty,
requestTreeResponse: noop,
forceUpdateRovingTabIndex: noop,
appearance: 'subtle',
size: 'medium',
// ------ defaultTreeContextValue
open: false
};
}
function noop() {
/* do nothing */ }

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/FlatTree/useFlatTree.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { useRootTree } from '../../hooks/useRootTree';\nimport { FlatTreeProps, FlatTreeState } from './FlatTree.types';\nimport { useEventCallback, useMergedRefs } from '@fluentui/react-utilities';\nimport { useFlatTreeNavigation } from '../../hooks/useFlatTreeNavigation';\nimport { useSubtree } from '../../hooks/useSubtree';\nimport { ImmutableSet } from '../../utils/ImmutableSet';\nimport { ImmutableMap } from '../../utils/ImmutableMap';\nimport { SubtreeContext } from '../../contexts/subtreeContext';\n\nexport const useFlatTree_unstable: (props: FlatTreeProps, ref: React.Ref<HTMLElement>) => FlatTreeState = (\n props,\n ref,\n) => {\n 'use no memo';\n\n const isRoot = React.useContext(SubtreeContext) === undefined;\n // as level is static, this doesn't break rule of hooks\n // and if this becomes an issue later on, this can be easily converted\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return isRoot ? useRootFlatTree(props, ref) : useSubFlatTree(props, ref);\n};\n\nfunction useRootFlatTree(props: FlatTreeProps, ref: React.Ref<HTMLElement>): FlatTreeState {\n const navigation = useFlatTreeNavigation(props.navigationMode);\n\n return Object.assign(\n useRootTree(\n {\n ...props,\n onNavigation: useEventCallback((event, data) => {\n props.onNavigation?.(event, data);\n if (!event.isDefaultPrevented()) {\n navigation.navigate(data);\n }\n }),\n },\n useMergedRefs(ref, navigation.rootRef),\n ),\n {\n treeType: 'flat',\n forceUpdateRovingTabIndex: navigation.forceUpdateRovingTabIndex,\n } as const,\n );\n}\n\nfunction useSubFlatTree(props: FlatTreeProps, ref: React.Ref<HTMLElement>): FlatTreeState {\n if (process.env.NODE_ENV === 'development') {\n throw new Error(/* #__DE-INDENT__ */ `\n @fluentui/react-tree [useFlatTree]:\n Subtrees are not allowed in a FlatTree!\n You cannot use a <FlatTree> component inside of another <FlatTree> nor a <Tree> component!\n `);\n }\n return {\n ...useSubtree(props, ref),\n // ------ defaultTreeContextValue\n level: 0,\n contextType: 'root',\n treeType: 'nested',\n selectionMode: 'none',\n openItems: ImmutableSet.empty,\n checkedItems: ImmutableMap.empty,\n requestTreeResponse: noop,\n forceUpdateRovingTabIndex: noop,\n appearance: 'subtle',\n size: 'medium',\n // ------ defaultTreeContextValue\n open: false,\n };\n}\n\nfunction noop() {\n /* do nothing */\n}\n"],"names":["React","useRootTree","useEventCallback","useMergedRefs","useFlatTreeNavigation","useSubtree","ImmutableSet","ImmutableMap","SubtreeContext","useFlatTree_unstable","props","ref","isRoot","useContext","undefined","useRootFlatTree","useSubFlatTree","navigation","navigationMode","Object","assign","onNavigation","event","data","isDefaultPrevented","navigate","rootRef","treeType","forceUpdateRovingTabIndex","process","env","NODE_ENV","Error","level","contextType","selectionMode","openItems","empty","checkedItems","requestTreeResponse","noop","appearance","size","open"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAC/B,SAASC,WAAW,QAAQ,0BAA0B;AAEtD,SAASC,gBAAgB,EAAEC,aAAa,QAAQ,4BAA4B;AAC5E,SAASC,qBAAqB,QAAQ,oCAAoC;AAC1E,SAASC,UAAU,QAAQ,yBAAyB;AACpD,SAASC,YAAY,QAAQ,2BAA2B;AACxD,SAASC,YAAY,QAAQ,2BAA2B;AACxD,SAASC,cAAc,QAAQ,gCAAgC;AAE/D,OAAO,MAAMC,uBAA6F,CACxGC,OACAC;IAEA;IAEA,MAAMC,SAASZ,MAAMa,UAAU,CAACL,oBAAoBM;IACpD,uDAAuD;IACvD,sEAAsE;IACtE,sDAAsD;IACtD,OAAOF,SAASG,gBAAgBL,OAAOC,OAAOK,eAAeN,OAAOC;AACtE,EAAE;AAEF,SAASI,gBAAgBL,KAAoB,EAAEC,GAA2B;IACxE,MAAMM,aAAab,sBAAsBM,MAAMQ,cAAc;IAE7D,OAAOC,OAAOC,MAAM,CAClBnB,YACE;QACE,GAAGS,KAAK;QACRW,cAAcnB,iBAAiB,CAACoB,OAAOC;gBACrCb;aAAAA,sBAAAA,MAAMW,YAAY,cAAlBX,0CAAAA,yBAAAA,OAAqBY,OAAOC;YAC5B,IAAI,CAACD,MAAME,kBAAkB,IAAI;gBAC/BP,WAAWQ,QAAQ,CAACF;YACtB;QACF;IACF,GACApB,cAAcQ,KAAKM,WAAWS,OAAO,IAEvC;QACEC,UAAU;QACVC,2BAA2BX,WAAWW,yBAAyB;IACjE;AAEJ;AAEA,SAASZ,eAAeN,KAAoB,EAAEC,GAA2B;IACvE,IAAIkB,QAAQC,GAAG,CAACC,QAAQ,KAAK,eAAe;QAC1C,MAAM,IAAIC,MAA2B,CAAC;;0FAItC,CAAC;IACH;IACA,OAAO;QACL,GAAG3B,WAAWK,OAAOC,IAAI;QACzB,iCAAiC;QACjCsB,OAAO;QACPC,aAAa;QACbP,UAAU;QACVQ,eAAe;QACfC,WAAW9B,aAAa+B,KAAK;QAC7BC,cAAc/B,aAAa8B,KAAK;QAChCE,qBAAqBC;QACrBZ,2BAA2BY;QAC3BC,YAAY;QACZC,MAAM;QACN,iCAAiC;QACjCC,MAAM;IACR;AACF;AAEA,SAASH;AACP,cAAc,GAChB"}

View File

@@ -0,0 +1,22 @@
export const useFlatTreeContextValues_unstable = (state)=>{
const { openItems, level, contextType, treeType, checkedItems, selectionMode, navigationMode, appearance, size, requestTreeResponse, forceUpdateRovingTabIndex } = state;
/**
* This context is created with "@fluentui/react-context-selector",
* there is no sense to memoize it
*/ const tree = {
treeType,
size,
openItems,
appearance,
checkedItems,
selectionMode,
navigationMode,
contextType,
level,
requestTreeResponse,
forceUpdateRovingTabIndex
};
return {
tree
};
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/FlatTree/useFlatTreeContextValues.ts"],"sourcesContent":["import type { TreeContextValue } from '../../contexts';\nimport type { FlatTreeContextValues, FlatTreeState } from './FlatTree.types';\n\nexport const useFlatTreeContextValues_unstable = (state: FlatTreeState): FlatTreeContextValues => {\n const {\n openItems,\n level,\n contextType,\n treeType,\n checkedItems,\n selectionMode,\n navigationMode,\n appearance,\n size,\n requestTreeResponse,\n forceUpdateRovingTabIndex,\n } = state;\n /**\n * This context is created with \"@fluentui/react-context-selector\",\n * there is no sense to memoize it\n */\n const tree: TreeContextValue = {\n treeType,\n size,\n openItems,\n appearance,\n checkedItems,\n selectionMode,\n navigationMode,\n contextType,\n level,\n requestTreeResponse,\n forceUpdateRovingTabIndex,\n };\n\n return { tree };\n};\n"],"names":["useFlatTreeContextValues_unstable","state","openItems","level","contextType","treeType","checkedItems","selectionMode","navigationMode","appearance","size","requestTreeResponse","forceUpdateRovingTabIndex","tree"],"mappings":"AAGA,OAAO,MAAMA,oCAAoC,CAACC;IAChD,MAAM,EACJC,SAAS,EACTC,KAAK,EACLC,WAAW,EACXC,QAAQ,EACRC,YAAY,EACZC,aAAa,EACbC,cAAc,EACdC,UAAU,EACVC,IAAI,EACJC,mBAAmB,EACnBC,yBAAyB,EAC1B,GAAGX;IACJ;;;GAGC,GACD,MAAMY,OAAyB;QAC7BR;QACAK;QACAR;QACAO;QACAH;QACAC;QACAC;QACAJ;QACAD;QACAQ;QACAC;IACF;IAEA,OAAO;QAAEC;IAAK;AAChB,EAAE"}

View File

@@ -0,0 +1,15 @@
'use client';
import { __resetStyles, mergeClasses } from '@griffel/react';
import { tokens } from '@fluentui/react-theme';
export const flatTreeClassNames = {
root: 'fui-FlatTree'
};
const useBaseStyles = /*#__PURE__*/__resetStyles("rnv2ez3", null, [".rnv2ez3{display:flex;flex-direction:column;row-gap:var(--spacingVerticalXXS);}"]);
export const useFlatTreeStyles_unstable = state => {
'use no memo';
const baseStyles = useBaseStyles();
state.root.className = mergeClasses(flatTreeClassNames.root, baseStyles, state.root.className);
return state;
};

View File

@@ -0,0 +1 @@
{"version":3,"names":["__resetStyles","mergeClasses","tokens","flatTreeClassNames","root","useBaseStyles","useFlatTreeStyles_unstable","state","baseStyles","className"],"sources":["useFlatTreeStyles.styles.js"],"sourcesContent":["'use client';\nimport { makeResetStyles, mergeClasses } from '@griffel/react';\nimport { tokens } from '@fluentui/react-theme';\nexport const flatTreeClassNames = {\n root: 'fui-FlatTree'\n};\nconst useBaseStyles = makeResetStyles({\n display: 'flex',\n flexDirection: 'column',\n rowGap: tokens.spacingVerticalXXS\n});\nexport const useFlatTreeStyles_unstable = (state)=>{\n 'use no memo';\n const baseStyles = useBaseStyles();\n state.root.className = mergeClasses(flatTreeClassNames.root, baseStyles, state.root.className);\n return state;\n};\n"],"mappings":"AAAA,YAAY;;AACZ,SAAAA,aAAA,EAA0BC,YAAY,QAAQ,gBAAgB;AAC9D,SAASC,MAAM,QAAQ,uBAAuB;AAC9C,OAAO,MAAMC,kBAAkB,GAAG;EAC9BC,IAAI,EAAE;AACV,CAAC;AACD,MAAMC,aAAa,gBAAGL,aAAA,qGAIrB,CAAC;AACF,OAAO,MAAMM,0BAA0B,GAAIC,KAAK,IAAG;EAC/C,aAAa;;EACb,MAAMC,UAAU,GAAGH,aAAa,CAAC,CAAC;EAClCE,KAAK,CAACH,IAAI,CAACK,SAAS,GAAGR,YAAY,CAACE,kBAAkB,CAACC,IAAI,EAAEI,UAAU,EAAED,KAAK,CAACH,IAAI,CAACK,SAAS,CAAC;EAC9F,OAAOF,KAAK;AAChB,CAAC","ignoreList":[]}

View File

@@ -0,0 +1,17 @@
'use client';
import { makeResetStyles, mergeClasses } from '@griffel/react';
import { tokens } from '@fluentui/react-theme';
export const flatTreeClassNames = {
root: 'fui-FlatTree'
};
const useBaseStyles = makeResetStyles({
display: 'flex',
flexDirection: 'column',
rowGap: tokens.spacingVerticalXXS
});
export const useFlatTreeStyles_unstable = (state)=>{
'use no memo';
const baseStyles = useBaseStyles();
state.root.className = mergeClasses(flatTreeClassNames.root, baseStyles, state.root.className);
return state;
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/FlatTree/useFlatTreeStyles.styles.ts"],"sourcesContent":["'use client';\n\nimport { makeResetStyles, mergeClasses } from '@griffel/react';\nimport type { SlotClassNames } from '@fluentui/react-utilities';\nimport { tokens } from '@fluentui/react-theme';\nimport { FlatTreeSlots, FlatTreeState } from './FlatTree.types';\n\nexport const flatTreeClassNames: SlotClassNames<Omit<FlatTreeSlots, 'collapseMotion'>> = {\n root: 'fui-FlatTree',\n};\n\nconst useBaseStyles = makeResetStyles({\n display: 'flex',\n flexDirection: 'column',\n rowGap: tokens.spacingVerticalXXS,\n});\n\nexport const useFlatTreeStyles_unstable = (state: FlatTreeState): FlatTreeState => {\n 'use no memo';\n\n const baseStyles = useBaseStyles();\n state.root.className = mergeClasses(flatTreeClassNames.root, baseStyles, state.root.className);\n return state;\n};\n"],"names":["makeResetStyles","mergeClasses","tokens","flatTreeClassNames","root","useBaseStyles","display","flexDirection","rowGap","spacingVerticalXXS","useFlatTreeStyles_unstable","state","baseStyles","className"],"mappings":"AAAA;AAEA,SAASA,eAAe,EAAEC,YAAY,QAAQ,iBAAiB;AAE/D,SAASC,MAAM,QAAQ,wBAAwB;AAG/C,OAAO,MAAMC,qBAA4E;IACvFC,MAAM;AACR,EAAE;AAEF,MAAMC,gBAAgBL,gBAAgB;IACpCM,SAAS;IACTC,eAAe;IACfC,QAAQN,OAAOO,kBAAkB;AACnC;AAEA,OAAO,MAAMC,6BAA6B,CAACC;IACzC;IAEA,MAAMC,aAAaP;IACnBM,MAAMP,IAAI,CAACS,SAAS,GAAGZ,aAAaE,mBAAmBC,IAAI,EAAEQ,YAAYD,MAAMP,IAAI,CAACS,SAAS;IAC7F,OAAOF;AACT,EAAE"}

View File

@@ -0,0 +1,119 @@
'use client';
import { useEventCallback, useMergedRefs } from '@fluentui/react-utilities';
import * as React from 'react';
import { createHeadlessTree } from '../../utils/createHeadlessTree';
import { treeDataTypes } from '../../utils/tokens';
import { useFlatTreeNavigation } from '../../hooks/useFlatTreeNavigation';
import { createNextOpenItems, useControllableOpenItems } from '../../hooks/useControllableOpenItems';
import { dataTreeItemValueAttrName } from '../../utils/getTreeItemValueFromElement';
import { ImmutableSet } from '../../utils/ImmutableSet';
import { createNextFlatCheckedItems, useFlatControllableCheckedItems } from './useFlatControllableCheckedItems';
import { ImmutableMap } from '../../utils/ImmutableMap';
/**
* this hook provides FlatTree API to manage all required mechanisms to convert a list of items into renderable TreeItems
* in multiple scenarios including virtualization.
*
* !!A flat tree is an unofficial spec for tree!!
*
* It should be used on cases where more complex interactions with a Tree is required.
* On simple scenarios it is advised to simply use a nested structure instead.
*
* @param props - a list of tree items
* @param options - in case control over the internal openItems is required
*/ export function useHeadlessFlatTree_unstable(props, options = {}) {
'use no memo';
const headlessTree = React.useMemo(()=>createHeadlessTree(props), [
props
]);
const [openItems, setOpenItems] = useControllableOpenItems(options);
const [checkedItems, setCheckedItems] = useFlatControllableCheckedItems(options, headlessTree);
const navigation = useFlatTreeNavigation();
const treeRef = React.useRef(null);
const handleOpenChange = useEventCallback((event, data)=>{
var _options_onOpenChange;
const nextOpenItems = createNextOpenItems(data, openItems);
(_options_onOpenChange = options.onOpenChange) === null || _options_onOpenChange === void 0 ? void 0 : _options_onOpenChange.call(options, event, {
...data,
openItems: ImmutableSet.dangerouslyGetInternalSet(nextOpenItems)
});
setOpenItems(nextOpenItems);
});
const handleCheckedChange = useEventCallback((event, data)=>{
var _options_onCheckedChange;
const nextCheckedItems = createNextFlatCheckedItems(data, checkedItems, headlessTree);
(_options_onCheckedChange = options.onCheckedChange) === null || _options_onCheckedChange === void 0 ? void 0 : _options_onCheckedChange.call(options, event, {
...data,
checkedItems: ImmutableMap.dangerouslyGetInternalMap(nextCheckedItems)
});
setCheckedItems(nextCheckedItems);
});
const getNextNavigableItem = useEventCallback((visibleItems, data)=>{
const item = headlessTree.get(data.value);
if (item) {
switch(data.type){
case treeDataTypes.TypeAhead:
return item;
case treeDataTypes.ArrowLeft:
return headlessTree.get(item.parentValue);
case treeDataTypes.ArrowRight:
return visibleItems[item.index + 1];
case treeDataTypes.End:
return visibleItems[visibleItems.length - 1];
case treeDataTypes.Home:
return visibleItems[0];
case treeDataTypes.ArrowDown:
return visibleItems[item.index + 1];
case treeDataTypes.ArrowUp:
return visibleItems[item.index - 1];
}
}
});
const getElementFromItem = React.useCallback((item)=>{
var _treeRef_current;
return (_treeRef_current = treeRef.current) === null || _treeRef_current === void 0 ? void 0 : _treeRef_current.querySelector(`[${dataTreeItemValueAttrName}="${item.value}"]`);
}, []);
const ref = useMergedRefs(treeRef, navigation.rootRef);
const getTreeProps = React.useCallback(()=>{
var _options_onNavigation;
return {
ref,
openItems,
selectionMode: options.selectionMode,
checkedItems,
onOpenChange: handleOpenChange,
onCheckedChange: handleCheckedChange,
onNavigation: (_options_onNavigation = options.onNavigation) !== null && _options_onNavigation !== void 0 ? _options_onNavigation : noop
};
}, // ref, handleOpenChange - useEventCallback, handleCheckedChange - useEventCallback
// eslint-disable-next-line react-hooks/exhaustive-deps
[
openItems,
checkedItems,
options.selectionMode,
options.onNavigation
]);
const items = React.useCallback(()=>headlessTree.visibleItems(openItems), [
openItems,
headlessTree
]);
const getItem = React.useCallback((value)=>headlessTree.get(value), [
headlessTree
]);
return React.useMemo(()=>({
navigate: navigation.navigate,
getTreeProps,
getNextNavigableItem,
getElementFromItem,
items,
getItem
}), [
navigation.navigate,
getTreeProps,
getNextNavigableItem,
getElementFromItem,
items,
getItem
]);
}
/** @internal */ function noop() {
/* noop */ }

File diff suppressed because one or more lines are too long