137 lines
5.7 KiB
JavaScript
137 lines
5.7 KiB
JavaScript
'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
|
|
};
|
|
};
|