easy-forms-core 1.1.7 → 1.1.9
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 +1 -0
- package/dist/easy-form.d.ts +17 -2
- package/dist/easy-form.js +228 -3
- package/dist/easy-form.js.map +1 -1
- package/dist/index.d.ts +18 -3
- package/dist/index.js +228 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -470,6 +470,7 @@ Puedes sobrescribir cualquier estilo usando las clases CSS del componente. Todas
|
|
|
470
470
|
- Arrays dinámicos
|
|
471
471
|
- **Rows (filas horizontales)** - Agrupa campos en filas
|
|
472
472
|
- **Slots (row)** - Inserta contenido HTML en posiciones concretas del formulario
|
|
473
|
+
- **Campo password** - Opciones `showToggle` (ver/ocultar) y `characterSeparated` (entrada por caracteres, tipo OTP)
|
|
473
474
|
- **Datos iniciales** - Carga valores iniciales desde datos externos
|
|
474
475
|
- Componentes visuales personalizables
|
|
475
476
|
- Eventos de submit, change y error
|
package/dist/easy-form.d.ts
CHANGED
|
@@ -125,7 +125,17 @@ interface BaseField {
|
|
|
125
125
|
* Campo de texto
|
|
126
126
|
*/
|
|
127
127
|
interface TextField extends BaseField {
|
|
128
|
-
type: 'text' | 'email'
|
|
128
|
+
type: 'text' | 'email';
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Campo password (con opciones de visibilidad y modo carácter separado)
|
|
132
|
+
*/
|
|
133
|
+
interface PasswordField extends BaseField {
|
|
134
|
+
type: 'password';
|
|
135
|
+
/** Mostrar botón para alternar visibilidad del texto */
|
|
136
|
+
showToggle?: boolean;
|
|
137
|
+
/** Modo carácter separado: true = longitud por defecto 6, number = cantidad de cajas */
|
|
138
|
+
characterSeparated?: boolean | number;
|
|
129
139
|
}
|
|
130
140
|
/**
|
|
131
141
|
* Campo numérico
|
|
@@ -277,7 +287,7 @@ interface OTPField extends BaseField {
|
|
|
277
287
|
/**
|
|
278
288
|
* Unión de todos los tipos de campos
|
|
279
289
|
*/
|
|
280
|
-
type Field = TextField | NumberField | TextareaField | SelectField | CheckboxField | RadioField | SwitchField | DateField | FileField | ArrayField | GroupField | RowField | CustomField | QuantityField | AccordionSelectField | ImageGridSelectField | OTPField;
|
|
290
|
+
type Field = TextField | PasswordField | NumberField | TextareaField | SelectField | CheckboxField | RadioField | SwitchField | DateField | FileField | ArrayField | GroupField | RowField | CustomField | QuantityField | AccordionSelectField | ImageGridSelectField | OTPField;
|
|
281
291
|
/**
|
|
282
292
|
* Step para formularios wizard
|
|
283
293
|
*/
|
|
@@ -464,6 +474,11 @@ declare class EasyForm extends BrowserHTMLElement {
|
|
|
464
474
|
* Cualquier valor inválido o fuera de rango se normaliza a -1 (final del formulario).
|
|
465
475
|
*/
|
|
466
476
|
private getSlotClonesByRow;
|
|
477
|
+
/**
|
|
478
|
+
* Aplica estado disabled/loading a un clone de slot: deshabilita elementos
|
|
479
|
+
* interactivos y añade clase para estilos. Respeta el estado del formulario.
|
|
480
|
+
*/
|
|
481
|
+
private applySlotDisabledState;
|
|
467
482
|
/**
|
|
468
483
|
* Renderiza campos normales
|
|
469
484
|
*/
|
package/dist/easy-form.js
CHANGED
|
@@ -689,6 +689,62 @@ function getBaseStyles(colors) {
|
|
|
689
689
|
.easy-form-otp-input:invalid {
|
|
690
690
|
border-color: var(--easy-form-error);
|
|
691
691
|
}
|
|
692
|
+
/* Password inner (input + toggle) */
|
|
693
|
+
.easy-form-password-inner {
|
|
694
|
+
display: flex;
|
|
695
|
+
align-items: center;
|
|
696
|
+
gap: 0.5rem;
|
|
697
|
+
}
|
|
698
|
+
.easy-form-password-inner .easy-form-password-input {
|
|
699
|
+
flex: 1;
|
|
700
|
+
min-width: 0;
|
|
701
|
+
}
|
|
702
|
+
.easy-form-password-toggle {
|
|
703
|
+
flex-shrink: 0;
|
|
704
|
+
display: inline-flex;
|
|
705
|
+
align-items: center;
|
|
706
|
+
justify-content: center;
|
|
707
|
+
padding: 0.35rem;
|
|
708
|
+
background: transparent;
|
|
709
|
+
border: 1px solid var(--easy-form-border);
|
|
710
|
+
border-radius: 4px;
|
|
711
|
+
cursor: pointer;
|
|
712
|
+
color: var(--easy-form-text);
|
|
713
|
+
}
|
|
714
|
+
.easy-form-password-toggle:hover {
|
|
715
|
+
border-color: var(--easy-form-primary);
|
|
716
|
+
color: var(--easy-form-primary);
|
|
717
|
+
}
|
|
718
|
+
.easy-form-password-toggle:focus {
|
|
719
|
+
outline: none;
|
|
720
|
+
box-shadow: 0 0 0 2px var(--easy-form-primary);
|
|
721
|
+
}
|
|
722
|
+
/* Password car\xE1cter separado */
|
|
723
|
+
.easy-form-password-separated {
|
|
724
|
+
display: flex;
|
|
725
|
+
flex-wrap: nowrap;
|
|
726
|
+
gap: 0.5rem;
|
|
727
|
+
align-items: center;
|
|
728
|
+
}
|
|
729
|
+
.easy-form-password-separated-input {
|
|
730
|
+
width: 2.5rem;
|
|
731
|
+
height: 2.5rem;
|
|
732
|
+
text-align: center;
|
|
733
|
+
font-size: 1.25rem;
|
|
734
|
+
border: 2px solid var(--easy-form-border);
|
|
735
|
+
border-radius: 4px;
|
|
736
|
+
transition: border-color 0.2s ease;
|
|
737
|
+
}
|
|
738
|
+
.easy-form-password-separated-input:focus {
|
|
739
|
+
border-color: var(--easy-form-primary);
|
|
740
|
+
outline: none;
|
|
741
|
+
}
|
|
742
|
+
.easy-form-password-separated-toggle {
|
|
743
|
+
flex-shrink: 0;
|
|
744
|
+
}
|
|
745
|
+
.easy-form-password-separated-toggle .easy-form-password-toggle {
|
|
746
|
+
padding: 0.35rem;
|
|
747
|
+
}
|
|
692
748
|
/* Loading Overlay (sobre el formulario) */
|
|
693
749
|
.easy-form-loading-overlay {
|
|
694
750
|
position: absolute;
|
|
@@ -741,14 +797,20 @@ function getBaseStyles(colors) {
|
|
|
741
797
|
}
|
|
742
798
|
/* Disabled State */
|
|
743
799
|
.easy-form-disabled,
|
|
744
|
-
.easy-form-disabled
|
|
800
|
+
.easy-form-disabled *,
|
|
801
|
+
.easy-form-slot-disabled,
|
|
802
|
+
.easy-form-slot-disabled * {
|
|
745
803
|
pointer-events: none;
|
|
746
804
|
opacity: 0.6;
|
|
747
805
|
}
|
|
748
806
|
.easy-form-disabled input,
|
|
749
807
|
.easy-form-disabled textarea,
|
|
750
808
|
.easy-form-disabled select,
|
|
751
|
-
.easy-form-disabled button
|
|
809
|
+
.easy-form-disabled button,
|
|
810
|
+
.easy-form-slot-disabled input,
|
|
811
|
+
.easy-form-slot-disabled textarea,
|
|
812
|
+
.easy-form-slot-disabled select,
|
|
813
|
+
.easy-form-slot-disabled button {
|
|
752
814
|
cursor: not-allowed;
|
|
753
815
|
}
|
|
754
816
|
.easy-form-input-disabled {
|
|
@@ -4190,13 +4252,159 @@ var OTPInput = class extends BaseInput {
|
|
|
4190
4252
|
}
|
|
4191
4253
|
};
|
|
4192
4254
|
|
|
4255
|
+
// src/components/inputs/password-input.ts
|
|
4256
|
+
var DEFAULT_SEPARATED_LENGTH = 6;
|
|
4257
|
+
function eyeSvg(visible) {
|
|
4258
|
+
if (visible) {
|
|
4259
|
+
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>`;
|
|
4260
|
+
}
|
|
4261
|
+
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>`;
|
|
4262
|
+
}
|
|
4263
|
+
var PasswordInput = class extends BaseInput {
|
|
4264
|
+
constructor() {
|
|
4265
|
+
super(...arguments);
|
|
4266
|
+
this.inputs = [];
|
|
4267
|
+
this.visible = false;
|
|
4268
|
+
}
|
|
4269
|
+
get passwordField() {
|
|
4270
|
+
return this.field;
|
|
4271
|
+
}
|
|
4272
|
+
render() {
|
|
4273
|
+
const pf = this.passwordField;
|
|
4274
|
+
const useSeparated = !!pf.characterSeparated;
|
|
4275
|
+
const length = useSeparated ? typeof pf.characterSeparated === "number" ? pf.characterSeparated : DEFAULT_SEPARATED_LENGTH : 0;
|
|
4276
|
+
if (useSeparated) {
|
|
4277
|
+
return this.createFieldContainer(this.renderSeparated(length));
|
|
4278
|
+
}
|
|
4279
|
+
return this.createFieldContainer(this.renderSingle());
|
|
4280
|
+
}
|
|
4281
|
+
renderSingle() {
|
|
4282
|
+
const pf = this.passwordField;
|
|
4283
|
+
const showToggle = !!pf.showToggle;
|
|
4284
|
+
const input = document.createElement("input");
|
|
4285
|
+
input.type = "password";
|
|
4286
|
+
input.value = this.value ?? "";
|
|
4287
|
+
input.id = this.getFieldId();
|
|
4288
|
+
this.applyCommonProps(input);
|
|
4289
|
+
if (this.field.placeholder) input.placeholder = this.field.placeholder;
|
|
4290
|
+
input.addEventListener("input", (e) => {
|
|
4291
|
+
this.onChange(e.target.value);
|
|
4292
|
+
});
|
|
4293
|
+
input.addEventListener("blur", () => this.onBlur());
|
|
4294
|
+
if (!showToggle) {
|
|
4295
|
+
return input;
|
|
4296
|
+
}
|
|
4297
|
+
const wrapper = document.createElement("div");
|
|
4298
|
+
wrapper.className = "easy-form-password-inner";
|
|
4299
|
+
input.classList.add("easy-form-password-input");
|
|
4300
|
+
wrapper.appendChild(input);
|
|
4301
|
+
const btn = document.createElement("button");
|
|
4302
|
+
btn.type = "button";
|
|
4303
|
+
btn.className = "easy-form-password-toggle";
|
|
4304
|
+
btn.setAttribute("aria-label", "Mostrar contrase\xF1a");
|
|
4305
|
+
btn.innerHTML = eyeSvg(false);
|
|
4306
|
+
btn.addEventListener("click", () => {
|
|
4307
|
+
this.visible = !this.visible;
|
|
4308
|
+
input.type = this.visible ? "text" : "password";
|
|
4309
|
+
btn.setAttribute("aria-label", this.visible ? "Ocultar contrase\xF1a" : "Mostrar contrase\xF1a");
|
|
4310
|
+
btn.innerHTML = eyeSvg(this.visible);
|
|
4311
|
+
});
|
|
4312
|
+
wrapper.appendChild(btn);
|
|
4313
|
+
return wrapper;
|
|
4314
|
+
}
|
|
4315
|
+
renderSeparated(length) {
|
|
4316
|
+
const pf = this.passwordField;
|
|
4317
|
+
const showToggle = !!pf.showToggle;
|
|
4318
|
+
const value = this.value ?? "";
|
|
4319
|
+
const valueString = String(value).slice(0, length);
|
|
4320
|
+
const container = document.createElement("div");
|
|
4321
|
+
container.className = "easy-form-password-separated";
|
|
4322
|
+
for (let i = 0; i < length; i++) {
|
|
4323
|
+
const input = document.createElement("input");
|
|
4324
|
+
input.type = "password";
|
|
4325
|
+
input.maxLength = 1;
|
|
4326
|
+
input.className = "easy-form-password-separated-input";
|
|
4327
|
+
input.setAttribute("aria-label", `Car\xE1cter ${i + 1} de ${length}`);
|
|
4328
|
+
if (valueString[i]) input.value = valueString[i];
|
|
4329
|
+
this.applyCommonProps(input);
|
|
4330
|
+
input.id = i === 0 ? this.getFieldId() : `${this.getFieldId()}-${i}`;
|
|
4331
|
+
if (i > 0) input.removeAttribute("name");
|
|
4332
|
+
const currentIndex = i;
|
|
4333
|
+
input.addEventListener("input", (e) => {
|
|
4334
|
+
const target = e.target;
|
|
4335
|
+
const val = target.value;
|
|
4336
|
+
if (val && currentIndex < length - 1) {
|
|
4337
|
+
this.inputs[currentIndex + 1].focus();
|
|
4338
|
+
}
|
|
4339
|
+
this.updateSeparatedValue();
|
|
4340
|
+
});
|
|
4341
|
+
input.addEventListener("keydown", (e) => {
|
|
4342
|
+
if (e.key === "Backspace" && !e.target.value && currentIndex > 0) {
|
|
4343
|
+
this.inputs[currentIndex - 1].focus();
|
|
4344
|
+
this.inputs[currentIndex - 1].value = "";
|
|
4345
|
+
this.updateSeparatedValue();
|
|
4346
|
+
}
|
|
4347
|
+
if (e.key === "ArrowLeft" && currentIndex > 0) {
|
|
4348
|
+
e.preventDefault();
|
|
4349
|
+
this.inputs[currentIndex - 1].focus();
|
|
4350
|
+
}
|
|
4351
|
+
if (e.key === "ArrowRight" && currentIndex < length - 1) {
|
|
4352
|
+
e.preventDefault();
|
|
4353
|
+
this.inputs[currentIndex + 1].focus();
|
|
4354
|
+
}
|
|
4355
|
+
});
|
|
4356
|
+
input.addEventListener("paste", (e) => {
|
|
4357
|
+
e.preventDefault();
|
|
4358
|
+
const text = (e.clipboardData || window.clipboardData)?.getData("text") || "";
|
|
4359
|
+
const chars = text.slice(0, length - currentIndex).split("");
|
|
4360
|
+
for (let j = 0; j < chars.length && currentIndex + j < length; j++) {
|
|
4361
|
+
this.inputs[currentIndex + j].value = chars[j];
|
|
4362
|
+
}
|
|
4363
|
+
const nextIdx = Math.min(currentIndex + chars.length, length - 1);
|
|
4364
|
+
this.inputs[nextIdx].focus();
|
|
4365
|
+
this.updateSeparatedValue();
|
|
4366
|
+
});
|
|
4367
|
+
input.addEventListener("focus", (e) => e.target.select());
|
|
4368
|
+
input.addEventListener("blur", () => this.onBlur());
|
|
4369
|
+
this.inputs.push(input);
|
|
4370
|
+
container.appendChild(input);
|
|
4371
|
+
}
|
|
4372
|
+
if (showToggle) {
|
|
4373
|
+
const toggleWrap = document.createElement("div");
|
|
4374
|
+
toggleWrap.className = "easy-form-password-separated-toggle";
|
|
4375
|
+
const btn = document.createElement("button");
|
|
4376
|
+
btn.type = "button";
|
|
4377
|
+
btn.className = "easy-form-password-toggle";
|
|
4378
|
+
btn.setAttribute("aria-label", "Mostrar contrase\xF1a");
|
|
4379
|
+
btn.innerHTML = eyeSvg(false);
|
|
4380
|
+
btn.addEventListener("click", () => {
|
|
4381
|
+
this.visible = !this.visible;
|
|
4382
|
+
const type = this.visible ? "text" : "password";
|
|
4383
|
+
this.inputs.forEach((inp) => {
|
|
4384
|
+
inp.type = type;
|
|
4385
|
+
});
|
|
4386
|
+
btn.setAttribute("aria-label", this.visible ? "Ocultar contrase\xF1a" : "Mostrar contrase\xF1a");
|
|
4387
|
+
btn.innerHTML = eyeSvg(this.visible);
|
|
4388
|
+
});
|
|
4389
|
+
toggleWrap.appendChild(btn);
|
|
4390
|
+
container.appendChild(toggleWrap);
|
|
4391
|
+
}
|
|
4392
|
+
return container;
|
|
4393
|
+
}
|
|
4394
|
+
updateSeparatedValue() {
|
|
4395
|
+
const value = this.inputs.map((inp) => inp.value).join("");
|
|
4396
|
+
this.onChange(value || null);
|
|
4397
|
+
}
|
|
4398
|
+
};
|
|
4399
|
+
|
|
4193
4400
|
// src/components/inputs/index.ts
|
|
4194
4401
|
function createInput(field, value, error, onChange, onBlur) {
|
|
4195
4402
|
switch (field.type) {
|
|
4196
4403
|
case "text":
|
|
4197
4404
|
case "email":
|
|
4198
|
-
case "password":
|
|
4199
4405
|
return new TextInput(field, value, error, onChange, onBlur).render();
|
|
4406
|
+
case "password":
|
|
4407
|
+
return new PasswordInput(field, value, error, onChange, onBlur).render();
|
|
4200
4408
|
case "number":
|
|
4201
4409
|
return new NumberInput(field, value, error, onChange, onBlur).render();
|
|
4202
4410
|
case "textarea":
|
|
@@ -5312,6 +5520,20 @@ var EasyForm = class extends BrowserHTMLElement {
|
|
|
5312
5520
|
}
|
|
5313
5521
|
return result;
|
|
5314
5522
|
}
|
|
5523
|
+
/**
|
|
5524
|
+
* Aplica estado disabled/loading a un clone de slot: deshabilita elementos
|
|
5525
|
+
* interactivos y añade clase para estilos. Respeta el estado del formulario.
|
|
5526
|
+
*/
|
|
5527
|
+
applySlotDisabledState(slotElement) {
|
|
5528
|
+
if (!this.disabled && !this.loading) return;
|
|
5529
|
+
slotElement.classList.add("easy-form-slot-disabled");
|
|
5530
|
+
const interactives = slotElement.querySelectorAll(
|
|
5531
|
+
"input, textarea, select, button"
|
|
5532
|
+
);
|
|
5533
|
+
interactives.forEach((el) => {
|
|
5534
|
+
if ("disabled" in el) el.disabled = true;
|
|
5535
|
+
});
|
|
5536
|
+
}
|
|
5315
5537
|
/**
|
|
5316
5538
|
* Renderiza campos normales
|
|
5317
5539
|
*/
|
|
@@ -5321,6 +5543,7 @@ var EasyForm = class extends BrowserHTMLElement {
|
|
|
5321
5543
|
const endSlots2 = slotClones.get(-1);
|
|
5322
5544
|
if (endSlots2 && endSlots2.length > 0) {
|
|
5323
5545
|
for (const slotElement of endSlots2) {
|
|
5546
|
+
this.applySlotDisabledState(slotElement);
|
|
5324
5547
|
container.appendChild(slotElement);
|
|
5325
5548
|
}
|
|
5326
5549
|
}
|
|
@@ -5332,6 +5555,7 @@ var EasyForm = class extends BrowserHTMLElement {
|
|
|
5332
5555
|
const slotsForRow = slotClonesByRow.get(rowIndex);
|
|
5333
5556
|
if (slotsForRow && slotsForRow.length > 0) {
|
|
5334
5557
|
for (const slotElement of slotsForRow) {
|
|
5558
|
+
this.applySlotDisabledState(slotElement);
|
|
5335
5559
|
container.appendChild(slotElement);
|
|
5336
5560
|
}
|
|
5337
5561
|
slotClonesByRow.delete(rowIndex);
|
|
@@ -5345,6 +5569,7 @@ var EasyForm = class extends BrowserHTMLElement {
|
|
|
5345
5569
|
const endSlots = slotClonesByRow.get(-1);
|
|
5346
5570
|
if (endSlots && endSlots.length > 0) {
|
|
5347
5571
|
for (const slotElement of endSlots) {
|
|
5572
|
+
this.applySlotDisabledState(slotElement);
|
|
5348
5573
|
container.appendChild(slotElement);
|
|
5349
5574
|
}
|
|
5350
5575
|
}
|