voc-lib-js 1.0.0 → 1.0.2
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/main.d.mts +50 -0
- package/dist/main.d.ts +50 -0
- package/dist/main.js +204 -0
- package/dist/main.mjs +177 -0
- package/package.json +18 -6
- package/samples/sample.json +0 -115
- package/src/forms-lib/form-render.ts +0 -249
- package/src/forms-lib/form-submit.ts +0 -3
- package/src/forms-lib/index.ts +0 -5
- package/src/main.ts +0 -3
- package/tests/vanilla/index.html +0 -141
- package/tsconfig.json +0 -12
package/dist/main.d.mts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
type Validation = {
|
|
2
|
+
max_length?: number;
|
|
3
|
+
min_length?: number;
|
|
4
|
+
regex?: string;
|
|
5
|
+
};
|
|
6
|
+
type Option = {
|
|
7
|
+
label: string;
|
|
8
|
+
value: string;
|
|
9
|
+
};
|
|
10
|
+
type BaseField = {
|
|
11
|
+
label?: string;
|
|
12
|
+
key?: string;
|
|
13
|
+
type?: string;
|
|
14
|
+
required?: boolean;
|
|
15
|
+
validation?: Validation;
|
|
16
|
+
hint_text?: string;
|
|
17
|
+
direction?: "horizontal" | "vertical";
|
|
18
|
+
options?: Option[];
|
|
19
|
+
default?: string;
|
|
20
|
+
};
|
|
21
|
+
type RowField = {
|
|
22
|
+
label?: string;
|
|
23
|
+
type: "row";
|
|
24
|
+
items: BaseField[];
|
|
25
|
+
};
|
|
26
|
+
type Field = BaseField | RowField;
|
|
27
|
+
type FormData = {
|
|
28
|
+
id: string;
|
|
29
|
+
code: string;
|
|
30
|
+
form_name: string;
|
|
31
|
+
fields: Field[];
|
|
32
|
+
};
|
|
33
|
+
interface RenderedForm {
|
|
34
|
+
element: HTMLFormElement;
|
|
35
|
+
validate: () => Array<{
|
|
36
|
+
element: HTMLElement;
|
|
37
|
+
message: string;
|
|
38
|
+
}>;
|
|
39
|
+
submitForm: () => void;
|
|
40
|
+
getValues: () => {
|
|
41
|
+
[key: string]: FormDataEntryValue | FormDataEntryValue[];
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
declare function renderForm(element: string | HTMLElement, data: FormData): RenderedForm | null;
|
|
45
|
+
|
|
46
|
+
declare const FormsLib: {
|
|
47
|
+
renderForm: typeof renderForm;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export { FormsLib };
|
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
type Validation = {
|
|
2
|
+
max_length?: number;
|
|
3
|
+
min_length?: number;
|
|
4
|
+
regex?: string;
|
|
5
|
+
};
|
|
6
|
+
type Option = {
|
|
7
|
+
label: string;
|
|
8
|
+
value: string;
|
|
9
|
+
};
|
|
10
|
+
type BaseField = {
|
|
11
|
+
label?: string;
|
|
12
|
+
key?: string;
|
|
13
|
+
type?: string;
|
|
14
|
+
required?: boolean;
|
|
15
|
+
validation?: Validation;
|
|
16
|
+
hint_text?: string;
|
|
17
|
+
direction?: "horizontal" | "vertical";
|
|
18
|
+
options?: Option[];
|
|
19
|
+
default?: string;
|
|
20
|
+
};
|
|
21
|
+
type RowField = {
|
|
22
|
+
label?: string;
|
|
23
|
+
type: "row";
|
|
24
|
+
items: BaseField[];
|
|
25
|
+
};
|
|
26
|
+
type Field = BaseField | RowField;
|
|
27
|
+
type FormData = {
|
|
28
|
+
id: string;
|
|
29
|
+
code: string;
|
|
30
|
+
form_name: string;
|
|
31
|
+
fields: Field[];
|
|
32
|
+
};
|
|
33
|
+
interface RenderedForm {
|
|
34
|
+
element: HTMLFormElement;
|
|
35
|
+
validate: () => Array<{
|
|
36
|
+
element: HTMLElement;
|
|
37
|
+
message: string;
|
|
38
|
+
}>;
|
|
39
|
+
submitForm: () => void;
|
|
40
|
+
getValues: () => {
|
|
41
|
+
[key: string]: FormDataEntryValue | FormDataEntryValue[];
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
declare function renderForm(element: string | HTMLElement, data: FormData): RenderedForm | null;
|
|
45
|
+
|
|
46
|
+
declare const FormsLib: {
|
|
47
|
+
renderForm: typeof renderForm;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export { FormsLib };
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/main.ts
|
|
21
|
+
var main_exports = {};
|
|
22
|
+
__export(main_exports, {
|
|
23
|
+
FormsLib: () => FormsLib
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(main_exports);
|
|
26
|
+
|
|
27
|
+
// src/forms-lib/form-submit.ts
|
|
28
|
+
function submitForm(form) {
|
|
29
|
+
console.log("Form submitted:", form);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// src/forms-lib/form-render.ts
|
|
33
|
+
function renderForm(element, data) {
|
|
34
|
+
let containers = [];
|
|
35
|
+
if (typeof element === "string") {
|
|
36
|
+
containers = Array.from(document.querySelectorAll(element));
|
|
37
|
+
} else {
|
|
38
|
+
containers = [element];
|
|
39
|
+
}
|
|
40
|
+
if (containers.length === 0) return null;
|
|
41
|
+
function buildForm() {
|
|
42
|
+
const form = document.createElement("form");
|
|
43
|
+
form.id = data.id;
|
|
44
|
+
const title = document.createElement("h2");
|
|
45
|
+
title.textContent = data.form_name;
|
|
46
|
+
form.appendChild(title);
|
|
47
|
+
function applyValidation(el, validation) {
|
|
48
|
+
if (!validation) return;
|
|
49
|
+
if (validation.min_length !== void 0) {
|
|
50
|
+
el.minLength = validation.min_length;
|
|
51
|
+
}
|
|
52
|
+
if (validation.max_length !== void 0) {
|
|
53
|
+
el.maxLength = validation.max_length;
|
|
54
|
+
}
|
|
55
|
+
if (validation.regex) {
|
|
56
|
+
if (el instanceof HTMLInputElement) {
|
|
57
|
+
el.pattern = validation.regex;
|
|
58
|
+
} else {
|
|
59
|
+
el.dataset.pattern = validation.regex;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function renderField(field) {
|
|
64
|
+
if (!field.key && !field.label) return null;
|
|
65
|
+
const wrapper = document.createElement("div");
|
|
66
|
+
wrapper.className = "form-group";
|
|
67
|
+
if (field.label) {
|
|
68
|
+
const label = document.createElement("label");
|
|
69
|
+
label.htmlFor = field.key || "";
|
|
70
|
+
label.textContent = field.label + (field.required ? " *" : "");
|
|
71
|
+
wrapper.appendChild(label);
|
|
72
|
+
}
|
|
73
|
+
let input = null;
|
|
74
|
+
switch (field.type) {
|
|
75
|
+
case "textarea": {
|
|
76
|
+
const textarea = document.createElement("textarea");
|
|
77
|
+
if (field.key) textarea.name = field.key;
|
|
78
|
+
if (field.required) textarea.required = true;
|
|
79
|
+
applyValidation(textarea, field.validation);
|
|
80
|
+
input = textarea;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
case "select": {
|
|
84
|
+
const select = document.createElement("select");
|
|
85
|
+
if (field.key) select.name = field.key;
|
|
86
|
+
if (field.required) select.required = true;
|
|
87
|
+
if (field.options) {
|
|
88
|
+
field.options.forEach((opt) => {
|
|
89
|
+
const option = document.createElement("option");
|
|
90
|
+
option.value = opt.value;
|
|
91
|
+
option.textContent = opt.label;
|
|
92
|
+
if (field.default === opt.value) option.selected = true;
|
|
93
|
+
select.appendChild(option);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
input = select;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
case "radio": {
|
|
100
|
+
const radioGroup = document.createElement("div");
|
|
101
|
+
radioGroup.className = "radio-group " + (field.direction || "vertical");
|
|
102
|
+
if (field.options) {
|
|
103
|
+
field.options.forEach((opt) => {
|
|
104
|
+
const radioWrapper = document.createElement("label");
|
|
105
|
+
radioWrapper.style.display = field.direction === "horizontal" ? "inline-block" : "block";
|
|
106
|
+
const radio = document.createElement("input");
|
|
107
|
+
radio.type = "radio";
|
|
108
|
+
radio.name = field.key || "";
|
|
109
|
+
radio.value = opt.value;
|
|
110
|
+
radioWrapper.appendChild(radio);
|
|
111
|
+
radioWrapper.appendChild(
|
|
112
|
+
document.createTextNode(opt.label)
|
|
113
|
+
);
|
|
114
|
+
radioGroup.appendChild(radioWrapper);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
input = radioGroup;
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
default: {
|
|
121
|
+
const inp = document.createElement("input");
|
|
122
|
+
inp.type = field.type || "text";
|
|
123
|
+
if (field.key) inp.name = field.key;
|
|
124
|
+
if (field.required) inp.required = true;
|
|
125
|
+
applyValidation(inp, field.validation);
|
|
126
|
+
input = inp;
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (input) wrapper.appendChild(input);
|
|
131
|
+
if (field.hint_text) {
|
|
132
|
+
const hint = document.createElement("small");
|
|
133
|
+
hint.className = "hint";
|
|
134
|
+
hint.textContent = field.hint_text;
|
|
135
|
+
wrapper.appendChild(hint);
|
|
136
|
+
}
|
|
137
|
+
return wrapper;
|
|
138
|
+
}
|
|
139
|
+
data.fields.forEach((field) => {
|
|
140
|
+
if (field.type === "row") {
|
|
141
|
+
const rowWrapper = document.createElement("div");
|
|
142
|
+
rowWrapper.className = "form-row";
|
|
143
|
+
if (field.label) {
|
|
144
|
+
const rowLabel = document.createElement("h3");
|
|
145
|
+
rowLabel.textContent = field.label;
|
|
146
|
+
rowWrapper.appendChild(rowLabel);
|
|
147
|
+
}
|
|
148
|
+
field.items.forEach((item) => {
|
|
149
|
+
const rendered = renderField(item);
|
|
150
|
+
if (rendered) rowWrapper.appendChild(rendered);
|
|
151
|
+
});
|
|
152
|
+
form.appendChild(rowWrapper);
|
|
153
|
+
} else {
|
|
154
|
+
const rendered = renderField(field);
|
|
155
|
+
if (rendered) form.appendChild(rendered);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
const submitBtn = document.createElement("button");
|
|
159
|
+
submitBtn.type = "submit";
|
|
160
|
+
submitBtn.textContent = "Submit";
|
|
161
|
+
form.appendChild(submitBtn);
|
|
162
|
+
return form;
|
|
163
|
+
}
|
|
164
|
+
const formElement = buildForm();
|
|
165
|
+
containers[0].appendChild(formElement);
|
|
166
|
+
return {
|
|
167
|
+
element: formElement,
|
|
168
|
+
// this method validates the form and returns an array of errors
|
|
169
|
+
validate: () => {
|
|
170
|
+
const errors = [];
|
|
171
|
+
const invalidElements = formElement.querySelectorAll(":invalid");
|
|
172
|
+
invalidElements.forEach((element2) => {
|
|
173
|
+
const message = element2.validationMessage;
|
|
174
|
+
errors.push({ element: element2, message });
|
|
175
|
+
});
|
|
176
|
+
return errors;
|
|
177
|
+
},
|
|
178
|
+
// this method submits the form to the portal vocphone server
|
|
179
|
+
submitForm: () => {
|
|
180
|
+
submitForm(formElement);
|
|
181
|
+
},
|
|
182
|
+
// This method gets the data from the form.
|
|
183
|
+
getValues: () => {
|
|
184
|
+
const formData = new FormData(formElement);
|
|
185
|
+
const values = {};
|
|
186
|
+
formData.forEach((value, key) => {
|
|
187
|
+
values[key] = value;
|
|
188
|
+
});
|
|
189
|
+
return values;
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// src/forms-lib/index.ts
|
|
195
|
+
var forms_lib_default = {
|
|
196
|
+
renderForm
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// src/main.ts
|
|
200
|
+
var FormsLib = forms_lib_default;
|
|
201
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
202
|
+
0 && (module.exports = {
|
|
203
|
+
FormsLib
|
|
204
|
+
});
|
package/dist/main.mjs
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// src/forms-lib/form-submit.ts
|
|
2
|
+
function submitForm(form) {
|
|
3
|
+
console.log("Form submitted:", form);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// src/forms-lib/form-render.ts
|
|
7
|
+
function renderForm(element, data) {
|
|
8
|
+
let containers = [];
|
|
9
|
+
if (typeof element === "string") {
|
|
10
|
+
containers = Array.from(document.querySelectorAll(element));
|
|
11
|
+
} else {
|
|
12
|
+
containers = [element];
|
|
13
|
+
}
|
|
14
|
+
if (containers.length === 0) return null;
|
|
15
|
+
function buildForm() {
|
|
16
|
+
const form = document.createElement("form");
|
|
17
|
+
form.id = data.id;
|
|
18
|
+
const title = document.createElement("h2");
|
|
19
|
+
title.textContent = data.form_name;
|
|
20
|
+
form.appendChild(title);
|
|
21
|
+
function applyValidation(el, validation) {
|
|
22
|
+
if (!validation) return;
|
|
23
|
+
if (validation.min_length !== void 0) {
|
|
24
|
+
el.minLength = validation.min_length;
|
|
25
|
+
}
|
|
26
|
+
if (validation.max_length !== void 0) {
|
|
27
|
+
el.maxLength = validation.max_length;
|
|
28
|
+
}
|
|
29
|
+
if (validation.regex) {
|
|
30
|
+
if (el instanceof HTMLInputElement) {
|
|
31
|
+
el.pattern = validation.regex;
|
|
32
|
+
} else {
|
|
33
|
+
el.dataset.pattern = validation.regex;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function renderField(field) {
|
|
38
|
+
if (!field.key && !field.label) return null;
|
|
39
|
+
const wrapper = document.createElement("div");
|
|
40
|
+
wrapper.className = "form-group";
|
|
41
|
+
if (field.label) {
|
|
42
|
+
const label = document.createElement("label");
|
|
43
|
+
label.htmlFor = field.key || "";
|
|
44
|
+
label.textContent = field.label + (field.required ? " *" : "");
|
|
45
|
+
wrapper.appendChild(label);
|
|
46
|
+
}
|
|
47
|
+
let input = null;
|
|
48
|
+
switch (field.type) {
|
|
49
|
+
case "textarea": {
|
|
50
|
+
const textarea = document.createElement("textarea");
|
|
51
|
+
if (field.key) textarea.name = field.key;
|
|
52
|
+
if (field.required) textarea.required = true;
|
|
53
|
+
applyValidation(textarea, field.validation);
|
|
54
|
+
input = textarea;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
case "select": {
|
|
58
|
+
const select = document.createElement("select");
|
|
59
|
+
if (field.key) select.name = field.key;
|
|
60
|
+
if (field.required) select.required = true;
|
|
61
|
+
if (field.options) {
|
|
62
|
+
field.options.forEach((opt) => {
|
|
63
|
+
const option = document.createElement("option");
|
|
64
|
+
option.value = opt.value;
|
|
65
|
+
option.textContent = opt.label;
|
|
66
|
+
if (field.default === opt.value) option.selected = true;
|
|
67
|
+
select.appendChild(option);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
input = select;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
case "radio": {
|
|
74
|
+
const radioGroup = document.createElement("div");
|
|
75
|
+
radioGroup.className = "radio-group " + (field.direction || "vertical");
|
|
76
|
+
if (field.options) {
|
|
77
|
+
field.options.forEach((opt) => {
|
|
78
|
+
const radioWrapper = document.createElement("label");
|
|
79
|
+
radioWrapper.style.display = field.direction === "horizontal" ? "inline-block" : "block";
|
|
80
|
+
const radio = document.createElement("input");
|
|
81
|
+
radio.type = "radio";
|
|
82
|
+
radio.name = field.key || "";
|
|
83
|
+
radio.value = opt.value;
|
|
84
|
+
radioWrapper.appendChild(radio);
|
|
85
|
+
radioWrapper.appendChild(
|
|
86
|
+
document.createTextNode(opt.label)
|
|
87
|
+
);
|
|
88
|
+
radioGroup.appendChild(radioWrapper);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
input = radioGroup;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
default: {
|
|
95
|
+
const inp = document.createElement("input");
|
|
96
|
+
inp.type = field.type || "text";
|
|
97
|
+
if (field.key) inp.name = field.key;
|
|
98
|
+
if (field.required) inp.required = true;
|
|
99
|
+
applyValidation(inp, field.validation);
|
|
100
|
+
input = inp;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (input) wrapper.appendChild(input);
|
|
105
|
+
if (field.hint_text) {
|
|
106
|
+
const hint = document.createElement("small");
|
|
107
|
+
hint.className = "hint";
|
|
108
|
+
hint.textContent = field.hint_text;
|
|
109
|
+
wrapper.appendChild(hint);
|
|
110
|
+
}
|
|
111
|
+
return wrapper;
|
|
112
|
+
}
|
|
113
|
+
data.fields.forEach((field) => {
|
|
114
|
+
if (field.type === "row") {
|
|
115
|
+
const rowWrapper = document.createElement("div");
|
|
116
|
+
rowWrapper.className = "form-row";
|
|
117
|
+
if (field.label) {
|
|
118
|
+
const rowLabel = document.createElement("h3");
|
|
119
|
+
rowLabel.textContent = field.label;
|
|
120
|
+
rowWrapper.appendChild(rowLabel);
|
|
121
|
+
}
|
|
122
|
+
field.items.forEach((item) => {
|
|
123
|
+
const rendered = renderField(item);
|
|
124
|
+
if (rendered) rowWrapper.appendChild(rendered);
|
|
125
|
+
});
|
|
126
|
+
form.appendChild(rowWrapper);
|
|
127
|
+
} else {
|
|
128
|
+
const rendered = renderField(field);
|
|
129
|
+
if (rendered) form.appendChild(rendered);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
const submitBtn = document.createElement("button");
|
|
133
|
+
submitBtn.type = "submit";
|
|
134
|
+
submitBtn.textContent = "Submit";
|
|
135
|
+
form.appendChild(submitBtn);
|
|
136
|
+
return form;
|
|
137
|
+
}
|
|
138
|
+
const formElement = buildForm();
|
|
139
|
+
containers[0].appendChild(formElement);
|
|
140
|
+
return {
|
|
141
|
+
element: formElement,
|
|
142
|
+
// this method validates the form and returns an array of errors
|
|
143
|
+
validate: () => {
|
|
144
|
+
const errors = [];
|
|
145
|
+
const invalidElements = formElement.querySelectorAll(":invalid");
|
|
146
|
+
invalidElements.forEach((element2) => {
|
|
147
|
+
const message = element2.validationMessage;
|
|
148
|
+
errors.push({ element: element2, message });
|
|
149
|
+
});
|
|
150
|
+
return errors;
|
|
151
|
+
},
|
|
152
|
+
// this method submits the form to the portal vocphone server
|
|
153
|
+
submitForm: () => {
|
|
154
|
+
submitForm(formElement);
|
|
155
|
+
},
|
|
156
|
+
// This method gets the data from the form.
|
|
157
|
+
getValues: () => {
|
|
158
|
+
const formData = new FormData(formElement);
|
|
159
|
+
const values = {};
|
|
160
|
+
formData.forEach((value, key) => {
|
|
161
|
+
values[key] = value;
|
|
162
|
+
});
|
|
163
|
+
return values;
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/forms-lib/index.ts
|
|
169
|
+
var forms_lib_default = {
|
|
170
|
+
renderForm
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// src/main.ts
|
|
174
|
+
var FormsLib = forms_lib_default;
|
|
175
|
+
export {
|
|
176
|
+
FormsLib
|
|
177
|
+
};
|
package/package.json
CHANGED
|
@@ -1,22 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "voc-lib-js",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "A JavaScript library for VocPhone",
|
|
5
5
|
"main": "./dist/main.cjs",
|
|
6
6
|
"module": "./dist/main.mjs",
|
|
7
7
|
"types": "./dist/main.d.ts",
|
|
8
8
|
"scripts": {
|
|
9
|
-
"build": "tsup src/main.ts --format cjs,esm --dts"
|
|
9
|
+
"build": "tsup src/main.ts --format cjs,esm --dts",
|
|
10
|
+
"publish": "rimraf dist && npm run build && npm publish --access public"
|
|
10
11
|
},
|
|
11
12
|
"repository": {
|
|
12
13
|
"type": "git",
|
|
13
14
|
"url": "https://git.vocphone.com/vocphone-voice/voc-lib-js.git"
|
|
14
15
|
},
|
|
15
|
-
"keywords": [
|
|
16
|
+
"keywords": [
|
|
17
|
+
"vocphone",
|
|
18
|
+
"voice",
|
|
19
|
+
"library",
|
|
20
|
+
"javascript",
|
|
21
|
+
"typescript"
|
|
22
|
+
],
|
|
16
23
|
"author": "VocPhone Team",
|
|
17
24
|
"license": "ISC",
|
|
18
25
|
"devDependencies": {
|
|
19
26
|
"tsup": "^8.5.0",
|
|
20
|
-
"typescript": "^5.9.2"
|
|
21
|
-
|
|
22
|
-
}
|
|
27
|
+
"typescript": "^5.9.2",
|
|
28
|
+
"rimraf": "^5.0.5"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md"
|
|
33
|
+
]
|
|
34
|
+
}
|
package/samples/sample.json
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "SDFSD345634563456",
|
|
3
|
-
"code": "USER_REG_01",
|
|
4
|
-
"form_name": "User Registration",
|
|
5
|
-
"fields": [
|
|
6
|
-
{
|
|
7
|
-
"label": "Name", // optional show the label of the group if exists
|
|
8
|
-
"type": "row",
|
|
9
|
-
"items": [
|
|
10
|
-
{
|
|
11
|
-
"label": "First Name",
|
|
12
|
-
"key": "first_name",
|
|
13
|
-
"type": "text",
|
|
14
|
-
"required": true,
|
|
15
|
-
"validation": {
|
|
16
|
-
"max_length": 30,
|
|
17
|
-
"min_length": 8,
|
|
18
|
-
"regex": "^(?=.*[0-9])(?=.*[a-zA-Z]).*$"
|
|
19
|
-
},
|
|
20
|
-
"hint_text": "Your first name must be at least 8 characters long and contain both letters and numbers."
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
"label": "Last Name",
|
|
24
|
-
"key": "last_name",
|
|
25
|
-
"type": "text",
|
|
26
|
-
"required": true,
|
|
27
|
-
"validation": {
|
|
28
|
-
"min_length": 8
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
]
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
"label": "Username",
|
|
35
|
-
"key": "username",
|
|
36
|
-
"type": "text",
|
|
37
|
-
"required": true,
|
|
38
|
-
"validation": {
|
|
39
|
-
"min_length": 8
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
"label": "Password",
|
|
44
|
-
"key": "password",
|
|
45
|
-
"type": "password",
|
|
46
|
-
"required": true,
|
|
47
|
-
"validation": {
|
|
48
|
-
"min_length": 8,
|
|
49
|
-
"regex": "^(?=.*[0-9])(?=.*[a-zA-Z]).*$"
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
"label": "Text Area",
|
|
54
|
-
"key": "text_area",
|
|
55
|
-
"type": "textarea",
|
|
56
|
-
"required": true,
|
|
57
|
-
"validation": {
|
|
58
|
-
"min_length": 8
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
"label": "Email",
|
|
63
|
-
"key": "email",
|
|
64
|
-
"type": "email",
|
|
65
|
-
"required": false
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
"validation": {
|
|
69
|
-
"min_length": 8
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
"label": "Gender",
|
|
74
|
-
"type": "radio",
|
|
75
|
-
"key": "gender",
|
|
76
|
-
"required": false,
|
|
77
|
-
"direction": "horizontal",
|
|
78
|
-
"options": [
|
|
79
|
-
{
|
|
80
|
-
"label": "Male",
|
|
81
|
-
"value": "male"
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
"label": "Female",
|
|
85
|
-
"value": "female"
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
"label": "Other",
|
|
89
|
-
"value": "other"
|
|
90
|
-
}
|
|
91
|
-
]
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
"label": "Country",
|
|
95
|
-
"type": "select",
|
|
96
|
-
"key": "country",
|
|
97
|
-
"default": "usa",
|
|
98
|
-
"required": false,
|
|
99
|
-
"options": [
|
|
100
|
-
{
|
|
101
|
-
"label": "USA",
|
|
102
|
-
"value": "usa"
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
"label": "Canada",
|
|
106
|
-
"value": "canada"
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
"label": "UK",
|
|
110
|
-
"value": "uk"
|
|
111
|
-
}
|
|
112
|
-
]
|
|
113
|
-
}
|
|
114
|
-
]
|
|
115
|
-
}
|
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
import { submitForm } from "./form-submit";
|
|
2
|
-
|
|
3
|
-
// ---- Types ----
|
|
4
|
-
type Validation = {
|
|
5
|
-
max_length?: number;
|
|
6
|
-
min_length?: number;
|
|
7
|
-
regex?: string;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
type Option = {
|
|
11
|
-
label: string;
|
|
12
|
-
value: string;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
type BaseField = {
|
|
16
|
-
label?: string;
|
|
17
|
-
key?: string;
|
|
18
|
-
type?: string;
|
|
19
|
-
required?: boolean;
|
|
20
|
-
validation?: Validation;
|
|
21
|
-
hint_text?: string;
|
|
22
|
-
direction?: "horizontal" | "vertical";
|
|
23
|
-
options?: Option[];
|
|
24
|
-
default?: string;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
type RowField = {
|
|
28
|
-
label?: string; // optional group label
|
|
29
|
-
type: "row";
|
|
30
|
-
items: BaseField[];
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
type Field = BaseField | RowField;
|
|
34
|
-
|
|
35
|
-
export type FormData = {
|
|
36
|
-
id: string;
|
|
37
|
-
code: string;
|
|
38
|
-
form_name: string;
|
|
39
|
-
fields: Field[];
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
// ---- Main return type ----
|
|
43
|
-
export interface RenderedForm {
|
|
44
|
-
element: HTMLFormElement;
|
|
45
|
-
validate: () => Array<{ element: HTMLElement, message: string }>;
|
|
46
|
-
submitForm: () => void;
|
|
47
|
-
getValues: () => { [key: string]: FormDataEntryValue | FormDataEntryValue[] };
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// ---- Implementation ----
|
|
51
|
-
export function renderForm(element: string | HTMLElement, data: FormData): RenderedForm | null {
|
|
52
|
-
let containers: HTMLElement[] = [];
|
|
53
|
-
|
|
54
|
-
if (typeof element === "string") {
|
|
55
|
-
containers = Array.from(document.querySelectorAll<HTMLElement>(element));
|
|
56
|
-
} else {
|
|
57
|
-
containers = [element];
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (containers.length === 0) return null;
|
|
61
|
-
|
|
62
|
-
function buildForm(): HTMLFormElement {
|
|
63
|
-
const form = document.createElement("form");
|
|
64
|
-
form.id = data.id;
|
|
65
|
-
|
|
66
|
-
const title = document.createElement("h2");
|
|
67
|
-
title.textContent = data.form_name;
|
|
68
|
-
form.appendChild(title);
|
|
69
|
-
|
|
70
|
-
function applyValidation(
|
|
71
|
-
el: HTMLInputElement | HTMLTextAreaElement,
|
|
72
|
-
validation?: Validation
|
|
73
|
-
) {
|
|
74
|
-
if (!validation) return;
|
|
75
|
-
|
|
76
|
-
if (validation.min_length !== undefined) {
|
|
77
|
-
el.minLength = validation.min_length;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (validation.max_length !== undefined) {
|
|
81
|
-
el.maxLength = validation.max_length;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (validation.regex) {
|
|
85
|
-
if (el instanceof HTMLInputElement) {
|
|
86
|
-
// pattern only works on inputs
|
|
87
|
-
el.pattern = validation.regex;
|
|
88
|
-
} else {
|
|
89
|
-
// fallback for textarea → use dataset
|
|
90
|
-
el.dataset.pattern = validation.regex;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function renderField(field: BaseField): HTMLElement | null {
|
|
96
|
-
if (!field.key && !field.label) return null;
|
|
97
|
-
|
|
98
|
-
const wrapper = document.createElement("div");
|
|
99
|
-
wrapper.className = "form-group";
|
|
100
|
-
|
|
101
|
-
if (field.label) {
|
|
102
|
-
const label = document.createElement("label");
|
|
103
|
-
label.htmlFor = field.key || "";
|
|
104
|
-
label.textContent = field.label + (field.required ? " *" : "");
|
|
105
|
-
wrapper.appendChild(label);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
let input: HTMLElement | null = null;
|
|
109
|
-
|
|
110
|
-
switch (field.type) {
|
|
111
|
-
case "textarea": {
|
|
112
|
-
const textarea = document.createElement("textarea");
|
|
113
|
-
if (field.key) textarea.name = field.key;
|
|
114
|
-
if (field.required) textarea.required = true;
|
|
115
|
-
applyValidation(textarea, field.validation);
|
|
116
|
-
input = textarea;
|
|
117
|
-
break;
|
|
118
|
-
}
|
|
119
|
-
case "select": {
|
|
120
|
-
const select = document.createElement("select");
|
|
121
|
-
if (field.key) select.name = field.key;
|
|
122
|
-
if (field.required) select.required = true;
|
|
123
|
-
if (field.options) {
|
|
124
|
-
field.options.forEach((opt) => {
|
|
125
|
-
const option = document.createElement("option");
|
|
126
|
-
option.value = opt.value;
|
|
127
|
-
option.textContent = opt.label;
|
|
128
|
-
if (field.default === opt.value) option.selected = true;
|
|
129
|
-
select.appendChild(option);
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
input = select;
|
|
133
|
-
break;
|
|
134
|
-
}
|
|
135
|
-
case "radio": {
|
|
136
|
-
const radioGroup = document.createElement("div");
|
|
137
|
-
radioGroup.className =
|
|
138
|
-
"radio-group " + (field.direction || "vertical");
|
|
139
|
-
if (field.options) {
|
|
140
|
-
field.options.forEach((opt) => {
|
|
141
|
-
const radioWrapper = document.createElement("label");
|
|
142
|
-
radioWrapper.style.display =
|
|
143
|
-
field.direction === "horizontal"
|
|
144
|
-
? "inline-block"
|
|
145
|
-
: "block";
|
|
146
|
-
|
|
147
|
-
const radio = document.createElement("input");
|
|
148
|
-
radio.type = "radio";
|
|
149
|
-
radio.name = field.key || "";
|
|
150
|
-
radio.value = opt.value;
|
|
151
|
-
|
|
152
|
-
radioWrapper.appendChild(radio);
|
|
153
|
-
radioWrapper.appendChild(
|
|
154
|
-
document.createTextNode(opt.label)
|
|
155
|
-
);
|
|
156
|
-
radioGroup.appendChild(radioWrapper);
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
input = radioGroup;
|
|
160
|
-
break;
|
|
161
|
-
}
|
|
162
|
-
default: {
|
|
163
|
-
const inp = document.createElement("input");
|
|
164
|
-
inp.type = field.type || "text";
|
|
165
|
-
if (field.key) inp.name = field.key;
|
|
166
|
-
if (field.required) inp.required = true;
|
|
167
|
-
applyValidation(inp, field.validation);
|
|
168
|
-
input = inp;
|
|
169
|
-
break;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (input) wrapper.appendChild(input);
|
|
174
|
-
|
|
175
|
-
if (field.hint_text) {
|
|
176
|
-
const hint = document.createElement("small");
|
|
177
|
-
hint.className = "hint";
|
|
178
|
-
hint.textContent = field.hint_text;
|
|
179
|
-
wrapper.appendChild(hint);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return wrapper;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
data.fields.forEach((field) => {
|
|
186
|
-
if (field.type === "row") {
|
|
187
|
-
const rowWrapper = document.createElement("div");
|
|
188
|
-
rowWrapper.className = "form-row";
|
|
189
|
-
|
|
190
|
-
if (field.label) {
|
|
191
|
-
const rowLabel = document.createElement("h3");
|
|
192
|
-
rowLabel.textContent = field.label;
|
|
193
|
-
rowWrapper.appendChild(rowLabel);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
//@ts-ignore
|
|
197
|
-
field.items.forEach((item) => {
|
|
198
|
-
const rendered = renderField(item);
|
|
199
|
-
if (rendered) rowWrapper.appendChild(rendered);
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
form.appendChild(rowWrapper);
|
|
203
|
-
} else {
|
|
204
|
-
const rendered = renderField(field as BaseField);
|
|
205
|
-
if (rendered) form.appendChild(rendered);
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
// Add a default submit button
|
|
210
|
-
const submitBtn = document.createElement("button");
|
|
211
|
-
submitBtn.type = "submit";
|
|
212
|
-
submitBtn.textContent = "Submit";
|
|
213
|
-
form.appendChild(submitBtn);
|
|
214
|
-
|
|
215
|
-
return form;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Attach form to the first container
|
|
219
|
-
const formElement = buildForm();
|
|
220
|
-
containers[0].appendChild(formElement);
|
|
221
|
-
|
|
222
|
-
// ---- API to return ----
|
|
223
|
-
return {
|
|
224
|
-
element: formElement,
|
|
225
|
-
// this method validates the form and returns an array of errors
|
|
226
|
-
validate: () => {
|
|
227
|
-
const errors: Array<{ element: HTMLElement, message: string }> = [];
|
|
228
|
-
const invalidElements = formElement.querySelectorAll(":invalid");
|
|
229
|
-
invalidElements.forEach((element) => {
|
|
230
|
-
const message = (element as HTMLInputElement | HTMLTextAreaElement).validationMessage;
|
|
231
|
-
errors.push({ element: element as HTMLElement, message });
|
|
232
|
-
});
|
|
233
|
-
return errors;
|
|
234
|
-
},
|
|
235
|
-
// this method submits the form to the portal vocphone server
|
|
236
|
-
submitForm: () => {
|
|
237
|
-
submitForm(formElement);
|
|
238
|
-
},
|
|
239
|
-
// This method gets the data from the form.
|
|
240
|
-
getValues: () => {
|
|
241
|
-
const formData = new FormData(formElement);
|
|
242
|
-
const values: { [key: string]: FormDataEntryValue | FormDataEntryValue[] } = {};
|
|
243
|
-
formData.forEach((value, key) => {
|
|
244
|
-
values[key] = value;
|
|
245
|
-
});
|
|
246
|
-
return values;
|
|
247
|
-
}
|
|
248
|
-
};
|
|
249
|
-
}
|
package/src/forms-lib/index.ts
DELETED
package/src/main.ts
DELETED
package/tests/vanilla/index.html
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
|
|
4
|
-
<head>
|
|
5
|
-
<meta charset="UTF-8">
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
-
<title>Document</title>
|
|
8
|
-
</head>
|
|
9
|
-
|
|
10
|
-
<body>
|
|
11
|
-
<div id="the-form"></div>
|
|
12
|
-
|
|
13
|
-
<script type="module">
|
|
14
|
-
import { FormsLib } from '../../../dist/main.mjs';
|
|
15
|
-
|
|
16
|
-
const form = FormsLib.renderForm('#the-form', {
|
|
17
|
-
"id": "SDFSD345634563456",
|
|
18
|
-
"code": "USER_REG_01",
|
|
19
|
-
"form_name": "User Registration",
|
|
20
|
-
"fields": [
|
|
21
|
-
{
|
|
22
|
-
"label": "Name", // optional show the label of the group if exists
|
|
23
|
-
"type": "row",
|
|
24
|
-
"items": [
|
|
25
|
-
{
|
|
26
|
-
"label": "First Name",
|
|
27
|
-
"key": "first_name",
|
|
28
|
-
"type": "text",
|
|
29
|
-
"required": true,
|
|
30
|
-
"validation": {
|
|
31
|
-
"max_length": 30,
|
|
32
|
-
"min_length": 8,
|
|
33
|
-
},
|
|
34
|
-
"hint_text": "Your first name must be at least 8 characters long and contain both letters and numbers."
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
"label": "Last Name",
|
|
38
|
-
"key": "last_name",
|
|
39
|
-
"type": "text",
|
|
40
|
-
"required": true,
|
|
41
|
-
"validation": {
|
|
42
|
-
"min_length": 8
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
]
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
"label": "Username",
|
|
49
|
-
"key": "username",
|
|
50
|
-
"type": "text",
|
|
51
|
-
"required": true,
|
|
52
|
-
"validation": {
|
|
53
|
-
"min_length": 8
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
"label": "Password",
|
|
58
|
-
"key": "password",
|
|
59
|
-
"type": "password",
|
|
60
|
-
"required": true,
|
|
61
|
-
"validation": {
|
|
62
|
-
"min_length": 8
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
"label": "Text Area",
|
|
67
|
-
"key": "text_area",
|
|
68
|
-
"type": "textarea",
|
|
69
|
-
"required": true,
|
|
70
|
-
"validation": {
|
|
71
|
-
"min_length": 8
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
"label": "Email",
|
|
76
|
-
"key": "email",
|
|
77
|
-
"type": "email",
|
|
78
|
-
"required": false
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
"validation": {
|
|
82
|
-
"min_length": 8
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
"label": "Gender",
|
|
87
|
-
"type": "radio",
|
|
88
|
-
"key": "gender",
|
|
89
|
-
"required": false,
|
|
90
|
-
"direction": "horizontal",
|
|
91
|
-
"options": [
|
|
92
|
-
{
|
|
93
|
-
"label": "Male",
|
|
94
|
-
"value": "male"
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
"label": "Female",
|
|
98
|
-
"value": "female"
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
"label": "Other",
|
|
102
|
-
"value": "other"
|
|
103
|
-
}
|
|
104
|
-
]
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
"label": "Country",
|
|
108
|
-
"type": "select",
|
|
109
|
-
"key": "country",
|
|
110
|
-
"default": "usa",
|
|
111
|
-
"required": false,
|
|
112
|
-
"options": [
|
|
113
|
-
{
|
|
114
|
-
"label": "USA",
|
|
115
|
-
"value": "usa"
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
"label": "Canada",
|
|
119
|
-
"value": "canada"
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
"label": "UK",
|
|
123
|
-
"value": "uk"
|
|
124
|
-
}
|
|
125
|
-
]
|
|
126
|
-
}
|
|
127
|
-
]
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
const validate = form.validate();
|
|
131
|
-
console.log(validate);
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
form.submitForm();
|
|
135
|
-
|
|
136
|
-
const values = form.getValues();
|
|
137
|
-
console.log('Form values:', values);
|
|
138
|
-
</script>
|
|
139
|
-
</body>
|
|
140
|
-
|
|
141
|
-
</html>
|