summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/NodeEditor.css (renamed from src/NodeEditor.css)0
-rw-r--r--src/components/NodeEditor.tsx (renamed from src/NodeEditor.tsx)80
-rw-r--r--src/components/index.ts4
-rw-r--r--src/index.tsx5
-rw-r--r--src/pages/Editor.tsx30
-rw-r--r--src/pages/index.ts5
6 files changed, 78 insertions, 46 deletions
diff --git a/src/NodeEditor.css b/src/components/NodeEditor.css
index 5a8a683..5a8a683 100644
--- a/src/NodeEditor.css
+++ b/src/components/NodeEditor.css
diff --git a/src/NodeEditor.tsx b/src/components/NodeEditor.tsx
index 781e64b..758fb76 100644
--- a/src/NodeEditor.tsx
+++ b/src/components/NodeEditor.tsx
@@ -1,11 +1,14 @@
import { useContext, useEffect, useMemo, useCallback, useRef } from 'preact/hooks';
import { signal, computed, batch, useSignal, useComputed, Signal } from '@preact/signals';
-import { Pb } from './context.ts';
-import { NodeComponent, SocketHandlers } from './node.ts';
-import { nodeRegistry } from './nodes';
-import type { SocketHandler, NodeInfo } from './node.ts';
-import { InputSocket } from './dataflow.ts';
-import { Toolbar, ButtonMenu, MenuItem } from './components';
+import { Pb } from '../context.ts';
+import type { Project } from '../types.ts';
+import { NodeComponent, SocketHandlers } from '../node.ts';
+import { nodeRegistry } from '../nodes';
+import type { SocketHandler, NodeInfo } from '../node.ts';
+import { InputSocket } from '../dataflow.ts';
+import Toolbar from './Toolbar.tsx';
+import ButtonMenu from './ButtonMenu.tsx';
+import MenuItem from './MenuItem.tsx';
import './NodeEditor.css';
interface NodeInstance {
@@ -17,22 +20,19 @@ interface NodeInstance {
outputs: Record<string, Signal<any>>;
}
-const nodeFactory = () => {
- let nextNodeId = 0;
- return (x: number, y: number, { component, func, inputs }: NodeInfo<any, any>): NodeInstance => {
- const mapEntries = (obj: {}, f: (x: [string, any]) => [string, any]) => (
- Object.fromEntries(Object.entries(obj).map(f))
- );
- const instanceInputs = mapEntries(inputs, ([k, v]) => [k, new InputSocket(v)]);
- const output = computed(() => func(mapEntries(instanceInputs, ([k, v]) => [k, v.value])));
- return {
- id: nextNodeId++,
- component,
- x: signal(x),
- y: signal(y),
- inputs: instanceInputs,
- outputs: mapEntries(output.value, ([k, _]) => [k, computed(() => output.value[k])]),
- };
+const instantiateNode = (id: string, x: number, y: number, { component, func, inputs }: NodeInfo<any, any>): NodeInstance => {
+ const mapEntries = (obj: {}, f: (x: [string, any]) => [string, any]) => (
+ Object.fromEntries(Object.entries(obj).map(f))
+ );
+ const instanceInputs = mapEntries(inputs, ([k, v]) => [k, new InputSocket(v)]);
+ const output = computed(() => func(mapEntries(instanceInputs, ([k, v]) => [k, v.value])));
+ return {
+ id,
+ component,
+ x: signal(x),
+ y: signal(y),
+ inputs: instanceInputs,
+ outputs: mapEntries(output.value, ([k, _]) => [k, computed(() => output.value[k])]),
};
};
@@ -64,18 +64,16 @@ interface LinkData extends LinkProps {
}
export interface NodeEditorProps {
- user: string;
- project: string;
+ project: Project;
}
-const NodeEditor = ({ user, project }: NodeEditorProps) => {
+const NodeEditor = ({ project }: NodeEditorProps) => {
const pb = useContext(Pb)!;
const offsetX = useSignal(0);
const offsetY = useSignal(0);
const scale = useSignal(1);
- const instantiateNode = useMemo(nodeFactory, []);
const svgRef = useRef<SVGSVGElement | null>(null);
const nodes = useSignal<NodeInstance[]>([]);
@@ -84,14 +82,14 @@ const NodeEditor = ({ user, project }: NodeEditorProps) => {
const links = useSignal<LinkData[]>([]);
const allLinks = useComputed(() => (links.value as LinkProps[]).concat(currentLink.value as LinkProps ?? []));
- useEffect(async () => {
- const projectData = await pb.collection('projects')
- .getFirstListItem(pb.filter('name = {:project} && owner.username = {:user}', { project, user }));
- const filter = pb.filter('project.id = {:id}', { id: projectData.id });
- const projectNodes = await pb.collection('nodes').getFullList({ filter });
- const projectLinks = await pb.collection('links').getFullList({ filter });
- const instances = projectNodes.map(node => instantiateNode(node.x, node.y, node.name));
- nodes.value = nodes.value.concat(instances);
+ useEffect(() => {
+ (async () => {
+ const filter = pb.filter('project.id = {:id}', { id: project.id });
+ const projectNodes = await pb.collection('nodes').getFullList({ filter });
+ const projectLinks = await pb.collection('links').getFullList({ filter });
+ const instances = projectNodes.map(node => instantiateNode(node.id, node.x, node.y, node.name));
+ nodes.value = nodes.value.concat(instances);
+ })();
}, []);
const onOutMouseDown: SocketHandler = useCallback((nodeId, socket, event) => {
@@ -200,11 +198,11 @@ const NodeEditor = ({ user, project }: NodeEditorProps) => {
});
}, []);
- const socketHandlers = {
+ const socketHandlers = useMemo(() => ({
onOutMouseDown,
onInMouseDown,
onInMouseUp,
- };
+ }), []);
const onKeyDown = useCallback((event: KeyboardEvent) => {
if (event.code === 'KeyX') {
@@ -239,16 +237,18 @@ const NodeEditor = ({ user, project }: NodeEditorProps) => {
scale.value *= 1 + delta;
}), []);
- const addNode = useCallback((node: NodeInfo<any, any>) => {
- nodes.value = nodes.value.concat(instantiateNode(100, 100, node));
+ const addNode = useCallback(async (name: string, info: NodeInfo<any, any>) => {
+ const node = await pb.collection('nodes').create({ x: 100, y: 100, type: name, project: projectId, collapsed: false });
+ alert(JSON.stringify(node));
+ nodes.value = nodes.value.concat(instantiateNode(node.id, node.x, node.y, info));
}, []);
return (
<div class="__NodeEditor">
- <Toolbar title={project}>
+ <Toolbar title={project.name}>
<ButtonMenu label="Add">
{Object.entries(nodeRegistry).map(([name, node]) => (
- <MenuItem label={name} onClick={() => addNode(node)} />
+ <MenuItem label={name} onClick={e => addNode(name, node)} />
))}
</ButtonMenu>
</Toolbar>
diff --git a/src/components/index.ts b/src/components/index.ts
index badd32e..21b1267 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -8,6 +8,7 @@ export { default as FormLabel } from './FormLabel.tsx';
export { default as Header } from './Header.tsx';
export { default as Menu } from './Menu.tsx';
export { default as MenuItem } from './MenuItem.tsx';
+export { default as NodeEditor } from './NodeEditor.tsx';
export { default as TextInput } from './TextInput.tsx';
export { default as Toolbar } from './Toolbar.tsx';
@@ -19,7 +20,8 @@ export type { ContentProps } from './Content.tsx';
export type { FormLabelProps } from './FormLabel.tsx';
export type { FormProps } from './Form.tsx';
export type { HeaderProps } from './Header.tsx';
-export type { MenuItemProps } from './MenuItem.tsx';
export type { MenuProps } from './Menu.tsx';
+export type { MenuItemProps } from './MenuItem.tsx';
+export type { NodeEditorProps } from './NodeEditor.tsx';
export type { TextInputProps } from './TextInput.tsx';
export type { ToolbarProps } from './Toolbar.tsx';
diff --git a/src/index.tsx b/src/index.tsx
index 6fdf07e..cd8c870 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -3,8 +3,7 @@ import { useMemo } from 'preact/hooks';
import { Router } from 'preact-router';
import PocketBase from 'pocketbase';
import { Pb } from './context.ts';
-import { Home, SignUp, LogIn, ProjectsList } from './pages';
-import NodeEditor from './NodeEditor.tsx';
+import { Home, SignUp, LogIn, ProjectsList, Editor } from './pages';
import './index.css';
export const App = () => {
@@ -16,7 +15,7 @@ export const App = () => {
<SignUp path="/signup" />
<LogIn path="/login" />
<ProjectsList path="/:user" />
- <NodeEditor path="/:user/:project" />
+ <Editor path="/:user/:project" />
</Router>
</Pb.Provider>
);
diff --git a/src/pages/Editor.tsx b/src/pages/Editor.tsx
new file mode 100644
index 0000000..c929595
--- /dev/null
+++ b/src/pages/Editor.tsx
@@ -0,0 +1,30 @@
+import { useEffect, useMemo, useContext } from 'preact/hooks';
+import { useSignal } from '@preact/signals';
+import { Pb } from '../context.ts';
+import type { Project } from '../types.ts';
+import { NodeEditor } from '../components';
+
+export interface EditorProps {
+ user: string;
+ project: string;
+}
+
+const Editor = ({ user, project }: EditorProps) => {
+ const pb = useContext(Pb)!;
+ const projectData = useSignal<Project | null>(null);
+
+ useEffect(() => {
+ (async () => {
+ projectData.value = await pb.collection('projects')
+ .getFirstListItem(pb.filter('owner.username = {:user} && name = {:project}', { user, project }));
+ })();
+ }, []);
+
+ return (
+ <>
+ {!!projectData.value && <NodeEditor project={projectData.value} />}
+ </>
+ );
+};
+
+export default Editor;
diff --git a/src/pages/index.ts b/src/pages/index.ts
index bd162cf..27e3906 100644
--- a/src/pages/index.ts
+++ b/src/pages/index.ts
@@ -1,4 +1,5 @@
+export { default as Editor } from './Editor.tsx';
export { default as Home } from './Home.tsx';
-export { default as SignUp } from './SignUp.tsx';
export { default as LogIn } from './LogIn.tsx';
-export { default as ProjectsList } from './ProjectsList.tsx'; \ No newline at end of file
+export { default as ProjectsList } from './ProjectsList.tsx';
+export { default as SignUp } from './SignUp.tsx';