openscad-playground/src/components/FilePicker.tsx

112 lines
3.4 KiB
TypeScript
Raw Normal View History

2023-03-26 19:29:24 +01:00
// Portions of this file are Copyright 2021 Google LLC, and licensed under GPL2+. See COPYING.
import { CSSProperties, useContext } from 'react';
import { TreeSelect } from 'primereact/treeselect';
import TreeNode from 'primereact/treenode';
import { ModelContext, FSContext } from './contexts';
// import { isFileWritable } from '../state/model';
import { join } from '../fs/filesystem';
import { defaultSourcePath } from '../state/initial-state';
import { zipArchives } from '../fs/zip-archives';
2023-03-26 21:36:47 +01:00
function listFilesAsNodes(fs: FS, path: string, accept?: (path: string) => boolean): TreeNode[] {
2023-03-26 19:29:24 +01:00
const files: [string, string][] = []
const dirs: [string, string][] = []
for (const name of fs.readdirSync(path)) {
if (name.startsWith('.')) {
continue;
}
const childPath = join(path, name);
2023-03-26 21:36:47 +01:00
if (accept && !accept(childPath)) {
continue;
}
2023-03-26 19:29:24 +01:00
const stat = fs.lstatSync(childPath);
const isDirectory = stat.isDirectory();
if (!isDirectory && !name.endsWith('.scad')) {
continue;
}
(isDirectory ? dirs : files).push([name, childPath]);
}
[files, dirs].forEach(arr => arr.sort(([a], [b]) => a.localeCompare(b)));
const nodes: TreeNode[] = []
for (const [arr, isDirectory] of [[files, false], [dirs, true]] as [[string, string][], boolean][]) {
for (const [name, path] of arr) {
let children: TreeNode[] = [];
let label = name;
if (path.lastIndexOf('/') === 0) {
const config = zipArchives[name];
if (config && config.gitOrigin) {
const repoUrl = config.gitOrigin.repoUrl;
if (!children) children = [];
children.push({
icon: 'pi pi-github',
label: repoUrl,
key: repoUrl,
selectable: true,
});
for (const [label, link] of Object.entries(config.docs ?? [])) {
children.push({
icon: 'pi pi-book',
label,
key: link,
selectable: true,
});
}
}
2023-03-26 19:29:24 +01:00
}
if (isDirectory) {
children = [...children, ...listFilesAsNodes(fs, path, accept)];
if (children.length == 0) {
continue;
}
}
2023-03-26 19:29:24 +01:00
nodes.push({
// icon: isDirectory ? 'pi pi-folder' : isFileWritable(path) ? 'pi pi-file' : 'pi pi-lock',
icon: isDirectory ? 'pi pi-folder' : path === defaultSourcePath ? 'pi pi-home' : 'pi pi-file',
label,
2023-03-26 19:29:24 +01:00
data: path,
key: path,
children,
selectable: !isDirectory
2023-03-26 19:29:24 +01:00
});
}
}
return nodes;
}
export default function FilePicker({className, style}: {className?: string, style?: CSSProperties}) {
const model = useContext(ModelContext);
if (!model) throw new Error('No model');
const state = model.state;
const fs = useContext(FSContext);
const fsItems = fs && listFilesAsNodes(fs, '/')
2023-03-26 19:29:24 +01:00
return (
<TreeSelect
className={className}
title='OpenSCAD Playground Files'
value={state.params.sourcePath}
resetFilterOnHide={true}
filterBy="key"
onChange={e => {
const key = e.value;
if (typeof key === 'string') {
if (key.startsWith('https://')) {
window.open(key, '_blank')
} else {
model.openFile(key);
}
}
}}
2023-03-26 19:29:24 +01:00
filter
style={style}
options={fsItems} />
)
}