Skip to content

Commit 5b414a6

Browse files
committed
taskbar for dragging nodes onto canvas
1 parent 63b9fff commit 5b414a6

File tree

6 files changed

+136
-1
lines changed

6 files changed

+136
-1
lines changed

libs/app-canvas/src/app/component-interface/app-nav-component.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
AnimatePathFromThumbFunction,
88
AnimatePathFunction,
99
BaseNodeInfo,
10+
NodeTaskFactory,
1011
} from '@devhelpr/visual-programming-system';
1112
import { StorageProvider } from '../storage/StorageProvider';
1213
import { NodeInfo } from '@devhelpr/web-flow-executor';
@@ -41,6 +42,7 @@ export interface AppNavComponentsProps<T extends BaseNodeInfo> {
4142
) => void;
4243
isReadOnly?: boolean;
4344
hideFlowPresets?: boolean;
45+
getNodeFactory: (name: string) => NodeTaskFactory<T>;
4446
}
4547

4648
export interface GenericAppNavComponentsProps<T extends BaseNodeInfo> {

libs/app-canvas/src/app/components/navbar-components.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,5 +629,6 @@ export const NavbarComponents = (props: AppNavComponentsProps<NodeInfo>) => {
629629
executeCommand: props.executeCommand,
630630
isReadOnly: props.isReadOnly,
631631
hideFlowPresets: props.hideFlowPresets,
632+
getNodeFactory: props.getNodeFactory,
632633
});
633634
};

libs/app-canvas/src/app/components/node-sidebar-menu.ts renamed to libs/app-canvas/src/app/components/node-sidebar-menu.tsx

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,22 @@ import {
1616
IRectNodeComponent,
1717
INodeComponent,
1818
getSelectedNode,
19+
renderElement,
20+
createJSXElement,
21+
NodeTaskFactory,
22+
transformCameraSpaceToWorldSpace,
23+
getPointerPos,
24+
pointerDown,
1925
} from '@devhelpr/visual-programming-system';
2026
import {
2127
getFollowNodeExecution,
2228
setFollowNodeExecution,
2329
} from '../follow-path/followNodeExecution';
24-
import { NodeInfo } from '@devhelpr/web-flow-executor';
30+
import {
31+
canvasNodeTaskRegistryLabels,
32+
getNodeFactoryNames,
33+
NodeInfo,
34+
} from '@devhelpr/web-flow-executor';
2535
import { createInputDialog } from '../utils/create-input-dialog';
2636

2737
export class NodeSidebarMenuComponent extends Component<
@@ -42,12 +52,14 @@ export class NodeSidebarMenuComponent extends Component<
4252
followNodeExecution: HTMLButtonElement | null = null;
4353
apikeysButton: HTMLButtonElement | null = null;
4454
showDependencyConnections = false;
55+
getNodeFactory: (name: string) => NodeTaskFactory<NodeInfo>;
4556

