vanjs-jsf 0.0.19 → 0.1.0

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.
@@ -1,6 +1,5 @@
1
1
  import { State } from "vanjs-core";
2
2
  import { VanJSComponent } from "./VanJSComponent";
3
- import "van-ui-extended/dist/index.css";
4
3
  export interface Option {
5
4
  label: string;
6
5
  value: string;
@@ -15,7 +14,7 @@ export declare class VanJsfField extends VanJSComponent {
15
14
  handleChange: (field: VanJsfField, value: MultiType) => void;
16
15
  isVisibleState: State<boolean>;
17
16
  errorState: State<string>;
18
- constructor(field: Record<string, unknown>, initVal: string, handleChange: (field: VanJsfField, value: MultiType) => void);
17
+ constructor(field: Record<string, unknown>, initVal: MultiType, handleChange: (field: VanJsfField, value: MultiType) => void);
19
18
  get inputType(): string;
20
19
  get label(): string;
21
20
  get class(): string;
@@ -32,5 +31,5 @@ export declare class VanJsfField extends VanJSComponent {
32
31
  get error(): string;
33
32
  set error(val: string);
34
33
  render(): Element;
35
- isVanJsfFieldArray(fields: any): fields is VanJsfField[];
34
+ isVanJsfFieldArray(fields: unknown): fields is VanJsfField[];
36
35
  }
@@ -6,16 +6,12 @@ import { javascript, esLint } from "@codemirror/lang-javascript";
6
6
  import { json, jsonParseLinter } from "@codemirror/lang-json";
7
7
  import { lintGutter, linter, forEachDiagnostic } from "@codemirror/lint";
8
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
9
  import globals from "globals";
10
+ const { div, p, input, label, textarea, legend, link, fieldset, span, select, option } = van.tags;
14
11
  var FieldType;
15
12
  (function (FieldType) {
16
13
  FieldType["text"] = "text";
17
14
  FieldType["code"] = "code";
18
- FieldType["cron"] = "cron";
19
15
  FieldType["number"] = "number";
20
16
  FieldType["textarea"] = "textarea";
21
17
  FieldType["select"] = "select";
@@ -23,6 +19,21 @@ var FieldType;
23
19
  FieldType["date"] = "date";
24
20
  FieldType["fieldset"] = "fieldset";
25
21
  })(FieldType || (FieldType = {}));
22
+ const eslintConfig = {
23
+ // eslint configuration
24
+ languageOptions: {
25
+ globals: {
26
+ ...globals.node,
27
+ },
28
+ parserOptions: {
29
+ ecmaVersion: 2022,
30
+ sourceType: "module",
31
+ },
32
+ },
33
+ rules: {
34
+ semi: ["error", "never"],
35
+ },
36
+ };
26
37
  export class VanJsfField extends VanJSComponent {
27
38
  name;
28
39
  field;
@@ -38,7 +49,6 @@ export class VanJsfField extends VanJSComponent {
38
49
  this.handleChange = handleChange;
39
50
  this.isVisibleState = van.state(this.field.isVisible);
40
51
  this.errorState = van.state("");
41
- van.derive(() => console.log(`Field ${this.name} isVisible: ${this.isVisibleState.val}`));
42
52
  }
43
53
  get inputType() {
44
54
  return this.field.inputType;
@@ -52,108 +62,26 @@ export class VanJsfField extends VanJSComponent {
52
62
  get errorClass() {
53
63
  return this.field.errorClass;
54
64
  }
65
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
66
  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
67
  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",
68
+ '.cm-content, .cm-gutter': {
69
+ "min-height": "150px",
141
70
  },
142
- ".cm-gutters": {
143
- margin: "1px",
71
+ '.cm-content': {
72
+ "min-height": "150px",
144
73
  },
145
- ".cm-scroller": {
146
- overflow: "auto",
74
+ '.cm-gutters': {
75
+ margin: '1px',
147
76
  },
148
- ".cm-wrap": {
149
- border: "1px solid silver",
77
+ '.cm-scroller': {
78
+ overflow: 'auto',
79
+ },
80
+ '.cm-wrap': {
81
+ border: '1px solid silver',
150
82
  },
151
- }, {
152
- dark: true,
153
83
  });
154
- const extensions = [
155
- dracula,
156
- EditorView.updateListener.of((e) => {
84
+ const extensions = [theme, EditorView.updateListener.of((e) => {
157
85
  this.field.error = null;
158
86
  forEachDiagnostic(e.state, (diag) => {
159
87
  if (diag.severity === "error") {
@@ -161,10 +89,7 @@ export class VanJsfField extends VanJSComponent {
161
89
  }
162
90
  });
163
91
  this.handleChange(this, e.state.doc.toString());
164
- }),
165
- basicSetup,
166
- lintGutter(),
167
- ];
92
+ }), basicSetup, lintGutter()];
168
93
  switch (this.field.codemirrorType) {
169
94
  case "json":
170
95
  extensions.push(json(), linter(jsonParseLinter()));
@@ -213,179 +138,111 @@ export class VanJsfField extends VanJSComponent {
213
138
  }
214
139
  render() {
215
140
  let el;
141
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
216
142
  const props = {
217
143
  style: () => (this.isVisible ? "display: block" : "display: none"),
218
- class: this.containerClass ? this.containerClass : "",
144
+ class: this.containerClass || ''
219
145
  };
220
146
  switch (this.inputType) {
221
147
  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({
148
+ el = div(props, label({ for: this.name, style: "margin-right: 5px;", class: this.titleClass || '' }, this.label), this.description &&
149
+ div({ id: `${this.name}-description`, class: this.descriptionClass || '' }, this.description), input({
231
150
  id: this.name,
232
151
  type: "text",
233
- class: this.class ? this.class : "",
152
+ class: this.class || '',
234
153
  value: this.iniVal,
235
154
  oninput: (e) => this.handleChange(this, e.target.value),
236
155
  }), p({ class: this.errorClass }, () => this.error));
237
156
  break;
238
157
  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({
158
+ el = div(props, label({ for: this.name, style: "margin-right: 5px;", class: this.titleClass || '' }, this.label), this.description &&
159
+ div({ id: `${this.name}-description`, class: this.descriptionClass || '' }, this.description), textarea({
248
160
  id: this.name,
249
161
  name: this.name,
250
- class: this.class ? this.class : null,
162
+ class: this.class || '',
251
163
  rows: this.field.rows,
252
164
  cols: this.field.columns,
253
165
  oninput: (e) => this.handleChange(this, e.target.value),
254
166
  }), p({ class: this.errorClass }, () => this.error));
255
167
  break;
256
168
  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));
169
+ el = div(props, label({ for: this.name, style: "margin-right: 5px;", class: this.titleClass || '' }, this.label), this.description &&
170
+ div({ id: `${this.name}-description`, class: this.descriptionClass || '' }, this.description));
266
171
  new EditorView({
267
- doc: new String(this.iniVal).toString(),
172
+ doc: String(this.iniVal),
268
173
  parent: el,
269
- extensions: this.codemirrorExtension,
174
+ extensions: this.codemirrorExtension
270
175
  });
271
176
  break;
272
177
  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({
178
+ el = div(props, label({ for: this.name, style: "margin-right: 5px;", class: this.titleClass || '' }, this.label), this.description &&
179
+ div({ id: `${this.name}-description`, class: this.descriptionClass || '' }, this.description), select({
282
180
  id: this.name,
283
181
  name: this.name,
284
- class: this.class ? this.class : null,
182
+ class: this.class || '',
285
183
  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));
184
+ }, this.options?.map((opt) => option({ class: this.class || '', value: opt.value }, opt.label, opt.description))), p({ class: this.errorClass }, () => this.error));
287
185
  break;
288
- case FieldType.date:
186
+ case FieldType.date: {
289
187
  const calendarInput = input({
290
188
  id: this.name,
291
189
  type: "text",
292
- class: this.class ? this.class : null,
190
+ class: this.class || '',
293
191
  value: this.iniVal,
294
192
  onchange: (e) => this.handleChange(this, e.target.value),
295
193
  });
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
- }));
194
+ el =
195
+ div(props, label({ for: this.name, style: "margin-right: 5px;", class: this.titleClass || '' }, this.label), this.description &&
196
+ div({ id: `${this.name}-description`, class: this.descriptionClass || '' }, this.description), calendarInput, p({ class: this.errorClass }, () => this.error),
197
+ // External CDN dependency for Pikaday CSS — consider bundling for production
198
+ link({ rel: "stylesheet", type: "text/css", href: "https://cdn.jsdelivr.net/npm/pikaday/css/pikaday.css" }));
309
199
  new pikaday({
310
200
  field: calendarInput,
311
- format: "YYYY/MM/DD",
201
+ format: 'YYYY-MM-DD',
312
202
  container: el,
313
203
  firstDay: 1,
314
204
  toString(date) {
315
- // you should do formatting based on the passed format,
316
- // but we will just return 'D/M/YYYY' for simplicity
317
205
  const day = date.getDate();
318
206
  const month = date.getMonth() + 1;
319
207
  const year = date.getFullYear();
320
208
  return `${year}-${("0" + month).slice(-2)}-${("0" + day).slice(-2)}`;
321
209
  },
322
- parse(dateString, format) {
323
- // dateString is the result of `toString` method
324
- const parts = dateString.split("/");
325
- const day = parseInt(parts[0], 10);
210
+ parse(dateString) {
211
+ const parts = dateString.split('-');
212
+ const year = parseInt(parts[0], 10);
326
213
  const month = parseInt(parts[1], 10) - 1;
327
- const year = parseInt(parts[2], 10);
214
+ const day = parseInt(parts[2], 10);
328
215
  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
216
  }
350
- return ele;
351
217
  });
352
218
  break;
219
+ }
353
220
  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({
221
+ el = div(props, label({ for: this.name, style: "margin-right: 5px;", class: this.titleClass || '' }, this.label), this.description &&
222
+ div({ id: `${this.name}-description`, class: this.descriptionClass || '' }, this.description), input({
363
223
  id: this.name,
364
224
  type: "number",
365
- class: this.class ? this.class : null,
225
+ class: this.class || '',
366
226
  value: this.iniVal,
367
- oninput: (e) => this.handleChange(this, e.target.value),
227
+ oninput: (e) => {
228
+ const val = e.target.value;
229
+ this.handleChange(this, val === "" ? "" : Number(val));
230
+ },
368
231
  }), p({ class: this.errorClass }, () => this.error));
369
232
  break;
370
233
  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));
234
+ el = div(props, fieldset(legend({ class: this.titleClass || '' }, this.label), this.description &&
235
+ span({ id: `${this.name}-description`, class: this.descriptionClass || '' }, this.description), this.isVanJsfFieldArray(this.field.fields) ? this.field.fields.map((field) => field.render()) : null));
379
236
  break;
380
237
  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({
238
+ el = div(legend({ class: this.titleClass || '' }, this.label), this.description && div(this.description), div(this.options?.map((opt) => label(input({
382
239
  type: "radio",
383
240
  name: this.name,
384
- class: this.class ? this.class : null,
241
+ class: this.class || '',
385
242
  value: opt.value,
386
243
  checked: this.iniVal === opt.value,
387
244
  onchange: (e) => this.handleChange(this, e.target.value),
388
- }), opt.label, opt.description), p({ class: this.errorClass }, () => this.error))));
245
+ }), opt.label, opt.description))), p({ class: this.errorClass }, () => this.error));
389
246
  break;
390
247
  default:
391
248
  el = div({ style: "border: 1px dashed gray; padding: 8px;" }, `Field "${this.name}" unsupported: The type "${this.inputType}" has no UI component built yet.`);
@@ -393,7 +250,6 @@ export class VanJsfField extends VanJSComponent {
393
250
  return el;
394
251
  }
395
252
  isVanJsfFieldArray(fields) {
396
- return (Array.isArray(fields) &&
397
- fields.every((field) => field instanceof VanJsfField));
253
+ return Array.isArray(fields) && fields.every(field => field instanceof VanJsfField);
398
254
  }
399
255
  }
@@ -55,23 +55,17 @@ class VanJsfForm {
55
55
  getFieldsAndValuesFromJsf(headlessForm, initialValues) {
56
56
  const fields = headlessForm.fields;
57
57
  const formValues = {};
58
- const values = { ...initialValues };
59
- console.log(values);
60
58
  const vanJsfFields = this.processFields(fields, initialValues, formValues);
61
59
  return { vanJsfFields, formValues };
62
60
  }
63
61
  handleFieldChange(field, value) {
64
- console.log(value);
65
- console.log(field.name);
66
62
  this.formValues[field.name] = value;
67
63
  this.config.formValues = this.formValues;
68
64
  const { formErrors } = this.headlessForm.handleValidation(this.formValues);
69
65
  let extraError = false;
70
- console.log("formErrors", formErrors);
71
66
  this.formFields.forEach((f) => {
72
67
  f.isVisible = f.field.isVisible;
73
68
  f.error = formErrors?.[f.name] ?? "";
74
- console.log(f.field.error);
75
69
  if (f.field.error) {
76
70
  extraError = true;
77
71
  }
@@ -106,32 +100,28 @@ export function jsform(attributes, ...children) {
106
100
  if (!attributes.schema) {
107
101
  throw new Error("JSON Schema is required");
108
102
  }
109
- let config = attributes.config;
110
- let isValid = attributes.isValid;
111
- if (!config) {
112
- config = { initialValues: {}, formValues: {} };
113
- }
114
- else if (!config.initialValues) {
103
+ const config = attributes.config ?? { initialValues: {}, formValues: {} };
104
+ if (!config.initialValues)
115
105
  config.initialValues = {};
116
- }
117
- else if (!config.formValues) {
106
+ if (!config.formValues)
118
107
  config.formValues = {};
119
- }
108
+ const isValid = attributes.isValid;
120
109
  const vanJsfForm = new VanJsfForm(attributes.schema, config, isValid);
121
- console.log(vanJsfForm);
122
110
  const fields = vanJsfForm.formFields.map((field) => field.render());
123
111
  const childrenWithFields = [...fields, ...children]; // Concatenate fields with other children
124
112
  const originalOnSubmit = attributes.onsubmit;
125
113
  const handleSubmit = (e) => {
126
114
  e.preventDefault();
127
115
  config.formValues = vanJsfForm.formValues;
128
- originalOnSubmit && originalOnSubmit(e);
116
+ if (originalOnSubmit)
117
+ originalOnSubmit(e);
129
118
  };
130
119
  const originalOnChange = attributes.onchange;
131
120
  const handleChange = (e) => {
132
121
  e.preventDefault();
133
122
  config.formValues = vanJsfForm.formValues;
134
- originalOnChange && originalOnChange(vanJsfForm, e);
123
+ if (originalOnChange)
124
+ originalOnChange(vanJsfForm, e);
135
125
  };
136
126
  attributes.onsubmit = handleSubmit;
137
127
  attributes.onchange = handleChange;