@datalayer/core
Version:
[](https://datalayer.io)
81 lines (80 loc) • 3.69 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/*
* Copyright (c) 2023-2025 Datalayer, Inc.
* Distributed under the terms of the Modified BSD License.
*/
import { useCallback, useRef, useState } from 'react';
import { TreeView } from '@primer/react';
import { FileIcon } from '@primer/octicons-react';
export function modelToView(models, docRegistry) {
let items = models.map(model => ({
...model,
fileType: docRegistry?.getFileTypeForModel(model),
}));
items = items.filter(model => !model.name.startsWith('.'));
items.sort((a, b) => {
if (a.type === 'directory' && b.type !== 'directory') {
return -1;
}
if (a.type !== 'directory' && b.type === 'directory') {
return 1;
}
return a.name.localeCompare(b.name);
});
return items;
}
/**
* Directory tree item component
*/
export function DirectoryItem(props) {
const { item, contents, current, documentRegistry, onContextMenu, onSelect } = props;
const ref = useRef(null);
const [children, setChildren] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const refresh = useCallback(() => {
setIsLoading(true);
contents
.get(item.path)
.then(model => {
setIsLoading(false);
setChildren(modelToView(model.content, documentRegistry));
})
.catch(() => {
setIsLoading(false);
});
}, [contents, item, documentRegistry]);
const onExpandedChange = useCallback(async (isExpanded) => {
if (children === null && isExpanded) {
refresh();
}
}, [children, item, contents]);
return (_jsxs(TreeView.Item, { ref: ref, id: `${item.type}-${item.name}`, onExpandedChange: onExpandedChange, onSelect: () => {
onSelect(item, refresh);
}, current: item.path === current?.path, children: [_jsx(TreeView.LeadingVisual, { children: _jsx(TreeView.DirectoryIcon, {}) }), _jsx("span", { onContextMenu: event => {
event.preventDefault();
if (current?.path !== item.path) {
onSelect(item, refresh);
}
onContextMenu(ref);
}, children: item.name }), _jsx(TreeView.SubTree, { state: isLoading
? 'loading'
: (children?.length ?? -1) >= 0
? 'done'
: 'initial', children: children?.map(child => {
return child.type === 'directory' ? (_jsx(DirectoryItem, { item: child, contents: contents, current: current, documentRegistry: documentRegistry, onContextMenu: onContextMenu, onSelect: onSelect }, child.name)) : (_jsx(TreeItem, { item: child, current: child.path === current?.path, onSelect: item => onSelect(item, refresh), onContextMenu: onContextMenu }, child.name));
}) })] }));
}
export function TreeItem(props) {
const { item, current, onSelect, onContextMenu } = props;
const ref = useRef(null);
const icon = item.fileType?.icon;
return (_jsxs(TreeView.Item, { ref: ref, id: `${item.type}-${item.name}`, current: current, onSelect: () => {
onSelect(item);
}, children: [_jsx(TreeView.LeadingVisual, { children: icon ? _jsx(icon.react, { tag: 'span' }) : _jsx(FileIcon, {}) }), _jsx("span", { onContextMenu: event => {
event.preventDefault();
if (!current) {
onSelect(item);
}
onContextMenu(ref);
}, children: item.name })] }, item.name));
}