112 lines
4.5 KiB
JavaScript
112 lines
4.5 KiB
JavaScript
|
import { getIntrinsicElementProps, useEventCallback, slot } from '@fluentui/react-utilities';
|
||
|
import * as React from 'react';
|
||
|
import { Collapse } from '@fluentui/react-motion-components-preview';
|
||
|
import { createCheckedItems } from '../utils/createCheckedItems';
|
||
|
import { treeDataTypes } from '../utils/tokens';
|
||
|
import { createNextOpenItems } from './useControllableOpenItems';
|
||
|
import { ImmutableSet } from '../utils/ImmutableSet';
|
||
|
import { ImmutableMap } from '../utils/ImmutableMap';
|
||
|
/**
|
||
|
* Create the state required to render the root level tree.
|
||
|
*
|
||
|
* @param props - props from this instance of tree
|
||
|
* @param ref - reference to root HTMLElement of tree
|
||
|
*/ export function useRootTree(props, ref) {
|
||
|
warnIfNoProperPropsRootTree(props);
|
||
|
const { appearance = 'subtle', size = 'medium', selectionMode = 'none' } = props;
|
||
|
const openItems = React.useMemo(()=>ImmutableSet.from(props.openItems), [
|
||
|
props.openItems
|
||
|
]);
|
||
|
const checkedItems = React.useMemo(()=>createCheckedItems(props.checkedItems), [
|
||
|
props.checkedItems
|
||
|
]);
|
||
|
const requestOpenChange = (request)=>{
|
||
|
var _props_onOpenChange;
|
||
|
(_props_onOpenChange = props.onOpenChange) === null || _props_onOpenChange === void 0 ? void 0 : _props_onOpenChange.call(props, request.event, {
|
||
|
...request,
|
||
|
openItems: ImmutableSet.dangerouslyGetInternalSet(createNextOpenItems(request, openItems))
|
||
|
});
|
||
|
};
|
||
|
const requestCheckedChange = (request)=>{
|
||
|
var _props_onCheckedChange;
|
||
|
if (selectionMode === 'none') {
|
||
|
return;
|
||
|
}
|
||
|
(_props_onCheckedChange = props.onCheckedChange) === null || _props_onCheckedChange === void 0 ? void 0 : _props_onCheckedChange.call(props, request.event, {
|
||
|
...request,
|
||
|
selectionMode,
|
||
|
checkedItems: ImmutableMap.dangerouslyGetInternalMap(checkedItems)
|
||
|
});
|
||
|
};
|
||
|
const requestNavigation = (request)=>{
|
||
|
var _props_onNavigation;
|
||
|
let isScrollPrevented = false;
|
||
|
(_props_onNavigation = props.onNavigation) === null || _props_onNavigation === void 0 ? void 0 : _props_onNavigation.call(props, request.event, {
|
||
|
...request,
|
||
|
preventScroll: ()=>{
|
||
|
isScrollPrevented = true;
|
||
|
},
|
||
|
isScrollPrevented: ()=>isScrollPrevented
|
||
|
});
|
||
|
switch(request.type){
|
||
|
case treeDataTypes.ArrowDown:
|
||
|
case treeDataTypes.ArrowUp:
|
||
|
case treeDataTypes.Home:
|
||
|
case treeDataTypes.End:
|
||
|
// stop the default behavior of the event
|
||
|
// which is to scroll the page
|
||
|
request.event.preventDefault();
|
||
|
}
|
||
|
};
|
||
|
const requestTreeResponse = useEventCallback((request)=>{
|
||
|
switch(request.requestType){
|
||
|
case 'navigate':
|
||
|
return requestNavigation(request);
|
||
|
case 'open':
|
||
|
return requestOpenChange(request);
|
||
|
case 'selection':
|
||
|
return requestCheckedChange(request);
|
||
|
}
|
||
|
});
|
||
|
return {
|
||
|
components: {
|
||
|
root: 'div',
|
||
|
// TODO: remove once React v18 slot API is modified
|
||
|
// This is a problem at the moment due to UnknownSlotProps assumption
|
||
|
// that `children` property is `ReactNode`, which in this case is not valid
|
||
|
// as PresenceComponentProps['children'] is `ReactElement`
|
||
|
collapseMotion: Collapse
|
||
|
},
|
||
|
contextType: 'root',
|
||
|
selectionMode,
|
||
|
open: true,
|
||
|
appearance,
|
||
|
size,
|
||
|
level: 1,
|
||
|
openItems,
|
||
|
checkedItems,
|
||
|
requestTreeResponse,
|
||
|
root: slot.always(getIntrinsicElementProps('div', {
|
||
|
// FIXME:
|
||
|
// `ref` is wrongly assigned to be `HTMLElement` instead of `HTMLDivElement`
|
||
|
// but since it would be a breaking change to fix it, we are casting ref to it's proper type
|
||
|
ref: ref,
|
||
|
role: 'tree',
|
||
|
'aria-multiselectable': selectionMode === 'multiselect' ? true : undefined,
|
||
|
...props
|
||
|
}), {
|
||
|
elementType: 'div'
|
||
|
}),
|
||
|
collapseMotion: undefined
|
||
|
};
|
||
|
}
|
||
|
function warnIfNoProperPropsRootTree(props) {
|
||
|
if (process.env.NODE_ENV === 'development') {
|
||
|
if (!props['aria-label'] && !props['aria-labelledby']) {
|
||
|
// eslint-disable-next-line no-console
|
||
|
console.warn(`@fluentui/react-tree [useRootTree]:
|
||
|
Tree must have either a \`aria-label\` or \`aria-labelledby\` property defined`);
|
||
|
}
|
||
|
}
|
||
|
}
|