173 lines
7.0 KiB
JavaScript
173 lines
7.0 KiB
JavaScript
/**
|
|
* creates a list of virtual tree items
|
|
* and provides a map to access each item by id
|
|
*/ export function createHeadlessTree(initialProps = []) {
|
|
const root = createHeadlessTreeRootItem();
|
|
const itemsPerValue = new Map([
|
|
[
|
|
root.value,
|
|
root
|
|
]
|
|
]);
|
|
const headlessTree = {
|
|
root,
|
|
get size () {
|
|
return itemsPerValue.size;
|
|
},
|
|
getParent: (key)=>{
|
|
var _itemsPerValue_get;
|
|
var _itemsPerValue_get_parentValue, _itemsPerValue_get1;
|
|
return (_itemsPerValue_get1 = itemsPerValue.get((_itemsPerValue_get_parentValue = (_itemsPerValue_get = itemsPerValue.get(key)) === null || _itemsPerValue_get === void 0 ? void 0 : _itemsPerValue_get.parentValue) !== null && _itemsPerValue_get_parentValue !== void 0 ? _itemsPerValue_get_parentValue : root.value)) !== null && _itemsPerValue_get1 !== void 0 ? _itemsPerValue_get1 : root;
|
|
},
|
|
get: (key)=>itemsPerValue.get(key),
|
|
has: (key)=>itemsPerValue.has(key),
|
|
add (props) {
|
|
const { parentValue = headlessTreeRootId, ...propsWithoutParentValue } = props;
|
|
const parentItem = itemsPerValue.get(parentValue);
|
|
if (!parentItem) {
|
|
if (process.env.NODE_ENV === 'development') {
|
|
// eslint-disable-next-line no-console
|
|
console.error(`@fluentui/react-tree [createHeadlessTree]:
|
|
TreeItem "${props.value}" is wrongly positioned, did you properly ordered provided item props? make sure provided items are organized, parents should come before children`);
|
|
}
|
|
return;
|
|
}
|
|
parentItem.itemType = 'branch';
|
|
var _propsWithoutParentValue_itemType;
|
|
const item = {
|
|
value: props.value,
|
|
getTreeItemProps: ()=>({
|
|
...propsWithoutParentValue,
|
|
parentValue,
|
|
'aria-level': item.level,
|
|
'aria-posinset': item.position,
|
|
'aria-setsize': parentItem.childrenValues.length,
|
|
itemType: item.itemType
|
|
}),
|
|
itemType: (_propsWithoutParentValue_itemType = propsWithoutParentValue.itemType) !== null && _propsWithoutParentValue_itemType !== void 0 ? _propsWithoutParentValue_itemType : 'leaf',
|
|
level: parentItem.level + 1,
|
|
parentValue,
|
|
childrenValues: [],
|
|
index: -1,
|
|
position: parentItem.childrenValues.push(props.value)
|
|
};
|
|
itemsPerValue.set(item.value, item);
|
|
},
|
|
subtree: (key)=>HeadlessTreeSubtreeGenerator(key, headlessTree),
|
|
children: (key)=>HeadlessTreeChildrenGenerator(key, headlessTree),
|
|
ancestors: (key)=>HeadlessTreeAncestorsGenerator(key, headlessTree),
|
|
visibleItems: (openItems)=>HeadlessTreeVisibleItemsGenerator(openItems, headlessTree)
|
|
};
|
|
initialProps.forEach(headlessTree.add);
|
|
return headlessTree;
|
|
}
|
|
export const headlessTreeRootId = '__fuiHeadlessTreeRoot';
|
|
function createHeadlessTreeRootItem() {
|
|
return {
|
|
parentValue: undefined,
|
|
value: headlessTreeRootId,
|
|
itemType: 'branch',
|
|
getTreeItemProps: ()=>{
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
// eslint-disable-next-line no-console
|
|
console.error(`@fluentui/react-tree [createHeadlessTree]:
|
|
Internal error, trying to access treeitem props from invalid root element`);
|
|
}
|
|
return {
|
|
id: headlessTreeRootId,
|
|
parentValue: undefined,
|
|
value: headlessTreeRootId,
|
|
'aria-setsize': -1,
|
|
'aria-level': -1,
|
|
'aria-posinset': -1,
|
|
itemType: 'branch'
|
|
};
|
|
},
|
|
childrenValues: [],
|
|
get index () {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
// eslint-disable-next-line no-console
|
|
console.error(`@fluentui/react-tree [createHeadlessTree]:
|
|
Internal error, trying to access treeitem props from invalid root element`);
|
|
}
|
|
return -1;
|
|
},
|
|
get position () {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
// eslint-disable-next-line no-console
|
|
console.error(`@fluentui/react-tree [createHeadlessTree]:
|
|
Internal error, trying to access treeitem props from invalid root element`);
|
|
}
|
|
return -1;
|
|
},
|
|
level: 0
|
|
};
|
|
}
|
|
/**
|
|
* Generator that returns all subtree of a given virtual tree item
|
|
* @param key - the key of the item to get the subtree from
|
|
*/ // eslint-disable-next-line @typescript-eslint/naming-convention
|
|
function* HeadlessTreeSubtreeGenerator(key, virtualTreeItems) {
|
|
const item = virtualTreeItems.get(key);
|
|
if (!item || item.childrenValues.length === 0) {
|
|
return;
|
|
}
|
|
for (const childValue of item.childrenValues){
|
|
yield virtualTreeItems.get(childValue);
|
|
yield* HeadlessTreeSubtreeGenerator(childValue, virtualTreeItems);
|
|
}
|
|
}
|
|
/**
|
|
* Generator that returns all children of a given virtual tree item
|
|
* @param key - the key of the item to get the children from
|
|
*/ // eslint-disable-next-line @typescript-eslint/naming-convention
|
|
function* HeadlessTreeChildrenGenerator(key, virtualTreeItems) {
|
|
const item = virtualTreeItems.get(key);
|
|
if (!item || item.childrenValues.length === 0) {
|
|
return;
|
|
}
|
|
for (const childValue of item.childrenValues){
|
|
yield virtualTreeItems.get(childValue);
|
|
}
|
|
}
|
|
/**
|
|
* Generator that returns all ancestors of a given virtual tree item
|
|
* @param key - the key of the item to get the children from
|
|
*/ // eslint-disable-next-line @typescript-eslint/naming-convention
|
|
function* HeadlessTreeAncestorsGenerator(key, virtualTreeItems) {
|
|
let parent = virtualTreeItems.getParent(key);
|
|
while(parent !== virtualTreeItems.root){
|
|
yield parent;
|
|
parent = virtualTreeItems.getParent(parent.value);
|
|
}
|
|
}
|
|
/**
|
|
* Generator that returns all visible items of a given virtual tree
|
|
* @param openItems - the open items of the tree
|
|
*/ // eslint-disable-next-line @typescript-eslint/naming-convention
|
|
function* HeadlessTreeVisibleItemsGenerator(openItems, virtualTreeItems) {
|
|
let index = 0;
|
|
for (const item of HeadlessTreeSubtreeGenerator(virtualTreeItems.root.value, virtualTreeItems)){
|
|
if (isItemVisible(item, openItems, virtualTreeItems)) {
|
|
item.index = index++;
|
|
yield item;
|
|
}
|
|
}
|
|
}
|
|
function isItemVisible(item, openItems, virtualTreeItems) {
|
|
if (item.level === 1) {
|
|
return true;
|
|
}
|
|
while(item.parentValue && item.parentValue !== virtualTreeItems.root.value){
|
|
if (!openItems.has(item.parentValue)) {
|
|
return false;
|
|
}
|
|
const parent = virtualTreeItems.get(item.parentValue);
|
|
if (!parent) {
|
|
return false;
|
|
}
|
|
item = parent;
|
|
}
|
|
return true;
|
|
}
|