4657
constructor(
4758
parent: BaseComponent | null,
4859
props: AppNavComponentsProps<NodeInfo>
4960
) {
5061
super(parent, props);
62+
this.getNodeFactory = props.getNodeFactory;
5163
this.template = createTemplate(
5264
`<div class="z-20 flex flex-col absolute right-0 top-1/2 bg-slate-700 -translate-y-1/2 p-[4px] rounded-l-lg">
5365
<button title="Node properties" class="${navBarButtonNomargin} flex w-[32px] h-[32px] mb-1"><span class="icon icon-tune text-[16px]"></span></button>
@@ -131,6 +143,61 @@ export class NodeSidebarMenuComponent extends Component<
131143
this.apikeysButton
132144
);
133145
this.rootElement.append(this.element);
146+
let taskbar: HTMLElement | null = null;
147+
let taskbarContainer: HTMLElement | null = null;
148+
renderElement(
149+
<div
150+
class={`taskbar-container transition-transform z-[1050] flex flex-col absolute left-0 top-[58px] max-h-[calc(100vh-108px)] bg-slate-700 p-[4px] rounded-l-lg overflow-y-scroll`}
151+
getElement={(element: HTMLElement) => {
152+
taskbarContainer = element;
153+
}}
154+
>
155+
<div
156+
class={`overflow-visible flex flex-col `}
157+
getElement={(element: HTMLElement) => {
158+
taskbar = element;
159+
160+
const nodeTasks = getNodeFactoryNames();
161+
nodeTasks.forEach((nodeTask) => {
162+
//const factory = this.getNodeFactory(nodeTask);
163+
//let categoryName = 'Default';
164+
// if (factory) {
165+
// const node = factory(canvasUpdated);
166+
// if (allowedNodeTasks.indexOf(node.name) < 0) {
167+
// return;
168+
// }
169+
// categoryName = node.category || 'uncategorized';
170+
// }
171+
const label =
172+
canvasNodeTaskRegistryLabels[nodeTask] || nodeTask;
173+
174+
renderElement(
175+
<div
176+
class={`cursor-pointer border border-white border-solid rounded px-4 py-2 text-white`}
177+
pointerdown={(event: PointerEvent) => {
178+
this.startDragNode(
179+
event,
180+
taskbar,
181+
taskbarContainer,
182+
nodeTask
183+
);
184+
}}
185+
>
186+
{label}
187+
</div>,
188+
taskbar
189+
);
190+
});
191+
}}
192+
></div>
193+
</div>,
194+
this.rootElement
195+
);
196+
this.rootElement.addEventListener('pointerup', (_event) => {
197+
if (taskbarContainer) {
198+
taskbarContainer.classList.remove('-translate-x-[100%]');
199+
}
200+
});
134201
}
135202
}
136203
this.isMounted = true;
@@ -144,6 +211,65 @@ export class NodeSidebarMenuComponent extends Component<
144211
this.isMounted = false;
145212
}
146213

214+
startDragNode = (
215+
event: PointerEvent,
216+
taskbar: HTMLElement | null,
217+
taskbarContainer: HTMLElement | null,
218+
nodeType: string
219+
) => {
220+
const factory = this.getNodeFactory(nodeType);
221+
const canvasApp = this.props.getCanvasApp();
222+
223+
if (factory && canvasApp && taskbar && taskbarContainer) {
224+
const { pointerXPos, pointerYPos, rootX, rootY } = getPointerPos(
225+
canvasApp.canvas.domElement as HTMLElement,
226+
canvasApp.rootElement,
227+
event
228+
);
229+
const { x, y } = transformCameraSpaceToWorldSpace(
230+
pointerXPos,
231+
pointerYPos - (window?.visualViewport?.offsetTop ?? 0)
232+
);
233+
234+
taskbarContainer.classList.add('-translate-x-[100%]');
235+
const nodeTask = factory(this.props.canvasUpdated, canvasApp.theme);
236+
const nodeInfo = undefined;
237+
const node = nodeTask.createVisualNode(
238+
canvasApp,
239+
x,
240+
y,
241+
undefined,
242+
undefined,
243+
undefined,
244+
undefined,
245+
undefined,
246+
undefined,
247+
nodeInfo
248+
);
249+
if (node && node.nodeInfo) {
250+
const elementRect = (
251+
node.domElement as unknown as HTMLElement | SVGElement
252+
).getBoundingClientRect();
253+
254+
const rect = transformCameraSpaceToWorldSpace(
255+
elementRect.x - rootX,
256+
elementRect.y - rootY
257+
);
258+
// TODO : IMPROVE THIS
259+
(node.nodeInfo as any).taskType = nodeType;
260+
(node.domElement as HTMLElement).setPointerCapture(event.pointerId);
261+
262+
pointerDown(
263+
x - rect.x + (node.width ?? 0) / 2,
264+
y - rect.y + (node.height ?? 0) / 2,
265+
node,
266+
canvasApp.canvas,
267+
canvasApp.interactionStateMachine
268+
);
269+
}
270+
}
271+
};
272+
147273
onClickSettingsNode = (event: Event) => {
148274
event.preventDefault();
149275
const nodeInfo = this.getSelectedNodeInfo();
@@ -437,5 +563,6 @@ export const NodeSidebarMenuComponents = (
437563
setIsStoring: props.setIsStoring,
438564
showPopup: props.showPopup,
439565
executeCommand: props.executeCommand,
566+
getNodeFactory: props.getNodeFactory,
440567
});
441568
};

libs/app-canvas/src/app/flow-app.element.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,7 @@ export class FlowAppElement extends AppElement<NodeInfo> {
680680
if (this.storageProvider && this.canvasApp && this.rootElement) {
681681
this.navbarComponent = NavbarComponents({
682682
isReadOnly: isReadOnly,
683+
getNodeFactory: getNodeTaskFactory,
683684
clearCanvas: this.clearCanvas,
684685
initializeNodes: initializeNodes,
685686
storageProvider: this.storageProvider,
@@ -896,6 +897,7 @@ export class FlowAppElement extends AppElement<NodeInfo> {
896897
if (!isReadOnly) {
897898
NodeSidebarMenuComponents({
898899
clearCanvas: this.clearCanvas,
900+
getNodeFactory: getNodeTaskFactory,
899901
initializeNodes: initializeNodes,
900902
storageProvider: this.storageProvider,
901903
selectNodeType: this.selectNodeType

libs/visual-programming-system/src/canvas-app/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ export class FlowCanvas<T extends BaseNodeInfo>
317317
event.target &&
318318
(event.target as any).closest &&
319319
((event.target as any).closest('.menu') ||
320+
(event.target as any).closest('.taskbar-container') ||
320321
(event.target as any).closest('.menu-container') ||
321322
(event.target as any).closest('.toolbar-task-list'))
322323
) {

libs/visual-programming-system/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,5 @@ export * from './utils/node-flow-queries';
5959
export * from './forms/custom-code-editor';
6060
export * from './canvas-app/composition-runtime-flow-context';
6161
export * from './interfaces/run-counter';
62+
export * from './utils/pointer-pos';
63+
export * from './components/events/pointer-events';

0 commit comments

Comments
 (0)