Skip to content

Commit 14b82ed

Browse files
committed
extending form-node functionality with improved expression and flow-variables support
1 parent db96a27 commit 14b82ed

File tree

15 files changed

+925
-120
lines changed

15 files changed

+925
-120
lines changed

apps/vps-web/src/app/custom-nodes-v2/form-visual.tsx

Lines changed: 16 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -4,68 +4,26 @@ import { initFormGenerator } from './form/VanillaFormRenderer';
44

55
export class FormVisual extends NodeVisual<NodeInfo> {
66
additionalContainerCssClasses = 'overflow-y-auto p-8';
7+
8+
private wheelEventHandler(e: WheelEvent) {
9+
e.stopPropagation();
10+
}
11+
712
updateVisual = (
813
incomingData: unknown,
914
parentNode: HTMLElement,
1015
// Using underscore prefix to indicate intentionally unused parameter
1116
// eslint-disable-next-line @typescript-eslint/no-unused-vars
12-
nodeInfo: NodeInfo
17+
nodeInfo: NodeInfo,
18+
scopeId?: string | undefined
1319
) => {
14-
//parentNode.setAttribute('data-disable-interaction', 'true');
15-
parentNode.addEventListener('wheel', (e) => {
16-
e.stopPropagation();
17-
});
20+
parentNode.removeEventListener('wheel', this.wheelEventHandler);
21+
parentNode.addEventListener('wheel', this.wheelEventHandler);
22+
if (!this.canvasAppInstance) {
23+
return;
24+
}
25+
console.log('updateVisual FormVisual scopeId', scopeId);
1826

19-
// const schema = {
20-
// app: {
21-
// title: 'Multi-step Form Example',
22-
// pages: [
23-
// {
24-
// id: 'personal-info',
25-
// title: 'Personal Information',
26-
// route: '/personal',
27-
// isEndPage: true,
28-
// components: [
29-
// {
30-
// type: 'text',
31-
// id: 'intro',
32-
// props: {
33-
// text: 'Please provide your personal information',
34-
// },
35-
// },
36-
// {
37-
// type: 'input',
38-
// id: 'name',
39-
// label: 'Full Name',
40-
// validation: {
41-
// required: true,
42-
// minLength: 2,
43-
// },
44-
// },
45-
// {
46-
// type: 'radio',
47-
// id: 'question',
48-
// label: 'Question',
49-
// props: {
50-
// optionsExpression: 'o',
51-
// },
52-
// validation: {
53-
// required: true,
54-
// },
55-
// },
56-
// {
57-
// type: 'text',
58-
// id: 'intro-add',
59-
// props: {
60-
// text: `lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit.
61-
// lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit.`,
62-
// },
63-
// },
64-
// ],
65-
// },
66-
// ],
67-
// },
68-
// };
6927
try {
7028
const schema = JSON.parse(nodeInfo.formValues?.formJson) ?? {};
7129

@@ -85,7 +43,9 @@ export class FormVisual extends NodeVisual<NodeInfo> {
8543
}
8644
this.triggerOutputs(port, data);
8745
},
88-
incomingData as any
46+
incomingData as any,
47+
this.canvasAppInstance,
48+
scopeId
8949
);
9050
} catch (error) {
9151
console.error('Error initializing form visual:', error);

apps/vps-web/src/app/custom-nodes-v2/form/VanillaFormCore.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@ import {
22
compileExpressionAsInfo,
33
runExpression,
44
} from '@devhelpr/expression-compiler';
5-
import { FormSchema, Component, Action } from './types';
5+
import { FormSchema, Component, Action, Page } from './types';
6+
import { IFlowCanvasBase } from '@devhelpr/visual-programming-system';
7+
import {
8+
getVariablePayloadInputUtils,
9+
NodeInfo,
10+
} from '@devhelpr/web-flow-executor';
611

7-
interface Page {
12+
interface FormPage extends Page {
813
id: string;
914
title: string;
1015
route: string;
11-
layout?: string;
1216
components: Component[];
1317
isEndPage?: boolean;
1418
branches?: Array<{
@@ -29,22 +33,28 @@ export interface ExtendedFormSchema extends FormSchema {
2933
export class VanillaFormCore {
3034
private _schema: ExtendedFormSchema;
3135
private container: HTMLElement;
32-
private currentPage: Page | null = null;
36+
public currentPage: FormPage | null = null;
3337
private formData: Record<string, unknown> = {};
3438
private pageHistory: string[] = [];
3539
private branchHistory: Array<{ pageId: string; branchIndex: number | null }> =
3640
[];
3741
private touchedFields: Set<string> = new Set();
3842
private validationErrors: Record<string, string> = {};
3943
private payload: any = {};
44+
private scopeId: string | undefined;
45+
private canvasAppInstance: IFlowCanvasBase<NodeInfo>;
4046
constructor(
4147
schema: ExtendedFormSchema,
4248
containerElement: HTMLElement,
43-
payload: any
49+
payload: any,
50+
canvasAppInstance: IFlowCanvasBase<NodeInfo>,
51+
scopeId?: string | undefined
4452
) {
4553
this.payload = payload;
4654
this._schema = schema;
4755
this.container = containerElement;
56+
this.canvasAppInstance = canvasAppInstance;
57+
this.scopeId = scopeId;
4858
}
4959

5060
private onSubmitEvent: ((data: any) => void) | undefined;
@@ -268,6 +278,15 @@ export class VanillaFormCore {
268278
return text;
269279
}
270280
private getTextViaExpression(expression: string) {
281+
const payloadForExpression = getVariablePayloadInputUtils(
282+
this.payload,
283+
this.payload,
284+
'string',
285+
0,
286+
0,
287+
this.scopeId,
288+
this.canvasAppInstance
289+
);
271290
const compiledExpression = compileExpressionAsInfo(expression);
272291
const expressionFunction = (
273292
new Function('payload', `${compiledExpression.script}`) as unknown as (
@@ -277,7 +296,7 @@ export class VanillaFormCore {
277296
return (
278297
runExpression(
279298
expressionFunction,
280-
this.payload,
299+
payloadForExpression,
281300
false,
282301
compiledExpression.payloadProperties
283302
) ?? ''

apps/vps-web/src/app/custom-nodes-v2/form/VanillaFormRenderer.ts

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
1+
import { IFlowCanvasBase } from '@devhelpr/visual-programming-system';
12
import { VanillaFormCore } from './VanillaFormCore';
23
import { ExtendedFormSchema } from './VanillaFormCore';
4+
import { NodeInfo } from '@devhelpr/web-flow-executor';
35

46
export function initFormGenerator(
57
schema: ExtendedFormSchema,
68
container: HTMLElement,
79
onSubmit: ((data: any) => void) | undefined = undefined,
8-
payload: any = {}
10+
payload: any = {},
11+
canvasAppInstance: IFlowCanvasBase<NodeInfo>,
12+
scopeId?: string | undefined
913
): void {
10-
const formRenderer = new VanillaFormRenderer(schema, container, payload);
14+
const formRenderer = new VanillaFormRenderer(
15+
schema,
16+
container,
17+
payload,
18+
canvasAppInstance,
19+
scopeId
20+
);
1121
formRenderer.init(onSubmit);
1222
}
1323

@@ -16,29 +26,51 @@ export class VanillaFormRenderer {
1626
private container: HTMLElement;
1727
private currentStep = 0;
1828
private totalSteps: number;
29+
// private canvasAppInstance: IFlowCanvasBase<NodeInfo>;
30+
// private scopeId: string | undefined;
1931
// @ts-expect-error payload is written but not read.. this will be used later
2032
private payload: any;
2133
constructor(
2234
schema: ExtendedFormSchema,
2335
containerElement: HTMLElement,
24-
payload: any = {}
36+
payload: any = {},
37+
canvasAppInstance: IFlowCanvasBase<NodeInfo>,
38+
scopeId?: string | undefined
2539
) {
2640
this.payload = payload;
27-
this.formCore = new VanillaFormCore(schema, containerElement, payload);
41+
this.formCore = new VanillaFormCore(
42+
schema,
43+
containerElement,
44+
payload,
45+
canvasAppInstance,
46+
scopeId
47+
);
2848
this.container = containerElement;
2949
this.totalSteps = schema.app.pages.length;
50+
// this.canvasAppInstance = canvasAppInstance;
51+
// this.scopeId = scopeId;
3052
}
3153

3254
onSubmit: ((data: any) => void) | undefined;
3355
public init(onSubmit: ((data: any) => void) | undefined): void {
3456
this.onSubmit = onSubmit;
3557
this.formCore.init(onSubmit);
58+
this.renderFormControls();
59+
}
60+
61+
private renderFormControls(): void {
3662
this.renderStepIndicator();
3763
this.renderNavigationControls();
3864
this.setupEventListeners();
3965
}
4066

4167
private renderStepIndicator(): void {
68+
if (
69+
this.formCore.currentPage?.hasStepIndicator !== undefined &&
70+
!this.formCore.currentPage?.hasStepIndicator
71+
) {
72+
return;
73+
}
4274
const indicator = document.createElement('div');
4375
indicator.className = 'flex justify-center items-center space-x-2 mb-6';
4476

@@ -74,6 +106,12 @@ export class VanillaFormRenderer {
74106
}
75107

76108
private renderNavigationControls(): void {
109+
if (
110+
this.formCore.currentPage?.hasSubmitButtons !== undefined &&
111+
!this.formCore.currentPage?.hasSubmitButtons
112+
) {
113+
return;
114+
}
77115
const controls = document.createElement('div');
78116
controls.className = 'flex justify-between items-center mt-6';
79117

@@ -102,18 +140,22 @@ export class VanillaFormRenderer {
102140
this.container.appendChild(controls);
103141
}
104142

143+
private submitHandler(event: Event): void {
144+
const customEvent = event as CustomEvent;
145+
console.log('Form submitted with data:', customEvent.detail);
146+
}
147+
105148
private setupEventListeners(): void {
106149
// Listen for form submission events
107-
this.container.addEventListener('formSubmit', (event: Event) => {
108-
const customEvent = event as CustomEvent;
109-
console.log('Form submitted with data:', customEvent.detail);
110-
});
150+
this.container.removeEventListener('formSubmit', this.submitHandler);
151+
this.container.addEventListener('formSubmit', this.submitHandler);
111152
}
112153

113154
private handlePrevious(): void {
114155
if (this.currentStep > 0) {
115156
this.currentStep--;
116157
this.formCore.handlePrevious();
158+
this.renderFormControls();
117159
this.updateNavigationControls();
118160
}
119161
}
@@ -122,6 +164,7 @@ export class VanillaFormRenderer {
122164
if (this.currentStep < this.totalSteps - 1) {
123165
this.currentStep++;
124166
this.formCore.handleNext();
167+
this.renderFormControls();
125168
this.updateNavigationControls();
126169
} else if (this.currentStep === this.totalSteps - 1) {
127170
// Submit the form

apps/vps-web/src/app/custom-nodes-v2/form/types.ts

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,26 @@ export interface Page {
1111
id: string;
1212
title: string;
1313
route: string;
14-
layout?: "grid" | "flex" | "vertical" | "horizontal";
14+
layout?: 'grid' | 'flex' | 'vertical' | 'horizontal';
1515
components: Component[];
1616
isEndPage?: boolean;
17+
hasSubmitButtons?: boolean;
18+
hasStepIndicator?: boolean;
1719
}
1820

1921
export interface Component {
2022
type:
21-
| "text"
22-
| "input"
23-
| "textarea"
24-
| "checkbox"
25-
| "radio"
26-
| "select"
27-
| "button"
28-
| "table"
29-
| "form"
30-
| "section"
31-
| "array";
23+
| 'text'
24+
| 'input'
25+
| 'textarea'
26+
| 'checkbox'
27+
| 'radio'
28+
| 'select'
29+
| 'button'
30+
| 'table'
31+
| 'form'
32+
| 'section'
33+
| 'array';
3234
id: string;
3335
label?: string;
3436
props?: Record<string, any>;
@@ -53,7 +55,7 @@ export interface Component {
5355

5456
export interface VisibilityCondition {
5557
field: string;
56-
operator: "==" | "!=" | ">" | "<" | ">=" | "<=";
58+
operator: '==' | '!=' | '>' | '<' | '>=' | '<=';
5759
value: any;
5860
}
5961

@@ -64,7 +66,7 @@ export interface EventHandlers {
6466
}
6567

6668
export interface Action {
67-
type: "navigate" | "submit" | "apiRequest" | "showMessage";
69+
type: 'navigate' | 'submit' | 'apiRequest' | 'showMessage';
6870
params?: Record<string, any>;
6971
dataSource?: string;
7072
targetPage?: string;
@@ -74,9 +76,9 @@ export interface Action {
7476

7577
export interface DataSource {
7678
id: string;
77-
type: "rest" | "graphql";
79+
type: 'rest' | 'graphql';
7880
url: string;
79-
method?: "GET" | "POST" | "PUT" | "DELETE";
81+
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
8082
query?: string;
8183
params?: Record<string, any>;
8284
responseMapping?: Record<string, any>;
@@ -85,7 +87,7 @@ export interface DataSource {
8587
export interface Branch {
8688
condition: {
8789
field: string;
88-
operator: "==" | "!=" | ">" | "<" | ">=" | "<=";
90+
operator: '==' | '!=' | '>' | '<' | '>=' | '<=';
8991
value: string;
9092
};
9193
nextPage: string;

apps/vps-web/src/app/custom-nodes/classes/base-rect-node-class.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ export class BaseRectNode {
7979
//
8080
}
8181

82-
updateVisual: ((data: any) => void) | undefined = undefined;
82+
updateVisual:
83+
| ((data: any, scopeId?: string | undefined) => void)
84+
| undefined = undefined;
8385

8486
getSettingsPopup:
8587
| ((popupContainer: HTMLElement) => IDOMElement | undefined)

0 commit comments

Comments
 (0)