easy-forms-core 1.1.8 → 1.1.10
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 +3 -0
- package/dist/easy-form.d.ts +68 -3
- package/dist/easy-form.js +844 -15
- package/dist/easy-form.js.map +1 -1
- package/dist/index.d.ts +69 -4
- package/dist/index.js +844 -15
- package/dist/index.js.map +1 -1
- package/package.json +10 -3
package/dist/easy-form.js
CHANGED
|
@@ -59,6 +59,10 @@ var SchemaParser = class {
|
|
|
59
59
|
"switch",
|
|
60
60
|
"date",
|
|
61
61
|
"file",
|
|
62
|
+
"file-drop",
|
|
63
|
+
"map",
|
|
64
|
+
"rating",
|
|
65
|
+
"slider",
|
|
62
66
|
"array",
|
|
63
67
|
"group",
|
|
64
68
|
"row",
|
|
@@ -158,6 +162,22 @@ var SchemaParser = class {
|
|
|
158
162
|
defaults.numeric = true;
|
|
159
163
|
}
|
|
160
164
|
break;
|
|
165
|
+
case "slider":
|
|
166
|
+
if (!("min" in field)) {
|
|
167
|
+
defaults.min = 0;
|
|
168
|
+
}
|
|
169
|
+
if (!("max" in field)) {
|
|
170
|
+
defaults.max = 100;
|
|
171
|
+
}
|
|
172
|
+
if (!("step" in field)) {
|
|
173
|
+
defaults.step = 1;
|
|
174
|
+
}
|
|
175
|
+
break;
|
|
176
|
+
case "rating":
|
|
177
|
+
if (!("max" in field)) {
|
|
178
|
+
defaults.max = 5;
|
|
179
|
+
}
|
|
180
|
+
break;
|
|
161
181
|
}
|
|
162
182
|
return { ...defaults, ...field };
|
|
163
183
|
}
|
|
@@ -234,6 +254,140 @@ function getBaseStyles(colors) {
|
|
|
234
254
|
font-weight: 500;
|
|
235
255
|
color: var(--easy-form-label-color);
|
|
236
256
|
}
|
|
257
|
+
.easy-form-label-down .easy-form-label {
|
|
258
|
+
margin-bottom: 0;
|
|
259
|
+
margin-top: 0.5rem;
|
|
260
|
+
}
|
|
261
|
+
.easy-form-field-inner-horizontal {
|
|
262
|
+
display: flex;
|
|
263
|
+
flex-direction: row;
|
|
264
|
+
align-items: center;
|
|
265
|
+
gap: 0.75rem;
|
|
266
|
+
flex-wrap: wrap;
|
|
267
|
+
}
|
|
268
|
+
.easy-form-field-inner-horizontal .easy-form-label {
|
|
269
|
+
margin-bottom: 0;
|
|
270
|
+
margin-top: 0;
|
|
271
|
+
flex-shrink: 0;
|
|
272
|
+
}
|
|
273
|
+
.easy-form-direction-vertical {
|
|
274
|
+
display: block;
|
|
275
|
+
}
|
|
276
|
+
.easy-form-direction-horizontal {
|
|
277
|
+
display: flex;
|
|
278
|
+
flex-direction: row;
|
|
279
|
+
flex-wrap: wrap;
|
|
280
|
+
gap: 1rem;
|
|
281
|
+
}
|
|
282
|
+
.easy-form-direction-horizontal > * {
|
|
283
|
+
flex: 1 1 auto;
|
|
284
|
+
}
|
|
285
|
+
.easy-form-completed-indicator {
|
|
286
|
+
margin-bottom: 1rem;
|
|
287
|
+
}
|
|
288
|
+
.easy-form-completed-track {
|
|
289
|
+
height: 6px;
|
|
290
|
+
background: var(--easy-form-border);
|
|
291
|
+
border-radius: 3px;
|
|
292
|
+
overflow: hidden;
|
|
293
|
+
}
|
|
294
|
+
.easy-form-completed-fill {
|
|
295
|
+
height: 100%;
|
|
296
|
+
background: var(--easy-form-primary);
|
|
297
|
+
border-radius: 3px;
|
|
298
|
+
transition: width 0.2s ease;
|
|
299
|
+
}
|
|
300
|
+
.easy-form-slider-wrapper {
|
|
301
|
+
display: flex;
|
|
302
|
+
align-items: center;
|
|
303
|
+
gap: 1rem;
|
|
304
|
+
}
|
|
305
|
+
.easy-form-slider-value {
|
|
306
|
+
min-width: 2rem;
|
|
307
|
+
font-weight: 500;
|
|
308
|
+
}
|
|
309
|
+
.easy-form-rating {
|
|
310
|
+
display: inline-flex;
|
|
311
|
+
gap: 0.25rem;
|
|
312
|
+
}
|
|
313
|
+
.easy-form-rating-stars {
|
|
314
|
+
display: flex;
|
|
315
|
+
gap: 0.25rem;
|
|
316
|
+
}
|
|
317
|
+
.easy-form-rating-star {
|
|
318
|
+
background: none;
|
|
319
|
+
border: none;
|
|
320
|
+
padding: 0;
|
|
321
|
+
cursor: pointer;
|
|
322
|
+
color: var(--easy-form-border);
|
|
323
|
+
}
|
|
324
|
+
.easy-form-rating-star svg {
|
|
325
|
+
display: block;
|
|
326
|
+
}
|
|
327
|
+
.easy-form-rating-star-filled {
|
|
328
|
+
color: #f5a623;
|
|
329
|
+
}
|
|
330
|
+
.easy-form-rating-star:hover,
|
|
331
|
+
.easy-form-rating-star:focus {
|
|
332
|
+
color: var(--easy-form-primary);
|
|
333
|
+
}
|
|
334
|
+
.easy-form-file-drop {
|
|
335
|
+
border: 2px dashed var(--easy-form-border);
|
|
336
|
+
border-radius: 8px;
|
|
337
|
+
padding: 2rem;
|
|
338
|
+
text-align: center;
|
|
339
|
+
cursor: pointer;
|
|
340
|
+
transition: border-color 0.2s, background 0.2s;
|
|
341
|
+
}
|
|
342
|
+
.easy-form-file-drop:hover,
|
|
343
|
+
.easy-form-file-drop-over {
|
|
344
|
+
border-color: var(--easy-form-primary);
|
|
345
|
+
background: rgba(0, 123, 255, 0.05);
|
|
346
|
+
}
|
|
347
|
+
.easy-form-file-drop-list {
|
|
348
|
+
margin-top: 0.75rem;
|
|
349
|
+
display: flex;
|
|
350
|
+
flex-direction: column;
|
|
351
|
+
gap: 0.25rem;
|
|
352
|
+
}
|
|
353
|
+
.easy-form-file-drop-item {
|
|
354
|
+
font-size: 0.875rem;
|
|
355
|
+
color: var(--easy-form-text);
|
|
356
|
+
padding: 0.25rem 0;
|
|
357
|
+
border-bottom: 1px solid var(--easy-form-border);
|
|
358
|
+
}
|
|
359
|
+
.easy-form-map-inputs {
|
|
360
|
+
display: flex;
|
|
361
|
+
gap: 1rem;
|
|
362
|
+
margin-bottom: 0.5rem;
|
|
363
|
+
}
|
|
364
|
+
.easy-form-map-inputs input {
|
|
365
|
+
flex: 1;
|
|
366
|
+
}
|
|
367
|
+
.easy-form-map-placeholder,
|
|
368
|
+
.easy-form-map-container {
|
|
369
|
+
min-height: 300px;
|
|
370
|
+
height: 300px;
|
|
371
|
+
border-radius: 4px;
|
|
372
|
+
position: relative;
|
|
373
|
+
overflow: hidden;
|
|
374
|
+
width: 100%;
|
|
375
|
+
display: block;
|
|
376
|
+
box-sizing: border-box;
|
|
377
|
+
}
|
|
378
|
+
.easy-form-map-container {
|
|
379
|
+
background: #f5f5f5;
|
|
380
|
+
}
|
|
381
|
+
.easy-form-map-container .leaflet-container {
|
|
382
|
+
height: 100% !important;
|
|
383
|
+
width: 100% !important;
|
|
384
|
+
position: relative !important;
|
|
385
|
+
}
|
|
386
|
+
.easy-form-map-container .leaflet-tile-container {
|
|
387
|
+
position: absolute;
|
|
388
|
+
left: 0;
|
|
389
|
+
top: 0;
|
|
390
|
+
}
|
|
237
391
|
.easy-form-required {
|
|
238
392
|
color: var(--easy-form-error);
|
|
239
393
|
}
|
|
@@ -689,6 +843,62 @@ function getBaseStyles(colors) {
|
|
|
689
843
|
.easy-form-otp-input:invalid {
|
|
690
844
|
border-color: var(--easy-form-error);
|
|
691
845
|
}
|
|
846
|
+
/* Password inner (input + toggle) */
|
|
847
|
+
.easy-form-password-inner {
|
|
848
|
+
display: flex;
|
|
849
|
+
align-items: center;
|
|
850
|
+
gap: 0.5rem;
|
|
851
|
+
}
|
|
852
|
+
.easy-form-password-inner .easy-form-password-input {
|
|
853
|
+
flex: 1;
|
|
854
|
+
min-width: 0;
|
|
855
|
+
}
|
|
856
|
+
.easy-form-password-toggle {
|
|
857
|
+
flex-shrink: 0;
|
|
858
|
+
display: inline-flex;
|
|
859
|
+
align-items: center;
|
|
860
|
+
justify-content: center;
|
|
861
|
+
padding: 0.35rem;
|
|
862
|
+
background: transparent;
|
|
863
|
+
border: 1px solid var(--easy-form-border);
|
|
864
|
+
border-radius: 4px;
|
|
865
|
+
cursor: pointer;
|
|
866
|
+
color: var(--easy-form-text);
|
|
867
|
+
}
|
|
868
|
+
.easy-form-password-toggle:hover {
|
|
869
|
+
border-color: var(--easy-form-primary);
|
|
870
|
+
color: var(--easy-form-primary);
|
|
871
|
+
}
|
|
872
|
+
.easy-form-password-toggle:focus {
|
|
873
|
+
outline: none;
|
|
874
|
+
box-shadow: 0 0 0 2px var(--easy-form-primary);
|
|
875
|
+
}
|
|
876
|
+
/* Password car\xE1cter separado */
|
|
877
|
+
.easy-form-password-separated {
|
|
878
|
+
display: flex;
|
|
879
|
+
flex-wrap: nowrap;
|
|
880
|
+
gap: 0.5rem;
|
|
881
|
+
align-items: center;
|
|
882
|
+
}
|
|
883
|
+
.easy-form-password-separated-input {
|
|
884
|
+
width: 2.5rem;
|
|
885
|
+
height: 2.5rem;
|
|
886
|
+
text-align: center;
|
|
887
|
+
font-size: 1.25rem;
|
|
888
|
+
border: 2px solid var(--easy-form-border);
|
|
889
|
+
border-radius: 4px;
|
|
890
|
+
transition: border-color 0.2s ease;
|
|
891
|
+
}
|
|
892
|
+
.easy-form-password-separated-input:focus {
|
|
893
|
+
border-color: var(--easy-form-primary);
|
|
894
|
+
outline: none;
|
|
895
|
+
}
|
|
896
|
+
.easy-form-password-separated-toggle {
|
|
897
|
+
flex-shrink: 0;
|
|
898
|
+
}
|
|
899
|
+
.easy-form-password-separated-toggle .easy-form-password-toggle {
|
|
900
|
+
padding: 0.35rem;
|
|
901
|
+
}
|
|
692
902
|
/* Loading Overlay (sobre el formulario) */
|
|
693
903
|
.easy-form-loading-overlay {
|
|
694
904
|
position: absolute;
|
|
@@ -2842,25 +3052,54 @@ var BaseInput = class {
|
|
|
2842
3052
|
const componentContainerClasses = inputClasses.filter(
|
|
2843
3053
|
(cls) => cls.startsWith("easy-form-") && cls !== "easy-form-field" && (cls.includes("-container") || cls.includes("-wrapper"))
|
|
2844
3054
|
);
|
|
3055
|
+
const labelPosition = this.field.labelPosition ?? "up";
|
|
2845
3056
|
if (componentContainerClasses.length > 0) {
|
|
2846
|
-
container.className = `easy-form-field ${componentContainerClasses.join(" ")}`;
|
|
3057
|
+
container.className = `easy-form-field easy-form-label-${labelPosition} ${componentContainerClasses.join(" ")}`;
|
|
2847
3058
|
} else {
|
|
2848
|
-
container.className =
|
|
3059
|
+
container.className = `easy-form-field easy-form-label-${labelPosition}`;
|
|
2849
3060
|
}
|
|
2850
|
-
if (this.field.label) {
|
|
2851
|
-
const
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
3061
|
+
if (labelPosition === "none" && this.field.label) {
|
|
3062
|
+
const el = input.querySelector("input, textarea, select") || input;
|
|
3063
|
+
if (el instanceof HTMLElement) {
|
|
3064
|
+
el.setAttribute("aria-label", this.field.label);
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
const createLabel = () => {
|
|
3068
|
+
if (!this.field.label || labelPosition === "none") return null;
|
|
3069
|
+
const label2 = document.createElement("label");
|
|
3070
|
+
label2.className = "easy-form-label";
|
|
3071
|
+
label2.setAttribute("for", this.getFieldId());
|
|
3072
|
+
label2.textContent = this.field.label;
|
|
2855
3073
|
if (this.field.validations?.some((v) => v.type === "required")) {
|
|
2856
3074
|
const required = document.createElement("span");
|
|
2857
3075
|
required.className = "easy-form-required";
|
|
2858
3076
|
required.textContent = " *";
|
|
2859
|
-
|
|
3077
|
+
label2.appendChild(required);
|
|
2860
3078
|
}
|
|
2861
|
-
|
|
3079
|
+
return label2;
|
|
3080
|
+
};
|
|
3081
|
+
const label = createLabel();
|
|
3082
|
+
if (labelPosition === "up") {
|
|
3083
|
+
if (label) container.appendChild(label);
|
|
3084
|
+
container.appendChild(input);
|
|
3085
|
+
} else if (labelPosition === "down") {
|
|
3086
|
+
container.appendChild(input);
|
|
3087
|
+
if (label) container.appendChild(label);
|
|
3088
|
+
} else if (labelPosition === "left") {
|
|
3089
|
+
const wrapper = document.createElement("div");
|
|
3090
|
+
wrapper.className = "easy-form-field-inner easy-form-field-inner-horizontal";
|
|
3091
|
+
if (label) wrapper.appendChild(label);
|
|
3092
|
+
wrapper.appendChild(input);
|
|
3093
|
+
container.appendChild(wrapper);
|
|
3094
|
+
} else if (labelPosition === "right") {
|
|
3095
|
+
const wrapper = document.createElement("div");
|
|
3096
|
+
wrapper.className = "easy-form-field-inner easy-form-field-inner-horizontal";
|
|
3097
|
+
wrapper.appendChild(input);
|
|
3098
|
+
if (label) wrapper.appendChild(label);
|
|
3099
|
+
container.appendChild(wrapper);
|
|
3100
|
+
} else {
|
|
3101
|
+
container.appendChild(input);
|
|
2862
3102
|
}
|
|
2863
|
-
container.appendChild(input);
|
|
2864
3103
|
if (this.field.description) {
|
|
2865
3104
|
const description = document.createElement("p");
|
|
2866
3105
|
description.className = "easy-form-description";
|
|
@@ -4196,13 +4435,508 @@ var OTPInput = class extends BaseInput {
|
|
|
4196
4435
|
}
|
|
4197
4436
|
};
|
|
4198
4437
|
|
|
4438
|
+
// src/components/inputs/password-input.ts
|
|
4439
|
+
var DEFAULT_SEPARATED_LENGTH = 6;
|
|
4440
|
+
function eyeSvg(visible) {
|
|
4441
|
+
if (visible) {
|
|
4442
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/><line x1="1" y1="1" x2="23" y2="23"/></svg>`;
|
|
4443
|
+
}
|
|
4444
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>`;
|
|
4445
|
+
}
|
|
4446
|
+
var PasswordInput = class extends BaseInput {
|
|
4447
|
+
constructor() {
|
|
4448
|
+
super(...arguments);
|
|
4449
|
+
this.inputs = [];
|
|
4450
|
+
this.visible = false;
|
|
4451
|
+
}
|
|
4452
|
+
get passwordField() {
|
|
4453
|
+
return this.field;
|
|
4454
|
+
}
|
|
4455
|
+
render() {
|
|
4456
|
+
const pf = this.passwordField;
|
|
4457
|
+
const useSeparated = !!pf.characterSeparated;
|
|
4458
|
+
const length = useSeparated ? typeof pf.characterSeparated === "number" ? pf.characterSeparated : DEFAULT_SEPARATED_LENGTH : 0;
|
|
4459
|
+
if (useSeparated) {
|
|
4460
|
+
return this.createFieldContainer(this.renderSeparated(length));
|
|
4461
|
+
}
|
|
4462
|
+
return this.createFieldContainer(this.renderSingle());
|
|
4463
|
+
}
|
|
4464
|
+
renderSingle() {
|
|
4465
|
+
const pf = this.passwordField;
|
|
4466
|
+
const showToggle = !!pf.showToggle;
|
|
4467
|
+
const input = document.createElement("input");
|
|
4468
|
+
input.type = "password";
|
|
4469
|
+
input.value = this.value ?? "";
|
|
4470
|
+
input.id = this.getFieldId();
|
|
4471
|
+
this.applyCommonProps(input);
|
|
4472
|
+
if (this.field.placeholder) input.placeholder = this.field.placeholder;
|
|
4473
|
+
input.addEventListener("input", (e) => {
|
|
4474
|
+
this.onChange(e.target.value);
|
|
4475
|
+
});
|
|
4476
|
+
input.addEventListener("blur", () => this.onBlur());
|
|
4477
|
+
if (!showToggle) {
|
|
4478
|
+
return input;
|
|
4479
|
+
}
|
|
4480
|
+
const wrapper = document.createElement("div");
|
|
4481
|
+
wrapper.className = "easy-form-password-inner";
|
|
4482
|
+
input.classList.add("easy-form-password-input");
|
|
4483
|
+
wrapper.appendChild(input);
|
|
4484
|
+
const btn = document.createElement("button");
|
|
4485
|
+
btn.type = "button";
|
|
4486
|
+
btn.className = "easy-form-password-toggle";
|
|
4487
|
+
btn.setAttribute("aria-label", "Mostrar contrase\xF1a");
|
|
4488
|
+
btn.innerHTML = eyeSvg(false);
|
|
4489
|
+
btn.addEventListener("click", () => {
|
|
4490
|
+
this.visible = !this.visible;
|
|
4491
|
+
input.type = this.visible ? "text" : "password";
|
|
4492
|
+
btn.setAttribute("aria-label", this.visible ? "Ocultar contrase\xF1a" : "Mostrar contrase\xF1a");
|
|
4493
|
+
btn.innerHTML = eyeSvg(this.visible);
|
|
4494
|
+
});
|
|
4495
|
+
wrapper.appendChild(btn);
|
|
4496
|
+
return wrapper;
|
|
4497
|
+
}
|
|
4498
|
+
renderSeparated(length) {
|
|
4499
|
+
const pf = this.passwordField;
|
|
4500
|
+
const showToggle = !!pf.showToggle;
|
|
4501
|
+
const value = this.value ?? "";
|
|
4502
|
+
const valueString = String(value).slice(0, length);
|
|
4503
|
+
const container = document.createElement("div");
|
|
4504
|
+
container.className = "easy-form-password-separated";
|
|
4505
|
+
for (let i = 0; i < length; i++) {
|
|
4506
|
+
const input = document.createElement("input");
|
|
4507
|
+
input.type = "password";
|
|
4508
|
+
input.maxLength = 1;
|
|
4509
|
+
input.className = "easy-form-password-separated-input";
|
|
4510
|
+
input.setAttribute("aria-label", `Car\xE1cter ${i + 1} de ${length}`);
|
|
4511
|
+
if (valueString[i]) input.value = valueString[i];
|
|
4512
|
+
this.applyCommonProps(input);
|
|
4513
|
+
input.id = i === 0 ? this.getFieldId() : `${this.getFieldId()}-${i}`;
|
|
4514
|
+
if (i > 0) input.removeAttribute("name");
|
|
4515
|
+
const currentIndex = i;
|
|
4516
|
+
input.addEventListener("input", (e) => {
|
|
4517
|
+
const target = e.target;
|
|
4518
|
+
const val = target.value;
|
|
4519
|
+
if (val && currentIndex < length - 1) {
|
|
4520
|
+
this.inputs[currentIndex + 1].focus();
|
|
4521
|
+
}
|
|
4522
|
+
this.updateSeparatedValue();
|
|
4523
|
+
});
|
|
4524
|
+
input.addEventListener("keydown", (e) => {
|
|
4525
|
+
if (e.key === "Backspace" && !e.target.value && currentIndex > 0) {
|
|
4526
|
+
this.inputs[currentIndex - 1].focus();
|
|
4527
|
+
this.inputs[currentIndex - 1].value = "";
|
|
4528
|
+
this.updateSeparatedValue();
|
|
4529
|
+
}
|
|
4530
|
+
if (e.key === "ArrowLeft" && currentIndex > 0) {
|
|
4531
|
+
e.preventDefault();
|
|
4532
|
+
this.inputs[currentIndex - 1].focus();
|
|
4533
|
+
}
|
|
4534
|
+
if (e.key === "ArrowRight" && currentIndex < length - 1) {
|
|
4535
|
+
e.preventDefault();
|
|
4536
|
+
this.inputs[currentIndex + 1].focus();
|
|
4537
|
+
}
|
|
4538
|
+
});
|
|
4539
|
+
input.addEventListener("paste", (e) => {
|
|
4540
|
+
e.preventDefault();
|
|
4541
|
+
const text = (e.clipboardData || window.clipboardData)?.getData("text") || "";
|
|
4542
|
+
const chars = text.slice(0, length - currentIndex).split("");
|
|
4543
|
+
for (let j = 0; j < chars.length && currentIndex + j < length; j++) {
|
|
4544
|
+
this.inputs[currentIndex + j].value = chars[j];
|
|
4545
|
+
}
|
|
4546
|
+
const nextIdx = Math.min(currentIndex + chars.length, length - 1);
|
|
4547
|
+
this.inputs[nextIdx].focus();
|
|
4548
|
+
this.updateSeparatedValue();
|
|
4549
|
+
});
|
|
4550
|
+
input.addEventListener("focus", (e) => e.target.select());
|
|
4551
|
+
input.addEventListener("blur", () => this.onBlur());
|
|
4552
|
+
this.inputs.push(input);
|
|
4553
|
+
container.appendChild(input);
|
|
4554
|
+
}
|
|
4555
|
+
if (showToggle) {
|
|
4556
|
+
const toggleWrap = document.createElement("div");
|
|
4557
|
+
toggleWrap.className = "easy-form-password-separated-toggle";
|
|
4558
|
+
const btn = document.createElement("button");
|
|
4559
|
+
btn.type = "button";
|
|
4560
|
+
btn.className = "easy-form-password-toggle";
|
|
4561
|
+
btn.setAttribute("aria-label", "Mostrar contrase\xF1a");
|
|
4562
|
+
btn.innerHTML = eyeSvg(false);
|
|
4563
|
+
btn.addEventListener("click", () => {
|
|
4564
|
+
this.visible = !this.visible;
|
|
4565
|
+
const type = this.visible ? "text" : "password";
|
|
4566
|
+
this.inputs.forEach((inp) => {
|
|
4567
|
+
inp.type = type;
|
|
4568
|
+
});
|
|
4569
|
+
btn.setAttribute("aria-label", this.visible ? "Ocultar contrase\xF1a" : "Mostrar contrase\xF1a");
|
|
4570
|
+
btn.innerHTML = eyeSvg(this.visible);
|
|
4571
|
+
});
|
|
4572
|
+
toggleWrap.appendChild(btn);
|
|
4573
|
+
container.appendChild(toggleWrap);
|
|
4574
|
+
}
|
|
4575
|
+
return container;
|
|
4576
|
+
}
|
|
4577
|
+
updateSeparatedValue() {
|
|
4578
|
+
const value = this.inputs.map((inp) => inp.value).join("");
|
|
4579
|
+
this.onChange(value || null);
|
|
4580
|
+
}
|
|
4581
|
+
};
|
|
4582
|
+
|
|
4583
|
+
// src/components/inputs/file-drop-input.ts
|
|
4584
|
+
var FileDropInput = class extends BaseInput {
|
|
4585
|
+
render() {
|
|
4586
|
+
const fileDropField = this.field;
|
|
4587
|
+
const accept = fileDropField.accept ?? "";
|
|
4588
|
+
const multiple = fileDropField.multiple ?? false;
|
|
4589
|
+
const dropZone = document.createElement("div");
|
|
4590
|
+
dropZone.className = "easy-form-file-drop";
|
|
4591
|
+
dropZone.setAttribute("tabindex", "0");
|
|
4592
|
+
const hiddenInput = document.createElement("input");
|
|
4593
|
+
hiddenInput.type = "file";
|
|
4594
|
+
hiddenInput.className = "easy-form-file-drop-input";
|
|
4595
|
+
hiddenInput.style.display = "none";
|
|
4596
|
+
if (accept) hiddenInput.accept = accept;
|
|
4597
|
+
if (multiple) hiddenInput.multiple = true;
|
|
4598
|
+
const label = document.createElement("span");
|
|
4599
|
+
label.className = "easy-form-file-drop-label";
|
|
4600
|
+
label.textContent = "Arrastra archivos aqu\xED o haz clic para seleccionar";
|
|
4601
|
+
dropZone.appendChild(label);
|
|
4602
|
+
dropZone.appendChild(hiddenInput);
|
|
4603
|
+
const fileList = document.createElement("div");
|
|
4604
|
+
fileList.className = "easy-form-file-drop-list";
|
|
4605
|
+
const renderFileList = (files) => {
|
|
4606
|
+
const names = !files ? [] : Array.isArray(files) ? files.map((f) => f.name) : [files.name];
|
|
4607
|
+
fileList.innerHTML = "";
|
|
4608
|
+
if (names.length === 0) {
|
|
4609
|
+
fileList.style.display = "none";
|
|
4610
|
+
return;
|
|
4611
|
+
}
|
|
4612
|
+
fileList.style.display = "block";
|
|
4613
|
+
names.forEach((name) => {
|
|
4614
|
+
const item = document.createElement("div");
|
|
4615
|
+
item.className = "easy-form-file-drop-item";
|
|
4616
|
+
item.textContent = name;
|
|
4617
|
+
fileList.appendChild(item);
|
|
4618
|
+
});
|
|
4619
|
+
};
|
|
4620
|
+
renderFileList(this.value);
|
|
4621
|
+
const handleFiles = (files) => {
|
|
4622
|
+
if (!files || files.length === 0) return;
|
|
4623
|
+
const arr = multiple ? Array.from(files) : files[0];
|
|
4624
|
+
this.onChange(arr);
|
|
4625
|
+
this.onBlur();
|
|
4626
|
+
renderFileList(arr);
|
|
4627
|
+
};
|
|
4628
|
+
dropZone.addEventListener("click", () => {
|
|
4629
|
+
hiddenInput.click();
|
|
4630
|
+
});
|
|
4631
|
+
hiddenInput.addEventListener("change", (e) => {
|
|
4632
|
+
const target = e.target;
|
|
4633
|
+
handleFiles(target.files);
|
|
4634
|
+
target.value = "";
|
|
4635
|
+
});
|
|
4636
|
+
dropZone.addEventListener("dragover", (e) => {
|
|
4637
|
+
e.preventDefault();
|
|
4638
|
+
e.stopPropagation();
|
|
4639
|
+
dropZone.classList.add("easy-form-file-drop-over");
|
|
4640
|
+
});
|
|
4641
|
+
dropZone.addEventListener("dragleave", (e) => {
|
|
4642
|
+
e.preventDefault();
|
|
4643
|
+
e.stopPropagation();
|
|
4644
|
+
dropZone.classList.remove("easy-form-file-drop-over");
|
|
4645
|
+
});
|
|
4646
|
+
dropZone.addEventListener("drop", (e) => {
|
|
4647
|
+
e.preventDefault();
|
|
4648
|
+
e.stopPropagation();
|
|
4649
|
+
dropZone.classList.remove("easy-form-file-drop-over");
|
|
4650
|
+
handleFiles(e.dataTransfer?.files ?? null);
|
|
4651
|
+
});
|
|
4652
|
+
dropZone.appendChild(fileList);
|
|
4653
|
+
return this.createFieldContainer(dropZone);
|
|
4654
|
+
}
|
|
4655
|
+
};
|
|
4656
|
+
|
|
4657
|
+
// src/components/inputs/map-input.ts
|
|
4658
|
+
var LEAFLET_CSS = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.css";
|
|
4659
|
+
var MapInput = class extends BaseInput {
|
|
4660
|
+
constructor() {
|
|
4661
|
+
super(...arguments);
|
|
4662
|
+
this.mapInstance = null;
|
|
4663
|
+
this.markerInstance = null;
|
|
4664
|
+
}
|
|
4665
|
+
render() {
|
|
4666
|
+
const mapField = this.field;
|
|
4667
|
+
const center = mapField.center ?? { lat: 0, lng: 0 };
|
|
4668
|
+
const zoom = mapField.zoom ?? 13;
|
|
4669
|
+
const value = this.value;
|
|
4670
|
+
const lat = typeof value === "object" && value?.lat != null ? value.lat : center.lat;
|
|
4671
|
+
const lng = typeof value === "object" && value?.lng != null ? value.lng : center.lng;
|
|
4672
|
+
const container = document.createElement("div");
|
|
4673
|
+
container.className = "easy-form-map";
|
|
4674
|
+
const inputsWrapper = document.createElement("div");
|
|
4675
|
+
inputsWrapper.className = "easy-form-map-inputs";
|
|
4676
|
+
const latInput = document.createElement("input");
|
|
4677
|
+
latInput.type = "number";
|
|
4678
|
+
latInput.step = "any";
|
|
4679
|
+
latInput.placeholder = "Latitud";
|
|
4680
|
+
latInput.value = String(lat);
|
|
4681
|
+
latInput.className = "easy-form-map-lat";
|
|
4682
|
+
const lngInput = document.createElement("input");
|
|
4683
|
+
lngInput.type = "number";
|
|
4684
|
+
lngInput.step = "any";
|
|
4685
|
+
lngInput.placeholder = "Longitud";
|
|
4686
|
+
lngInput.value = String(lng);
|
|
4687
|
+
lngInput.className = "easy-form-map-lng";
|
|
4688
|
+
const updateFromInputs = () => {
|
|
4689
|
+
const latVal = parseFloat(latInput.value);
|
|
4690
|
+
const lngVal = parseFloat(lngInput.value);
|
|
4691
|
+
if (!isNaN(latVal) && !isNaN(lngVal)) {
|
|
4692
|
+
this.onChange({ lat: latVal, lng: lngVal });
|
|
4693
|
+
this.syncMarkerPosition(latVal, lngVal);
|
|
4694
|
+
}
|
|
4695
|
+
this.onBlur();
|
|
4696
|
+
};
|
|
4697
|
+
latInput.addEventListener("change", updateFromInputs);
|
|
4698
|
+
lngInput.addEventListener("change", updateFromInputs);
|
|
4699
|
+
inputsWrapper.appendChild(latInput);
|
|
4700
|
+
inputsWrapper.appendChild(lngInput);
|
|
4701
|
+
container.appendChild(inputsWrapper);
|
|
4702
|
+
const mapContainer = document.createElement("div");
|
|
4703
|
+
mapContainer.className = "easy-form-map-container";
|
|
4704
|
+
container.appendChild(mapContainer);
|
|
4705
|
+
const linkId = "easy-form-leaflet-css";
|
|
4706
|
+
let cssLoaded = false;
|
|
4707
|
+
const link = document.createElement("link");
|
|
4708
|
+
link.id = linkId;
|
|
4709
|
+
link.rel = "stylesheet";
|
|
4710
|
+
link.href = LEAFLET_CSS;
|
|
4711
|
+
link.onload = () => {
|
|
4712
|
+
cssLoaded = true;
|
|
4713
|
+
};
|
|
4714
|
+
container.insertBefore(link, container.firstChild);
|
|
4715
|
+
const initMap = async () => {
|
|
4716
|
+
if (typeof window === "undefined") return;
|
|
4717
|
+
mapContainer.style.height = "300px";
|
|
4718
|
+
mapContainer.style.width = "100%";
|
|
4719
|
+
mapContainer.style.position = "relative";
|
|
4720
|
+
mapContainer.style.overflow = "hidden";
|
|
4721
|
+
mapContainer.style.display = "block";
|
|
4722
|
+
await new Promise((resolve) => {
|
|
4723
|
+
const checkReady = () => {
|
|
4724
|
+
if (cssLoaded && mapContainer.offsetWidth > 0 && mapContainer.offsetHeight > 0) {
|
|
4725
|
+
resolve(void 0);
|
|
4726
|
+
} else {
|
|
4727
|
+
requestAnimationFrame(checkReady);
|
|
4728
|
+
}
|
|
4729
|
+
};
|
|
4730
|
+
checkReady();
|
|
4731
|
+
});
|
|
4732
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
4733
|
+
let L;
|
|
4734
|
+
try {
|
|
4735
|
+
L = await import("leaflet");
|
|
4736
|
+
} catch {
|
|
4737
|
+
mapContainer.innerHTML = `
|
|
4738
|
+
<div style="padding:2rem;text-align:center;color:#666;font-size:0.875rem;background:#f9f9f9;border-radius:4px;">
|
|
4739
|
+
<strong>Campo map requiere Leaflet</strong><br><br>
|
|
4740
|
+
Instala e importa Leaflet para usar el mapa interactivo:<br>
|
|
4741
|
+
<code>npm install leaflet</code><br><br>
|
|
4742
|
+
En tu app: <code>import 'leaflet'</code> y <code>import 'leaflet/dist/leaflet.css'</code><br>
|
|
4743
|
+
<a href="https://easyforms.dev/docs/tipos-campos#map" target="_blank" rel="noopener" style="color:var(--easy-form-primary,#007bff);">Ver documentaci\xF3n</a>
|
|
4744
|
+
</div>
|
|
4745
|
+
`;
|
|
4746
|
+
return;
|
|
4747
|
+
}
|
|
4748
|
+
delete L.Icon.Default.prototype._getIconUrl;
|
|
4749
|
+
L.Icon.Default.mergeOptions({
|
|
4750
|
+
iconRetinaUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png",
|
|
4751
|
+
iconUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png",
|
|
4752
|
+
shadowUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png"
|
|
4753
|
+
});
|
|
4754
|
+
this.mapInstance = L.map(mapContainer, {
|
|
4755
|
+
preferCanvas: false,
|
|
4756
|
+
zoomControl: true
|
|
4757
|
+
}).setView([lat, lng], zoom);
|
|
4758
|
+
const tileLayer = L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
|
4759
|
+
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
|
4760
|
+
maxZoom: 19
|
|
4761
|
+
}).addTo(this.mapInstance);
|
|
4762
|
+
this.mapInstance.invalidateSize();
|
|
4763
|
+
tileLayer.on("load", () => {
|
|
4764
|
+
if (this.mapInstance) {
|
|
4765
|
+
this.mapInstance.invalidateSize();
|
|
4766
|
+
}
|
|
4767
|
+
});
|
|
4768
|
+
const invalidateSize = () => {
|
|
4769
|
+
if (this.mapInstance) {
|
|
4770
|
+
this.mapInstance.invalidateSize();
|
|
4771
|
+
}
|
|
4772
|
+
};
|
|
4773
|
+
setTimeout(invalidateSize, 100);
|
|
4774
|
+
setTimeout(invalidateSize, 300);
|
|
4775
|
+
setTimeout(invalidateSize, 500);
|
|
4776
|
+
const observer = new IntersectionObserver((entries) => {
|
|
4777
|
+
entries.forEach((entry) => {
|
|
4778
|
+
if (entry.isIntersecting && this.mapInstance) {
|
|
4779
|
+
setTimeout(() => {
|
|
4780
|
+
this.mapInstance?.invalidateSize();
|
|
4781
|
+
}, 100);
|
|
4782
|
+
}
|
|
4783
|
+
});
|
|
4784
|
+
});
|
|
4785
|
+
observer.observe(mapContainer);
|
|
4786
|
+
this.markerInstance = L.marker([lat, lng], { draggable: true }).addTo(this.mapInstance).on("dragend", () => {
|
|
4787
|
+
const pos = this.markerInstance.getLatLng();
|
|
4788
|
+
latInput.value = String(Number(pos.lat.toFixed(6)));
|
|
4789
|
+
lngInput.value = String(Number(pos.lng.toFixed(6)));
|
|
4790
|
+
this.onChange({ lat: pos.lat, lng: pos.lng });
|
|
4791
|
+
this.onBlur();
|
|
4792
|
+
});
|
|
4793
|
+
this.mapInstance.on("click", (e) => {
|
|
4794
|
+
const { lat: newLat, lng: newLng } = e.latlng;
|
|
4795
|
+
this.markerInstance.setLatLng([newLat, newLng]);
|
|
4796
|
+
latInput.value = String(Number(newLat.toFixed(6)));
|
|
4797
|
+
lngInput.value = String(Number(newLng.toFixed(6)));
|
|
4798
|
+
this.onChange({ lat: newLat, lng: newLng });
|
|
4799
|
+
this.onBlur();
|
|
4800
|
+
});
|
|
4801
|
+
};
|
|
4802
|
+
initMap();
|
|
4803
|
+
return this.createFieldContainer(container);
|
|
4804
|
+
}
|
|
4805
|
+
syncMarkerPosition(newLat, newLng) {
|
|
4806
|
+
if (this.mapInstance && this.markerInstance) {
|
|
4807
|
+
this.markerInstance.setLatLng([newLat, newLng]);
|
|
4808
|
+
this.mapInstance.setView([newLat, newLng]);
|
|
4809
|
+
setTimeout(() => {
|
|
4810
|
+
this.mapInstance?.invalidateSize();
|
|
4811
|
+
}, 50);
|
|
4812
|
+
}
|
|
4813
|
+
}
|
|
4814
|
+
};
|
|
4815
|
+
|
|
4816
|
+
// src/components/inputs/rating-input.ts
|
|
4817
|
+
var STAR_SVG = (filled) => `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="${filled ? "currentColor" : "none"}" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-hidden="true"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>`;
|
|
4818
|
+
var RatingInput = class extends BaseInput {
|
|
4819
|
+
render() {
|
|
4820
|
+
const ratingField = this.field;
|
|
4821
|
+
const max = ratingField.max ?? 5;
|
|
4822
|
+
const half = ratingField.half ?? false;
|
|
4823
|
+
const currentValue = this.value != null ? Number(this.value) : 0;
|
|
4824
|
+
const container = document.createElement("div");
|
|
4825
|
+
container.className = "easy-form-rating";
|
|
4826
|
+
container.setAttribute("role", "slider");
|
|
4827
|
+
container.setAttribute("aria-valuemin", "0");
|
|
4828
|
+
container.setAttribute("aria-valuemax", String(max));
|
|
4829
|
+
container.setAttribute("aria-valuenow", String(currentValue));
|
|
4830
|
+
container.setAttribute("aria-label", this.field.label || "Rating");
|
|
4831
|
+
container.setAttribute("tabindex", "0");
|
|
4832
|
+
const starsContainer = document.createElement("div");
|
|
4833
|
+
starsContainer.className = "easy-form-rating-stars";
|
|
4834
|
+
for (let i = 1; i <= max; i++) {
|
|
4835
|
+
const starWrapper = document.createElement("button");
|
|
4836
|
+
starWrapper.type = "button";
|
|
4837
|
+
const filled = currentValue >= i || half && currentValue >= i - 0.5;
|
|
4838
|
+
starWrapper.className = filled ? "easy-form-rating-star easy-form-rating-star-filled" : "easy-form-rating-star";
|
|
4839
|
+
starWrapper.innerHTML = STAR_SVG(filled);
|
|
4840
|
+
starWrapper.setAttribute("aria-label", `${i} de ${max}`);
|
|
4841
|
+
starWrapper.addEventListener("click", (e) => {
|
|
4842
|
+
e.preventDefault();
|
|
4843
|
+
const rect = e.target.closest("button")?.getBoundingClientRect();
|
|
4844
|
+
let val = i;
|
|
4845
|
+
if (half && rect) {
|
|
4846
|
+
const mid = rect.left + rect.width / 2;
|
|
4847
|
+
val = e.clientX < mid ? i - 0.5 : i;
|
|
4848
|
+
}
|
|
4849
|
+
this.onChange(val);
|
|
4850
|
+
this.onBlur();
|
|
4851
|
+
this.rerenderStars(container, max, val, half);
|
|
4852
|
+
container.setAttribute("aria-valuenow", String(val));
|
|
4853
|
+
});
|
|
4854
|
+
starsContainer.appendChild(starWrapper);
|
|
4855
|
+
}
|
|
4856
|
+
container.appendChild(starsContainer);
|
|
4857
|
+
container.addEventListener("keydown", (e) => {
|
|
4858
|
+
const cur = Number(container.getAttribute("aria-valuenow")) || 0;
|
|
4859
|
+
let newVal = cur;
|
|
4860
|
+
if (e.key === "ArrowRight" || e.key === "ArrowUp") {
|
|
4861
|
+
e.preventDefault();
|
|
4862
|
+
newVal = Math.min(max, half ? cur + 0.5 : cur + 1);
|
|
4863
|
+
this.onChange(newVal);
|
|
4864
|
+
} else if (e.key === "ArrowLeft" || e.key === "ArrowDown") {
|
|
4865
|
+
e.preventDefault();
|
|
4866
|
+
newVal = Math.max(0, half ? cur - 0.5 : cur - 1);
|
|
4867
|
+
this.onChange(newVal);
|
|
4868
|
+
}
|
|
4869
|
+
if (newVal !== cur) {
|
|
4870
|
+
this.rerenderStars(container, max, newVal, half);
|
|
4871
|
+
container.setAttribute("aria-valuenow", String(newVal));
|
|
4872
|
+
}
|
|
4873
|
+
});
|
|
4874
|
+
return this.createFieldContainer(container);
|
|
4875
|
+
}
|
|
4876
|
+
rerenderStars(container, max, value, half) {
|
|
4877
|
+
const stars = container.querySelectorAll(".easy-form-rating-star");
|
|
4878
|
+
stars.forEach((star, i) => {
|
|
4879
|
+
const idx = i + 1;
|
|
4880
|
+
const filled = value >= idx || half && value >= idx - 0.5;
|
|
4881
|
+
star.className = filled ? "easy-form-rating-star easy-form-rating-star-filled" : "easy-form-rating-star";
|
|
4882
|
+
star.innerHTML = STAR_SVG(filled);
|
|
4883
|
+
});
|
|
4884
|
+
}
|
|
4885
|
+
};
|
|
4886
|
+
|
|
4887
|
+
// src/components/inputs/slider-input.ts
|
|
4888
|
+
var SliderInput = class extends BaseInput {
|
|
4889
|
+
render() {
|
|
4890
|
+
const sliderField = this.field;
|
|
4891
|
+
const min = sliderField.min ?? 0;
|
|
4892
|
+
const max = sliderField.max ?? 100;
|
|
4893
|
+
const step = sliderField.step ?? 1;
|
|
4894
|
+
const showValue = sliderField.showValue ?? false;
|
|
4895
|
+
const input = document.createElement("input");
|
|
4896
|
+
input.type = "range";
|
|
4897
|
+
input.min = String(min);
|
|
4898
|
+
input.max = String(max);
|
|
4899
|
+
input.step = String(step);
|
|
4900
|
+
input.value = this.value != null ? String(this.value) : String(min);
|
|
4901
|
+
input.setAttribute("aria-valuemin", String(min));
|
|
4902
|
+
input.setAttribute("aria-valuemax", String(max));
|
|
4903
|
+
input.setAttribute("aria-valuenow", input.value);
|
|
4904
|
+
this.applyCommonProps(input);
|
|
4905
|
+
input.addEventListener("input", (e) => {
|
|
4906
|
+
const target = e.target;
|
|
4907
|
+
const val = step >= 1 ? parseInt(target.value, 10) : parseFloat(target.value);
|
|
4908
|
+
this.onChange(isNaN(val) ? min : val);
|
|
4909
|
+
if (valueSpan) {
|
|
4910
|
+
valueSpan.textContent = target.value;
|
|
4911
|
+
}
|
|
4912
|
+
input.setAttribute("aria-valuenow", target.value);
|
|
4913
|
+
});
|
|
4914
|
+
input.addEventListener("change", () => {
|
|
4915
|
+
this.onBlur();
|
|
4916
|
+
});
|
|
4917
|
+
let valueSpan = null;
|
|
4918
|
+
if (showValue) {
|
|
4919
|
+
valueSpan = document.createElement("span");
|
|
4920
|
+
valueSpan.className = "easy-form-slider-value";
|
|
4921
|
+
valueSpan.textContent = input.value;
|
|
4922
|
+
const wrapper = document.createElement("div");
|
|
4923
|
+
wrapper.className = "easy-form-slider-wrapper";
|
|
4924
|
+
wrapper.appendChild(input);
|
|
4925
|
+
wrapper.appendChild(valueSpan);
|
|
4926
|
+
return this.createFieldContainer(wrapper);
|
|
4927
|
+
}
|
|
4928
|
+
return this.createFieldContainer(input);
|
|
4929
|
+
}
|
|
4930
|
+
};
|
|
4931
|
+
|
|
4199
4932
|
// src/components/inputs/index.ts
|
|
4200
4933
|
function createInput(field, value, error, onChange, onBlur) {
|
|
4201
4934
|
switch (field.type) {
|
|
4202
4935
|
case "text":
|
|
4203
4936
|
case "email":
|
|
4204
|
-
case "password":
|
|
4205
4937
|
return new TextInput(field, value, error, onChange, onBlur).render();
|
|
4938
|
+
case "password":
|
|
4939
|
+
return new PasswordInput(field, value, error, onChange, onBlur).render();
|
|
4206
4940
|
case "number":
|
|
4207
4941
|
return new NumberInput(field, value, error, onChange, onBlur).render();
|
|
4208
4942
|
case "textarea":
|
|
@@ -4219,6 +4953,14 @@ function createInput(field, value, error, onChange, onBlur) {
|
|
|
4219
4953
|
return new DateInput(field, value, error, onChange, onBlur).render();
|
|
4220
4954
|
case "file":
|
|
4221
4955
|
return new FileInput(field, value, error, onChange, onBlur).render();
|
|
4956
|
+
case "file-drop":
|
|
4957
|
+
return new FileDropInput(field, value, error, onChange, onBlur).render();
|
|
4958
|
+
case "map":
|
|
4959
|
+
return new MapInput(field, value, error, onChange, onBlur).render();
|
|
4960
|
+
case "rating":
|
|
4961
|
+
return new RatingInput(field, value, error, onChange, onBlur).render();
|
|
4962
|
+
case "slider":
|
|
4963
|
+
return new SliderInput(field, value, error, onChange, onBlur).render();
|
|
4222
4964
|
case "quantity":
|
|
4223
4965
|
return new QuantityInput(field, value, error, onChange, onBlur).render();
|
|
4224
4966
|
case "accordion-select":
|
|
@@ -4791,7 +5533,10 @@ var EasyForm = class extends BrowserHTMLElement {
|
|
|
4791
5533
|
"max-attempts",
|
|
4792
5534
|
"block-duration-minutes",
|
|
4793
5535
|
"attempts-storage-key",
|
|
4794
|
-
"submit-button"
|
|
5536
|
+
"submit-button",
|
|
5537
|
+
"label-position",
|
|
5538
|
+
"show-completed-indicator",
|
|
5539
|
+
"form-direction"
|
|
4795
5540
|
];
|
|
4796
5541
|
}
|
|
4797
5542
|
/**
|
|
@@ -5116,6 +5861,12 @@ var EasyForm = class extends BrowserHTMLElement {
|
|
|
5116
5861
|
if (this.disabled || this.loading) {
|
|
5117
5862
|
newFormElement.classList.add("easy-form-disabled");
|
|
5118
5863
|
}
|
|
5864
|
+
const directionAttr = this.getAttribute("form-direction");
|
|
5865
|
+
const direction = schema.direction ?? (directionAttr === "vertical" || directionAttr === "horizontal" ? directionAttr : "vertical");
|
|
5866
|
+
newFormElement.classList.add(`easy-form-direction-${direction}`);
|
|
5867
|
+
const showCompletedAttr = this.getAttribute("show-completed-indicator");
|
|
5868
|
+
const completedIndicator = schema.completedIndicator ?? (showCompletedAttr !== null && showCompletedAttr !== "false");
|
|
5869
|
+
const completedPosition = typeof completedIndicator === "object" && completedIndicator?.position ? completedIndicator.position : "top";
|
|
5119
5870
|
if (finalWizardState) {
|
|
5120
5871
|
this.renderWizard(newFormElement, schema);
|
|
5121
5872
|
} else {
|
|
@@ -5137,6 +5888,33 @@ var EasyForm = class extends BrowserHTMLElement {
|
|
|
5137
5888
|
newFormElement.appendChild(submitWrapper);
|
|
5138
5889
|
}
|
|
5139
5890
|
}
|
|
5891
|
+
let formWrapper = null;
|
|
5892
|
+
if (completedIndicator) {
|
|
5893
|
+
const { completed, total } = this.getCompletedRequiredProgress(schema);
|
|
5894
|
+
const progressBar = document.createElement("div");
|
|
5895
|
+
progressBar.className = "easy-form-completed-indicator";
|
|
5896
|
+
progressBar.setAttribute("role", "progressbar");
|
|
5897
|
+
progressBar.setAttribute("aria-valuenow", String(completed));
|
|
5898
|
+
progressBar.setAttribute("aria-valuemin", "0");
|
|
5899
|
+
progressBar.setAttribute("aria-valuemax", String(total));
|
|
5900
|
+
progressBar.setAttribute("aria-label", `Campos obligatorios completados: ${completed} de ${total}`);
|
|
5901
|
+
const track = document.createElement("div");
|
|
5902
|
+
track.className = "easy-form-completed-track";
|
|
5903
|
+
const fill = document.createElement("div");
|
|
5904
|
+
fill.className = "easy-form-completed-fill";
|
|
5905
|
+
fill.style.width = total > 0 ? `${completed / total * 100}%` : "0%";
|
|
5906
|
+
track.appendChild(fill);
|
|
5907
|
+
progressBar.appendChild(track);
|
|
5908
|
+
formWrapper = document.createElement("div");
|
|
5909
|
+
formWrapper.className = "easy-form-wrapper";
|
|
5910
|
+
if (completedPosition === "top") {
|
|
5911
|
+
formWrapper.appendChild(progressBar);
|
|
5912
|
+
formWrapper.appendChild(newFormElement);
|
|
5913
|
+
} else {
|
|
5914
|
+
formWrapper.appendChild(newFormElement);
|
|
5915
|
+
formWrapper.appendChild(progressBar);
|
|
5916
|
+
}
|
|
5917
|
+
}
|
|
5140
5918
|
const oldForm = this.shadow.querySelector("form");
|
|
5141
5919
|
if (oldForm && oldForm.parentNode === this.shadow && oldForm !== newFormElement) {
|
|
5142
5920
|
try {
|
|
@@ -5145,7 +5923,9 @@ var EasyForm = class extends BrowserHTMLElement {
|
|
|
5145
5923
|
console.warn("Error al eliminar formulario anterior:", e);
|
|
5146
5924
|
}
|
|
5147
5925
|
}
|
|
5148
|
-
this.shadow.
|
|
5926
|
+
const oldWrapper = this.shadow.querySelector(".easy-form-wrapper");
|
|
5927
|
+
if (oldWrapper) oldWrapper.remove();
|
|
5928
|
+
this.shadow.appendChild(formWrapper || newFormElement);
|
|
5149
5929
|
if (this.loading) {
|
|
5150
5930
|
this.updateLoadingOverlay(newFormElement);
|
|
5151
5931
|
}
|
|
@@ -5428,9 +6208,13 @@ var EasyForm = class extends BrowserHTMLElement {
|
|
|
5428
6208
|
const errors = this.stateManager.getErrors(field.name);
|
|
5429
6209
|
const error = errors.length > 0 ? errors[0] : void 0;
|
|
5430
6210
|
const isFormDisabled = this.disabled || this.loading;
|
|
6211
|
+
const labelPositionAttr = this.getAttribute("label-position");
|
|
6212
|
+
const validLabelPositions = ["up", "down", "left", "right", "none"];
|
|
6213
|
+
const effectiveLabelPosition = field.labelPosition ?? (labelPositionAttr && validLabelPositions.includes(labelPositionAttr) ? labelPositionAttr : "up");
|
|
5431
6214
|
const fieldWithDependencies = {
|
|
5432
6215
|
...field,
|
|
5433
|
-
disabled: isFormDisabled || !isEnabled || field.disabled
|
|
6216
|
+
disabled: isFormDisabled || !isEnabled || field.disabled,
|
|
6217
|
+
labelPosition: effectiveLabelPosition
|
|
5434
6218
|
};
|
|
5435
6219
|
const customComponent = getCustomComponent(field.type);
|
|
5436
6220
|
if (customComponent) {
|
|
@@ -5469,7 +6253,9 @@ var EasyForm = class extends BrowserHTMLElement {
|
|
|
5469
6253
|
*/
|
|
5470
6254
|
renderGroup(field) {
|
|
5471
6255
|
const groupContainer = document.createElement("div");
|
|
5472
|
-
|
|
6256
|
+
const groupField = field;
|
|
6257
|
+
const dir = groupField.direction ?? "vertical";
|
|
6258
|
+
groupContainer.className = `easy-form-group easy-form-direction-${dir}`;
|
|
5473
6259
|
if (field.label) {
|
|
5474
6260
|
const label = document.createElement("h3");
|
|
5475
6261
|
label.className = "easy-form-group-label";
|
|
@@ -5600,6 +6386,49 @@ var EasyForm = class extends BrowserHTMLElement {
|
|
|
5600
6386
|
onBlur: () => this.handleFieldBlur(field.name)
|
|
5601
6387
|
});
|
|
5602
6388
|
}
|
|
6389
|
+
/**
|
|
6390
|
+
* Obtiene el progreso de campos obligatorios completados
|
|
6391
|
+
*/
|
|
6392
|
+
getCompletedRequiredProgress(schema) {
|
|
6393
|
+
const extractFields = (fields2) => {
|
|
6394
|
+
const result = [];
|
|
6395
|
+
for (const f of fields2) {
|
|
6396
|
+
if (f.type === "array" && "itemSchema" in f && f.itemSchema?.fields) {
|
|
6397
|
+
const items = this.stateManager.getValue(f.name);
|
|
6398
|
+
const count = Array.isArray(items) ? items.length : 0;
|
|
6399
|
+
for (let i = 0; i < count; i++) {
|
|
6400
|
+
for (const sf of f.itemSchema.fields) {
|
|
6401
|
+
result.push({ ...sf, name: `${f.name}.${i}.${sf.name}` });
|
|
6402
|
+
}
|
|
6403
|
+
}
|
|
6404
|
+
} else if ((f.type === "group" || f.type === "row") && "fields" in f && f.fields) {
|
|
6405
|
+
result.push(...extractFields(f.fields));
|
|
6406
|
+
} else {
|
|
6407
|
+
result.push(f);
|
|
6408
|
+
}
|
|
6409
|
+
}
|
|
6410
|
+
return result;
|
|
6411
|
+
};
|
|
6412
|
+
const fields = schema.steps ? this.stateManager.getCurrentStepFields() || [] : schema.fields || [];
|
|
6413
|
+
const allFields = extractFields(fields);
|
|
6414
|
+
const requiredFields = allFields.filter(
|
|
6415
|
+
(f) => f.validations?.some((v) => v.type === "required")
|
|
6416
|
+
);
|
|
6417
|
+
const visibleRequired = requiredFields.filter(
|
|
6418
|
+
(f) => this.stateManager.getFieldVisibility(f.name)
|
|
6419
|
+
);
|
|
6420
|
+
const total = visibleRequired.length;
|
|
6421
|
+
const completed = visibleRequired.filter((f) => {
|
|
6422
|
+
const errors = this.stateManager.getErrors(f.name);
|
|
6423
|
+
if (errors.length > 0) return false;
|
|
6424
|
+
const val = this.stateManager.getValue(f.name);
|
|
6425
|
+
if (val === null || val === void 0) return false;
|
|
6426
|
+
if (typeof val === "string" && val.trim() === "") return false;
|
|
6427
|
+
if (Array.isArray(val) && val.length === 0 && f.type === "array") return false;
|
|
6428
|
+
return true;
|
|
6429
|
+
}).length;
|
|
6430
|
+
return { completed, total };
|
|
6431
|
+
}
|
|
5603
6432
|
/**
|
|
5604
6433
|
* Renderiza wizard
|
|
5605
6434
|
*/
|