easy-forms-core 1.1.6 → 1.1.8
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 +63 -0
- package/dist/easy-form.d.ts +20 -0
- package/dist/easy-form.js +121 -9
- package/dist/easy-form.js.map +1 -1
- package/dist/index.d.ts +20 -0
- package/dist/index.js +121 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,6 +108,68 @@ form.schema = {
|
|
|
108
108
|
}
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
+
### Slots con inserción por fila (row)
|
|
112
|
+
|
|
113
|
+
Puedes insertar contenido HTML personalizado en posiciones concretas del formulario usando **hijos directos** del componente con el atributo `row`. El índice es 0-based (cada campo o fila del schema cuenta como una posición). Usa `row="-1"` para insertar al final, antes del botón de envío.
|
|
114
|
+
|
|
115
|
+
| Valor | Comportamiento |
|
|
116
|
+
|--------|-----------------|
|
|
117
|
+
| `row="0"` | Antes del primer campo |
|
|
118
|
+
| `row="1"` | Entre el primer y el segundo campo |
|
|
119
|
+
| `row="-1"` | Al final del formulario (antes del submit) |
|
|
120
|
+
| Sin `row` o valor inválido | Se trata como `-1` (al final) |
|
|
121
|
+
|
|
122
|
+
**HTML Vanilla**
|
|
123
|
+
|
|
124
|
+
```html
|
|
125
|
+
<easy-form id="form">
|
|
126
|
+
<div row="0">Mensaje al inicio</div>
|
|
127
|
+
<div row="2">Mensaje después del segundo campo</div>
|
|
128
|
+
<div row="-1">Mensaje al final</div>
|
|
129
|
+
</easy-form>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
import 'easy-forms-core'
|
|
134
|
+
|
|
135
|
+
const form = document.querySelector('#form')
|
|
136
|
+
form.schema = {
|
|
137
|
+
fields: [
|
|
138
|
+
{ type: 'text', name: 'name', label: 'Nombre' },
|
|
139
|
+
{ type: 'email', name: 'email', label: 'Email' },
|
|
140
|
+
{ type: 'textarea', name: 'message', label: 'Mensaje' }
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**React**
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
import 'easy-forms-core'
|
|
149
|
+
|
|
150
|
+
function MyForm() {
|
|
151
|
+
return (
|
|
152
|
+
<easy-form schema={{ fields: [...] }}>
|
|
153
|
+
<div row="0">Contenido al inicio</div>
|
|
154
|
+
<div row="-1">Contenido al final</div>
|
|
155
|
+
</easy-form>
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Vue**
|
|
161
|
+
|
|
162
|
+
```vue
|
|
163
|
+
<template>
|
|
164
|
+
<easy-form :schema="schema">
|
|
165
|
+
<div row="0">Contenido al inicio</div>
|
|
166
|
+
<div row="-1">Contenido al final</div>
|
|
167
|
+
</easy-form>
|
|
168
|
+
</template>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Los slots son solo visuales: los inputs dentro de un slot **no** forman parte del estado ni del envío del formulario. En formularios por pasos (wizard), el índice `row` es relativo al paso actual.
|
|
172
|
+
|
|
111
173
|
### React
|
|
112
174
|
|
|
113
175
|
```tsx
|
|
@@ -407,6 +469,7 @@ Puedes sobrescribir cualquier estilo usando las clases CSS del componente. Todas
|
|
|
407
469
|
- Formularios anidados
|
|
408
470
|
- Arrays dinámicos
|
|
409
471
|
- **Rows (filas horizontales)** - Agrupa campos en filas
|
|
472
|
+
- **Slots (row)** - Inserta contenido HTML en posiciones concretas del formulario
|
|
410
473
|
- **Datos iniciales** - Carga valores iniciales desde datos externos
|
|
411
474
|
- Componentes visuales personalizables
|
|
412
475
|
- Eventos de submit, change y error
|
package/dist/easy-form.d.ts
CHANGED
|
@@ -361,6 +361,11 @@ declare class EasyForm extends BrowserHTMLElement {
|
|
|
361
361
|
private isRendering;
|
|
362
362
|
private attemptsLock;
|
|
363
363
|
private lockCountdownInterval;
|
|
364
|
+
/**
|
|
365
|
+
* Plantillas de slots basados en atributo `row` en el light DOM.
|
|
366
|
+
* Se inicializan una sola vez y se clonan en cada render.
|
|
367
|
+
*/
|
|
368
|
+
private slotTemplates;
|
|
364
369
|
static get observedAttributes(): string[];
|
|
365
370
|
constructor();
|
|
366
371
|
/**
|
|
@@ -449,6 +454,21 @@ declare class EasyForm extends BrowserHTMLElement {
|
|
|
449
454
|
* Retorna un objeto con los valores preservados
|
|
450
455
|
*/
|
|
451
456
|
private preserveCurrentValues;
|
|
457
|
+
/**
|
|
458
|
+
* Inicializa las plantillas de slots a partir del light DOM.
|
|
459
|
+
* Cualquier hijo directo que sea HTMLElement se considera slot; si tiene atributo `row` se usa para la posición, si no se inserta al final (-1).
|
|
460
|
+
*/
|
|
461
|
+
private initializeSlotTemplates;
|
|
462
|
+
/**
|
|
463
|
+
* Obtiene clones de slots agrupados por índice de fila efectivo.
|
|
464
|
+
* Cualquier valor inválido o fuera de rango se normaliza a -1 (final del formulario).
|
|
465
|
+
*/
|
|
466
|
+
private getSlotClonesByRow;
|
|
467
|
+
/**
|
|
468
|
+
* Aplica estado disabled/loading a un clone de slot: deshabilita elementos
|
|
469
|
+
* interactivos y añade clase para estilos. Respeta el estado del formulario.
|
|
470
|
+
*/
|
|
471
|
+
private applySlotDisabledState;
|
|
452
472
|
/**
|
|
453
473
|
* Renderiza campos normales
|
|
454
474
|
*/
|
package/dist/easy-form.js
CHANGED
|
@@ -741,14 +741,20 @@ function getBaseStyles(colors) {
|
|
|
741
741
|
}
|
|
742
742
|
/* Disabled State */
|
|
743
743
|
.easy-form-disabled,
|
|
744
|
-
.easy-form-disabled
|
|
744
|
+
.easy-form-disabled *,
|
|
745
|
+
.easy-form-slot-disabled,
|
|
746
|
+
.easy-form-slot-disabled * {
|
|
745
747
|
pointer-events: none;
|
|
746
748
|
opacity: 0.6;
|
|
747
749
|
}
|
|
748
750
|
.easy-form-disabled input,
|
|
749
751
|
.easy-form-disabled textarea,
|
|
750
752
|
.easy-form-disabled select,
|
|
751
|
-
.easy-form-disabled button
|
|
753
|
+
.easy-form-disabled button,
|
|
754
|
+
.easy-form-slot-disabled input,
|
|
755
|
+
.easy-form-slot-disabled textarea,
|
|
756
|
+
.easy-form-slot-disabled select,
|
|
757
|
+
.easy-form-slot-disabled button {
|
|
752
758
|
cursor: not-allowed;
|
|
753
759
|
}
|
|
754
760
|
.easy-form-input-disabled {
|
|
@@ -4763,6 +4769,11 @@ var EasyForm = class extends BrowserHTMLElement {
|
|
|
4763
4769
|
this.isRendering = false;
|
|
4764
4770
|
this.attemptsLock = null;
|
|
4765
4771
|
this.lockCountdownInterval = null;
|
|
4772
|
+
/**
|
|
4773
|
+
* Plantillas de slots basados en atributo `row` en el light DOM.
|
|
4774
|
+
* Se inicializan una sola vez y se clonan en cada render.
|
|
4775
|
+
*/
|
|
4776
|
+
this.slotTemplates = null;
|
|
4766
4777
|
this.dependencyRenderTimeout = null;
|
|
4767
4778
|
this.stateManager = new StateManager();
|
|
4768
4779
|
this.shadow = this.attachShadow({ mode: "open" });
|
|
@@ -5207,6 +5218,16 @@ var EasyForm = class extends BrowserHTMLElement {
|
|
|
5207
5218
|
* Retorna un objeto con los valores preservados
|
|
5208
5219
|
*/
|
|
5209
5220
|
preserveCurrentValues() {
|
|
5221
|
+
let currentSchema = null;
|
|
5222
|
+
const templateName = this.template;
|
|
5223
|
+
if (templateName) {
|
|
5224
|
+
currentSchema = this.getSchemaFromTemplate(templateName);
|
|
5225
|
+
} else {
|
|
5226
|
+
currentSchema = this.schema;
|
|
5227
|
+
}
|
|
5228
|
+
if (!currentSchema) {
|
|
5229
|
+
return {};
|
|
5230
|
+
}
|
|
5210
5231
|
const form = this.shadow.querySelector("form");
|
|
5211
5232
|
const preservedValues = {};
|
|
5212
5233
|
if (!form) return preservedValues;
|
|
@@ -5214,6 +5235,8 @@ var EasyForm = class extends BrowserHTMLElement {
|
|
|
5214
5235
|
for (const input of inputs) {
|
|
5215
5236
|
const name = input.getAttribute("name");
|
|
5216
5237
|
if (!name) continue;
|
|
5238
|
+
const belongsToSchema = this.findFieldInSchema(currentSchema, name) !== null;
|
|
5239
|
+
if (!belongsToSchema) continue;
|
|
5217
5240
|
let value;
|
|
5218
5241
|
if (input instanceof HTMLInputElement) {
|
|
5219
5242
|
if (input.type === "checkbox") {
|
|
@@ -5244,16 +5267,110 @@ var EasyForm = class extends BrowserHTMLElement {
|
|
|
5244
5267
|
}
|
|
5245
5268
|
return preservedValues;
|
|
5246
5269
|
}
|
|
5270
|
+
/**
|
|
5271
|
+
* Inicializa las plantillas de slots a partir del light DOM.
|
|
5272
|
+
* Cualquier hijo directo que sea HTMLElement se considera slot; si tiene atributo `row` se usa para la posición, si no se inserta al final (-1).
|
|
5273
|
+
*/
|
|
5274
|
+
initializeSlotTemplates() {
|
|
5275
|
+
if (this.slotTemplates !== null) return;
|
|
5276
|
+
const elements = [];
|
|
5277
|
+
for (const child of Array.from(this.children)) {
|
|
5278
|
+
if (child instanceof HTMLElement) {
|
|
5279
|
+
elements.push(child);
|
|
5280
|
+
}
|
|
5281
|
+
}
|
|
5282
|
+
if (elements.length === 0) {
|
|
5283
|
+
this.slotTemplates = [];
|
|
5284
|
+
return;
|
|
5285
|
+
}
|
|
5286
|
+
this.slotTemplates = elements.map((el) => {
|
|
5287
|
+
const raw = el.hasAttribute("row") ? el.getAttribute("row") : null;
|
|
5288
|
+
const parsed = raw != null && raw !== "" ? Number(raw) : NaN;
|
|
5289
|
+
const row = Number.isFinite(parsed) ? parsed : null;
|
|
5290
|
+
return {
|
|
5291
|
+
template: el.cloneNode(true),
|
|
5292
|
+
row
|
|
5293
|
+
};
|
|
5294
|
+
});
|
|
5295
|
+
}
|
|
5296
|
+
/**
|
|
5297
|
+
* Obtiene clones de slots agrupados por índice de fila efectivo.
|
|
5298
|
+
* Cualquier valor inválido o fuera de rango se normaliza a -1 (final del formulario).
|
|
5299
|
+
*/
|
|
5300
|
+
getSlotClonesByRow(totalRows) {
|
|
5301
|
+
this.initializeSlotTemplates();
|
|
5302
|
+
const result = /* @__PURE__ */ new Map();
|
|
5303
|
+
if (!this.slotTemplates || this.slotTemplates.length === 0) {
|
|
5304
|
+
return result;
|
|
5305
|
+
}
|
|
5306
|
+
for (const { template, row } of this.slotTemplates) {
|
|
5307
|
+
let effectiveRow = typeof row === "number" ? row : -1;
|
|
5308
|
+
if (!Number.isFinite(effectiveRow)) {
|
|
5309
|
+
effectiveRow = -1;
|
|
5310
|
+
}
|
|
5311
|
+
if (effectiveRow < 0 || effectiveRow >= totalRows) {
|
|
5312
|
+
effectiveRow = -1;
|
|
5313
|
+
}
|
|
5314
|
+
const clone = template.cloneNode(true);
|
|
5315
|
+
const existing = result.get(effectiveRow) ?? [];
|
|
5316
|
+
existing.push(clone);
|
|
5317
|
+
result.set(effectiveRow, existing);
|
|
5318
|
+
}
|
|
5319
|
+
return result;
|
|
5320
|
+
}
|
|
5321
|
+
/**
|
|
5322
|
+
* Aplica estado disabled/loading a un clone de slot: deshabilita elementos
|
|
5323
|
+
* interactivos y añade clase para estilos. Respeta el estado del formulario.
|
|
5324
|
+
*/
|
|
5325
|
+
applySlotDisabledState(slotElement) {
|
|
5326
|
+
if (!this.disabled && !this.loading) return;
|
|
5327
|
+
slotElement.classList.add("easy-form-slot-disabled");
|
|
5328
|
+
const interactives = slotElement.querySelectorAll(
|
|
5329
|
+
"input, textarea, select, button"
|
|
5330
|
+
);
|
|
5331
|
+
interactives.forEach((el) => {
|
|
5332
|
+
if ("disabled" in el) el.disabled = true;
|
|
5333
|
+
});
|
|
5334
|
+
}
|
|
5247
5335
|
/**
|
|
5248
5336
|
* Renderiza campos normales
|
|
5249
5337
|
*/
|
|
5250
5338
|
renderFields(container, fields) {
|
|
5251
|
-
|
|
5339
|
+
if (fields.length === 0) {
|
|
5340
|
+
const slotClones = this.getSlotClonesByRow(0);
|
|
5341
|
+
const endSlots2 = slotClones.get(-1);
|
|
5342
|
+
if (endSlots2 && endSlots2.length > 0) {
|
|
5343
|
+
for (const slotElement of endSlots2) {
|
|
5344
|
+
this.applySlotDisabledState(slotElement);
|
|
5345
|
+
container.appendChild(slotElement);
|
|
5346
|
+
}
|
|
5347
|
+
}
|
|
5348
|
+
return;
|
|
5349
|
+
}
|
|
5350
|
+
const totalRows = fields.length;
|
|
5351
|
+
const slotClonesByRow = this.getSlotClonesByRow(totalRows);
|
|
5352
|
+
for (let rowIndex = 0; rowIndex < fields.length; rowIndex++) {
|
|
5353
|
+
const slotsForRow = slotClonesByRow.get(rowIndex);
|
|
5354
|
+
if (slotsForRow && slotsForRow.length > 0) {
|
|
5355
|
+
for (const slotElement of slotsForRow) {
|
|
5356
|
+
this.applySlotDisabledState(slotElement);
|
|
5357
|
+
container.appendChild(slotElement);
|
|
5358
|
+
}
|
|
5359
|
+
slotClonesByRow.delete(rowIndex);
|
|
5360
|
+
}
|
|
5361
|
+
const field = fields[rowIndex];
|
|
5252
5362
|
const fieldElement = this.renderField(field);
|
|
5253
5363
|
if (fieldElement) {
|
|
5254
5364
|
container.appendChild(fieldElement);
|
|
5255
5365
|
}
|
|
5256
5366
|
}
|
|
5367
|
+
const endSlots = slotClonesByRow.get(-1);
|
|
5368
|
+
if (endSlots && endSlots.length > 0) {
|
|
5369
|
+
for (const slotElement of endSlots) {
|
|
5370
|
+
this.applySlotDisabledState(slotElement);
|
|
5371
|
+
container.appendChild(slotElement);
|
|
5372
|
+
}
|
|
5373
|
+
}
|
|
5257
5374
|
}
|
|
5258
5375
|
/**
|
|
5259
5376
|
* Renderiza un campo
|
|
@@ -5511,12 +5628,7 @@ var EasyForm = class extends BrowserHTMLElement {
|
|
|
5511
5628
|
const fieldsContainer = document.createElement("div");
|
|
5512
5629
|
fieldsContainer.className = "easy-form-wizard-fields";
|
|
5513
5630
|
const currentFields = this.stateManager.getCurrentStepFields();
|
|
5514
|
-
|
|
5515
|
-
const fieldElement = this.renderField(field);
|
|
5516
|
-
if (fieldElement) {
|
|
5517
|
-
fieldsContainer.appendChild(fieldElement);
|
|
5518
|
-
}
|
|
5519
|
-
}
|
|
5631
|
+
this.renderFields(fieldsContainer, currentFields);
|
|
5520
5632
|
wizardContainer.appendChild(fieldsContainer);
|
|
5521
5633
|
const navContainer = document.createElement("div");
|
|
5522
5634
|
navContainer.className = "easy-form-wizard-nav";
|