vanjs-jsf 0.0.18 → 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/JsfUtils.d.ts +4 -0
- package/dist/JsfUtils.js +10 -0
- package/dist/VanJSComponent.d.ts +3 -0
- package/dist/VanJSComponent.js +2 -0
- package/dist/VanJsfField.d.ts +36 -0
- package/dist/VanJsfField.js +399 -0
- package/dist/VanJsfForm.d.ts +1 -0
- package/dist/VanJsfForm.js +139 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -116476
- package/dist/main.d.ts +1 -0
- package/dist/main.js +78 -0
- package/package.json +1 -1
package/dist/JsfUtils.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { State } from "vanjs-core";
|
|
2
|
+
import { VanJSComponent } from "./VanJSComponent";
|
|
3
|
+
import "van-ui-extended/dist/index.css";
|
|
4
|
+
export interface Option {
|
|
5
|
+
label: string;
|
|
6
|
+
value: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
img?: string;
|
|
9
|
+
}
|
|
10
|
+
export type MultiType = string | number | boolean;
|
|
11
|
+
export declare class VanJsfField extends VanJSComponent {
|
|
12
|
+
name: string;
|
|
13
|
+
field: Record<string, unknown>;
|
|
14
|
+
iniVal: MultiType;
|
|
15
|
+
handleChange: (field: VanJsfField, value: MultiType) => void;
|
|
16
|
+
isVisibleState: State<boolean>;
|
|
17
|
+
errorState: State<string>;
|
|
18
|
+
constructor(field: Record<string, unknown>, initVal: string, handleChange: (field: VanJsfField, value: MultiType) => void);
|
|
19
|
+
get inputType(): string;
|
|
20
|
+
get label(): string;
|
|
21
|
+
get class(): string;
|
|
22
|
+
get errorClass(): string;
|
|
23
|
+
get codemirrorExtension(): Array<any>;
|
|
24
|
+
get containerClass(): string;
|
|
25
|
+
get containerId(): string;
|
|
26
|
+
get titleClass(): string;
|
|
27
|
+
get descriptionClass(): string;
|
|
28
|
+
get description(): string;
|
|
29
|
+
get options(): Option[];
|
|
30
|
+
get isVisible(): boolean;
|
|
31
|
+
set isVisible(val: boolean);
|
|
32
|
+
get error(): string;
|
|
33
|
+
set error(val: string);
|
|
34
|
+
render(): Element;
|
|
35
|
+
isVanJsfFieldArray(fields: any): fields is VanJsfField[];
|
|
36
|
+
}
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import van from "vanjs-core";
|
|
2
|
+
import { VanJSComponent } from "./VanJSComponent";
|
|
3
|
+
import pikaday from "pikaday";
|
|
4
|
+
import { basicSetup, EditorView } from "codemirror";
|
|
5
|
+
import { javascript, esLint } from "@codemirror/lang-javascript";
|
|
6
|
+
import { json, jsonParseLinter } from "@codemirror/lang-json";
|
|
7
|
+
import { lintGutter, linter, forEachDiagnostic } from "@codemirror/lint";
|
|
8
|
+
import * as eslint from "eslint-linter-browserify";
|
|
9
|
+
import { CronComponent } from "van-ui-extended";
|
|
10
|
+
import { dracula } from "thememirror";
|
|
11
|
+
import "van-ui-extended/dist/index.css";
|
|
12
|
+
const { div, p, input, label, textarea, legend, link, fieldset, span, select, option, } = van.tags;
|
|
13
|
+
import globals from "globals";
|
|
14
|
+
var FieldType;
|
|
15
|
+
(function (FieldType) {
|
|
16
|
+
FieldType["text"] = "text";
|
|
17
|
+
FieldType["code"] = "code";
|
|
18
|
+
FieldType["cron"] = "cron";
|
|
19
|
+
FieldType["number"] = "number";
|
|
20
|
+
FieldType["textarea"] = "textarea";
|
|
21
|
+
FieldType["select"] = "select";
|
|
22
|
+
FieldType["radio"] = "radio";
|
|
23
|
+
FieldType["date"] = "date";
|
|
24
|
+
FieldType["fieldset"] = "fieldset";
|
|
25
|
+
})(FieldType || (FieldType = {}));
|
|
26
|
+
export class VanJsfField extends VanJSComponent {
|
|
27
|
+
name;
|
|
28
|
+
field;
|
|
29
|
+
iniVal;
|
|
30
|
+
handleChange;
|
|
31
|
+
isVisibleState;
|
|
32
|
+
errorState;
|
|
33
|
+
constructor(field, initVal, handleChange) {
|
|
34
|
+
super();
|
|
35
|
+
this.field = field;
|
|
36
|
+
this.name = field.name;
|
|
37
|
+
this.iniVal = initVal;
|
|
38
|
+
this.handleChange = handleChange;
|
|
39
|
+
this.isVisibleState = van.state(this.field.isVisible);
|
|
40
|
+
this.errorState = van.state("");
|
|
41
|
+
van.derive(() => console.log(`Field ${this.name} isVisible: ${this.isVisibleState.val}`));
|
|
42
|
+
}
|
|
43
|
+
get inputType() {
|
|
44
|
+
return this.field.inputType;
|
|
45
|
+
}
|
|
46
|
+
get label() {
|
|
47
|
+
return this.field.label;
|
|
48
|
+
}
|
|
49
|
+
get class() {
|
|
50
|
+
return this.field.class;
|
|
51
|
+
}
|
|
52
|
+
get errorClass() {
|
|
53
|
+
return this.field.errorClass;
|
|
54
|
+
}
|
|
55
|
+
get codemirrorExtension() {
|
|
56
|
+
const fieldGlobals = this.field.globals && typeof this.field.globals === "object"
|
|
57
|
+
? this.field.globals
|
|
58
|
+
: {};
|
|
59
|
+
const eslintConfig = {
|
|
60
|
+
// eslint configuration
|
|
61
|
+
languageOptions: {
|
|
62
|
+
globals: {
|
|
63
|
+
...fieldGlobals,
|
|
64
|
+
...globals.node,
|
|
65
|
+
},
|
|
66
|
+
parserOptions: {
|
|
67
|
+
ecmaVersion: 2022,
|
|
68
|
+
sourceType: "module",
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
rules: {
|
|
72
|
+
"constructor-super": "error",
|
|
73
|
+
"for-direction": "error",
|
|
74
|
+
"getter-return": "error",
|
|
75
|
+
"no-async-promise-executor": "error",
|
|
76
|
+
"no-case-declarations": "error",
|
|
77
|
+
"no-class-assign": "error",
|
|
78
|
+
"no-compare-neg-zero": "error",
|
|
79
|
+
"no-cond-assign": "error",
|
|
80
|
+
"no-const-assign": "error",
|
|
81
|
+
"no-constant-binary-expression": "error",
|
|
82
|
+
"no-constant-condition": "error",
|
|
83
|
+
"no-control-regex": "error",
|
|
84
|
+
"no-debugger": "error",
|
|
85
|
+
"no-delete-var": "error",
|
|
86
|
+
"no-dupe-args": "error",
|
|
87
|
+
"no-dupe-class-members": "error",
|
|
88
|
+
"no-dupe-else-if": "error",
|
|
89
|
+
"no-dupe-keys": "error",
|
|
90
|
+
"no-duplicate-case": "error",
|
|
91
|
+
"no-empty": "error",
|
|
92
|
+
"no-empty-character-class": "error",
|
|
93
|
+
"no-empty-pattern": "error",
|
|
94
|
+
"no-empty-static-block": "error",
|
|
95
|
+
"no-ex-assign": "error",
|
|
96
|
+
"no-extra-boolean-cast": "error",
|
|
97
|
+
"no-fallthrough": "error",
|
|
98
|
+
"no-func-assign": "error",
|
|
99
|
+
"no-global-assign": "error",
|
|
100
|
+
"no-import-assign": "error",
|
|
101
|
+
"no-invalid-regexp": "error",
|
|
102
|
+
"no-irregular-whitespace": "error",
|
|
103
|
+
"no-loss-of-precision": "error",
|
|
104
|
+
"no-misleading-character-class": "error",
|
|
105
|
+
"no-new-native-nonconstructor": "error",
|
|
106
|
+
"no-nonoctal-decimal-escape": "error",
|
|
107
|
+
"no-obj-calls": "error",
|
|
108
|
+
"no-octal": "error",
|
|
109
|
+
"no-prototype-builtins": "error",
|
|
110
|
+
"no-redeclare": "error",
|
|
111
|
+
"no-regex-spaces": "error",
|
|
112
|
+
"no-self-assign": "error",
|
|
113
|
+
"no-setter-return": "error",
|
|
114
|
+
"no-shadow-restricted-names": "error",
|
|
115
|
+
"no-sparse-arrays": "error",
|
|
116
|
+
"no-this-before-super": "error",
|
|
117
|
+
"no-undef": "error",
|
|
118
|
+
"no-unexpected-multiline": "error",
|
|
119
|
+
"no-unreachable": "error",
|
|
120
|
+
"no-unsafe-finally": "error",
|
|
121
|
+
"no-unsafe-negation": "error",
|
|
122
|
+
"no-unsafe-optional-chaining": "error",
|
|
123
|
+
"no-unused-labels": "error",
|
|
124
|
+
"no-unused-private-class-members": "error",
|
|
125
|
+
"no-unused-vars": "error",
|
|
126
|
+
"no-useless-backreference": "error",
|
|
127
|
+
"no-useless-catch": "error",
|
|
128
|
+
"no-useless-escape": "error",
|
|
129
|
+
"no-with": "error",
|
|
130
|
+
"require-yield": "error",
|
|
131
|
+
"use-isnan": "error",
|
|
132
|
+
"valid-typeof": "error",
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
const theme = EditorView.theme({
|
|
136
|
+
".cm-content, .cm-gutter": {
|
|
137
|
+
minHeight: this.field["min-height"] &&
|
|
138
|
+
typeof this.field["min-height"] === "string"
|
|
139
|
+
? this.field["min-height"]
|
|
140
|
+
: "150px",
|
|
141
|
+
},
|
|
142
|
+
".cm-gutters": {
|
|
143
|
+
margin: "1px",
|
|
144
|
+
},
|
|
145
|
+
".cm-scroller": {
|
|
146
|
+
overflow: "auto",
|
|
147
|
+
},
|
|
148
|
+
".cm-wrap": {
|
|
149
|
+
border: "1px solid silver",
|
|
150
|
+
},
|
|
151
|
+
}, {
|
|
152
|
+
dark: true,
|
|
153
|
+
});
|
|
154
|
+
const extensions = [
|
|
155
|
+
dracula,
|
|
156
|
+
EditorView.updateListener.of((e) => {
|
|
157
|
+
this.field.error = null;
|
|
158
|
+
forEachDiagnostic(e.state, (diag) => {
|
|
159
|
+
if (diag.severity === "error") {
|
|
160
|
+
this.field.error = diag.message;
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
this.handleChange(this, e.state.doc.toString());
|
|
164
|
+
}),
|
|
165
|
+
basicSetup,
|
|
166
|
+
lintGutter(),
|
|
167
|
+
];
|
|
168
|
+
switch (this.field.codemirrorType) {
|
|
169
|
+
case "json":
|
|
170
|
+
extensions.push(json(), linter(jsonParseLinter()));
|
|
171
|
+
break;
|
|
172
|
+
case "javascript":
|
|
173
|
+
extensions.push(javascript(), linter(esLint(new eslint.Linter(), eslintConfig)));
|
|
174
|
+
break;
|
|
175
|
+
case "typescript":
|
|
176
|
+
extensions.push(javascript({ typescript: true }), linter(esLint(new eslint.Linter(), eslintConfig)));
|
|
177
|
+
break;
|
|
178
|
+
default:
|
|
179
|
+
extensions.push(javascript(), linter(esLint(new eslint.Linter(), eslintConfig)));
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
return extensions;
|
|
183
|
+
}
|
|
184
|
+
get containerClass() {
|
|
185
|
+
return this.field.containerClass;
|
|
186
|
+
}
|
|
187
|
+
get containerId() {
|
|
188
|
+
return this.field.containerId;
|
|
189
|
+
}
|
|
190
|
+
get titleClass() {
|
|
191
|
+
return this.field.titleClass;
|
|
192
|
+
}
|
|
193
|
+
get descriptionClass() {
|
|
194
|
+
return this.field.descriptionClass;
|
|
195
|
+
}
|
|
196
|
+
get description() {
|
|
197
|
+
return this.field.description;
|
|
198
|
+
}
|
|
199
|
+
get options() {
|
|
200
|
+
return this.field.options;
|
|
201
|
+
}
|
|
202
|
+
get isVisible() {
|
|
203
|
+
return this.isVisibleState.val;
|
|
204
|
+
}
|
|
205
|
+
set isVisible(val) {
|
|
206
|
+
this.isVisibleState.val = val;
|
|
207
|
+
}
|
|
208
|
+
get error() {
|
|
209
|
+
return this.errorState.val;
|
|
210
|
+
}
|
|
211
|
+
set error(val) {
|
|
212
|
+
this.errorState.val = val;
|
|
213
|
+
}
|
|
214
|
+
render() {
|
|
215
|
+
let el;
|
|
216
|
+
const props = {
|
|
217
|
+
style: () => (this.isVisible ? "display: block" : "display: none"),
|
|
218
|
+
class: this.containerClass ? this.containerClass : "",
|
|
219
|
+
};
|
|
220
|
+
switch (this.inputType) {
|
|
221
|
+
case FieldType.text:
|
|
222
|
+
el = div(props, label({
|
|
223
|
+
for: this.name,
|
|
224
|
+
style: "margin-right: 5px;",
|
|
225
|
+
class: this.titleClass ? this.titleClass : "",
|
|
226
|
+
}, this.label), this.description &&
|
|
227
|
+
div({
|
|
228
|
+
id: `${this.name}-description`,
|
|
229
|
+
class: this.descriptionClass ? this.descriptionClass : "",
|
|
230
|
+
}, this.description), input({
|
|
231
|
+
id: this.name,
|
|
232
|
+
type: "text",
|
|
233
|
+
class: this.class ? this.class : "",
|
|
234
|
+
value: this.iniVal,
|
|
235
|
+
oninput: (e) => this.handleChange(this, e.target.value),
|
|
236
|
+
}), p({ class: this.errorClass }, () => this.error));
|
|
237
|
+
break;
|
|
238
|
+
case FieldType.textarea:
|
|
239
|
+
el = div(props, label({
|
|
240
|
+
for: this.name,
|
|
241
|
+
style: "margin-right: 5px;",
|
|
242
|
+
class: this.titleClass ? this.titleClass : "",
|
|
243
|
+
}, this.label), this.description &&
|
|
244
|
+
div({
|
|
245
|
+
id: `${this.name}-description`,
|
|
246
|
+
class: this.descriptionClass ? this.descriptionClass : "",
|
|
247
|
+
}, this.description), textarea({
|
|
248
|
+
id: this.name,
|
|
249
|
+
name: this.name,
|
|
250
|
+
class: this.class ? this.class : null,
|
|
251
|
+
rows: this.field.rows,
|
|
252
|
+
cols: this.field.columns,
|
|
253
|
+
oninput: (e) => this.handleChange(this, e.target.value),
|
|
254
|
+
}), p({ class: this.errorClass }, () => this.error));
|
|
255
|
+
break;
|
|
256
|
+
case FieldType.code:
|
|
257
|
+
el = div(props, label({
|
|
258
|
+
for: this.name,
|
|
259
|
+
style: "margin-right: 5px;",
|
|
260
|
+
class: this.titleClass ? this.titleClass : "",
|
|
261
|
+
}, this.label), this.description &&
|
|
262
|
+
div({
|
|
263
|
+
id: `${this.name}-description`,
|
|
264
|
+
class: this.descriptionClass ? this.descriptionClass : "",
|
|
265
|
+
}, this.description));
|
|
266
|
+
new EditorView({
|
|
267
|
+
doc: new String(this.iniVal).toString(),
|
|
268
|
+
parent: el,
|
|
269
|
+
extensions: this.codemirrorExtension,
|
|
270
|
+
});
|
|
271
|
+
break;
|
|
272
|
+
case FieldType.select:
|
|
273
|
+
el = div(props, label({
|
|
274
|
+
for: this.name,
|
|
275
|
+
style: "margin-right: 5px;",
|
|
276
|
+
class: this.titleClass ? this.titleClass : "",
|
|
277
|
+
}, this.label), this.description &&
|
|
278
|
+
div({
|
|
279
|
+
id: `${this.name}-description`,
|
|
280
|
+
class: this.descriptionClass ? this.descriptionClass : "",
|
|
281
|
+
}, this.description), select({
|
|
282
|
+
id: this.name,
|
|
283
|
+
name: this.name,
|
|
284
|
+
class: this.class ? this.class : null,
|
|
285
|
+
oninput: (e) => this.handleChange(this, e.target.value),
|
|
286
|
+
}, this.options?.map((opt) => option({ class: this.class ? this.class : null, value: opt.value }, opt.label, opt.description))), p({ class: this.errorClass }, () => this.error));
|
|
287
|
+
break;
|
|
288
|
+
case FieldType.date:
|
|
289
|
+
const calendarInput = input({
|
|
290
|
+
id: this.name,
|
|
291
|
+
type: "text",
|
|
292
|
+
class: this.class ? this.class : null,
|
|
293
|
+
value: this.iniVal,
|
|
294
|
+
onchange: (e) => this.handleChange(this, e.target.value),
|
|
295
|
+
});
|
|
296
|
+
el = div(props, label({
|
|
297
|
+
for: this.name,
|
|
298
|
+
style: "margin-right: 5px;",
|
|
299
|
+
class: this.titleClass ? this.titleClass : "",
|
|
300
|
+
}, this.label), this.description &&
|
|
301
|
+
div({
|
|
302
|
+
id: `${this.name}-description`,
|
|
303
|
+
class: this.descriptionClass ? this.descriptionClass : "",
|
|
304
|
+
}, this.description), calendarInput, p({ class: this.errorClass }, () => this.error), link({
|
|
305
|
+
rel: "stylesheet",
|
|
306
|
+
type: "text/css",
|
|
307
|
+
href: "https://cdn.jsdelivr.net/npm/pikaday/css/pikaday.css",
|
|
308
|
+
}));
|
|
309
|
+
new pikaday({
|
|
310
|
+
field: calendarInput,
|
|
311
|
+
format: "YYYY/MM/DD",
|
|
312
|
+
container: el,
|
|
313
|
+
firstDay: 1,
|
|
314
|
+
toString(date) {
|
|
315
|
+
// you should do formatting based on the passed format,
|
|
316
|
+
// but we will just return 'D/M/YYYY' for simplicity
|
|
317
|
+
const day = date.getDate();
|
|
318
|
+
const month = date.getMonth() + 1;
|
|
319
|
+
const year = date.getFullYear();
|
|
320
|
+
return `${year}-${("0" + month).slice(-2)}-${("0" + day).slice(-2)}`;
|
|
321
|
+
},
|
|
322
|
+
parse(dateString, format) {
|
|
323
|
+
// dateString is the result of `toString` method
|
|
324
|
+
const parts = dateString.split("/");
|
|
325
|
+
const day = parseInt(parts[0], 10);
|
|
326
|
+
const month = parseInt(parts[1], 10) - 1;
|
|
327
|
+
const year = parseInt(parts[2], 10);
|
|
328
|
+
return new Date(year, month, day);
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
break;
|
|
332
|
+
case FieldType.cron:
|
|
333
|
+
el = div(props, label({
|
|
334
|
+
for: this.name,
|
|
335
|
+
style: "margin-right: 5px;",
|
|
336
|
+
class: this.titleClass ? this.titleClass : "",
|
|
337
|
+
}, this.label), this.description &&
|
|
338
|
+
div({
|
|
339
|
+
id: `${this.name}-description`,
|
|
340
|
+
class: this.descriptionClass ? this.descriptionClass : "",
|
|
341
|
+
}, this.description), p({ class: this.errorClass }, () => this.error), () => {
|
|
342
|
+
let ele;
|
|
343
|
+
if (CronComponent) {
|
|
344
|
+
ele = new CronComponent() || null;
|
|
345
|
+
ele.setAttribute("color", "d58512");
|
|
346
|
+
ele.setAttribute("extraClass", this.class ? this.class : "");
|
|
347
|
+
ele.setAttribute("value", this.iniVal.toString());
|
|
348
|
+
ele.oninput = (e) => this.handleChange(this, e.detail.value);
|
|
349
|
+
}
|
|
350
|
+
return ele;
|
|
351
|
+
});
|
|
352
|
+
break;
|
|
353
|
+
case FieldType.number:
|
|
354
|
+
el = div(props, label({
|
|
355
|
+
for: this.name,
|
|
356
|
+
style: "margin-right: 5px;",
|
|
357
|
+
class: this.titleClass ? this.titleClass : "",
|
|
358
|
+
}, this.label), this.description &&
|
|
359
|
+
div({
|
|
360
|
+
id: `${this.name}-description`,
|
|
361
|
+
class: this.descriptionClass ? this.descriptionClass : "",
|
|
362
|
+
}, this.description), input({
|
|
363
|
+
id: this.name,
|
|
364
|
+
type: "number",
|
|
365
|
+
class: this.class ? this.class : null,
|
|
366
|
+
value: this.iniVal,
|
|
367
|
+
oninput: (e) => this.handleChange(this, e.target.value),
|
|
368
|
+
}), p({ class: this.errorClass }, () => this.error));
|
|
369
|
+
break;
|
|
370
|
+
case FieldType.fieldset:
|
|
371
|
+
console.log(this.field);
|
|
372
|
+
el = div(props, fieldset(legend({ class: this.titleClass ? this.titleClass : "" }, this.label), this.description &&
|
|
373
|
+
span({
|
|
374
|
+
id: `${this.name}-description`,
|
|
375
|
+
class: this.descriptionClass ? this.descriptionClass : "",
|
|
376
|
+
}, this.description), this.isVanJsfFieldArray(this.field.fields)
|
|
377
|
+
? this.field.fields.map((field) => field.render())
|
|
378
|
+
: null));
|
|
379
|
+
break;
|
|
380
|
+
case FieldType.radio:
|
|
381
|
+
el = div(legend({ class: this.titleClass ? this.titleClass : "" }, this.label), this.description && div(this.description), div(this.options?.map((opt) => label(input({
|
|
382
|
+
type: "radio",
|
|
383
|
+
name: this.name,
|
|
384
|
+
class: this.class ? this.class : null,
|
|
385
|
+
value: opt.value,
|
|
386
|
+
checked: this.iniVal === opt.value,
|
|
387
|
+
onchange: (e) => this.handleChange(this, e.target.value),
|
|
388
|
+
}), opt.label, opt.description), p({ class: this.errorClass }, () => this.error))));
|
|
389
|
+
break;
|
|
390
|
+
default:
|
|
391
|
+
el = div({ style: "border: 1px dashed gray; padding: 8px;" }, `Field "${this.name}" unsupported: The type "${this.inputType}" has no UI component built yet.`);
|
|
392
|
+
}
|
|
393
|
+
return el;
|
|
394
|
+
}
|
|
395
|
+
isVanJsfFieldArray(fields) {
|
|
396
|
+
return (Array.isArray(fields) &&
|
|
397
|
+
fields.every((field) => field instanceof VanJsfField));
|
|
398
|
+
}
|
|
399
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function jsform(attributes: Record<string, any>, ...children: any[]): HTMLFormElement;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import van from "vanjs-core";
|
|
2
|
+
import { createHeadlessForm, } from "@remoteoss/json-schema-form";
|
|
3
|
+
import { VanJsfField } from "./VanJsfField";
|
|
4
|
+
const { form } = van.tags;
|
|
5
|
+
class VanJsfForm {
|
|
6
|
+
schema;
|
|
7
|
+
config;
|
|
8
|
+
isValid;
|
|
9
|
+
headlessForm;
|
|
10
|
+
formFields;
|
|
11
|
+
formValues;
|
|
12
|
+
constructor(jsonSchema, config, isValid) {
|
|
13
|
+
// Bind methods to instance. Needed to pass functions as props to child components
|
|
14
|
+
//this.handleSubmit = this.handleSubmit.bind(this);
|
|
15
|
+
this.handleFieldChange = this.handleFieldChange.bind(this);
|
|
16
|
+
// Receive parameters
|
|
17
|
+
this.schema = jsonSchema;
|
|
18
|
+
this.config = config;
|
|
19
|
+
this.isValid = isValid || undefined;
|
|
20
|
+
// Working with parameters
|
|
21
|
+
const initialValues = { ...config?.initialValues };
|
|
22
|
+
this.headlessForm = createHeadlessForm(jsonSchema, config);
|
|
23
|
+
this.config.initialValues = initialValues;
|
|
24
|
+
// Read documentation about `getFieldsAndValuedFromJsf` method below
|
|
25
|
+
const { vanJsfFields, formValues } = this.getFieldsAndValuesFromJsf(this.headlessForm, this.config.initialValues);
|
|
26
|
+
this.formFields = vanJsfFields;
|
|
27
|
+
this.formValues = formValues;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Generates fields and their initial values from a headless JSON Schema Form (JSF).
|
|
31
|
+
* This method processes the fields provided by the headless form, maps them to `VanJsfField` instances,
|
|
32
|
+
* and initializes the corresponding form values.
|
|
33
|
+
*
|
|
34
|
+
* @param headlessForm - The output of the `createHeadlessForm` function, containing metadata and configuration for the form fields.
|
|
35
|
+
* @param initialValues - A record object where the keys represent field names, and the values are the initial values for the fields.
|
|
36
|
+
*
|
|
37
|
+
* @returns An object containing:
|
|
38
|
+
* - `vanJsfFields`: An array of `VanJsfField` instances representing the fields in the form.
|
|
39
|
+
* - `formValues`: A record object mapping field names to their respective initial values.
|
|
40
|
+
*
|
|
41
|
+
* @remarks
|
|
42
|
+
* - **Field Sets**: The method currently does not support field sets recursively. This needs to be implemented as part of future enhancements.
|
|
43
|
+
* - **Default Values**:
|
|
44
|
+
* - The default values are determined based on the following precedence:
|
|
45
|
+
* 1. Value in `initialValues`.
|
|
46
|
+
* 2. The `field.default` property.
|
|
47
|
+
* 3. An empty string (`""`) if neither is present.
|
|
48
|
+
* - Note: The `field.default` property is not clearly documented in the JSF API. The documentation mentions `defaultValue` instead, but this is not observed in practice.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* const { vanJsfFields, formValues } = getFieldsFromJsf(headlessForm, initialValues);
|
|
52
|
+
* console.log(vanJsfFields); // Array of VanJsfField instances
|
|
53
|
+
* console.log(formValues); // Record of field names and their initial values
|
|
54
|
+
*/
|
|
55
|
+
getFieldsAndValuesFromJsf(headlessForm, initialValues) {
|
|
56
|
+
const fields = headlessForm.fields;
|
|
57
|
+
const formValues = {};
|
|
58
|
+
const values = { ...initialValues };
|
|
59
|
+
console.log(values);
|
|
60
|
+
const vanJsfFields = this.processFields(fields, initialValues, formValues);
|
|
61
|
+
return { vanJsfFields, formValues };
|
|
62
|
+
}
|
|
63
|
+
handleFieldChange(field, value) {
|
|
64
|
+
console.log(value);
|
|
65
|
+
console.log(field.name);
|
|
66
|
+
this.formValues[field.name] = value;
|
|
67
|
+
this.config.formValues = this.formValues;
|
|
68
|
+
const { formErrors } = this.headlessForm.handleValidation(this.formValues);
|
|
69
|
+
let extraError = false;
|
|
70
|
+
console.log("formErrors", formErrors);
|
|
71
|
+
this.formFields.forEach((f) => {
|
|
72
|
+
f.isVisible = f.field.isVisible;
|
|
73
|
+
f.error = formErrors?.[f.name] ?? "";
|
|
74
|
+
console.log(f.field.error);
|
|
75
|
+
if (f.field.error) {
|
|
76
|
+
extraError = true;
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
if (this.isValid) {
|
|
80
|
+
if (formErrors || extraError) {
|
|
81
|
+
this.isValid.val = false;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
this.isValid.val = true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
processFields(fields, initialValues, formValues, parentPath = "") {
|
|
89
|
+
return fields.map((field) => {
|
|
90
|
+
// Construct the full path for the field
|
|
91
|
+
const fieldPath = parentPath ? `${parentPath}.${field.name}` : field.name;
|
|
92
|
+
// Determine the initial value for the field
|
|
93
|
+
const initVal = initialValues[fieldPath] || field.default || "";
|
|
94
|
+
// Store the initial value in the form values map
|
|
95
|
+
formValues[fieldPath] = initVal;
|
|
96
|
+
// Check if the field has nested fields and process them recursively
|
|
97
|
+
if (field.fields && field.fields.length > 0) {
|
|
98
|
+
field.fields = this.processFields(field.fields, initialValues, formValues, fieldPath);
|
|
99
|
+
}
|
|
100
|
+
// Create and return a new VanJsfField instance for this field
|
|
101
|
+
return new VanJsfField(field, initVal, this.handleFieldChange);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
export function jsform(attributes, ...children) {
|
|
106
|
+
if (!attributes.schema) {
|
|
107
|
+
throw new Error("JSON Schema is required");
|
|
108
|
+
}
|
|
109
|
+
let config = attributes.config;
|
|
110
|
+
let isValid = attributes.isValid;
|
|
111
|
+
if (!config) {
|
|
112
|
+
config = { initialValues: {}, formValues: {} };
|
|
113
|
+
}
|
|
114
|
+
else if (!config.initialValues) {
|
|
115
|
+
config.initialValues = {};
|
|
116
|
+
}
|
|
117
|
+
else if (!config.formValues) {
|
|
118
|
+
config.formValues = {};
|
|
119
|
+
}
|
|
120
|
+
const vanJsfForm = new VanJsfForm(attributes.schema, config, isValid);
|
|
121
|
+
console.log(vanJsfForm);
|
|
122
|
+
const fields = vanJsfForm.formFields.map((field) => field.render());
|
|
123
|
+
const childrenWithFields = [...fields, ...children]; // Concatenate fields with other children
|
|
124
|
+
const originalOnSubmit = attributes.onsubmit;
|
|
125
|
+
const handleSubmit = (e) => {
|
|
126
|
+
e.preventDefault();
|
|
127
|
+
config.formValues = vanJsfForm.formValues;
|
|
128
|
+
originalOnSubmit && originalOnSubmit(e);
|
|
129
|
+
};
|
|
130
|
+
const originalOnChange = attributes.onchange;
|
|
131
|
+
const handleChange = (e) => {
|
|
132
|
+
e.preventDefault();
|
|
133
|
+
config.formValues = vanJsfForm.formValues;
|
|
134
|
+
originalOnChange && originalOnChange(vanJsfForm, e);
|
|
135
|
+
};
|
|
136
|
+
attributes.onsubmit = handleSubmit;
|
|
137
|
+
attributes.onchange = handleChange;
|
|
138
|
+
return form(attributes, ...childrenWithFields);
|
|
139
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { jsform } from "./VanJsfForm";
|