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';
|
2023-03-26 23:39:41 +01:00
|
|
|
// import { isFileWritable } from '../state/model';
|
|
|
|
|
import { join } from '../fs/filesystem';
|
2023-03-27 00:20:40 +01:00
|
|
|
import { defaultSourcePath } from '../state/initial-state';
|
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;
|
|
|
|
|
}
|
2023-03-26 23:39:41 +01:00
|
|
|
const childPath = join(path, name);//`${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) {
|
|
|
|
|
const children = isDirectory ? listFilesAsNodes(fs, path) : undefined;
|
|
|
|
|
if (isDirectory && children!.length == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
nodes.push({
|
|
|
|
|
// icon: path == '/home' ? 'pi-home' : ...
|
2023-03-26 23:39:41 +01:00
|
|
|
// icon: isDirectory ? 'pi pi-folder' : isFileWritable(path) ? 'pi pi-file' : 'pi pi-lock',
|
2023-03-27 00:20:40 +01:00
|
|
|
icon: isDirectory ? 'pi pi-folder' : path === defaultSourcePath ? 'pi pi-home' : 'pi pi-file',
|
2023-03-26 19:29:24 +01:00
|
|
|
label: name,
|
|
|
|
|
data: path,
|
|
|
|
|
key: path,
|
|
|
|
|
children,
|
|
|
|
|
selectable: !isDirectory // && (name == 'LICENSE' || name.endsWith('.scad') || name.endsWith('.scad')
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
|
2023-03-27 00:20:40 +01:00
|
|
|
const fsItems = fs && listFilesAsNodes(fs, '/')
|
|
|
|
|
// [
|
|
|
|
|
// {
|
|
|
|
|
// icon: 'pi pi-home',
|
|
|
|
|
// label: 'User files',
|
|
|
|
|
// key: '/',
|
|
|
|
|
// children: listFilesAsNodes(fs, '/'),//
|
|
|
|
|
// // children: listFilesAsNodes(fs, '/', f => f != librariesFolder && !f.startsWith(`${librariesFolder}/`)),
|
|
|
|
|
// selectable: false
|
|
|
|
|
// },
|
|
|
|
|
// {
|
|
|
|
|
// icon: 'pi pi-database',
|
|
|
|
|
// label: 'Builtin libraries',
|
|
|
|
|
// key: '/libraries',
|
|
|
|
|
// children: listFilesAsNodes(fs, '/libraries'),
|
|
|
|
|
// selectable: false
|
|
|
|
|
// },
|
|
|
|
|
// ] || [];
|
2023-03-26 19:29:24 +01:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<TreeSelect
|
|
|
|
|
className={className}
|
|
|
|
|
title='OpenSCAD Playground Files'
|
|
|
|
|
value={state.params.sourcePath}
|
|
|
|
|
onChange={(e) => model.openFile(String(e.value))}
|
|
|
|
|
// dropdownIcon="pi pi-folder-open"
|
|
|
|
|
filter
|
|
|
|
|
style={style}
|
|
|
|
|
// style={{style}}
|
|
|
|
|
options={fsItems} />
|
|
|
|
|
)
|
|
|
|
|
}
|