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,116 @@
'use client';
import { useEventCallback, useMergedRefs } from '@fluentui/react-utilities';
import { nextTypeAheadElement } from '../utils/nextTypeAheadElement';
import { treeDataTypes } from '../utils/tokens';
import { useRovingTabIndex } from './useRovingTabIndexes';
import { dataTreeItemValueAttrName } from '../utils/getTreeItemValueFromElement';
import * as React from 'react';
import { useHTMLElementWalkerRef } from './useHTMLElementWalkerRef';
import { useFocusFinders } from '@fluentui/react-tabster';
import { treeItemLayoutClassNames } from '../TreeItemLayout';
export function useFlatTreeNavigation(navigationMode = 'tree') {
'use no memo';
const { walkerRef, rootRef: walkerRootRef } = useHTMLElementWalkerRef();
const { rove, forceUpdate: forceUpdateRovingTabIndex, initialize: initializeRovingTabIndex } = useRovingTabIndex();
const { findFirstFocusable } = useFocusFinders();
const rootRefCallback = React.useCallback((root)=>{
if (walkerRef.current && root) {
initializeRovingTabIndex(walkerRef.current);
}
}, [
initializeRovingTabIndex,
walkerRef
]);
function getNextElement(data) {
if (!walkerRef.current) {
return null;
}
switch(data.type){
case treeDataTypes.Click:
return data.target;
case treeDataTypes.TypeAhead:
walkerRef.current.currentElement = data.target;
return nextTypeAheadElement(walkerRef.current, data.event.key);
case treeDataTypes.ArrowLeft:
{
const actions = queryActions(data.target);
if (navigationMode === 'treegrid' && (actions === null || actions === void 0 ? void 0 : actions.contains(data.target.ownerDocument.activeElement))) {
return data.target;
}
const nextElement = parentElement(data.parentValue, walkerRef.current);
if (!nextElement && process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line no-console
console.warn(`@fluentui/react-tree [useFlatTreeNavigation]:
\'ArrowLeft\' navigation was not possible.
No parent element found for the current element:`, data.target);
}
return nextElement;
}
case treeDataTypes.ArrowRight:
{
if (navigationMode === 'treegrid') {
const actions = queryActions(data.target);
if (actions) {
var _findFirstFocusable;
(_findFirstFocusable = findFirstFocusable(actions)) === null || _findFirstFocusable === void 0 ? void 0 : _findFirstFocusable.focus();
}
return null;
}
walkerRef.current.currentElement = data.target;
const nextElement = firstChild(data.target, walkerRef.current);
if (!nextElement && process.env.NODE_ENV !== 'production') {
const ariaLevel = Number(data.target.getAttribute('aria-level'));
// eslint-disable-next-line no-console
console.warn(`@fluentui/react-tree [useFlatTreeNavigation]:
\'ArrowRight\' navigation was not possible.
No element with "aria-posinset=1" and "aria-level=${ariaLevel + 1}"
was found after the current element!`, data.target);
}
return nextElement;
}
case treeDataTypes.End:
walkerRef.current.currentElement = walkerRef.current.root;
return walkerRef.current.lastChild();
case treeDataTypes.Home:
walkerRef.current.currentElement = walkerRef.current.root;
return walkerRef.current.firstChild();
case treeDataTypes.ArrowDown:
walkerRef.current.currentElement = data.target;
return walkerRef.current.nextElement();
case treeDataTypes.ArrowUp:
walkerRef.current.currentElement = data.target;
return walkerRef.current.previousElement();
}
}
const navigate = useEventCallback((data)=>{
const nextElement = getNextElement(data);
if (nextElement) {
rove(nextElement);
}
});
return {
navigate,
rootRef: useMergedRefs(walkerRootRef, rootRefCallback),
forceUpdateRovingTabIndex
};
}
function firstChild(target, treeWalker) {
const nextElement = treeWalker.nextElement();
if (!nextElement) {
return null;
}
const nextElementAriaPosInSet = nextElement.getAttribute('aria-posinset');
const nextElementAriaLevel = nextElement.getAttribute('aria-level');
const targetAriaLevel = target.getAttribute('aria-level');
if (nextElementAriaPosInSet === '1' && Number(nextElementAriaLevel) === Number(targetAriaLevel) + 1) {
return nextElement;
}
return null;
}
function parentElement(parentValue, treeWalker) {
if (parentValue === undefined) {
return null;
}
return treeWalker.root.querySelector(`[${dataTreeItemValueAttrName}="${parentValue}"]`);
}
const queryActions = (target)=>target.querySelector(`:scope > .${treeItemLayoutClassNames.root} > .${treeItemLayoutClassNames.actions}`);