@neo4j-ndl/react
Version:
React implementation of Neo4j Design System
149 lines • 6.32 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const jsx_runtime_1 = require("react/jsx-runtime");
/**
*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
require("@neo4j-ndl/base/lib/neo4j-ds-styles.css");
const icons_1 = require("@neo4j-ndl/react/icons");
const next_1 = require("@neo4j-ndl/react/next");
const react_1 = require("react");
const TREE_DATA = [
{
children: [
{
children: [
{ id: 'app-tsx', label: 'App.tsx' },
{ id: 'index-tsx', label: 'index.tsx' },
{ id: 'styles-css', label: 'styles.css' },
],
id: 'frontend',
label: 'Frontend',
},
{
children: [
{ id: 'server-ts', label: 'server.ts' },
{ id: 'routes-ts', label: 'routes.ts' },
],
id: 'backend',
label: 'Backend',
},
{ id: 'readme-md', label: 'README.md' },
],
id: 'projects',
label: 'Projects',
},
{
children: [
{
children: [
{ id: 'beach-jpg', label: 'beach.jpg' },
{ id: 'sunset-jpg', label: 'sunset.jpg' },
],
id: 'vacation',
label: 'Vacation',
},
{ id: 'profile-png', label: 'profile.png' },
],
id: 'photos',
label: 'Photos',
},
{
children: [
{ id: 'resume-pdf', label: 'resume.pdf' },
{ id: 'cover-letter-pdf', label: 'cover-letter.pdf' },
{ id: 'notes-txt', label: 'notes.txt' },
],
id: 'documents',
label: 'Documents',
},
{
children: [
{
children: [
{ id: 'chill-m3u', label: 'chill.m3u' },
{ id: 'workout-m3u', label: 'workout.m3u' },
],
id: 'playlists',
label: 'Playlists',
},
{ id: 'favorites-m3u', label: 'favorites.m3u' },
],
id: 'music',
label: 'Music',
},
{ id: 'todo-txt', label: 'TODO.txt' },
];
const LOAD_DELAY_MS = 600;
function iconForNode(node) {
if (node.children) {
return (0, jsx_runtime_1.jsx)(icons_1.FolderIconSolid, {});
}
if (/\.(jpg|jpeg|png|gif|svg)$/i.test(node.label)) {
return (0, jsx_runtime_1.jsx)(icons_1.PhotoIconOutline, {});
}
return (0, jsx_runtime_1.jsx)(icons_1.DocumentIconOutline, {});
}
const INITIAL_NODE_STATE = {
hasLoaded: false,
isExpanded: false,
isLoading: false,
};
function useTreeState() {
const [nodeStates, setNodeStates] = (0, react_1.useState)({});
const [selectedId, setSelectedId] = (0, react_1.useState)(null);
const timers = (0, react_1.useRef)({});
const getNodeState = (0, react_1.useCallback)((id) => { var _a; return (_a = nodeStates[id]) !== null && _a !== void 0 ? _a : INITIAL_NODE_STATE; }, [nodeStates]);
const handleExpand = (0, react_1.useCallback)((id, expanded) => {
setNodeStates((prev) => {
var _a;
const current = (_a = prev[id]) !== null && _a !== void 0 ? _a : INITIAL_NODE_STATE;
if (expanded && !current.hasLoaded) {
if (timers.current[id]) {
clearTimeout(timers.current[id]);
}
timers.current[id] = setTimeout(() => {
setNodeStates((p) => (Object.assign(Object.assign({}, p), { [id]: { hasLoaded: true, isExpanded: true, isLoading: false } })));
delete timers.current[id];
}, LOAD_DELAY_MS);
return Object.assign(Object.assign({}, prev), { [id]: { hasLoaded: false, isExpanded: true, isLoading: true } });
}
return Object.assign(Object.assign({}, prev), { [id]: Object.assign(Object.assign({}, current), { isExpanded: expanded }) });
});
}, []);
const handleSelect = (0, react_1.useCallback)((id) => setSelectedId((prev) => (prev === id ? null : id)), []);
return { getNodeState, handleExpand, handleSelect, selectedId };
}
function renderNode(node, ctx) {
const hasChildren = node.children !== undefined &&
node.children !== null &&
node.children.length > 0;
const state = ctx.getNodeState(node.id);
return ((0, jsx_runtime_1.jsxs)(next_1.TreeView.Item, { title: node.label, hasChildren: hasChildren, isExpanded: state.isExpanded, isLoading: state.isLoading, isSelected: ctx.selectedId === node.id, leadingVisual: iconForNode(node), onExpandedChange: (expanded) => ctx.handleExpand(node.id, expanded), onSelectedChange: () => ctx.handleSelect(node.id), children: [hasChildren && state.isLoading && !state.hasLoaded && ((0, jsx_runtime_1.jsx)(next_1.TreeView.SkeletonItem, { rows: 2 })), hasChildren &&
state.hasLoaded &&
node.children.map((child) => renderNode(child, ctx))] }, node.id));
}
const Component = () => {
const { getNodeState, selectedId, handleExpand, handleSelect } = useTreeState();
const ctx = (0, react_1.useMemo)(() => ({ getNodeState, handleExpand, handleSelect, selectedId }), [getNodeState, selectedId, handleExpand, handleSelect]);
return ((0, jsx_runtime_1.jsx)(next_1.TreeView, { ariaLabel: "Lazy-loaded file explorer", children: TREE_DATA.map((node) => renderNode(node, ctx)) }));
};
exports.default = Component;
//# sourceMappingURL=tree-view-lazy.story.js.map