easy-forms-core 1.0.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.
- package/README.md +133 -0
- package/dist/easy-form.d.ts +338 -0
- package/dist/easy-form.js +2093 -0
- package/dist/easy-form.js.map +1 -0
- package/dist/index.d.ts +637 -0
- package/dist/index.js +2115 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,2093 @@
|
|
|
1
|
+
// src/core/schema-parser.ts
|
|
2
|
+
var SchemaParser = class {
|
|
3
|
+
/**
|
|
4
|
+
* Parsea y valida un schema
|
|
5
|
+
*/
|
|
6
|
+
parse(schema) {
|
|
7
|
+
if (!schema) {
|
|
8
|
+
throw new Error("Schema es requerido");
|
|
9
|
+
}
|
|
10
|
+
if (!schema.fields && !schema.steps) {
|
|
11
|
+
throw new Error("Schema debe tener fields o steps");
|
|
12
|
+
}
|
|
13
|
+
if (schema.steps) {
|
|
14
|
+
for (const step of schema.steps) {
|
|
15
|
+
if (!step.fields || step.fields.length === 0) {
|
|
16
|
+
throw new Error("Cada step debe tener al menos un field");
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const normalizedFields = schema.fields ? this.normalizeFields(schema.fields) : [];
|
|
21
|
+
const normalizedSteps = schema.steps ? schema.steps.map((step) => ({
|
|
22
|
+
...step,
|
|
23
|
+
fields: this.normalizeFields(step.fields)
|
|
24
|
+
})) : void 0;
|
|
25
|
+
return {
|
|
26
|
+
fields: normalizedFields,
|
|
27
|
+
steps: normalizedSteps,
|
|
28
|
+
isWizard: Boolean(schema.steps && schema.steps.length > 0)
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Normaliza y valida campos
|
|
33
|
+
*/
|
|
34
|
+
normalizeFields(fields) {
|
|
35
|
+
return fields.map((field, index) => {
|
|
36
|
+
if (!field.type) {
|
|
37
|
+
throw new Error(`Field en \xEDndice ${index} debe tener un type`);
|
|
38
|
+
}
|
|
39
|
+
if (!field.name) {
|
|
40
|
+
throw new Error(`Field en \xEDndice ${index} debe tener un name`);
|
|
41
|
+
}
|
|
42
|
+
this.validateFieldType(field, index);
|
|
43
|
+
return this.applyDefaults(field);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Valida el tipo de campo
|
|
48
|
+
*/
|
|
49
|
+
validateFieldType(field, index) {
|
|
50
|
+
const validTypes = [
|
|
51
|
+
"text",
|
|
52
|
+
"email",
|
|
53
|
+
"number",
|
|
54
|
+
"password",
|
|
55
|
+
"textarea",
|
|
56
|
+
"select",
|
|
57
|
+
"checkbox",
|
|
58
|
+
"radio",
|
|
59
|
+
"switch",
|
|
60
|
+
"date",
|
|
61
|
+
"file",
|
|
62
|
+
"array",
|
|
63
|
+
"group",
|
|
64
|
+
"custom"
|
|
65
|
+
];
|
|
66
|
+
if (!validTypes.includes(field.type)) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
`Field en \xEDndice ${index} tiene un type inv\xE1lido: ${field.type}`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
switch (field.type) {
|
|
72
|
+
case "select":
|
|
73
|
+
case "radio":
|
|
74
|
+
if (!("options" in field) || !field.options || field.options.length === 0) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
`Field "${field.name}" de tipo ${field.type} debe tener options`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
break;
|
|
80
|
+
case "array":
|
|
81
|
+
if (!("itemSchema" in field) || !field.itemSchema) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
`Field "${field.name}" de tipo array debe tener itemSchema`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
break;
|
|
87
|
+
case "group":
|
|
88
|
+
if (!("fields" in field) || !field.fields || field.fields.length === 0) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`Field "${field.name}" de tipo group debe tener fields`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Aplica valores por defecto a un campo
|
|
98
|
+
*/
|
|
99
|
+
applyDefaults(field) {
|
|
100
|
+
const defaults = {
|
|
101
|
+
disabled: false,
|
|
102
|
+
hidden: false
|
|
103
|
+
};
|
|
104
|
+
switch (field.type) {
|
|
105
|
+
case "checkbox":
|
|
106
|
+
case "switch":
|
|
107
|
+
if (!("checked" in field)) {
|
|
108
|
+
defaults.checked = false;
|
|
109
|
+
}
|
|
110
|
+
break;
|
|
111
|
+
case "select":
|
|
112
|
+
if (!("multiple" in field)) {
|
|
113
|
+
defaults.multiple = false;
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
116
|
+
case "file":
|
|
117
|
+
if (!("multiple" in field)) {
|
|
118
|
+
defaults.multiple = false;
|
|
119
|
+
}
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
return { ...defaults, ...field };
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Obtiene todos los campos de un schema (incluyendo nested)
|
|
126
|
+
*/
|
|
127
|
+
getAllFields(schema) {
|
|
128
|
+
const parsed = this.parse(schema);
|
|
129
|
+
const allFields = [];
|
|
130
|
+
const extractFields = (fields) => {
|
|
131
|
+
for (const field of fields) {
|
|
132
|
+
allFields.push(field);
|
|
133
|
+
if (field.type === "group" && "fields" in field) {
|
|
134
|
+
extractFields(field.fields);
|
|
135
|
+
}
|
|
136
|
+
if (field.type === "array" && "itemSchema" in field && field.itemSchema.fields) {
|
|
137
|
+
extractFields(field.itemSchema.fields);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
if (parsed.fields) {
|
|
142
|
+
extractFields(parsed.fields);
|
|
143
|
+
}
|
|
144
|
+
if (parsed.steps) {
|
|
145
|
+
for (const step of parsed.steps) {
|
|
146
|
+
extractFields(step.fields);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return allFields;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// src/utils/styles.ts
|
|
154
|
+
var defaultColors = {
|
|
155
|
+
primary: "#007bff",
|
|
156
|
+
secondary: "#6c757d",
|
|
157
|
+
error: "#dc3545",
|
|
158
|
+
success: "#28a745",
|
|
159
|
+
text: "#212529",
|
|
160
|
+
border: "#ddd",
|
|
161
|
+
background: "#ffffff"
|
|
162
|
+
};
|
|
163
|
+
function getColors(colors) {
|
|
164
|
+
return { ...defaultColors, ...colors };
|
|
165
|
+
}
|
|
166
|
+
function getThemeStyles(theme, colors) {
|
|
167
|
+
const baseStyles = getBaseStyles(colors);
|
|
168
|
+
const themeStyles = getThemeSpecificStyles(theme, colors);
|
|
169
|
+
return baseStyles + themeStyles;
|
|
170
|
+
}
|
|
171
|
+
function getBaseStyles(colors) {
|
|
172
|
+
return `
|
|
173
|
+
:host {
|
|
174
|
+
display: block;
|
|
175
|
+
--easy-form-primary: ${colors.primary};
|
|
176
|
+
--easy-form-secondary: ${colors.secondary};
|
|
177
|
+
--easy-form-error: ${colors.error};
|
|
178
|
+
--easy-form-success: ${colors.success};
|
|
179
|
+
--easy-form-text: ${colors.text};
|
|
180
|
+
--easy-form-border: ${colors.border};
|
|
181
|
+
--easy-form-background: ${colors.background};
|
|
182
|
+
}
|
|
183
|
+
.easy-form-field {
|
|
184
|
+
margin-bottom: 1rem;
|
|
185
|
+
}
|
|
186
|
+
.easy-form-label {
|
|
187
|
+
display: block;
|
|
188
|
+
margin-bottom: 0.5rem;
|
|
189
|
+
font-weight: 500;
|
|
190
|
+
color: var(--easy-form-text);
|
|
191
|
+
}
|
|
192
|
+
.easy-form-required {
|
|
193
|
+
color: var(--easy-form-error);
|
|
194
|
+
}
|
|
195
|
+
.easy-form-input-error {
|
|
196
|
+
border-color: var(--easy-form-error) !important;
|
|
197
|
+
}
|
|
198
|
+
.easy-form-error {
|
|
199
|
+
color: var(--easy-form-error);
|
|
200
|
+
font-size: 0.875rem;
|
|
201
|
+
margin-top: 0.25rem;
|
|
202
|
+
}
|
|
203
|
+
.easy-form-description {
|
|
204
|
+
font-size: 0.875rem;
|
|
205
|
+
color: #666;
|
|
206
|
+
margin-top: 0.25rem;
|
|
207
|
+
}
|
|
208
|
+
.easy-form-submit {
|
|
209
|
+
padding: 0.5rem 1rem;
|
|
210
|
+
background: var(--easy-form-primary);
|
|
211
|
+
color: white;
|
|
212
|
+
border: none;
|
|
213
|
+
cursor: pointer;
|
|
214
|
+
font-size: 1rem;
|
|
215
|
+
transition: all 0.2s ease;
|
|
216
|
+
}
|
|
217
|
+
.easy-form-submit:hover {
|
|
218
|
+
opacity: 0.9;
|
|
219
|
+
}
|
|
220
|
+
.easy-form-submit:active {
|
|
221
|
+
transform: scale(0.98);
|
|
222
|
+
}
|
|
223
|
+
input:not([type="checkbox"]):not([type="radio"]), textarea, select {
|
|
224
|
+
width: 100%;
|
|
225
|
+
padding: 0.5rem;
|
|
226
|
+
font-size: 1rem;
|
|
227
|
+
color: var(--easy-form-text);
|
|
228
|
+
background: var(--easy-form-background);
|
|
229
|
+
transition: all 0.2s ease;
|
|
230
|
+
}
|
|
231
|
+
input[type="checkbox"],
|
|
232
|
+
input[type="radio"] {
|
|
233
|
+
width: 18px !important;
|
|
234
|
+
height: 18px !important;
|
|
235
|
+
min-width: 18px !important;
|
|
236
|
+
min-height: 18px !important;
|
|
237
|
+
max-width: 18px !important;
|
|
238
|
+
margin: 0;
|
|
239
|
+
padding: 0;
|
|
240
|
+
cursor: pointer;
|
|
241
|
+
accent-color: var(--easy-form-primary);
|
|
242
|
+
flex-shrink: 0;
|
|
243
|
+
}
|
|
244
|
+
input:focus, textarea:focus, select:focus {
|
|
245
|
+
outline: none;
|
|
246
|
+
}
|
|
247
|
+
.easy-form-group {
|
|
248
|
+
padding: 1rem;
|
|
249
|
+
margin-bottom: 1rem;
|
|
250
|
+
}
|
|
251
|
+
.easy-form-radio-group {
|
|
252
|
+
display: flex;
|
|
253
|
+
flex-direction: column;
|
|
254
|
+
gap: 0.75rem;
|
|
255
|
+
margin-top: 0.5rem;
|
|
256
|
+
}
|
|
257
|
+
.easy-form-radio-option {
|
|
258
|
+
display: flex;
|
|
259
|
+
align-items: center;
|
|
260
|
+
gap: 0.75rem;
|
|
261
|
+
padding: 0.5rem;
|
|
262
|
+
border-radius: 6px;
|
|
263
|
+
transition: background-color 0.2s ease;
|
|
264
|
+
}
|
|
265
|
+
.easy-form-radio-option:hover {
|
|
266
|
+
background-color: rgba(0, 0, 0, 0.03);
|
|
267
|
+
}
|
|
268
|
+
.easy-form-radio-option input[type="radio"] {
|
|
269
|
+
margin: 0;
|
|
270
|
+
flex-shrink: 0;
|
|
271
|
+
}
|
|
272
|
+
.easy-form-radio-label {
|
|
273
|
+
cursor: pointer;
|
|
274
|
+
user-select: none;
|
|
275
|
+
color: var(--easy-form-text);
|
|
276
|
+
font-weight: 400;
|
|
277
|
+
}
|
|
278
|
+
.easy-form-label-checkbox,
|
|
279
|
+
.easy-form-label-switch {
|
|
280
|
+
display: flex;
|
|
281
|
+
align-items: center;
|
|
282
|
+
gap: 0.75rem;
|
|
283
|
+
cursor: pointer;
|
|
284
|
+
user-select: none;
|
|
285
|
+
padding: 0.5rem;
|
|
286
|
+
border-radius: 6px;
|
|
287
|
+
transition: background-color 0.2s ease;
|
|
288
|
+
color: var(--easy-form-text);
|
|
289
|
+
}
|
|
290
|
+
.easy-form-label-checkbox input[type="checkbox"],
|
|
291
|
+
.easy-form-label-switch input[type="checkbox"] {
|
|
292
|
+
margin: 0;
|
|
293
|
+
flex-shrink: 0;
|
|
294
|
+
}
|
|
295
|
+
.easy-form-label-checkbox:hover,
|
|
296
|
+
.easy-form-label-switch:hover {
|
|
297
|
+
background-color: rgba(0, 0, 0, 0.03);
|
|
298
|
+
}
|
|
299
|
+
.easy-form-wizard-steps {
|
|
300
|
+
display: flex;
|
|
301
|
+
gap: 1rem;
|
|
302
|
+
margin-bottom: 2rem;
|
|
303
|
+
}
|
|
304
|
+
.easy-form-wizard-step {
|
|
305
|
+
padding: 0.5rem 1rem;
|
|
306
|
+
transition: all 0.2s ease;
|
|
307
|
+
}
|
|
308
|
+
.easy-form-wizard-step.active {
|
|
309
|
+
background: var(--easy-form-primary);
|
|
310
|
+
color: white;
|
|
311
|
+
}
|
|
312
|
+
.easy-form-wizard-step.completed {
|
|
313
|
+
background: var(--easy-form-success);
|
|
314
|
+
color: white;
|
|
315
|
+
}
|
|
316
|
+
.easy-form-wizard-nav {
|
|
317
|
+
display: flex;
|
|
318
|
+
gap: 1rem;
|
|
319
|
+
margin-top: 1rem;
|
|
320
|
+
}
|
|
321
|
+
.easy-form-array-item {
|
|
322
|
+
padding: 1rem;
|
|
323
|
+
margin-bottom: 1rem;
|
|
324
|
+
}
|
|
325
|
+
.easy-form-array-add,
|
|
326
|
+
.easy-form-array-remove {
|
|
327
|
+
padding: 0.25rem 0.5rem;
|
|
328
|
+
background: var(--easy-form-secondary);
|
|
329
|
+
color: white;
|
|
330
|
+
border: none;
|
|
331
|
+
cursor: pointer;
|
|
332
|
+
font-size: 0.875rem;
|
|
333
|
+
transition: all 0.2s ease;
|
|
334
|
+
}
|
|
335
|
+
.easy-form-array-remove {
|
|
336
|
+
background: var(--easy-form-error);
|
|
337
|
+
}
|
|
338
|
+
.easy-form-array-add:hover,
|
|
339
|
+
.easy-form-array-remove:hover {
|
|
340
|
+
opacity: 0.9;
|
|
341
|
+
}
|
|
342
|
+
`;
|
|
343
|
+
}
|
|
344
|
+
function getThemeSpecificStyles(theme, colors) {
|
|
345
|
+
switch (theme) {
|
|
346
|
+
case "plano":
|
|
347
|
+
return getPlanoStyles(colors);
|
|
348
|
+
case "tradicional":
|
|
349
|
+
return getTradicionalStyles(colors);
|
|
350
|
+
case "material":
|
|
351
|
+
return getMaterialStyles(colors);
|
|
352
|
+
case "rounded-shadow":
|
|
353
|
+
return getRoundedShadowStyles(colors);
|
|
354
|
+
case "lines":
|
|
355
|
+
return getLinesStyles(colors);
|
|
356
|
+
default:
|
|
357
|
+
return getPlanoStyles(colors);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
function getPlanoStyles(_colors) {
|
|
361
|
+
return `
|
|
362
|
+
input:not([type="checkbox"]):not([type="radio"]), textarea, select {
|
|
363
|
+
border: none;
|
|
364
|
+
border-bottom: 2px solid var(--easy-form-border);
|
|
365
|
+
border-radius: 0;
|
|
366
|
+
padding: 0.75rem 0;
|
|
367
|
+
}
|
|
368
|
+
input:not([type="checkbox"]):not([type="radio"]):focus, textarea:focus, select:focus {
|
|
369
|
+
border-bottom-color: var(--easy-form-primary);
|
|
370
|
+
}
|
|
371
|
+
.easy-form-submit {
|
|
372
|
+
border-radius: 0;
|
|
373
|
+
font-weight: 600;
|
|
374
|
+
}
|
|
375
|
+
.easy-form-group {
|
|
376
|
+
border: none;
|
|
377
|
+
border-bottom: 1px solid var(--easy-form-border);
|
|
378
|
+
border-radius: 0;
|
|
379
|
+
}
|
|
380
|
+
.easy-form-wizard-step {
|
|
381
|
+
border: none;
|
|
382
|
+
border-bottom: 2px solid var(--easy-form-border);
|
|
383
|
+
border-radius: 0;
|
|
384
|
+
}
|
|
385
|
+
.easy-form-array-item {
|
|
386
|
+
border: none;
|
|
387
|
+
border-bottom: 1px solid var(--easy-form-border);
|
|
388
|
+
border-radius: 0;
|
|
389
|
+
}
|
|
390
|
+
`;
|
|
391
|
+
}
|
|
392
|
+
function getTradicionalStyles(_colors) {
|
|
393
|
+
return `
|
|
394
|
+
input:not([type="checkbox"]):not([type="radio"]), textarea, select {
|
|
395
|
+
border: 2px solid var(--easy-form-border);
|
|
396
|
+
border-radius: 4px;
|
|
397
|
+
padding: 0.625rem;
|
|
398
|
+
}
|
|
399
|
+
input:not([type="checkbox"]):not([type="radio"]):focus, textarea:focus, select:focus {
|
|
400
|
+
border-color: var(--easy-form-primary);
|
|
401
|
+
box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
|
|
402
|
+
}
|
|
403
|
+
.easy-form-submit {
|
|
404
|
+
border-radius: 4px;
|
|
405
|
+
font-weight: 500;
|
|
406
|
+
}
|
|
407
|
+
.easy-form-group {
|
|
408
|
+
border: 2px solid var(--easy-form-border);
|
|
409
|
+
border-radius: 4px;
|
|
410
|
+
background: #f8f9fa;
|
|
411
|
+
}
|
|
412
|
+
.easy-form-wizard-step {
|
|
413
|
+
border: 2px solid var(--easy-form-border);
|
|
414
|
+
border-radius: 4px;
|
|
415
|
+
background: #f8f9fa;
|
|
416
|
+
}
|
|
417
|
+
.easy-form-wizard-step.active {
|
|
418
|
+
border-color: var(--easy-form-primary);
|
|
419
|
+
}
|
|
420
|
+
.easy-form-array-item {
|
|
421
|
+
border: 2px solid var(--easy-form-border);
|
|
422
|
+
border-radius: 4px;
|
|
423
|
+
background: #f8f9fa;
|
|
424
|
+
}
|
|
425
|
+
`;
|
|
426
|
+
}
|
|
427
|
+
function getMaterialStyles(_colors) {
|
|
428
|
+
return `
|
|
429
|
+
.easy-form-label {
|
|
430
|
+
font-size: 0.875rem;
|
|
431
|
+
font-weight: 500;
|
|
432
|
+
margin-bottom: 0.25rem;
|
|
433
|
+
color: rgba(0, 0, 0, 0.6);
|
|
434
|
+
}
|
|
435
|
+
input:not([type="checkbox"]):not([type="radio"]), textarea, select {
|
|
436
|
+
border: none;
|
|
437
|
+
border-bottom: 1px solid rgba(0, 0, 0, 0.42);
|
|
438
|
+
border-radius: 4px 4px 0 0;
|
|
439
|
+
padding: 0.75rem 0.75rem 0.5rem 0.75rem;
|
|
440
|
+
background: transparent;
|
|
441
|
+
}
|
|
442
|
+
input:not([type="checkbox"]):not([type="radio"]):focus, textarea:focus, select:focus {
|
|
443
|
+
border-bottom-color: var(--easy-form-primary);
|
|
444
|
+
border-bottom-width: 2px;
|
|
445
|
+
padding-bottom: calc(0.5rem - 1px);
|
|
446
|
+
}
|
|
447
|
+
.easy-form-submit {
|
|
448
|
+
border-radius: 4px;
|
|
449
|
+
text-transform: uppercase;
|
|
450
|
+
font-weight: 500;
|
|
451
|
+
letter-spacing: 0.5px;
|
|
452
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
|
453
|
+
}
|
|
454
|
+
.easy-form-submit:hover {
|
|
455
|
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
|
456
|
+
}
|
|
457
|
+
.easy-form-group {
|
|
458
|
+
border: none;
|
|
459
|
+
border-radius: 4px;
|
|
460
|
+
background: #f5f5f5;
|
|
461
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
|
|
462
|
+
}
|
|
463
|
+
.easy-form-wizard-step {
|
|
464
|
+
border: none;
|
|
465
|
+
border-radius: 4px;
|
|
466
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
|
|
467
|
+
}
|
|
468
|
+
.easy-form-array-item {
|
|
469
|
+
border: none;
|
|
470
|
+
border-radius: 4px;
|
|
471
|
+
background: #f5f5f5;
|
|
472
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
|
|
473
|
+
}
|
|
474
|
+
`;
|
|
475
|
+
}
|
|
476
|
+
function getRoundedShadowStyles(_colors) {
|
|
477
|
+
return `
|
|
478
|
+
input:not([type="checkbox"]):not([type="radio"]), textarea, select {
|
|
479
|
+
border: 1px solid var(--easy-form-border);
|
|
480
|
+
border-radius: 12px;
|
|
481
|
+
padding: 0.75rem 1rem;
|
|
482
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
|
483
|
+
}
|
|
484
|
+
input:not([type="checkbox"]):not([type="radio"]):focus, textarea:focus, select:focus {
|
|
485
|
+
border-color: var(--easy-form-primary);
|
|
486
|
+
box-shadow: 0 4px 12px rgba(0, 123, 255, 0.15);
|
|
487
|
+
transform: translateY(-1px);
|
|
488
|
+
}
|
|
489
|
+
.easy-form-submit {
|
|
490
|
+
border-radius: 12px;
|
|
491
|
+
font-weight: 600;
|
|
492
|
+
box-shadow: 0 4px 12px rgba(0, 123, 255, 0.3);
|
|
493
|
+
}
|
|
494
|
+
.easy-form-submit:hover {
|
|
495
|
+
box-shadow: 0 6px 16px rgba(0, 123, 255, 0.4);
|
|
496
|
+
transform: translateY(-2px);
|
|
497
|
+
}
|
|
498
|
+
.easy-form-group {
|
|
499
|
+
border: 1px solid var(--easy-form-border);
|
|
500
|
+
border-radius: 16px;
|
|
501
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
502
|
+
background: var(--easy-form-background);
|
|
503
|
+
}
|
|
504
|
+
.easy-form-wizard-step {
|
|
505
|
+
border: 1px solid var(--easy-form-border);
|
|
506
|
+
border-radius: 12px;
|
|
507
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
508
|
+
}
|
|
509
|
+
.easy-form-wizard-step.active {
|
|
510
|
+
box-shadow: 0 4px 16px rgba(0, 123, 255, 0.3);
|
|
511
|
+
}
|
|
512
|
+
.easy-form-array-item {
|
|
513
|
+
border: 1px solid var(--easy-form-border);
|
|
514
|
+
border-radius: 12px;
|
|
515
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
516
|
+
background: var(--easy-form-background);
|
|
517
|
+
}
|
|
518
|
+
`;
|
|
519
|
+
}
|
|
520
|
+
function getLinesStyles(_colors) {
|
|
521
|
+
return `
|
|
522
|
+
.easy-form-field {
|
|
523
|
+
border-bottom: 1px solid var(--easy-form-border);
|
|
524
|
+
padding-bottom: 1rem;
|
|
525
|
+
margin-bottom: 1.5rem;
|
|
526
|
+
}
|
|
527
|
+
.easy-form-field:last-child {
|
|
528
|
+
border-bottom: none;
|
|
529
|
+
}
|
|
530
|
+
input:not([type="checkbox"]):not([type="radio"]), textarea, select {
|
|
531
|
+
border: none;
|
|
532
|
+
border-radius: 0;
|
|
533
|
+
padding: 0.5rem 0;
|
|
534
|
+
background: transparent;
|
|
535
|
+
}
|
|
536
|
+
input:not([type="checkbox"]):not([type="radio"]):focus, textarea:focus, select:focus {
|
|
537
|
+
border-bottom: 2px solid var(--easy-form-primary);
|
|
538
|
+
padding-bottom: calc(0.5rem - 1px);
|
|
539
|
+
}
|
|
540
|
+
.easy-form-submit {
|
|
541
|
+
border-radius: 0;
|
|
542
|
+
border-bottom: 3px solid var(--easy-form-primary);
|
|
543
|
+
background: transparent;
|
|
544
|
+
color: var(--easy-form-primary);
|
|
545
|
+
font-weight: 600;
|
|
546
|
+
padding: 0.75rem 0;
|
|
547
|
+
}
|
|
548
|
+
.easy-form-submit:hover {
|
|
549
|
+
background: rgba(0, 123, 255, 0.05);
|
|
550
|
+
}
|
|
551
|
+
.easy-form-group {
|
|
552
|
+
border: none;
|
|
553
|
+
border-bottom: 2px solid var(--easy-form-border);
|
|
554
|
+
border-radius: 0;
|
|
555
|
+
padding-bottom: 1rem;
|
|
556
|
+
}
|
|
557
|
+
.easy-form-wizard-step {
|
|
558
|
+
border: none;
|
|
559
|
+
border-bottom: 3px solid var(--easy-form-border);
|
|
560
|
+
border-radius: 0;
|
|
561
|
+
background: transparent;
|
|
562
|
+
}
|
|
563
|
+
.easy-form-wizard-step.active {
|
|
564
|
+
border-bottom-color: var(--easy-form-primary);
|
|
565
|
+
background: transparent;
|
|
566
|
+
color: var(--easy-form-primary);
|
|
567
|
+
}
|
|
568
|
+
.easy-form-wizard-step.completed {
|
|
569
|
+
border-bottom-color: var(--easy-form-success);
|
|
570
|
+
background: transparent;
|
|
571
|
+
color: var(--easy-form-success);
|
|
572
|
+
}
|
|
573
|
+
.easy-form-array-item {
|
|
574
|
+
border: none;
|
|
575
|
+
border-bottom: 1px solid var(--easy-form-border);
|
|
576
|
+
border-radius: 0;
|
|
577
|
+
}
|
|
578
|
+
`;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// src/utils/index.ts
|
|
582
|
+
function attributeValue(value) {
|
|
583
|
+
if (value === null || value === void 0) {
|
|
584
|
+
return "";
|
|
585
|
+
}
|
|
586
|
+
if (typeof value === "boolean") {
|
|
587
|
+
return value ? "true" : "false";
|
|
588
|
+
}
|
|
589
|
+
if (typeof value === "object") {
|
|
590
|
+
return JSON.stringify(value);
|
|
591
|
+
}
|
|
592
|
+
return String(value);
|
|
593
|
+
}
|
|
594
|
+
function parseAttributeValue(value) {
|
|
595
|
+
if (!value) {
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
598
|
+
try {
|
|
599
|
+
return JSON.parse(value);
|
|
600
|
+
} catch {
|
|
601
|
+
return value;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
function getNestedValue(obj, path) {
|
|
605
|
+
return path.split(".").reduce((current, key) => current?.[key], obj);
|
|
606
|
+
}
|
|
607
|
+
function setNestedValue(obj, path, value) {
|
|
608
|
+
const keys = path.split(".");
|
|
609
|
+
const lastKey = keys.pop();
|
|
610
|
+
const target = keys.reduce((current, key) => {
|
|
611
|
+
if (!current[key] || typeof current[key] !== "object") {
|
|
612
|
+
current[key] = {};
|
|
613
|
+
}
|
|
614
|
+
return current[key];
|
|
615
|
+
}, obj);
|
|
616
|
+
target[lastKey] = value;
|
|
617
|
+
}
|
|
618
|
+
function isValidEmail(email) {
|
|
619
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
620
|
+
return emailRegex.test(email);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// src/core/validation-engine.ts
|
|
624
|
+
var ValidationEngine = class {
|
|
625
|
+
/**
|
|
626
|
+
* Valida un campo con todas sus validaciones
|
|
627
|
+
*/
|
|
628
|
+
async validateField(field, value) {
|
|
629
|
+
const errors = [];
|
|
630
|
+
if (!field.validations || field.validations.length === 0) {
|
|
631
|
+
return errors;
|
|
632
|
+
}
|
|
633
|
+
for (const validation of field.validations) {
|
|
634
|
+
const result = await this.validateValue(validation, value);
|
|
635
|
+
if (!result.isValid) {
|
|
636
|
+
errors.push(result.message || this.getDefaultMessage(validation));
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
return errors;
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Valida un valor con una validación específica
|
|
643
|
+
*/
|
|
644
|
+
async validateValue(validation, value) {
|
|
645
|
+
switch (validation.type) {
|
|
646
|
+
case "required":
|
|
647
|
+
return this.validateRequired(value);
|
|
648
|
+
case "email":
|
|
649
|
+
return this.validateEmail(value);
|
|
650
|
+
case "minLength":
|
|
651
|
+
return this.validateMinLength(value, validation.value);
|
|
652
|
+
case "maxLength":
|
|
653
|
+
return this.validateMaxLength(value, validation.value);
|
|
654
|
+
case "min":
|
|
655
|
+
return this.validateMin(value, validation.value);
|
|
656
|
+
case "max":
|
|
657
|
+
return this.validateMax(value, validation.value);
|
|
658
|
+
case "pattern":
|
|
659
|
+
return this.validatePattern(value, validation.value);
|
|
660
|
+
case "custom":
|
|
661
|
+
return await this.validateCustom(value, validation);
|
|
662
|
+
default:
|
|
663
|
+
return { isValid: true };
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Valida campo requerido
|
|
668
|
+
*/
|
|
669
|
+
validateRequired(value) {
|
|
670
|
+
const isValid = value !== null && value !== void 0 && value !== "" && !(Array.isArray(value) && value.length === 0);
|
|
671
|
+
return {
|
|
672
|
+
isValid,
|
|
673
|
+
message: isValid ? void 0 : "Este campo es requerido"
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Valida email
|
|
678
|
+
*/
|
|
679
|
+
validateEmail(value) {
|
|
680
|
+
if (!value) {
|
|
681
|
+
return { isValid: true };
|
|
682
|
+
}
|
|
683
|
+
const isValid = typeof value === "string" && isValidEmail(value);
|
|
684
|
+
return {
|
|
685
|
+
isValid,
|
|
686
|
+
message: isValid ? void 0 : "Debe ser un email v\xE1lido"
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Valida longitud mínima
|
|
691
|
+
*/
|
|
692
|
+
validateMinLength(value, minLength) {
|
|
693
|
+
if (!value) {
|
|
694
|
+
return { isValid: true };
|
|
695
|
+
}
|
|
696
|
+
const str = String(value);
|
|
697
|
+
const isValid = str.length >= minLength;
|
|
698
|
+
return {
|
|
699
|
+
isValid,
|
|
700
|
+
message: isValid ? void 0 : `Debe tener al menos ${minLength} caracteres`
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Valida longitud máxima
|
|
705
|
+
*/
|
|
706
|
+
validateMaxLength(value, maxLength) {
|
|
707
|
+
if (!value) {
|
|
708
|
+
return { isValid: true };
|
|
709
|
+
}
|
|
710
|
+
const str = String(value);
|
|
711
|
+
const isValid = str.length <= maxLength;
|
|
712
|
+
return {
|
|
713
|
+
isValid,
|
|
714
|
+
message: isValid ? void 0 : `Debe tener m\xE1ximo ${maxLength} caracteres`
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Valida valor mínimo
|
|
719
|
+
*/
|
|
720
|
+
validateMin(value, min) {
|
|
721
|
+
if (value === null || value === void 0 || value === "") {
|
|
722
|
+
return { isValid: true };
|
|
723
|
+
}
|
|
724
|
+
const num = Number(value);
|
|
725
|
+
const isValid = !isNaN(num) && num >= min;
|
|
726
|
+
return {
|
|
727
|
+
isValid,
|
|
728
|
+
message: isValid ? void 0 : `Debe ser mayor o igual a ${min}`
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Valida valor máximo
|
|
733
|
+
*/
|
|
734
|
+
validateMax(value, max) {
|
|
735
|
+
if (value === null || value === void 0 || value === "") {
|
|
736
|
+
return { isValid: true };
|
|
737
|
+
}
|
|
738
|
+
const num = Number(value);
|
|
739
|
+
const isValid = !isNaN(num) && num <= max;
|
|
740
|
+
return {
|
|
741
|
+
isValid,
|
|
742
|
+
message: isValid ? void 0 : `Debe ser menor o igual a ${max}`
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Valida patrón regex
|
|
747
|
+
*/
|
|
748
|
+
validatePattern(value, pattern) {
|
|
749
|
+
if (!value) {
|
|
750
|
+
return { isValid: true };
|
|
751
|
+
}
|
|
752
|
+
const regex = typeof pattern === "string" ? new RegExp(pattern) : pattern;
|
|
753
|
+
const isValid = regex.test(String(value));
|
|
754
|
+
return {
|
|
755
|
+
isValid,
|
|
756
|
+
message: isValid ? void 0 : "El formato no es v\xE1lido"
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Valida con función personalizada
|
|
761
|
+
*/
|
|
762
|
+
async validateCustom(value, validation) {
|
|
763
|
+
try {
|
|
764
|
+
const result = await validation.validator(value);
|
|
765
|
+
return {
|
|
766
|
+
isValid: Boolean(result),
|
|
767
|
+
message: result ? void 0 : validation.message || "Validaci\xF3n fallida"
|
|
768
|
+
};
|
|
769
|
+
} catch (error) {
|
|
770
|
+
return {
|
|
771
|
+
isValid: false,
|
|
772
|
+
message: validation.message || "Error en la validaci\xF3n"
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* Obtiene mensaje por defecto para una validación
|
|
778
|
+
*/
|
|
779
|
+
getDefaultMessage(validation) {
|
|
780
|
+
switch (validation.type) {
|
|
781
|
+
case "required":
|
|
782
|
+
return "Este campo es requerido";
|
|
783
|
+
case "email":
|
|
784
|
+
return "Debe ser un email v\xE1lido";
|
|
785
|
+
case "minLength":
|
|
786
|
+
return `Debe tener al menos ${validation.value} caracteres`;
|
|
787
|
+
case "maxLength":
|
|
788
|
+
return `Debe tener m\xE1ximo ${validation.value} caracteres`;
|
|
789
|
+
case "min":
|
|
790
|
+
return `Debe ser mayor o igual a ${validation.value}`;
|
|
791
|
+
case "max":
|
|
792
|
+
return `Debe ser menor o igual a ${validation.value}`;
|
|
793
|
+
case "pattern":
|
|
794
|
+
return "El formato no es v\xE1lido";
|
|
795
|
+
case "custom":
|
|
796
|
+
return "Validaci\xF3n fallida";
|
|
797
|
+
default:
|
|
798
|
+
return "Campo inv\xE1lido";
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* Valida todos los campos de un formulario
|
|
803
|
+
*/
|
|
804
|
+
async validateForm(fields, values) {
|
|
805
|
+
const errors = {};
|
|
806
|
+
for (const field of fields) {
|
|
807
|
+
const value = values[field.name];
|
|
808
|
+
const fieldErrors = await this.validateField(field, value);
|
|
809
|
+
if (fieldErrors.length > 0) {
|
|
810
|
+
errors[field.name] = fieldErrors;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
return errors;
|
|
814
|
+
}
|
|
815
|
+
};
|
|
816
|
+
|
|
817
|
+
// src/core/state-manager.ts
|
|
818
|
+
var StateManager = class {
|
|
819
|
+
constructor() {
|
|
820
|
+
this.wizardState = null;
|
|
821
|
+
this.schema = null;
|
|
822
|
+
this.parser = new SchemaParser();
|
|
823
|
+
this.validator = new ValidationEngine();
|
|
824
|
+
this.state = this.createInitialState();
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Crea el estado inicial
|
|
828
|
+
*/
|
|
829
|
+
createInitialState() {
|
|
830
|
+
return {
|
|
831
|
+
values: {},
|
|
832
|
+
errors: {},
|
|
833
|
+
touched: {},
|
|
834
|
+
isValid: true,
|
|
835
|
+
isSubmitting: false
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Inicializa el schema
|
|
840
|
+
*/
|
|
841
|
+
initializeSchema(schema) {
|
|
842
|
+
this.schema = this.parser.parse(schema);
|
|
843
|
+
this.initializeValues();
|
|
844
|
+
this.initializeWizard();
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* Inicializa los valores por defecto
|
|
848
|
+
*/
|
|
849
|
+
initializeValues() {
|
|
850
|
+
if (!this.schema) return;
|
|
851
|
+
const allFields = this.schema.isWizard ? this.schema.steps.flatMap((step) => step.fields) : this.schema.fields;
|
|
852
|
+
const existingValues = { ...this.state.values };
|
|
853
|
+
const values = {};
|
|
854
|
+
for (const field of allFields) {
|
|
855
|
+
const existingValue = getNestedValue(existingValues, field.name);
|
|
856
|
+
if (existingValue !== void 0 && existingValue !== null) {
|
|
857
|
+
values[field.name] = existingValue;
|
|
858
|
+
} else {
|
|
859
|
+
this.initializeFieldValue(field, values);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
for (const key in existingValues) {
|
|
863
|
+
if (!(key in values)) {
|
|
864
|
+
values[key] = existingValues[key];
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
this.state.values = values;
|
|
868
|
+
}
|
|
869
|
+
/**
|
|
870
|
+
* Inicializa el valor de un campo
|
|
871
|
+
*/
|
|
872
|
+
initializeFieldValue(field, values) {
|
|
873
|
+
if (field.defaultValue !== void 0) {
|
|
874
|
+
values[field.name] = field.defaultValue;
|
|
875
|
+
} else {
|
|
876
|
+
switch (field.type) {
|
|
877
|
+
case "checkbox":
|
|
878
|
+
case "switch":
|
|
879
|
+
values[field.name] = field.checked || false;
|
|
880
|
+
break;
|
|
881
|
+
case "select":
|
|
882
|
+
if ("multiple" in field && field.multiple) {
|
|
883
|
+
values[field.name] = [];
|
|
884
|
+
} else {
|
|
885
|
+
values[field.name] = null;
|
|
886
|
+
}
|
|
887
|
+
break;
|
|
888
|
+
case "array":
|
|
889
|
+
values[field.name] = [];
|
|
890
|
+
break;
|
|
891
|
+
case "group":
|
|
892
|
+
values[field.name] = {};
|
|
893
|
+
if ("fields" in field) {
|
|
894
|
+
for (const subField of field.fields) {
|
|
895
|
+
this.initializeFieldValue(subField, values[field.name]);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
break;
|
|
899
|
+
default:
|
|
900
|
+
values[field.name] = null;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Inicializa el estado del wizard
|
|
906
|
+
*/
|
|
907
|
+
initializeWizard() {
|
|
908
|
+
if (!this.schema || !this.schema.isWizard) {
|
|
909
|
+
this.wizardState = null;
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
this.wizardState = {
|
|
913
|
+
currentStep: 0,
|
|
914
|
+
totalSteps: this.schema.steps.length,
|
|
915
|
+
completedSteps: []
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Obtiene el estado actual
|
|
920
|
+
*/
|
|
921
|
+
getState() {
|
|
922
|
+
return { ...this.state };
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Obtiene el estado del wizard
|
|
926
|
+
*/
|
|
927
|
+
getWizardState() {
|
|
928
|
+
return this.wizardState ? { ...this.wizardState } : null;
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Obtiene un valor del formulario
|
|
932
|
+
*/
|
|
933
|
+
getValue(fieldName) {
|
|
934
|
+
return getNestedValue(this.state.values, fieldName);
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Establece un valor
|
|
938
|
+
*/
|
|
939
|
+
async setValue(fieldName, value) {
|
|
940
|
+
setNestedValue(this.state.values, fieldName, value);
|
|
941
|
+
this.state.touched[fieldName] = true;
|
|
942
|
+
if (this.state.touched[fieldName] && this.schema) {
|
|
943
|
+
await this.validateField(fieldName);
|
|
944
|
+
}
|
|
945
|
+
this.updateValidity();
|
|
946
|
+
}
|
|
947
|
+
/**
|
|
948
|
+
* Establece un valor sin validar (útil para preservar valores durante re-renderizado)
|
|
949
|
+
*/
|
|
950
|
+
setValueWithoutValidation(fieldName, value) {
|
|
951
|
+
setNestedValue(this.state.values, fieldName, value);
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
954
|
+
* Valida un campo específico
|
|
955
|
+
*/
|
|
956
|
+
async validateField(fieldName) {
|
|
957
|
+
if (!this.schema) return;
|
|
958
|
+
const allFields = this.schema.isWizard ? this.schema.steps.flatMap((step) => step.fields) : this.schema.fields;
|
|
959
|
+
const field = allFields.find((f) => f.name === fieldName);
|
|
960
|
+
if (!field) return;
|
|
961
|
+
const value = this.getValue(fieldName);
|
|
962
|
+
const errors = await this.validator.validateField(
|
|
963
|
+
field,
|
|
964
|
+
value
|
|
965
|
+
);
|
|
966
|
+
if (errors.length > 0) {
|
|
967
|
+
this.state.errors[fieldName] = errors;
|
|
968
|
+
} else {
|
|
969
|
+
delete this.state.errors[fieldName];
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* Valida todo el formulario
|
|
974
|
+
*/
|
|
975
|
+
async validateForm() {
|
|
976
|
+
if (!this.schema) {
|
|
977
|
+
return {};
|
|
978
|
+
}
|
|
979
|
+
const allFields = this.schema.isWizard ? this.schema.steps.flatMap((step) => step.fields) : this.schema.fields;
|
|
980
|
+
const errors = await this.validator.validateForm(
|
|
981
|
+
allFields,
|
|
982
|
+
this.state.values
|
|
983
|
+
);
|
|
984
|
+
this.state.errors = errors;
|
|
985
|
+
this.updateValidity();
|
|
986
|
+
return errors;
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Actualiza la validez del formulario
|
|
990
|
+
*/
|
|
991
|
+
updateValidity() {
|
|
992
|
+
this.state.isValid = Object.keys(this.state.errors).length === 0;
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Marca un campo como touched
|
|
996
|
+
*/
|
|
997
|
+
setTouched(fieldName) {
|
|
998
|
+
this.state.touched[fieldName] = true;
|
|
999
|
+
}
|
|
1000
|
+
/**
|
|
1001
|
+
* Obtiene los errores de un campo
|
|
1002
|
+
*/
|
|
1003
|
+
getErrors(fieldName) {
|
|
1004
|
+
return this.state.errors[fieldName] || [];
|
|
1005
|
+
}
|
|
1006
|
+
/**
|
|
1007
|
+
* Obtiene todos los errores
|
|
1008
|
+
*/
|
|
1009
|
+
getAllErrors() {
|
|
1010
|
+
return { ...this.state.errors };
|
|
1011
|
+
}
|
|
1012
|
+
/**
|
|
1013
|
+
* Resetea el formulario
|
|
1014
|
+
*/
|
|
1015
|
+
reset() {
|
|
1016
|
+
this.state = this.createInitialState();
|
|
1017
|
+
if (this.schema) {
|
|
1018
|
+
this.initializeValues();
|
|
1019
|
+
this.initializeWizard();
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
/**
|
|
1023
|
+
* Avanza al siguiente step del wizard
|
|
1024
|
+
*/
|
|
1025
|
+
nextStep() {
|
|
1026
|
+
if (!this.wizardState) return false;
|
|
1027
|
+
if (this.wizardState.currentStep < this.wizardState.totalSteps - 1) {
|
|
1028
|
+
this.wizardState.currentStep++;
|
|
1029
|
+
return true;
|
|
1030
|
+
}
|
|
1031
|
+
return false;
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
1034
|
+
* Retrocede al step anterior del wizard
|
|
1035
|
+
*/
|
|
1036
|
+
previousStep() {
|
|
1037
|
+
if (!this.wizardState) return false;
|
|
1038
|
+
if (this.wizardState.currentStep > 0) {
|
|
1039
|
+
this.wizardState.currentStep--;
|
|
1040
|
+
return true;
|
|
1041
|
+
}
|
|
1042
|
+
return false;
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Va a un step específico
|
|
1046
|
+
*/
|
|
1047
|
+
goToStep(stepIndex) {
|
|
1048
|
+
if (!this.wizardState) return false;
|
|
1049
|
+
if (stepIndex >= 0 && stepIndex < this.wizardState.totalSteps) {
|
|
1050
|
+
this.wizardState.currentStep = stepIndex;
|
|
1051
|
+
return true;
|
|
1052
|
+
}
|
|
1053
|
+
return false;
|
|
1054
|
+
}
|
|
1055
|
+
/**
|
|
1056
|
+
* Marca un step como completado
|
|
1057
|
+
*/
|
|
1058
|
+
completeStep(stepIndex) {
|
|
1059
|
+
if (!this.wizardState) return;
|
|
1060
|
+
if (!this.wizardState.completedSteps.includes(stepIndex)) {
|
|
1061
|
+
this.wizardState.completedSteps.push(stepIndex);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
/**
|
|
1065
|
+
* Establece el estado de submitting
|
|
1066
|
+
*/
|
|
1067
|
+
setSubmitting(isSubmitting) {
|
|
1068
|
+
this.state.isSubmitting = isSubmitting;
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Obtiene los campos del step actual
|
|
1072
|
+
*/
|
|
1073
|
+
getCurrentStepFields() {
|
|
1074
|
+
if (!this.schema || !this.schema.isWizard || !this.wizardState) {
|
|
1075
|
+
return this.schema?.fields || [];
|
|
1076
|
+
}
|
|
1077
|
+
return this.schema.steps[this.wizardState.currentStep].fields;
|
|
1078
|
+
}
|
|
1079
|
+
};
|
|
1080
|
+
|
|
1081
|
+
// src/components/inputs/base-input.ts
|
|
1082
|
+
var BaseInput = class {
|
|
1083
|
+
constructor(field, value, error, onChange, onBlur) {
|
|
1084
|
+
this.field = field;
|
|
1085
|
+
this.value = value;
|
|
1086
|
+
this.error = error;
|
|
1087
|
+
this.onChange = onChange;
|
|
1088
|
+
this.onBlur = onBlur;
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Crea un contenedor con label y error
|
|
1092
|
+
*/
|
|
1093
|
+
createFieldContainer(input) {
|
|
1094
|
+
const container = document.createElement("div");
|
|
1095
|
+
container.className = "easy-form-field";
|
|
1096
|
+
if (this.field.label) {
|
|
1097
|
+
const label = document.createElement("label");
|
|
1098
|
+
label.className = "easy-form-label";
|
|
1099
|
+
label.setAttribute("for", this.getFieldId());
|
|
1100
|
+
label.textContent = this.field.label;
|
|
1101
|
+
if (this.field.validations?.some((v) => v.type === "required")) {
|
|
1102
|
+
const required = document.createElement("span");
|
|
1103
|
+
required.className = "easy-form-required";
|
|
1104
|
+
required.textContent = " *";
|
|
1105
|
+
label.appendChild(required);
|
|
1106
|
+
}
|
|
1107
|
+
container.appendChild(label);
|
|
1108
|
+
}
|
|
1109
|
+
container.appendChild(input);
|
|
1110
|
+
if (this.field.description) {
|
|
1111
|
+
const description = document.createElement("p");
|
|
1112
|
+
description.className = "easy-form-description";
|
|
1113
|
+
description.textContent = this.field.description;
|
|
1114
|
+
container.appendChild(description);
|
|
1115
|
+
}
|
|
1116
|
+
if (this.error) {
|
|
1117
|
+
const errorEl = document.createElement("p");
|
|
1118
|
+
errorEl.className = "easy-form-error";
|
|
1119
|
+
errorEl.textContent = this.error;
|
|
1120
|
+
container.appendChild(errorEl);
|
|
1121
|
+
}
|
|
1122
|
+
return container;
|
|
1123
|
+
}
|
|
1124
|
+
/**
|
|
1125
|
+
* Obtiene el ID del campo
|
|
1126
|
+
*/
|
|
1127
|
+
getFieldId() {
|
|
1128
|
+
return `easy-form-${this.field.name}`;
|
|
1129
|
+
}
|
|
1130
|
+
/**
|
|
1131
|
+
* Aplica props comunes a un elemento
|
|
1132
|
+
*/
|
|
1133
|
+
applyCommonProps(element) {
|
|
1134
|
+
element.id = this.getFieldId();
|
|
1135
|
+
element.setAttribute("name", this.field.name);
|
|
1136
|
+
if (this.field.disabled) {
|
|
1137
|
+
element.setAttribute("disabled", "true");
|
|
1138
|
+
}
|
|
1139
|
+
if (this.field.hidden) {
|
|
1140
|
+
element.style.display = "none";
|
|
1141
|
+
}
|
|
1142
|
+
if (this.error) {
|
|
1143
|
+
element.classList.add("easy-form-input-error");
|
|
1144
|
+
}
|
|
1145
|
+
if (this.field.props) {
|
|
1146
|
+
for (const [key, value] of Object.entries(this.field.props)) {
|
|
1147
|
+
if (key.startsWith("data-") || key.startsWith("aria-")) {
|
|
1148
|
+
element.setAttribute(key, String(value));
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
};
|
|
1154
|
+
|
|
1155
|
+
// src/components/inputs/text-input.ts
|
|
1156
|
+
var TextInput = class extends BaseInput {
|
|
1157
|
+
render() {
|
|
1158
|
+
const input = document.createElement("input");
|
|
1159
|
+
input.type = this.field.type === "email" ? "email" : this.field.type === "password" ? "password" : "text";
|
|
1160
|
+
input.value = this.value ?? "";
|
|
1161
|
+
input.placeholder = this.field.placeholder || "";
|
|
1162
|
+
this.applyCommonProps(input);
|
|
1163
|
+
input.addEventListener("input", (e) => {
|
|
1164
|
+
const target = e.target;
|
|
1165
|
+
this.onChange(target.value);
|
|
1166
|
+
});
|
|
1167
|
+
input.addEventListener("blur", () => {
|
|
1168
|
+
this.onBlur();
|
|
1169
|
+
});
|
|
1170
|
+
return this.createFieldContainer(input);
|
|
1171
|
+
}
|
|
1172
|
+
};
|
|
1173
|
+
|
|
1174
|
+
// src/components/inputs/number-input.ts
|
|
1175
|
+
var NumberInput = class extends BaseInput {
|
|
1176
|
+
render() {
|
|
1177
|
+
const input = document.createElement("input");
|
|
1178
|
+
input.type = "number";
|
|
1179
|
+
input.value = this.value ?? "";
|
|
1180
|
+
input.placeholder = this.field.placeholder || "";
|
|
1181
|
+
const numberField = this.field;
|
|
1182
|
+
if (numberField.min !== void 0) {
|
|
1183
|
+
input.min = String(numberField.min);
|
|
1184
|
+
}
|
|
1185
|
+
if (numberField.max !== void 0) {
|
|
1186
|
+
input.max = String(numberField.max);
|
|
1187
|
+
}
|
|
1188
|
+
if (numberField.step !== void 0) {
|
|
1189
|
+
input.step = String(numberField.step);
|
|
1190
|
+
}
|
|
1191
|
+
this.applyCommonProps(input);
|
|
1192
|
+
input.addEventListener("input", (e) => {
|
|
1193
|
+
const target = e.target;
|
|
1194
|
+
const numValue = target.value === "" ? null : Number(target.value);
|
|
1195
|
+
this.onChange(numValue);
|
|
1196
|
+
});
|
|
1197
|
+
input.addEventListener("blur", () => {
|
|
1198
|
+
this.onBlur();
|
|
1199
|
+
});
|
|
1200
|
+
return this.createFieldContainer(input);
|
|
1201
|
+
}
|
|
1202
|
+
};
|
|
1203
|
+
|
|
1204
|
+
// src/components/inputs/textarea-input.ts
|
|
1205
|
+
var TextareaInput = class extends BaseInput {
|
|
1206
|
+
render() {
|
|
1207
|
+
const textarea = document.createElement("textarea");
|
|
1208
|
+
textarea.value = this.value ?? "";
|
|
1209
|
+
textarea.placeholder = this.field.placeholder || "";
|
|
1210
|
+
const textareaField = this.field;
|
|
1211
|
+
if (textareaField.rows) {
|
|
1212
|
+
textarea.rows = textareaField.rows;
|
|
1213
|
+
}
|
|
1214
|
+
if (textareaField.cols) {
|
|
1215
|
+
textarea.cols = textareaField.cols;
|
|
1216
|
+
}
|
|
1217
|
+
this.applyCommonProps(textarea);
|
|
1218
|
+
textarea.addEventListener("input", (e) => {
|
|
1219
|
+
const target = e.target;
|
|
1220
|
+
this.onChange(target.value);
|
|
1221
|
+
});
|
|
1222
|
+
textarea.addEventListener("blur", () => {
|
|
1223
|
+
this.onBlur();
|
|
1224
|
+
});
|
|
1225
|
+
return this.createFieldContainer(textarea);
|
|
1226
|
+
}
|
|
1227
|
+
};
|
|
1228
|
+
|
|
1229
|
+
// src/components/inputs/select-input.ts
|
|
1230
|
+
var SelectInput = class extends BaseInput {
|
|
1231
|
+
render() {
|
|
1232
|
+
const select = document.createElement("select");
|
|
1233
|
+
const selectField = this.field;
|
|
1234
|
+
if (selectField.multiple) {
|
|
1235
|
+
select.multiple = true;
|
|
1236
|
+
}
|
|
1237
|
+
for (const option of selectField.options) {
|
|
1238
|
+
const optionEl = document.createElement("option");
|
|
1239
|
+
if (typeof option === "string") {
|
|
1240
|
+
optionEl.value = option;
|
|
1241
|
+
optionEl.textContent = option;
|
|
1242
|
+
} else {
|
|
1243
|
+
optionEl.value = String(option.value);
|
|
1244
|
+
optionEl.textContent = option.label;
|
|
1245
|
+
}
|
|
1246
|
+
select.appendChild(optionEl);
|
|
1247
|
+
}
|
|
1248
|
+
if (selectField.multiple && Array.isArray(this.value)) {
|
|
1249
|
+
for (const option of select.options) {
|
|
1250
|
+
option.selected = this.value.includes(option.value);
|
|
1251
|
+
}
|
|
1252
|
+
} else {
|
|
1253
|
+
select.value = this.value ?? "";
|
|
1254
|
+
}
|
|
1255
|
+
this.applyCommonProps(select);
|
|
1256
|
+
select.addEventListener("change", (e) => {
|
|
1257
|
+
const target = e.target;
|
|
1258
|
+
if (selectField.multiple) {
|
|
1259
|
+
const selectedValues = Array.from(target.selectedOptions).map(
|
|
1260
|
+
(opt) => opt.value
|
|
1261
|
+
);
|
|
1262
|
+
this.onChange(selectedValues);
|
|
1263
|
+
} else {
|
|
1264
|
+
this.onChange(target.value || null);
|
|
1265
|
+
}
|
|
1266
|
+
});
|
|
1267
|
+
select.addEventListener("blur", () => {
|
|
1268
|
+
this.onBlur();
|
|
1269
|
+
});
|
|
1270
|
+
return this.createFieldContainer(select);
|
|
1271
|
+
}
|
|
1272
|
+
};
|
|
1273
|
+
|
|
1274
|
+
// src/components/inputs/checkbox-input.ts
|
|
1275
|
+
var CheckboxInput = class extends BaseInput {
|
|
1276
|
+
render() {
|
|
1277
|
+
const checkbox = document.createElement("input");
|
|
1278
|
+
checkbox.type = "checkbox";
|
|
1279
|
+
checkbox.checked = Boolean(this.value);
|
|
1280
|
+
this.applyCommonProps(checkbox);
|
|
1281
|
+
checkbox.addEventListener("change", (e) => {
|
|
1282
|
+
const target = e.target;
|
|
1283
|
+
this.onChange(target.checked);
|
|
1284
|
+
});
|
|
1285
|
+
checkbox.addEventListener("blur", () => {
|
|
1286
|
+
this.onBlur();
|
|
1287
|
+
});
|
|
1288
|
+
const container = document.createElement("div");
|
|
1289
|
+
container.className = "easy-form-field";
|
|
1290
|
+
const label = document.createElement("label");
|
|
1291
|
+
label.className = "easy-form-label-checkbox";
|
|
1292
|
+
label.setAttribute("for", this.getFieldId());
|
|
1293
|
+
label.appendChild(checkbox);
|
|
1294
|
+
if (this.field.label) {
|
|
1295
|
+
const labelText = document.createTextNode(this.field.label);
|
|
1296
|
+
label.appendChild(labelText);
|
|
1297
|
+
}
|
|
1298
|
+
container.appendChild(label);
|
|
1299
|
+
if (this.field.description) {
|
|
1300
|
+
const description = document.createElement("p");
|
|
1301
|
+
description.className = "easy-form-description";
|
|
1302
|
+
description.textContent = this.field.description;
|
|
1303
|
+
container.appendChild(description);
|
|
1304
|
+
}
|
|
1305
|
+
if (this.error) {
|
|
1306
|
+
const errorEl = document.createElement("p");
|
|
1307
|
+
errorEl.className = "easy-form-error";
|
|
1308
|
+
errorEl.textContent = this.error;
|
|
1309
|
+
container.appendChild(errorEl);
|
|
1310
|
+
}
|
|
1311
|
+
return container;
|
|
1312
|
+
}
|
|
1313
|
+
};
|
|
1314
|
+
|
|
1315
|
+
// src/components/inputs/radio-input.ts
|
|
1316
|
+
var RadioInput = class extends BaseInput {
|
|
1317
|
+
render() {
|
|
1318
|
+
const container = document.createElement("div");
|
|
1319
|
+
container.className = "easy-form-field";
|
|
1320
|
+
if (this.field.label) {
|
|
1321
|
+
const label = document.createElement("label");
|
|
1322
|
+
label.className = "easy-form-label";
|
|
1323
|
+
label.textContent = this.field.label;
|
|
1324
|
+
if (this.field.validations?.some((v) => v.type === "required")) {
|
|
1325
|
+
const required = document.createElement("span");
|
|
1326
|
+
required.className = "easy-form-required";
|
|
1327
|
+
required.textContent = " *";
|
|
1328
|
+
label.appendChild(required);
|
|
1329
|
+
}
|
|
1330
|
+
container.appendChild(label);
|
|
1331
|
+
}
|
|
1332
|
+
const radioGroup = document.createElement("div");
|
|
1333
|
+
radioGroup.className = "easy-form-radio-group";
|
|
1334
|
+
const radioField = this.field;
|
|
1335
|
+
for (const option of radioField.options) {
|
|
1336
|
+
const optionValue = typeof option === "string" ? option : option.value;
|
|
1337
|
+
const optionLabel = typeof option === "string" ? option : option.label;
|
|
1338
|
+
const radioContainer = document.createElement("div");
|
|
1339
|
+
radioContainer.className = "easy-form-radio-option";
|
|
1340
|
+
const radio = document.createElement("input");
|
|
1341
|
+
radio.type = "radio";
|
|
1342
|
+
radio.name = this.field.name;
|
|
1343
|
+
radio.id = `${this.getFieldId()}-${optionValue}`;
|
|
1344
|
+
radio.value = String(optionValue);
|
|
1345
|
+
radio.checked = String(this.value) === String(optionValue);
|
|
1346
|
+
if (this.field.disabled) {
|
|
1347
|
+
radio.disabled = true;
|
|
1348
|
+
}
|
|
1349
|
+
const label = document.createElement("label");
|
|
1350
|
+
label.setAttribute("for", radio.id);
|
|
1351
|
+
label.textContent = optionLabel;
|
|
1352
|
+
label.className = "easy-form-radio-label";
|
|
1353
|
+
radio.addEventListener("change", () => {
|
|
1354
|
+
this.onChange(optionValue);
|
|
1355
|
+
});
|
|
1356
|
+
radio.addEventListener("blur", () => {
|
|
1357
|
+
this.onBlur();
|
|
1358
|
+
});
|
|
1359
|
+
radioContainer.appendChild(radio);
|
|
1360
|
+
radioContainer.appendChild(label);
|
|
1361
|
+
radioGroup.appendChild(radioContainer);
|
|
1362
|
+
}
|
|
1363
|
+
container.appendChild(radioGroup);
|
|
1364
|
+
if (this.field.description) {
|
|
1365
|
+
const description = document.createElement("p");
|
|
1366
|
+
description.className = "easy-form-description";
|
|
1367
|
+
description.textContent = this.field.description;
|
|
1368
|
+
container.appendChild(description);
|
|
1369
|
+
}
|
|
1370
|
+
if (this.error) {
|
|
1371
|
+
const errorEl = document.createElement("p");
|
|
1372
|
+
errorEl.className = "easy-form-error";
|
|
1373
|
+
errorEl.textContent = this.error;
|
|
1374
|
+
container.appendChild(errorEl);
|
|
1375
|
+
}
|
|
1376
|
+
return container;
|
|
1377
|
+
}
|
|
1378
|
+
};
|
|
1379
|
+
|
|
1380
|
+
// src/components/inputs/switch-input.ts
|
|
1381
|
+
var SwitchInput = class extends BaseInput {
|
|
1382
|
+
render() {
|
|
1383
|
+
const switchEl = document.createElement("input");
|
|
1384
|
+
switchEl.type = "checkbox";
|
|
1385
|
+
switchEl.className = "easy-form-switch";
|
|
1386
|
+
switchEl.checked = Boolean(this.value);
|
|
1387
|
+
this.applyCommonProps(switchEl);
|
|
1388
|
+
switchEl.addEventListener("change", (e) => {
|
|
1389
|
+
const target = e.target;
|
|
1390
|
+
this.onChange(target.checked);
|
|
1391
|
+
});
|
|
1392
|
+
switchEl.addEventListener("blur", () => {
|
|
1393
|
+
this.onBlur();
|
|
1394
|
+
});
|
|
1395
|
+
const container = document.createElement("div");
|
|
1396
|
+
container.className = "easy-form-field";
|
|
1397
|
+
const label = document.createElement("label");
|
|
1398
|
+
label.className = "easy-form-label-switch";
|
|
1399
|
+
label.setAttribute("for", this.getFieldId());
|
|
1400
|
+
if (this.field.label) {
|
|
1401
|
+
const labelText = document.createTextNode(this.field.label);
|
|
1402
|
+
label.appendChild(labelText);
|
|
1403
|
+
}
|
|
1404
|
+
label.appendChild(switchEl);
|
|
1405
|
+
container.appendChild(label);
|
|
1406
|
+
if (this.field.description) {
|
|
1407
|
+
const description = document.createElement("p");
|
|
1408
|
+
description.className = "easy-form-description";
|
|
1409
|
+
description.textContent = this.field.description;
|
|
1410
|
+
container.appendChild(description);
|
|
1411
|
+
}
|
|
1412
|
+
if (this.error) {
|
|
1413
|
+
const errorEl = document.createElement("p");
|
|
1414
|
+
errorEl.className = "easy-form-error";
|
|
1415
|
+
errorEl.textContent = this.error;
|
|
1416
|
+
container.appendChild(errorEl);
|
|
1417
|
+
}
|
|
1418
|
+
return container;
|
|
1419
|
+
}
|
|
1420
|
+
};
|
|
1421
|
+
|
|
1422
|
+
// src/components/inputs/date-input.ts
|
|
1423
|
+
var DateInput = class extends BaseInput {
|
|
1424
|
+
render() {
|
|
1425
|
+
const input = document.createElement("input");
|
|
1426
|
+
input.type = "date";
|
|
1427
|
+
const dateField = this.field;
|
|
1428
|
+
if (dateField.min) {
|
|
1429
|
+
input.min = dateField.min;
|
|
1430
|
+
}
|
|
1431
|
+
if (dateField.max) {
|
|
1432
|
+
input.max = dateField.max;
|
|
1433
|
+
}
|
|
1434
|
+
if (this.value) {
|
|
1435
|
+
const date = this.value instanceof Date ? this.value.toISOString().split("T")[0] : String(this.value);
|
|
1436
|
+
input.value = date;
|
|
1437
|
+
}
|
|
1438
|
+
this.applyCommonProps(input);
|
|
1439
|
+
input.addEventListener("change", (e) => {
|
|
1440
|
+
const target = e.target;
|
|
1441
|
+
this.onChange(target.value || null);
|
|
1442
|
+
});
|
|
1443
|
+
input.addEventListener("blur", () => {
|
|
1444
|
+
this.onBlur();
|
|
1445
|
+
});
|
|
1446
|
+
return this.createFieldContainer(input);
|
|
1447
|
+
}
|
|
1448
|
+
};
|
|
1449
|
+
|
|
1450
|
+
// src/components/inputs/file-input.ts
|
|
1451
|
+
var FileInput = class extends BaseInput {
|
|
1452
|
+
render() {
|
|
1453
|
+
const input = document.createElement("input");
|
|
1454
|
+
input.type = "file";
|
|
1455
|
+
const fileField = this.field;
|
|
1456
|
+
if (fileField.accept) {
|
|
1457
|
+
input.accept = fileField.accept;
|
|
1458
|
+
}
|
|
1459
|
+
if (fileField.multiple) {
|
|
1460
|
+
input.multiple = true;
|
|
1461
|
+
}
|
|
1462
|
+
this.applyCommonProps(input);
|
|
1463
|
+
input.addEventListener("change", (e) => {
|
|
1464
|
+
const target = e.target;
|
|
1465
|
+
if (target.files) {
|
|
1466
|
+
if (fileField.multiple) {
|
|
1467
|
+
this.onChange(Array.from(target.files));
|
|
1468
|
+
} else {
|
|
1469
|
+
this.onChange(target.files[0] || null);
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
});
|
|
1473
|
+
input.addEventListener("blur", () => {
|
|
1474
|
+
this.onBlur();
|
|
1475
|
+
});
|
|
1476
|
+
return this.createFieldContainer(input);
|
|
1477
|
+
}
|
|
1478
|
+
};
|
|
1479
|
+
|
|
1480
|
+
// src/components/inputs/index.ts
|
|
1481
|
+
function createInput(field, value, error, onChange, onBlur) {
|
|
1482
|
+
switch (field.type) {
|
|
1483
|
+
case "text":
|
|
1484
|
+
case "email":
|
|
1485
|
+
case "password":
|
|
1486
|
+
return new TextInput(field, value, error, onChange, onBlur).render();
|
|
1487
|
+
case "number":
|
|
1488
|
+
return new NumberInput(field, value, error, onChange, onBlur).render();
|
|
1489
|
+
case "textarea":
|
|
1490
|
+
return new TextareaInput(field, value, error, onChange, onBlur).render();
|
|
1491
|
+
case "select":
|
|
1492
|
+
return new SelectInput(field, value, error, onChange, onBlur).render();
|
|
1493
|
+
case "checkbox":
|
|
1494
|
+
return new CheckboxInput(field, value, error, onChange, onBlur).render();
|
|
1495
|
+
case "radio":
|
|
1496
|
+
return new RadioInput(field, value, error, onChange, onBlur).render();
|
|
1497
|
+
case "switch":
|
|
1498
|
+
return new SwitchInput(field, value, error, onChange, onBlur).render();
|
|
1499
|
+
case "date":
|
|
1500
|
+
return new DateInput(field, value, error, onChange, onBlur).render();
|
|
1501
|
+
case "file":
|
|
1502
|
+
return new FileInput(field, value, error, onChange, onBlur).render();
|
|
1503
|
+
default:
|
|
1504
|
+
const div = document.createElement("div");
|
|
1505
|
+
div.textContent = `Tipo de campo no soportado: ${field.type}`;
|
|
1506
|
+
return div;
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
var customComponents = /* @__PURE__ */ new Map();
|
|
1510
|
+
function registerComponent(type, component) {
|
|
1511
|
+
customComponents.set(type, component);
|
|
1512
|
+
}
|
|
1513
|
+
function registerComponents(components) {
|
|
1514
|
+
for (const [type, component] of Object.entries(components)) {
|
|
1515
|
+
registerComponent(type, component);
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
function getCustomComponent(type) {
|
|
1519
|
+
return customComponents.get(type);
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
// src/components/easy-form.ts
|
|
1523
|
+
var BrowserHTMLElement = typeof HTMLElement !== "undefined" ? HTMLElement : class {
|
|
1524
|
+
};
|
|
1525
|
+
var EasyForm = class extends BrowserHTMLElement {
|
|
1526
|
+
constructor() {
|
|
1527
|
+
if (typeof HTMLElement === "undefined") {
|
|
1528
|
+
throw new Error("EasyForm can only be used in a browser environment");
|
|
1529
|
+
}
|
|
1530
|
+
super();
|
|
1531
|
+
this.customComponents = {};
|
|
1532
|
+
this.isRendering = false;
|
|
1533
|
+
this.stateManager = new StateManager();
|
|
1534
|
+
this.shadow = this.attachShadow({ mode: "open" });
|
|
1535
|
+
}
|
|
1536
|
+
static get observedAttributes() {
|
|
1537
|
+
return ["schema", "theme", "colors"];
|
|
1538
|
+
}
|
|
1539
|
+
/**
|
|
1540
|
+
* Obtiene el schema
|
|
1541
|
+
*/
|
|
1542
|
+
get schema() {
|
|
1543
|
+
const schemaAttr = this.getAttribute("schema");
|
|
1544
|
+
if (!schemaAttr) return null;
|
|
1545
|
+
return parseAttributeValue(schemaAttr);
|
|
1546
|
+
}
|
|
1547
|
+
/**
|
|
1548
|
+
* Establece el schema
|
|
1549
|
+
*/
|
|
1550
|
+
set schema(value) {
|
|
1551
|
+
if (value) {
|
|
1552
|
+
this.setAttribute("schema", attributeValue(value));
|
|
1553
|
+
} else {
|
|
1554
|
+
this.removeAttribute("schema");
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
/**
|
|
1558
|
+
* Se llama cuando el componente se conecta al DOM
|
|
1559
|
+
*/
|
|
1560
|
+
connectedCallback() {
|
|
1561
|
+
this.setupStyles();
|
|
1562
|
+
this.render();
|
|
1563
|
+
}
|
|
1564
|
+
/**
|
|
1565
|
+
* Se llama cuando un atributo cambia
|
|
1566
|
+
*/
|
|
1567
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
1568
|
+
if (name === "schema" && newValue !== oldValue) {
|
|
1569
|
+
this.handleSchemaChange();
|
|
1570
|
+
}
|
|
1571
|
+
if ((name === "theme" || name === "colors") && newValue !== oldValue) {
|
|
1572
|
+
this.setupStyles();
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
/**
|
|
1576
|
+
* Maneja el cambio de schema
|
|
1577
|
+
*/
|
|
1578
|
+
handleSchemaChange() {
|
|
1579
|
+
const schema = this.schema;
|
|
1580
|
+
if (schema) {
|
|
1581
|
+
this.stateManager.initializeSchema(schema);
|
|
1582
|
+
this.render();
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
/**
|
|
1586
|
+
* Renderiza el formulario
|
|
1587
|
+
*/
|
|
1588
|
+
async render() {
|
|
1589
|
+
if (this.isRendering) {
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1592
|
+
this.isRendering = true;
|
|
1593
|
+
try {
|
|
1594
|
+
const schema = this.schema;
|
|
1595
|
+
if (!schema) {
|
|
1596
|
+
const form = this.shadow.querySelector("form");
|
|
1597
|
+
if (form && form.parentNode === this.shadow) {
|
|
1598
|
+
form.remove();
|
|
1599
|
+
}
|
|
1600
|
+
return;
|
|
1601
|
+
}
|
|
1602
|
+
const preservedValues = this.preserveCurrentValues();
|
|
1603
|
+
if (preservedValues && Object.keys(preservedValues).length > 0) {
|
|
1604
|
+
for (const [key, value] of Object.entries(preservedValues)) {
|
|
1605
|
+
this.stateManager.setValueWithoutValidation(key, value);
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
this.stateManager.initializeSchema(schema);
|
|
1609
|
+
const wizardState = this.stateManager.getWizardState();
|
|
1610
|
+
const newFormElement = document.createElement("form");
|
|
1611
|
+
newFormElement.addEventListener("submit", (e) => this.handleSubmit(e));
|
|
1612
|
+
if (wizardState) {
|
|
1613
|
+
this.renderWizard(newFormElement, wizardState);
|
|
1614
|
+
} else {
|
|
1615
|
+
this.renderFields(newFormElement, schema.fields || []);
|
|
1616
|
+
}
|
|
1617
|
+
const submitButton = document.createElement("button");
|
|
1618
|
+
submitButton.type = "submit";
|
|
1619
|
+
submitButton.textContent = "Enviar";
|
|
1620
|
+
submitButton.className = "easy-form-submit";
|
|
1621
|
+
newFormElement.appendChild(submitButton);
|
|
1622
|
+
const oldForm = this.shadow.querySelector("form");
|
|
1623
|
+
if (oldForm && oldForm.parentNode === this.shadow && oldForm !== newFormElement) {
|
|
1624
|
+
try {
|
|
1625
|
+
oldForm.remove();
|
|
1626
|
+
} catch (e) {
|
|
1627
|
+
console.warn("Error al eliminar formulario anterior:", e);
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
this.shadow.appendChild(newFormElement);
|
|
1631
|
+
} finally {
|
|
1632
|
+
this.isRendering = false;
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
/**
|
|
1636
|
+
* Preserva los valores actuales del DOM antes de re-renderizar
|
|
1637
|
+
* Retorna un objeto con los valores preservados
|
|
1638
|
+
*/
|
|
1639
|
+
preserveCurrentValues() {
|
|
1640
|
+
const form = this.shadow.querySelector("form");
|
|
1641
|
+
const preservedValues = {};
|
|
1642
|
+
if (!form) return preservedValues;
|
|
1643
|
+
const inputs = form.querySelectorAll("input, textarea, select");
|
|
1644
|
+
for (const input of inputs) {
|
|
1645
|
+
const name = input.getAttribute("name");
|
|
1646
|
+
if (!name) continue;
|
|
1647
|
+
let value;
|
|
1648
|
+
if (input instanceof HTMLInputElement) {
|
|
1649
|
+
if (input.type === "checkbox") {
|
|
1650
|
+
value = input.checked;
|
|
1651
|
+
} else if (input.type === "radio") {
|
|
1652
|
+
if (input.checked) {
|
|
1653
|
+
value = input.value;
|
|
1654
|
+
} else {
|
|
1655
|
+
continue;
|
|
1656
|
+
}
|
|
1657
|
+
} else if (input.type === "number") {
|
|
1658
|
+
value = input.value === "" ? null : Number(input.value);
|
|
1659
|
+
} else {
|
|
1660
|
+
value = input.value;
|
|
1661
|
+
}
|
|
1662
|
+
} else if (input instanceof HTMLTextAreaElement) {
|
|
1663
|
+
value = input.value;
|
|
1664
|
+
} else if (input instanceof HTMLSelectElement) {
|
|
1665
|
+
if (input.multiple) {
|
|
1666
|
+
value = Array.from(input.selectedOptions).map((opt) => opt.value);
|
|
1667
|
+
} else {
|
|
1668
|
+
value = input.value || null;
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
if (value !== void 0) {
|
|
1672
|
+
preservedValues[name] = value === "" ? null : value;
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
return preservedValues;
|
|
1676
|
+
}
|
|
1677
|
+
/**
|
|
1678
|
+
* Renderiza campos normales
|
|
1679
|
+
*/
|
|
1680
|
+
renderFields(container, fields) {
|
|
1681
|
+
for (const field of fields) {
|
|
1682
|
+
const fieldElement = this.renderField(field);
|
|
1683
|
+
if (fieldElement) {
|
|
1684
|
+
container.appendChild(fieldElement);
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
/**
|
|
1689
|
+
* Renderiza un campo
|
|
1690
|
+
*/
|
|
1691
|
+
renderField(field) {
|
|
1692
|
+
if (field.type === "group") {
|
|
1693
|
+
return this.renderGroup(field);
|
|
1694
|
+
}
|
|
1695
|
+
if (field.type === "array") {
|
|
1696
|
+
return this.renderArray(field);
|
|
1697
|
+
}
|
|
1698
|
+
if (field.type === "custom") {
|
|
1699
|
+
return this.renderCustom(field);
|
|
1700
|
+
}
|
|
1701
|
+
const value = this.stateManager.getValue(field.name);
|
|
1702
|
+
const errors = this.stateManager.getErrors(field.name);
|
|
1703
|
+
const error = errors.length > 0 ? errors[0] : void 0;
|
|
1704
|
+
const customComponent = getCustomComponent(field.type);
|
|
1705
|
+
if (customComponent) {
|
|
1706
|
+
return customComponent({
|
|
1707
|
+
field,
|
|
1708
|
+
value,
|
|
1709
|
+
error,
|
|
1710
|
+
onChange: (val) => this.handleFieldChange(field.name, val),
|
|
1711
|
+
onBlur: () => this.handleFieldBlur(field.name)
|
|
1712
|
+
});
|
|
1713
|
+
}
|
|
1714
|
+
return createInput(
|
|
1715
|
+
field,
|
|
1716
|
+
value,
|
|
1717
|
+
error,
|
|
1718
|
+
(val) => this.handleFieldChange(field.name, val),
|
|
1719
|
+
() => this.handleFieldBlur(field.name)
|
|
1720
|
+
);
|
|
1721
|
+
}
|
|
1722
|
+
/**
|
|
1723
|
+
* Renderiza un grupo de campos
|
|
1724
|
+
*/
|
|
1725
|
+
renderGroup(field) {
|
|
1726
|
+
const groupContainer = document.createElement("div");
|
|
1727
|
+
groupContainer.className = "easy-form-group";
|
|
1728
|
+
if (field.label) {
|
|
1729
|
+
const label = document.createElement("h3");
|
|
1730
|
+
label.className = "easy-form-group-label";
|
|
1731
|
+
label.textContent = field.label;
|
|
1732
|
+
groupContainer.appendChild(label);
|
|
1733
|
+
}
|
|
1734
|
+
if ("fields" in field && field.fields) {
|
|
1735
|
+
for (const subField of field.fields) {
|
|
1736
|
+
const fieldElement = this.renderField(subField);
|
|
1737
|
+
if (fieldElement) {
|
|
1738
|
+
groupContainer.appendChild(fieldElement);
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
return groupContainer;
|
|
1743
|
+
}
|
|
1744
|
+
/**
|
|
1745
|
+
* Renderiza un array dinámico
|
|
1746
|
+
*/
|
|
1747
|
+
renderArray(field) {
|
|
1748
|
+
const arrayContainer = document.createElement("div");
|
|
1749
|
+
arrayContainer.className = "easy-form-array";
|
|
1750
|
+
if (field.label) {
|
|
1751
|
+
const label = document.createElement("label");
|
|
1752
|
+
label.className = "easy-form-label";
|
|
1753
|
+
label.textContent = field.label;
|
|
1754
|
+
arrayContainer.appendChild(label);
|
|
1755
|
+
}
|
|
1756
|
+
const values = this.stateManager.getValue(field.name) || [];
|
|
1757
|
+
const arrayField = field;
|
|
1758
|
+
const itemsContainer = document.createElement("div");
|
|
1759
|
+
itemsContainer.className = "easy-form-array-items";
|
|
1760
|
+
for (let i = 0; i < values.length; i++) {
|
|
1761
|
+
const itemContainer = document.createElement("div");
|
|
1762
|
+
itemContainer.className = "easy-form-array-item";
|
|
1763
|
+
if (arrayField.itemSchema?.fields) {
|
|
1764
|
+
for (const itemField of arrayField.itemSchema.fields) {
|
|
1765
|
+
const itemFieldWithName = {
|
|
1766
|
+
...itemField,
|
|
1767
|
+
name: `${field.name}.${i}.${itemField.name}`
|
|
1768
|
+
};
|
|
1769
|
+
const fieldElement = this.renderField(itemFieldWithName);
|
|
1770
|
+
if (fieldElement) {
|
|
1771
|
+
itemContainer.appendChild(fieldElement);
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
const removeButton = document.createElement("button");
|
|
1776
|
+
removeButton.type = "button";
|
|
1777
|
+
removeButton.textContent = "Eliminar";
|
|
1778
|
+
removeButton.className = "easy-form-array-remove";
|
|
1779
|
+
removeButton.addEventListener("click", () => {
|
|
1780
|
+
const newValues = [...values];
|
|
1781
|
+
newValues.splice(i, 1);
|
|
1782
|
+
this.handleFieldChange(field.name, newValues);
|
|
1783
|
+
});
|
|
1784
|
+
itemContainer.appendChild(removeButton);
|
|
1785
|
+
itemsContainer.appendChild(itemContainer);
|
|
1786
|
+
}
|
|
1787
|
+
arrayContainer.appendChild(itemsContainer);
|
|
1788
|
+
const addButton = document.createElement("button");
|
|
1789
|
+
addButton.type = "button";
|
|
1790
|
+
addButton.textContent = "Agregar";
|
|
1791
|
+
addButton.className = "easy-form-array-add";
|
|
1792
|
+
addButton.addEventListener("click", () => {
|
|
1793
|
+
const newValues = [...values, {}];
|
|
1794
|
+
this.handleFieldChange(field.name, newValues);
|
|
1795
|
+
});
|
|
1796
|
+
arrayContainer.appendChild(addButton);
|
|
1797
|
+
return arrayContainer;
|
|
1798
|
+
}
|
|
1799
|
+
/**
|
|
1800
|
+
* Renderiza un campo custom
|
|
1801
|
+
*/
|
|
1802
|
+
renderCustom(field) {
|
|
1803
|
+
const customComponent = getCustomComponent("custom");
|
|
1804
|
+
if (!customComponent) {
|
|
1805
|
+
const div = document.createElement("div");
|
|
1806
|
+
div.textContent = `Componente custom no registrado para: ${field.name}`;
|
|
1807
|
+
return div;
|
|
1808
|
+
}
|
|
1809
|
+
const value = this.stateManager.getValue(field.name);
|
|
1810
|
+
const errors = this.stateManager.getErrors(field.name);
|
|
1811
|
+
const error = errors.length > 0 ? errors[0] : void 0;
|
|
1812
|
+
return customComponent({
|
|
1813
|
+
field,
|
|
1814
|
+
value,
|
|
1815
|
+
error,
|
|
1816
|
+
onChange: (val) => this.handleFieldChange(field.name, val),
|
|
1817
|
+
onBlur: () => this.handleFieldBlur(field.name)
|
|
1818
|
+
});
|
|
1819
|
+
}
|
|
1820
|
+
/**
|
|
1821
|
+
* Renderiza wizard
|
|
1822
|
+
*/
|
|
1823
|
+
renderWizard(container, wizardState) {
|
|
1824
|
+
const wizardContainer = document.createElement("div");
|
|
1825
|
+
wizardContainer.className = "easy-form-wizard";
|
|
1826
|
+
const stepsIndicator = document.createElement("div");
|
|
1827
|
+
stepsIndicator.className = "easy-form-wizard-steps";
|
|
1828
|
+
const schema = this.schema;
|
|
1829
|
+
if (schema?.steps) {
|
|
1830
|
+
for (let i = 0; i < schema.steps.length; i++) {
|
|
1831
|
+
const stepEl = document.createElement("div");
|
|
1832
|
+
stepEl.className = "easy-form-wizard-step";
|
|
1833
|
+
if (i === wizardState.currentStep) {
|
|
1834
|
+
stepEl.classList.add("active");
|
|
1835
|
+
}
|
|
1836
|
+
if (wizardState.completedSteps.includes(i)) {
|
|
1837
|
+
stepEl.classList.add("completed");
|
|
1838
|
+
}
|
|
1839
|
+
stepEl.textContent = schema.steps[i].title;
|
|
1840
|
+
stepsIndicator.appendChild(stepEl);
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
wizardContainer.appendChild(stepsIndicator);
|
|
1844
|
+
const fieldsContainer = document.createElement("div");
|
|
1845
|
+
fieldsContainer.className = "easy-form-wizard-fields";
|
|
1846
|
+
const currentFields = this.stateManager.getCurrentStepFields();
|
|
1847
|
+
for (const field of currentFields) {
|
|
1848
|
+
const fieldElement = this.renderField(field);
|
|
1849
|
+
if (fieldElement) {
|
|
1850
|
+
fieldsContainer.appendChild(fieldElement);
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
wizardContainer.appendChild(fieldsContainer);
|
|
1854
|
+
const navContainer = document.createElement("div");
|
|
1855
|
+
navContainer.className = "easy-form-wizard-nav";
|
|
1856
|
+
const prevButton = document.createElement("button");
|
|
1857
|
+
prevButton.type = "button";
|
|
1858
|
+
prevButton.textContent = "Anterior";
|
|
1859
|
+
prevButton.className = "easy-form-wizard-prev";
|
|
1860
|
+
prevButton.disabled = wizardState.currentStep === 0;
|
|
1861
|
+
prevButton.addEventListener("click", () => {
|
|
1862
|
+
if (this.stateManager.previousStep()) {
|
|
1863
|
+
this.render();
|
|
1864
|
+
this.emitStepChange();
|
|
1865
|
+
}
|
|
1866
|
+
});
|
|
1867
|
+
navContainer.appendChild(prevButton);
|
|
1868
|
+
const nextButton = document.createElement("button");
|
|
1869
|
+
nextButton.type = "button";
|
|
1870
|
+
nextButton.textContent = wizardState.currentStep === wizardState.totalSteps - 1 ? "Enviar" : "Siguiente";
|
|
1871
|
+
nextButton.className = "easy-form-wizard-next";
|
|
1872
|
+
nextButton.addEventListener("click", async () => {
|
|
1873
|
+
if (wizardState.currentStep === wizardState.totalSteps - 1) {
|
|
1874
|
+
await this.handleSubmit(new Event("submit"));
|
|
1875
|
+
} else {
|
|
1876
|
+
const currentFields2 = this.stateManager.getCurrentStepFields();
|
|
1877
|
+
const errors = await this.stateManager.validateForm();
|
|
1878
|
+
const hasErrors = currentFields2.some(
|
|
1879
|
+
(f) => errors[f.name] && errors[f.name].length > 0
|
|
1880
|
+
);
|
|
1881
|
+
if (!hasErrors) {
|
|
1882
|
+
this.stateManager.completeStep(wizardState.currentStep);
|
|
1883
|
+
if (this.stateManager.nextStep()) {
|
|
1884
|
+
this.render();
|
|
1885
|
+
this.emitStepChange();
|
|
1886
|
+
}
|
|
1887
|
+
} else {
|
|
1888
|
+
this.emitError(errors);
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
});
|
|
1892
|
+
navContainer.appendChild(nextButton);
|
|
1893
|
+
wizardContainer.appendChild(navContainer);
|
|
1894
|
+
container.appendChild(wizardContainer);
|
|
1895
|
+
}
|
|
1896
|
+
/**
|
|
1897
|
+
* Maneja el cambio de un campo
|
|
1898
|
+
*/
|
|
1899
|
+
async handleFieldChange(fieldName, value) {
|
|
1900
|
+
await this.stateManager.setValue(fieldName, value);
|
|
1901
|
+
const changeEvent = new CustomEvent("change", {
|
|
1902
|
+
detail: {
|
|
1903
|
+
field: fieldName,
|
|
1904
|
+
value,
|
|
1905
|
+
values: this.stateManager.getState().values
|
|
1906
|
+
},
|
|
1907
|
+
bubbles: true,
|
|
1908
|
+
composed: true
|
|
1909
|
+
});
|
|
1910
|
+
this.dispatchEvent(changeEvent);
|
|
1911
|
+
}
|
|
1912
|
+
/**
|
|
1913
|
+
* Maneja el blur de un campo
|
|
1914
|
+
*/
|
|
1915
|
+
async handleFieldBlur(fieldName) {
|
|
1916
|
+
this.stateManager.setTouched(fieldName);
|
|
1917
|
+
await this.stateManager.validateField(fieldName);
|
|
1918
|
+
this.updateSingleField(fieldName);
|
|
1919
|
+
}
|
|
1920
|
+
/**
|
|
1921
|
+
* Actualiza solo un campo específico sin re-renderizar todo el formulario
|
|
1922
|
+
*/
|
|
1923
|
+
updateSingleField(fieldName) {
|
|
1924
|
+
const schema = this.schema;
|
|
1925
|
+
if (!schema) return;
|
|
1926
|
+
const field = this.findFieldInSchema(schema, fieldName);
|
|
1927
|
+
if (!field) return;
|
|
1928
|
+
const errors = this.stateManager.getErrors(fieldName);
|
|
1929
|
+
const error = errors.length > 0 ? errors[0] : void 0;
|
|
1930
|
+
const fieldContainer = this.shadow.querySelector(`[name="${fieldName}"]`)?.closest(".easy-form-field");
|
|
1931
|
+
if (!fieldContainer) return;
|
|
1932
|
+
const errorElement = fieldContainer.querySelector(".easy-form-error");
|
|
1933
|
+
if (error) {
|
|
1934
|
+
if (!errorElement) {
|
|
1935
|
+
const errorEl = document.createElement("p");
|
|
1936
|
+
errorEl.className = "easy-form-error";
|
|
1937
|
+
errorEl.textContent = error;
|
|
1938
|
+
fieldContainer.appendChild(errorEl);
|
|
1939
|
+
} else {
|
|
1940
|
+
errorElement.textContent = error;
|
|
1941
|
+
}
|
|
1942
|
+
const input = fieldContainer.querySelector("input, textarea, select");
|
|
1943
|
+
input?.classList.add("easy-form-input-error");
|
|
1944
|
+
} else {
|
|
1945
|
+
errorElement?.remove();
|
|
1946
|
+
const input = fieldContainer.querySelector("input, textarea, select");
|
|
1947
|
+
input?.classList.remove("easy-form-input-error");
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
/**
|
|
1951
|
+
* Busca un campo por nombre en el schema
|
|
1952
|
+
*/
|
|
1953
|
+
findFieldInSchema(schema, name) {
|
|
1954
|
+
const fields = schema.fields || [];
|
|
1955
|
+
for (const field of fields) {
|
|
1956
|
+
if (field.name === name) {
|
|
1957
|
+
return field;
|
|
1958
|
+
}
|
|
1959
|
+
if (field.type === "group" && "fields" in field) {
|
|
1960
|
+
const found = this.findFieldInSchema({ fields: field.fields }, name);
|
|
1961
|
+
if (found) return found;
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
return null;
|
|
1965
|
+
}
|
|
1966
|
+
/**
|
|
1967
|
+
* Maneja el submit del formulario
|
|
1968
|
+
*/
|
|
1969
|
+
async handleSubmit(event) {
|
|
1970
|
+
event.preventDefault();
|
|
1971
|
+
const errors = await this.stateManager.validateForm();
|
|
1972
|
+
const state = this.stateManager.getState();
|
|
1973
|
+
if (Object.keys(errors).length > 0) {
|
|
1974
|
+
this.emitError(errors);
|
|
1975
|
+
this.render();
|
|
1976
|
+
return;
|
|
1977
|
+
}
|
|
1978
|
+
const submitEvent = new CustomEvent("submit", {
|
|
1979
|
+
detail: {
|
|
1980
|
+
values: state.values,
|
|
1981
|
+
isValid: true,
|
|
1982
|
+
errors: {}
|
|
1983
|
+
},
|
|
1984
|
+
bubbles: true,
|
|
1985
|
+
composed: true
|
|
1986
|
+
});
|
|
1987
|
+
this.dispatchEvent(submitEvent);
|
|
1988
|
+
}
|
|
1989
|
+
/**
|
|
1990
|
+
* Emite evento de error
|
|
1991
|
+
*/
|
|
1992
|
+
emitError(errors) {
|
|
1993
|
+
const errorEvent = new CustomEvent("error", {
|
|
1994
|
+
detail: {
|
|
1995
|
+
errors
|
|
1996
|
+
},
|
|
1997
|
+
bubbles: true,
|
|
1998
|
+
composed: true
|
|
1999
|
+
});
|
|
2000
|
+
this.dispatchEvent(errorEvent);
|
|
2001
|
+
}
|
|
2002
|
+
/**
|
|
2003
|
+
* Emite evento de cambio de step
|
|
2004
|
+
*/
|
|
2005
|
+
emitStepChange() {
|
|
2006
|
+
const wizardState = this.stateManager.getWizardState();
|
|
2007
|
+
if (!wizardState) return;
|
|
2008
|
+
const stepChangeEvent = new CustomEvent(
|
|
2009
|
+
"stepChange",
|
|
2010
|
+
{
|
|
2011
|
+
detail: {
|
|
2012
|
+
currentStep: wizardState.currentStep,
|
|
2013
|
+
previousStep: wizardState.currentStep > 0 ? wizardState.currentStep - 1 : wizardState.currentStep,
|
|
2014
|
+
totalSteps: wizardState.totalSteps
|
|
2015
|
+
},
|
|
2016
|
+
bubbles: true,
|
|
2017
|
+
composed: true
|
|
2018
|
+
}
|
|
2019
|
+
);
|
|
2020
|
+
this.dispatchEvent(stepChangeEvent);
|
|
2021
|
+
}
|
|
2022
|
+
/**
|
|
2023
|
+
* Registra componentes personalizados
|
|
2024
|
+
*/
|
|
2025
|
+
registerComponents(components) {
|
|
2026
|
+
this.customComponents = { ...this.customComponents, ...components };
|
|
2027
|
+
registerComponents(components);
|
|
2028
|
+
}
|
|
2029
|
+
/**
|
|
2030
|
+
* Obtiene el tema del formulario
|
|
2031
|
+
*/
|
|
2032
|
+
get theme() {
|
|
2033
|
+
const themeAttr = this.getAttribute("theme");
|
|
2034
|
+
if (themeAttr && ["plano", "tradicional", "material", "rounded-shadow", "lines"].includes(themeAttr)) {
|
|
2035
|
+
return themeAttr;
|
|
2036
|
+
}
|
|
2037
|
+
return "plano";
|
|
2038
|
+
}
|
|
2039
|
+
/**
|
|
2040
|
+
* Establece el tema del formulario
|
|
2041
|
+
*/
|
|
2042
|
+
set theme(value) {
|
|
2043
|
+
if (value) {
|
|
2044
|
+
this.setAttribute("theme", value);
|
|
2045
|
+
} else {
|
|
2046
|
+
this.removeAttribute("theme");
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
/**
|
|
2050
|
+
* Obtiene los colores personalizados
|
|
2051
|
+
*/
|
|
2052
|
+
get colors() {
|
|
2053
|
+
const colorsAttr = this.getAttribute("colors");
|
|
2054
|
+
if (!colorsAttr) return null;
|
|
2055
|
+
return parseAttributeValue(colorsAttr);
|
|
2056
|
+
}
|
|
2057
|
+
/**
|
|
2058
|
+
* Establece los colores personalizados
|
|
2059
|
+
*/
|
|
2060
|
+
set colors(value) {
|
|
2061
|
+
if (value) {
|
|
2062
|
+
this.setAttribute("colors", attributeValue(value));
|
|
2063
|
+
} else {
|
|
2064
|
+
this.removeAttribute("colors");
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
/**
|
|
2068
|
+
* Configura estilos básicos
|
|
2069
|
+
*/
|
|
2070
|
+
setupStyles() {
|
|
2071
|
+
const existingStyle = this.shadow.querySelector("style");
|
|
2072
|
+
if (existingStyle) {
|
|
2073
|
+
existingStyle.remove();
|
|
2074
|
+
}
|
|
2075
|
+
const theme = this.theme;
|
|
2076
|
+
const colors = getColors(this.colors || void 0);
|
|
2077
|
+
const styles = getThemeStyles(theme, colors);
|
|
2078
|
+
const style = document.createElement("style");
|
|
2079
|
+
style.textContent = styles;
|
|
2080
|
+
if (this.shadow.firstChild) {
|
|
2081
|
+
this.shadow.insertBefore(style, this.shadow.firstChild);
|
|
2082
|
+
} else {
|
|
2083
|
+
this.shadow.appendChild(style);
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
};
|
|
2087
|
+
if (typeof window !== "undefined" && typeof customElements !== "undefined" && !customElements.get("easy-form")) {
|
|
2088
|
+
customElements.define("easy-form", EasyForm);
|
|
2089
|
+
}
|
|
2090
|
+
export {
|
|
2091
|
+
EasyForm
|
|
2092
|
+
};
|
|
2093
|
+
//# sourceMappingURL=easy-form.js.map
|