UNPKG

@datalayer/core

Version:
79 lines (78 loc) 3.71 kB
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)); }