@luis-angel-martin-dzul/input-styles 0.0.23 → 0.0.25

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 CHANGED
@@ -1,1002 +1,1041 @@
1
- # 📦 Input Style
2
-
3
- **Librería de componentes y formularios Vue 3**
4
-
5
- Incluye componentes de inputs, selects, botones, paginación y más, estilizados con TailwindCSS y listos para producción.
6
-
7
- ------
8
-
9
- ![Última actualización](https://img.shields.io/badge/Actualizado-28%20de%20Junio%20del%2026-blue)
10
- ![Versión](https://img.shields.io/badge/versión-0.0.20-indigo)
11
-
12
- ------
13
-
14
- # 📦 Instalación
15
-
16
- La librería se distribuye mediante **npm**:
17
-
18
- ```
19
- npm install @luis-angel-martin-dzul/input-styles
20
- ```
21
-
22
- ------
23
-
24
- # 🚀 Uso en Vue + Vite
25
-
26
- En `main.js` o `main.ts`:
27
-
28
- ```js
29
- import { createApp } from 'vue'
30
- import App from './App.vue'
31
-
32
- import '@luis-angel-martin-dzul/input-styles/style.css'
33
- import InputStyle from '@luis-angel-martin-dzul/input-styles'
34
-
35
- const app = createApp(App)
36
-
37
- app.use(InputStyle)
38
- app.mount('#app')
39
- ```
40
-
41
- Al instalar el plugin se registran automáticamente todos los componentes y las siguientes **propiedades globales** (disponibles en cualquier componente con la Options API):
42
-
43
- | Propiedad global | Equivale a | Descripción |
44
- | ---------------- | ---------- | ----------- |
45
- | `$alertstyle` | `AlertStyle` | Notificaciones tipo toast (ver [AlertStyle](#alertstyle-notificaciones-tipo-toast)). |
46
- | `$dialogstyle` | `DialogStyle` | Diálogos de confirmación / info / carga (ver [DialogStyle](#dialogstyle-diálogos-modales)). |
47
- | `$utilstyle` | `UtilStyle` | Utilidades internas (ej: generación de UUID). |
48
-
49
- ------
50
-
51
- # Estilos default
52
- Para cargar correctamente los estilos debes realizar algo como lo siguiente:
53
-
54
- Agrega el @source para indicar que el componente requiere de tailwindcss
55
-
56
- ```css
57
- @import "tailwindcss";
58
-
59
- @source "../node_modules/@luis-angel-martin-dzul/input-styles/**/*"
60
- ```
61
-
62
- # 🎨 Estilos personalizados (Style Registry)
63
-
64
- La librería expone un sistema de **tokens de estilo configurables** que permite cambiar la apariencia de todos los componentes desde un solo lugar, sin necesidad de modificar el código fuente.
65
-
66
- Los estilos se configuran **antes de montar la app**, importando `configureStyles` directamente.
67
-
68
- ## Opción A — vía `app.use` (recomendado)
69
-
70
- ```js
71
- import VueInputYuc from '@luis-angel-martin-dzul/input-styles'
72
-
73
- app.use(VueInputYuc, {
74
- styles: {
75
- INPUT_BASE: 'w-full px-0 py-2 bg-transparent border-b focus:outline-none ...',
76
- ICON_VALID: 'text-emerald-500',
77
- // solo los tok ens que quieras cambiar
78
- }
79
- })
80
- ```
81
-
82
- ## Opción B — importación directa
83
-
84
- ```js
85
- import { configureStyles } from '@luis-angel-martin-dzul/input-styles'
86
-
87
- configureStyles({
88
- LOADER_ARC_STROKE: '#3B82F6',
89
- REQUIRED_ASTERISK: 'text-red-500',
90
- })
91
- ```
92
-
93
- ⚠️ `configureStyles()` debe llamarse **antes** de que los componentes se monten. Los cambios no son reactivos después del montaje inicial.
94
-
95
- ------
96
-
97
- ## 📋 Referencia completa de tokens
98
-
99
- ### Tokens auxiliares de label
100
- _Afecta: todos los componentes que tienen `label` o `description`_
101
-
102
- Estos tokens controlan partes individuales del label y del texto descriptivo. Al sobrescribir uno, las funciones `inputLabelClass`, `inputCheckLabelClass` e `inputDescriptionClass` lo recogen automáticamente **sin necesidad de sobreescribir la función entera**.
103
-
104
- | Token | Tipo | Default | Descripción |
105
- | ----- | ---- | ------- | ----------- |
106
- | `INPUT_BASE_TITLE` | `string` | `'block text-sm font-medium'` | Clases estructurales del `<label>` de inputs de texto. |
107
- | `INPUT_CHECK_BASE_TITLE` | `string` | `'block text-sm font-medium'` | Clases estructurales del `<label>` de checkbox, radio y switch. |
108
- | `LABEL_DISABLED_STATIC` | `string` | `'text-gray-400'` | Color del label y descripción cuando el campo está deshabilitado. |
109
- | `LABEL_ENABLED_STATIC` | `string` | `'text-gray-700'` | Color del label cuando el campo está habilitado. |
110
-
111
- ```js
112
- configureStyles({
113
- LABEL_DISABLED_STATIC: 'text-gray-400',
114
- LABEL_ENABLED_STATIC: 'text-gray-900',
115
- INPUT_BASE_TITLE: 'block font-semibold mb-1 text-sm',
116
- })
117
- ```
118
-
119
- ---
120
-
121
- ### Inputs de texto
122
- _Afecta: `InputTextStyle`, `InputNumberStyle`, `InputPasswordStyle`, `TextAreaStyle`, `InputDateStyle`, `InputDateTimeStyle`, `InputTimeStyle`, `SelectStyle`_
123
-
124
- | Token | Tipo | Default | Descripción |
125
- | ----- | ---- | ------- | ----------- |
126
- | `INPUT_BASE` | `string` | `'w-full text-gray-800 bg-white px-3 py-1.5 rounded border ...'` | Clases base del `<input>`. No incluye borde ni ring (eso lo maneja `inputValidateClass`). |
127
- | `inputValidateClass` | `function(validate)` | Ver abajo | Retorna las clases de borde y ring según el estado de validación. `validate`: `true` / `false` / `null`. |
128
- | `inputLabelClass` | `function(disabled)` | Ver abajo | Retorna las clases del `<label>`. Internamente usa `INPUT_BASE_TITLE`, `LABEL_DISABLED_STATIC` y `LABEL_ENABLED_STATIC`. |
129
- | `inputDescriptionClass` | `function(validate, alignRight?)` | Ver abajo | Retorna las clases del texto de descripción. Internamente usa `ICON_VALID`, `ICON_INVALID` y `LABEL_DISABLED_STATIC`. |
130
-
131
- > 💡 Si solo quieres cambiar un color de validación, basta con sobrescribir `ICON_INVALID` o `ICON_VALID` — `inputDescriptionClass` los recoge automáticamente.
132
-
133
- ```js
134
- configureStyles({
135
- ICON_INVALID: 'text-red-500',
136
- ICON_VALID: 'text-emerald-500',
137
- })
138
-
139
- // Override completo de las funciones (cuando necesitas control total)
140
- configureStyles({
141
- inputValidateClass: (validate) => {
142
- if (validate === false) return 'border-red-500 focus:ring-red-200'
143
- if (validate === true) return 'border-emerald-500 focus:ring-emerald-200'
144
- return 'border-gray-200 focus:ring-indigo-200'
145
- },
146
- inputLabelClass: (disabled) => ['block font-bold mb-2', disabled ? 'text-gray-400' : 'text-gray-900'],
147
- inputDescriptionClass: (validate, alignRight = true) => [
148
- 'block w-full text-sm',
149
- alignRight && 'text-right',
150
- validate === false ? 'text-red-500' : validate === true ? 'text-emerald-500' : 'text-gray-500',
151
- ],
152
- })
153
- ```
154
-
155
- ---
156
-
157
- ### Select
158
- _Afecta: `SelectStyle`_
159
-
160
- | Token | Tipo | Default | Descripción |
161
- | ----- | ---- | ------- | ----------- |
162
- | `SELECT_DISABLED` | `string` | `'w-full px-3 py-1.5 rounded bg-gray-100 ...'` | Clases del input en estado disabled. |
163
- | `SELECT_DROPDOWN` | `string` | `'absolute z-20 w-full bg-white border ...'` | Clases del contenedor `<ul>` del dropdown. |
164
- | `SELECT_OPTION_BASE` | `string` | `'px-3 py-1 cursor-pointer text-md'` | Clases base de cada `<li>` del dropdown. |
165
- | `selectOptionClass` | `function(isSelected, isHighlighted)` | Ver abajo | Retorna clases dinámicas por opción según su estado. |
166
- | `SELECT_OPTGROUP` | `string` | `'px-2 pt-1 pb-0.5 text-xs font-semibold uppercase ...'` | Clases del encabezado de un grupo de opciones (`optgroup`). |
167
- | `SELECT_EMPTY` | `string` | `'px-3 py-1 text-gray-400 cursor-not-allowed'` | Clases del mensaje "Sin registros". |
168
-
169
- ```js
170
- configureStyles({
171
- selectOptionClass: (isSelected, isHighlighted) => {
172
- if (isSelected) return 'bg-indigo-100 text-indigo-900 font-medium'
173
- if (isHighlighted) return 'bg-indigo-50 text-gray-900'
174
- return 'hover:bg-indigo-50 text-gray-800'
175
- },
176
- })
177
- ```
178
-
179
- ---
180
-
181
- ### Checkbox, Radio y Switch
182
- _Afecta: `InputCheckStyle`, `InputRadioStyle`, `InputSwitchStyle`_
183
-
184
- | Token | Tipo | Default | Descripción |
185
- | ----- | ---- | ------- | ----------- |
186
- | `inputCheckLabelClass` | `function(disabled)` | Ver abajo | Clases del label de checks. |
187
- | `CHECK_BOX_BASE` | `string` | `'w-5 h-5 flex items-center justify-center rounded border-2 transition'` | Estructura base del cuadro del checkbox. |
188
- | `RADIO_BASE` | `string` | `'w-5 h-5 flex items-center justify-center rounded-full transition'` | Estructura base del círculo del radio. |
189
- | `SWITCH_BASE` | `string` | `'w-10 h-5 relative flex items-center rounded-full transition border-2'` | Estructura base de la pista del switch. |
190
- | `checkBoxClass` | `function(model, disabled)` | Ver abajo | Clases de color del checkbox según estado activo/disabled. |
191
- | `radioClass` | `function(model, disabled)` | Ver abajo | Clases de color del radio button según estado. |
192
- | `switchClass` | `function(model, disabled)` | Ver abajo | Clases de color de la pista del switch. |
193
- | `switchDotClass` | `function(model, disabled)` | Ver abajo | Clases de color de la bola interior del switch. |
194
-
195
- ```js
196
- configureStyles({
197
- checkBoxClass: (model, disabled) => {
198
- if (disabled) return model
199
- ? 'bg-indigo-200 border-indigo-200 cursor-not-allowed'
200
- : 'bg-gray-100 border-gray-200 cursor-not-allowed'
201
- return model
202
- ? 'bg-indigo-500 border-indigo-500 peer-focus:ring-2 peer-focus:ring-indigo-200'
203
- : 'bg-white border-gray-300 peer-focus:ring-2 peer-focus:ring-indigo-200'
204
- },
205
- })
206
- ```
207
-
208
- ---
209
-
210
- ### Paginación
211
- _Afecta: `PaginationStyle`_
212
-
213
- | Token | Tipo | Default | Descripción |
214
- | ----- | ---- | ------- | ----------- |
215
- | `PAGINATION_NAV_PREV` | `string` | `'bg-white hover:bg-indigo-50 border ...'` | Clases del botón de página anterior. |
216
- | `PAGINATION_NAV_NEXT` | `string` | `'bg-white hover:bg-indigo-50 border ...'` | Clases del botón de página siguiente. |
217
- | `PAGINATION_PAGE_BASE` | `string` | `'border px-2.5 py-2 cursor-pointer w-8 relative'` | Clases base de cada botón de página. |
218
- | `paginationPageClass` | `function(page, actualPage, index)` | Ver abajo | Retorna clases dinámicas por botón de página. |
219
- | `PAGINATION_SUMMARY` | `string` | `'text-sm text-gray-700'` | Clases del texto de resumen de registros. |
220
-
221
- ---
222
-
223
- ### Loader
224
- _Afecta: `LoaderStyle`_
225
-
226
- | Token | Tipo | Default | Descripción |
227
- | ----- | ---- | ------- | ----------- |
228
- | `LOADER_ARC_STROKE` | `string` | `'#6366F1'` | Color hex del arco animado del loader (indigo-500). |
229
- | `LOADER_MESSAGE` | `string` | `'text-gray-600 text-lg font-medium'` | Clases del mensaje de carga. |
230
-
231
- ---
232
-
233
- ### Iconos y asterisco de requerido
234
- _Afecta: todos los componentes con `required` o iconos de validación_
235
-
236
- | Token | Tipo | Default | Descripción |
237
- | ----- | ---- | ------- | ----------- |
238
- | `REQUIRED_ASTERISK` | `string` | `'text-red-500'` | Clase de color del asterisco `*` en campos requeridos. |
239
- | `ICON_VALID` | `string` | `'text-emerald-500'` | Clase de color del ícono de validación correcta. |
240
- | `ICON_INVALID` | `string` | `'text-red-500'` | Clase de color del ícono de validación errónea. |
241
- | `ICON_STATIC` | `string` | `'text-indigo-400'` | Clase de color de los íconos estáticos (reloj, calendario, ojo, chevron). |
242
- | `ICON_SIZE_STATIC` | `string` | `'w-5 h-5'` | Clases de tamaño de todos los íconos. |
243
-
244
- ---
245
-
246
- ### File Upload y Download
247
- _Afecta: `InputFileUploadStyle`, `InputFileDownloadStyle`_
248
-
249
- | Token | Tipo | Default | Descripción |
250
- | ----- | ---- | ------- | ----------- |
251
- | `FILE_BUTTON_BASE` | `string` | `'flex items-center justify-center gap-3 px-4 py-2 border-2 rounded-xl ...'` | Clases estructurales del botón (compartido entre upload y download). |
252
- | `FILE_BUTTON_ACTIVE` | `string` | `'border-gray-200 bg-white text-gray-700 hover:border-indigo-400 ...'` | Clases del botón en estado normal/activo. |
253
- | `FILE_BUTTON_DISABLED` | `string` | `'border-gray-200 bg-gray-100 text-gray-400'` | Clases del botón en estado disabled. |
254
- | `FILE_ICON_BG` | `string` | `'bg-indigo-300'` | Fondo del círculo de ícono en estado activo. |
255
- | `FILE_ICON_BG_COMPLETE` | `string` | `'bg-emerald-500'` | Fondo del círculo de ícono cuando la descarga está completa. |
256
- | `FILE_ICON_BG_DISABLED` | `string` | `'bg-gray-300'` | Fondo del círculo de ícono en estado disabled. |
257
-
258
- ---
259
-
260
- ### Time Picker
261
- _Afecta: `InputTimeStyle`_
262
-
263
- | Token | Tipo | Default | Descripción |
264
- | ----- | ---- | ------- | ----------- |
265
- | `TIME_PICKER_WRAPPER` | `string` | `'absolute z-20 left-0 top-full rounded-lg bg-white'` | Contenedor flotante del picker. |
266
- | `TIME_PICKER_CONTAINER` | `string` | `'border border-gray-300 rounded-lg p-3'` | Borde interior del picker. |
267
- | `TIME_PICKER_COLUMN` | `string` | `'h-48 overflow-y-auto border border-gray-200 rounded-md ...'` | Columna scrollable de horas/minutos. |
268
- | `TIME_PICKER_AMPM_COLUMN` | `string` | `'h-48 overflow-y-auto border border-gray-200 rounded-md ...'` | Columna scrollable AM/PM. |
269
- | `TIME_PICKER_COLUMN_LABEL` | `string` | `'block text-xs font-medium text-gray-500 mb-1 text-center'` | Label de cada columna ("Hora", "Minuto", "AM/PM"). |
270
- | `TIME_PICKER_ITEM` | `string` | `'px-3 py-1 text-center cursor-pointer hover:bg-indigo-50 ...'` | Fila base de cada ítem del picker. |
271
- | `timePickerItemClass` | `function(isActive)` | Ver abajo | Clases dinámicas de cada ítem según si está seleccionado. |
272
-
273
- ------
274
-
275
- # 🧩 Componentes
276
-
277
- ## Props comunes
278
-
279
- La mayoría de los componentes comparten las siguientes props:
280
-
281
- | Prop | Tipo | Default | Descripción |
282
- | ---- | ---- | ------- | ----------- |
283
- | `modelValue` | `String \| Number \| Boolean` | — | Valor del campo (v-model). |
284
- | `label` | `String` | `''` | Texto del label del campo. |
285
- | `description` | `String` | `''` | Texto de ayuda debajo del campo. Cambia de color según el estado de validación. |
286
- | `placeholder` | `String` | `''` | Placeholder del input. |
287
- | `disabled` | `Boolean` | `false` | Deshabilita el campo. |
288
- | `required` | `Boolean` | `false` | Marca el campo como requerido (muestra asterisco `*`). |
289
- | `validate` | `Boolean \| null` | `null` | Estado de validación: `true` = válido, `false` = inválido, `null` = sin estado. |
290
- | `mclass` | `String` | `null` | Clases CSS adicionales aplicadas directamente al elemento del input. |
291
-
292
- ## Eventos comunes
293
-
294
- | Evento | Payload | Descripción |
295
- | ------ | ------- | ----------- |
296
- | `update:modelValue` | `value` | Se dispara al cambiar el valor (permite `v-model`). |
297
- | `change` | `value` | Se dispara al cambiar el valor. |
298
- | `blur` | `event` | Se dispara al perder el foco. |
299
-
300
- ------
301
-
302
- ## InputTextStyle
303
-
304
- Input de texto genérico. Soporta tipos `text`, `email`, `search`, `tel`, `url` y `number`.
305
-
306
- ```vue
307
- <InputTextStyle
308
- v-model="nombre"
309
- label="Nombre"
310
- placeholder="Escribe tu nombre"
311
- :required="true"
312
- :validate="validate"
313
- description="Campo requerido"
314
- />
315
- ```
316
-
317
- | Prop | Tipo | Default | Descripción |
318
- | ---- | ---- | ------- | ----------- |
319
- | `modelValue` | `String \| Number` | `''` | Valor del campo. |
320
- | `label` | `String` | `''` | |
321
- | `description` | `String` | `''` | |
322
- | `placeholder` | `String` | `''` | |
323
- | `type` | `String` | `'text'` | Tipo del input: `text`, `email`, `search`, `tel`, `url`, `number`. |
324
- | `maxlength` | `String` | `null` | Longitud máxima de caracteres. |
325
- | `minlength` | `String` | `null` | Longitud mínima de caracteres. |
326
- | `min` | `String` | `null` | Valor mínimo (solo cuando `type="number"`, aplicado al perder el foco). |
327
- | `max` | `String` | `null` | Valor máximo (solo cuando `type="number"`, aplicado al perder el foco). |
328
- | `disabled` | `Boolean` | `false` | |
329
- | `required` | `Boolean` | `false` | |
330
- | `validate` | `Boolean \| null` | `null` | |
331
- | `mclass` | `String` | `null` | |
332
-
333
- ------
334
-
335
- ## InputNumberStyle
336
-
337
- Input numérico con control preciso de dígitos, decimales, rango y formato de visualización. El valor se emite siempre como string numérico limpio (ej: `"1234.56"`). Al perder el foco se formatea con `toLocaleString`.
338
-
339
- ```vue
340
- <InputNumberStyle
341
- v-model="monto"
342
- label="Monto"
343
- numeric="10,2"
344
- min="0"
345
- max="9999999999"
346
- :required="true"
347
- :validate="validate"
348
- />
349
- ```
350
-
351
- | Prop | Tipo | Default | Descripción |
352
- | ---- | ---- | ------- | ----------- |
353
- | `modelValue` | `String \| Number` | `''` | Valor del campo. |
354
- | `label` | `String` | `''` | |
355
- | `description` | `String` | `''` | |
356
- | `placeholder` | `String` | `''` | |
357
- | `numeric` | `String` | `null` | Formato tipo SQL: `"10,2"` → 10 dígitos enteros, 2 decimales. Si se omite, usa `10,2` por defecto. |
358
- | `min` | `String` | `null` | Valor mínimo (aplicado al perder el foco). Si es `null` o negativo, permite negativos. |
359
- | `max` | `String` | `null` | Valor máximo (aplicado al perder el foco). |
360
- | `disabled` | `Boolean` | `false` | |
361
- | `required` | `Boolean` | `false` | |
362
- | `validate` | `Boolean \| null` | `null` | |
363
- | `mclass` | `String` | `null` | |
364
-
365
- **Comportamiento:**
366
- - Puedes escribir `0` y luego `.` para empezar los decimales: el punto se conserva a la espera de los dígitos decimales (solo si los decimales están activados en `numeric`).
367
- - Al **enfocar** el campo, si el valor es `0` (o está vacío) se limpia, para que al escribir reemplaces el valor en lugar de anteponerlo.
368
- - Al **perder el foco**, si el campo queda vacío o en `0`, se conserva el valor `0` formateado (ej: `"0.00"`), aplicando además los límites `min`/`max`.
369
-
370
- **Navegación por teclado:**
371
- - `↑` / `↓` — aumentar/disminuir el valor en unidades, respetando `min`/`max`.
372
-
373
- ------
374
-
375
- ## InputPasswordStyle
376
-
377
- Input de contraseña con toggle para mostrar/ocultar el texto.
378
-
379
- ```vue
380
- <InputPasswordStyle
381
- v-model="password"
382
- label="Contraseña"
383
- placeholder="Mínimo 8 caracteres"
384
- :required="true"
385
- :validate="validate"
386
- />
387
- ```
388
-
389
- | Prop | Tipo | Default | Descripción |
390
- | ---- | ---- | ------- | ----------- |
391
- | `modelValue` | `String \| Number` | `''` | Valor del campo. |
392
- | `label` | `String` | `''` | |
393
- | `description` | `String` | `''` | |
394
- | `placeholder` | `String` | `''` | |
395
- | `maxlength` | `String` | `null` | Longitud máxima. |
396
- | `disabled` | `Boolean` | `false` | |
397
- | `required` | `Boolean` | `false` | |
398
- | `validate` | `Boolean \| null` | `null` | |
399
- | `mclass` | `String` | `null` | |
400
-
401
- ------
402
-
403
- ## TextAreaStyle
404
-
405
- Área de texto multi-línea.
406
-
407
- ```vue
408
- <TextAreaStyle
409
- v-model="comentario"
410
- label="Comentario"
411
- placeholder="Escribe aquí..."
412
- row="5"
413
- :maxlength="500"
414
- :validate="validate"
415
- />
416
- ```
417
-
418
- | Prop | Tipo | Default | Descripción |
419
- | ---- | ---- | ------- | ----------- |
420
- | `modelValue` | `String` | `''` | Valor del campo. |
421
- | `label` | `String` | `''` | |
422
- | `description` | `String` | `''` | |
423
- | `placeholder` | `String` | `''` | |
424
- | `row` | `String` | `'3'` | Número de filas visibles del textarea. |
425
- | `maxlength` | `String` | `null` | Longitud máxima de caracteres. |
426
- | `disabled` | `Boolean` | `false` | |
427
- | `required` | `Boolean` | `false` | |
428
- | `validate` | `Boolean \| null` | `null` | |
429
- | `mclass` | `String` | `null` | |
430
-
431
- ------
432
-
433
- ## SelectStyle
434
-
435
- Select personalizado con dropdown, navegación por teclado y búsqueda por tipeo. El dropdown se posiciona automáticamente arriba o abajo según el espacio disponible.
436
-
437
- ```vue
438
- <SelectStyle
439
- v-model="pais"
440
- label="País"
441
- :list="paises"
442
- return-value="value"
443
- :required="true"
444
- :validate="validate"
445
- />
446
- ```
447
-
448
- ```js
449
- // Formato del array `list` — lista plana
450
- const paises = [
451
- { value: 1, text: 'México' },
452
- { value: 2, text: 'España' },
453
- { value: 3, text: 'Argentina' },
454
- ]
455
-
456
- // El `value` es opcional: si se omite, se usa el `text` como valor
457
- const colores = [
458
- { text: 'Rojo' },
459
- { text: 'Verde' },
460
- ]
461
-
462
- // Lista agrupada — cada grupo tiene un `label` y sus `options`
463
- const ubicaciones = [
464
- { label: 'América', options: [{ value: 1, text: 'México' }, { value: 2, text: 'Argentina' }] },
465
- { label: 'Europa', options: [{ value: 3, text: 'España' }, { value: 4, text: 'Francia' }] },
466
- ]
467
- ```
468
-
469
- | Prop | Tipo | Default | Descripción |
470
- | ---- | ---- | ------- | ----------- |
471
- | `modelValue` | `String \| Number \| Object` | — | Valor seleccionado. |
472
- | `list` | `Array` | `[]` | Array de opciones. Acepta lista plana `{ value, text }` o agrupada `{ label, options: [...] }`. El `value` es opcional: si se omite, se usa el `text` como valor. |
473
- | `returnValue` | `String` | `'value'` | Qué se emite al seleccionar: `'value'` (item.value), `'text'` (item.text) u `'object'` (el item completo). |
474
- | `label` | `String` | `''` | |
475
- | `description` | `String` | `''` | |
476
- | `wrapOptions` | `Boolean` | `false` | `true` = permite que el texto de cada opción haga salto de línea; `false` = lo trunca con elipsis. |
477
- | `disabled` | `Boolean` | `false` | |
478
- | `required` | `Boolean` | `false` | |
479
- | `validate` | `Boolean \| null` | `null` | |
480
- | `mclass` | `String` | `null` | |
481
-
482
- **Navegación por teclado:**
483
- - `↑` / `↓` — navegar opciones
484
- - `Enter` — seleccionar la opción resaltada
485
- - `Escape` — cerrar el dropdown
486
- - Tipear letras — búsqueda inmediata en la lista
487
-
488
- ------
489
-
490
- ## InputCheckStyle
491
-
492
- Checkbox estilizado con label y descripción.
493
-
494
- ```vue
495
- <InputCheckStyle
496
- v-model="aceptaTerminos"
497
- label="Acepto los términos y condiciones"
498
- :required="true"
499
- />
500
- ```
501
-
502
- | Prop | Tipo | Default | Descripción |
503
- | ---- | ---- | ------- | ----------- |
504
- | `modelValue` | `Boolean` | `false` | Estado del checkbox. |
505
- | `label` | `String` | `''` | |
506
- | `description` | `String` | `''` | |
507
- | `disabled` | `Boolean` | `false` | |
508
- | `required` | `Boolean` | `false` | |
509
- | `validate` | `Boolean \| null` | `null` | |
510
- | `mclass` | `String` | `null` | Clases adicionales del cuadro del checkbox. |
511
-
512
- ------
513
-
514
- ## InputRadioStyle
515
-
516
- Radio button estilizado con label y descripción.
517
-
518
- ```vue
519
- <InputRadioStyle
520
- v-model="opcionA"
521
- label="Opción A"
522
- />
523
- ```
524
-
525
- | Prop | Tipo | Default | Descripción |
526
- | ---- | ---- | ------- | ----------- |
527
- | `modelValue` | `Boolean` | `false` | Estado del radio. |
528
- | `label` | `String` | `''` | |
529
- | `description` | `String` | `''` | |
530
- | `disabled` | `Boolean` | `false` | |
531
- | `required` | `Boolean` | `false` | |
532
- | `validate` | `Boolean \| null` | `null` | |
533
- | `mclass` | `String` | `null` | Clases adicionales del círculo del radio. |
534
-
535
- ------
536
-
537
- ## InputSwitchStyle
538
-
539
- Toggle switch estilizado.
540
-
541
- ```vue
542
- <InputSwitchStyle
543
- v-model="activo"
544
- label="Activar notificaciones"
545
- />
546
- ```
547
-
548
- | Prop | Tipo | Default | Descripción |
549
- | ---- | ---- | ------- | ----------- |
550
- | `modelValue` | `Boolean` | `false` | Estado del switch. |
551
- | `label` | `String` | `''` | |
552
- | `description` | `String` | `''` | |
553
- | `disabled` | `Boolean` | `false` | |
554
- | `required` | `Boolean` | `false` | |
555
- | `validate` | `Boolean \| null` | `null` | |
556
- | `mclass` | `String` | `null` | Clases adicionales de la pista del switch. |
557
-
558
- ------
559
-
560
- ## InputDateStyle
561
-
562
- Selector de fecha con máscara `dd/mm/aaaa`, navegación por teclado entre secciones y calendario visual nativo (sin dependencias externas). El valor se emite en formato `YYYY-MM-DD`.
563
-
564
- El calendario incluye vistas de días, meses y años, y un botón **Hoy** para navegar rápidamente a la fecha actual.
565
-
566
- ```vue
567
- <InputDateStyle
568
- v-model="fechaNacimiento"
569
- label="Fecha de nacimiento"
570
- min="1900-01-01"
571
- max="2100-12-31"
572
- :validate="validate"
573
- />
574
- ```
575
-
576
- | Prop | Tipo | Default | Descripción |
577
- | ---- | ---- | ------- | ----------- |
578
- | `modelValue` | `String \| Date` | — | Valor de la fecha en formato `YYYY-MM-DD` o `Date`. |
579
- | `label` | `String` | `''` | |
580
- | `description` | `String` | `''` | |
581
- | `min` | `String` | `'1900-01-01'` | Fecha mínima seleccionable en el calendario (formato `YYYY-MM-DD`). |
582
- | `max` | `String` | `'2100-12-31'` | Fecha máxima seleccionable en el calendario (formato `YYYY-MM-DD`). |
583
- | `disabledDates` | `Array` | `[]` | Fechas deshabilitadas. Array de strings `'YYYY-MM-DD'` o objetos `Date`. |
584
- | `disabled` | `Boolean` | `false` | |
585
- | `readonly` | `Boolean` | `false` | Solo lectura: muestra el valor pero bloquea la edición por teclado. |
586
- | `required` | `Boolean` | `false` | |
587
- | `validate` | `Boolean \| null` | `null` | |
588
- | `mclass` | `String` | `null` | |
589
-
590
- **Valor emitido:** `'YYYY-MM-DD'` (string) o `''` si la fecha es inválida o está vacía.
591
-
592
- **Navegación por teclado:**
593
- - Escribe los dígitos y el foco avanza automáticamente entre día → mes → año
594
- - `↑` / `↓` — incrementar/decrementar la sección activa (la fecha resultante se ajusta a los límites `min`/`max`)
595
- - `←` / `→` — mover entre secciones
596
- - `Tab` / `Shift + Tab` — mover entre secciones (al llegar al extremo, el foco sale del campo)
597
- - `Backspace` / `Delete` — limpiar la sección activa
598
- - Clic en el ícono 📅 — abre/cierra el calendario visual
599
-
600
- ------
601
-
602
- ## InputTimeStyle
603
-
604
- Selector de hora con picker de columnas scrollables. Soporta formato 12h (AM/PM) o 24h. El valor se emite en formato `HH:mm`.
605
-
606
- ```vue
607
- <InputTimeStyle
608
- v-model="horaInicio"
609
- label="Hora de inicio"
610
- min="08:00"
611
- max="18:00"
612
- :is24h="true"
613
- :validate="validate"
614
- />
615
- ```
616
-
617
- | Prop | Tipo | Default | Descripción |
618
- | ---- | ---- | ------- | ----------- |
619
- | `modelValue` | `String` | — | Valor de la hora en formato `HH:mm`. |
620
- | `label` | `String` | `''` | |
621
- | `description` | `String` | `''` | |
622
- | `min` | `String` | `null` | Hora mínima (ej: `'08:00'`). |
623
- | `max` | `String` | `null` | Hora máxima (ej: `'18:00'`). |
624
- | `is24h` | `Boolean` | `false` | `true` = formato 24h, `false` = formato 12h con selector AM/PM. |
625
- | `disabled` | `Boolean` | `false` | |
626
- | `required` | `Boolean` | `false` | |
627
- | `validate` | `Boolean \| null` | `null` | |
628
- | `mclass` | `String` | `null` | |
629
-
630
- **Valor emitido:** `'HH:mm'` en formato 24h siempre, independientemente del modo de visualización.
631
-
632
- ------
633
-
634
- ## InputDateTimeStyle
635
-
636
- Selector combinado de fecha y hora con máscara `dd/mm/aaaa hh:mm` (o `dd/mm/aaaa hh:mm a.m.` en modo 12h), calendario visual nativo y picker de columnas scrollables para la hora. El valor se emite en formato `YYYY-MM-DDTHH:mm:00.000`.
637
-
638
- ```vue
639
- <InputDateTimeStyle
640
- v-model="fechaHora"
641
- label="Fecha y hora"
642
- min="2000-01-01"
643
- max="2100-12-31"
644
- :is24h="true"
645
- :validate="validate"
646
- />
647
- ```
648
-
649
- | Prop | Tipo | Default | Descripción |
650
- | ---- | ---- | ------- | ----------- |
651
- | `modelValue` | `String \| Date` | — | Valor en formato `YYYY-MM-DDTHH:mm:00.000` o `Date`. |
652
- | `label` | `String` | `''` | |
653
- | `description` | `String` | `''` | |
654
- | `min` | `String` | `'1900-01-01'` | Fecha mínima seleccionable (formato `YYYY-MM-DD`). |
655
- | `max` | `String` | `'2100-12-31'` | Fecha máxima seleccionable (formato `YYYY-MM-DD`). |
656
- | `disabledDates` | `Array` | `[]` | Fechas deshabilitadas. Array de strings `'YYYY-MM-DD'` o `Date`. |
657
- | `is24h` | `Boolean` | `false` | `true` = formato 24h, `false` = formato 12h con selector a.m./p.m. |
658
- | `disabled` | `Boolean` | `false` | |
659
- | `readonly` | `Boolean` | `false` | Solo lectura. |
660
- | `required` | `Boolean` | `false` | |
661
- | `validate` | `Boolean \| null` | `null` | |
662
- | `mclass` | `String` | `null` | |
663
-
664
- **Valor emitido:** `'YYYY-MM-DDTHH:mm:00.000'` (string) o `''` si la fecha/hora es inválida o está vacía.
665
-
666
- **Comportamiento del picker:**
667
- - El calendario permanece abierto al seleccionar la fecha, para que puedas ajustar también la hora.
668
- - El selector de hora embebido en el picker usa las mismas columnas scrollables que `InputTimeStyle`.
669
- - Botón **Ahora** — establece la fecha y hora actuales de un solo clic.
670
-
671
- **Navegación por teclado:**
672
- - Escribe los dígitos y el foco avanza automáticamente: día → mes → año → hora → minuto → a.m./p.m.
673
- - `↑` / `↓` — incrementar/decrementar la sección activa (la fecha se ajusta a los límites `min`/`max`)
674
- - `←` / `→` — mover entre secciones
675
- - `Tab` / `Shift + Tab` — mover entre secciones (al llegar al extremo, el foco sale del campo)
676
- - `Backspace` / `Delete` — limpiar la sección activa
677
- - `a` / `p` — cambiar entre a.m. y p.m. (solo modo 12h)
678
- - Clic en el ícono 📅 — abre/cierra el picker
679
-
680
- ------
681
-
682
- ## InputFileUploadStyle
683
-
684
- Botón para adjuntar archivos con validación de tamaño y formato.
685
-
686
- ```vue
687
- <InputFileUploadStyle
688
- v-model="archivo"
689
- :formats="['pdf', 'docx']"
690
- max-size-m-b="10"
691
- :state="uploadState"
692
- />
693
- ```
694
-
695
- | Prop | Tipo | Default | Descripción |
696
- | ---- | ---- | ------- | ----------- |
697
- | `modelValue` | `File` | `null` | Archivo seleccionado. |
698
- | `disabled` | `Boolean` | `false` | Deshabilita el botón. |
699
- | `formats` | `Array` | `[]` | Extensiones permitidas (ej: `['pdf', 'xlsx', 'png']`). Array vacío = todos los formatos. |
700
- | `maxSizeMB` | `String \| Number` | `5` | Tamaño máximo del archivo en MB. |
701
- | `state` | `String` | `'uphold'` | Estado del botón: `'uphold'` (normal) o `'loader'` (cargando, muestra spinner). |
702
- | `delay` | `String` | `'1.5'` | Segundos de bloqueo del botón tras un clic, para evitar doble envío. |
703
-
704
- **Eventos:**
705
- - `update:modelValue` — emite el `File` seleccionado, o `null` si el archivo no es válido.
706
- - `change` — igual que `update:modelValue`.
707
-
708
- El componente valida automáticamente el tamaño y el formato, mostrando un mensaje de error inline si la validación falla.
709
-
710
- ------
711
-
712
- ## InputFileDownloadStyle
713
-
714
- Botón para descargar archivos con tres estados visuales.
715
-
716
- ```vue
717
- <InputFileDownloadStyle
718
- :state="downloadState"
719
- :disabled="false"
720
- @click="descargarArchivo"
721
- />
722
- ```
723
-
724
- | Prop | Tipo | Default | Descripción |
725
- | ---- | ---- | ------- | ----------- |
726
- | `disabled` | `Boolean` | `false` | Deshabilita el botón. |
727
- | `state` | `String` | `'download'` | Estado: `'download'` (normal), `'loader'` (descargando, spinner), `'complete'` (completado, check verde). |
728
- | `delay` | `String` | `'1.5'` | Segundos de bloqueo del botón tras un clic. |
729
-
730
- **Eventos:**
731
- - `click` — se dispara al hacer clic (solo si no está deshabilitado).
732
-
733
- ------
734
-
735
- ## ButtonStyle
736
-
737
- Botón genérico con bloqueo automático anti-doble-clic.
738
-
739
- ```vue
740
- <ButtonStyle
741
- class="px-4 py-2 bg-indigo-500 text-white rounded"
742
- :disabled="cargando"
743
- @click="guardar"
744
- >
745
- Guardar
746
- </ButtonStyle>
747
- ```
748
-
749
- | Prop | Tipo | Default | Descripción |
750
- | ---- | ---- | ------- | ----------- |
751
- | `type` | `String` | `'button'` | Tipo del botón: `'button'` o `'submit'`. |
752
- | `class` | `String` | `''` | Clases CSS del botón (TailwindCSS). |
753
- | `disabled` | `Boolean` | `false` | Deshabilita el botón. |
754
- | `delay` | `String` | `'1.5'` | Segundos de bloqueo automático tras hacer clic (anti-doble-clic). |
755
-
756
- **Eventos:**
757
- - `click` — se dispara al hacer clic (solo si no está deshabilitado ni en periodo de bloqueo).
758
-
759
- Acepta contenido mediante `<slot>` (texto, íconos, etc.).
760
-
761
- ------
762
-
763
- ## LoaderStyle
764
-
765
- Indicador de carga circular animado.
766
-
767
- ```vue
768
- <LoaderStyle message="Procesando datos..." />
769
- ```
770
-
771
- | Prop | Tipo | Default | Descripción |
772
- | ---- | ---- | ------- | ----------- |
773
- | `message` | `String` | `'Cargando...'` | Texto que se muestra debajo del arco animado. |
774
-
775
- El color del arco se controla con el token `LOADER_ARC_STROKE`.
776
-
777
- ------
778
-
779
- ## PaginationStyle
780
-
781
- Paginador con navegación anterior/siguiente, botones de página y resumen de registros.
782
-
783
- ```vue
784
- <PaginationStyle
785
- :total-pages="totalPaginas"
786
- :actual-page="paginaActual"
787
- :records="totalRegistros"
788
- :limit="registrosPorPagina"
789
- :summary="true"
790
- @page="cambiarPagina"
791
- />
792
- ```
793
-
794
- | Prop | Tipo | Default | Descripción |
795
- | ---- | ---- | ------- | ----------- |
796
- | `totalPages` | `Number` | `0` | Total de páginas. |
797
- | `actualPage` | `Number` | `1` | Página actualmente activa. |
798
- | `records` | `Number` | `0` | Total de registros (para el resumen). |
799
- | `limit` | `Number` | `0` | Registros por página (para calcular el rango del resumen). |
800
- | `summary` | `Boolean` | `false` | `true` = muestra el texto "Mostrando X - Y de Z". |
801
-
802
- **Eventos:**
803
- - `page` — emite el número de página a la que se desea navegar.
804
-
805
- El paginador muestra hasta 7 botones con elipsis (`...`) inteligentes cuando hay más páginas.
806
-
807
- ------
808
-
809
- ## ModalStyle
810
-
811
- Ventana modal con `Teleport` al `body`, animaciones de entrada/salida, bloqueo del scroll de fondo y cierre por tecla `Escape`. El estado de apertura se controla con `v-model`.
812
-
813
- ```vue
814
- <ButtonStyle @click="showModal = true">Abrir modal</ButtonStyle>
815
-
816
- <ModalStyle v-model="showModal" title="Detalles" size="lg">
817
- <p>Contenido del modal...</p>
818
-
819
- <template #footer="{ close }">
820
- <div class="flex justify-end gap-2">
821
- <ButtonStyle class="px-4 py-2 border rounded" @click="close">Cerrar</ButtonStyle>
822
- </div>
823
- </template>
824
- </ModalStyle>
825
- ```
826
-
827
- | Prop | Tipo | Default | Descripción |
828
- | ---- | ---- | ------- | ----------- |
829
- | `modelValue` | `Boolean` | `false` | Controla la visibilidad del modal (`v-model`). |
830
- | `title` | `String` | `'Sin titulo asignado'` | Título mostrado en el header (si no se usa el slot `title`). |
831
- | `size` | `String` | `'lg'` | Ancho máximo: `'sm'`, `'md'`, `'lg'`, `'xl'`, `'2xl'`, `'3xl'`, `'4xl'`, `'5xl'`, `'full'`. |
832
- | `showCloseButton` | `Boolean` | `true` | Muestra el botón `✕` de cierre en el header. |
833
- | `closeOnEscape` | `Boolean` | `true` | Permite cerrar el modal con la tecla `Escape`. |
834
- | `closeOnOverlayClick` | `Boolean` | `true` | Permite cerrar el modal al hacer clic en el fondo. |
835
-
836
- **Slots:**
837
- - `title` — reemplaza el título del header.
838
- - *default* — contenido principal. Recibe `{ close }` para cerrar desde dentro.
839
- - `footer` — pie del modal (solo se renderiza si se provee). Recibe `{ close }`.
840
-
841
- **Eventos:**
842
- - `update:modelValue` — emite `false` al cerrarse.
843
-
844
- ------
845
-
846
- ## AlertStyle (notificaciones tipo toast)
847
-
848
- Utilidad JavaScript (no es un componente) para mostrar notificaciones flotantes que se auto-descartan. Disponible como propiedad global `$alertstyle` tras instalar el plugin, o mediante importación directa.
849
-
850
- ```js
851
- // Vía global property (Options API)
852
- this.$alertstyle.success('Guardado', 'Los cambios se guardaron correctamente')
853
-
854
- // Vía importación directa
855
- import { AlertStyle } from '@luis-angel-martin-dzul/input-styles'
856
- AlertStyle.error('Error', 'No se pudo completar la operación')
857
- ```
858
-
859
- | Método | Parámetros | Descripción |
860
- | ------ | ---------- | ----------- |
861
- | `success(title, message?)` | `String, String` | Notificación verde de éxito. |
862
- | `error(title, message?)` | `String, String` | Notificación roja de error. |
863
- | `warning(title, message?)` | `String, String` | Notificación de advertencia. |
864
- | `info(title, message?)` | `String, String` | Notificación informativa. |
865
- | `neutral(title, message?)` | `String, String` | Notificación neutral. |
866
-
867
- Cada notificación se cierra automáticamente a los 4 segundos o manualmente con su botón `×`.
868
-
869
- ------
870
-
871
- ## DialogStyle (diálogos modales)
872
-
873
- Utilidad JavaScript para diálogos de confirmación, información y carga, con interfaz fluida (encadenable). Disponible como global `$dialogstyle` o por importación directa.
874
-
875
- ```js
876
- // Confirmación
877
- this.$dialogstyle.confirm('¿Eliminar registro?', 'Esta acción no se puede deshacer')
878
- .onAccept(() => eliminar())
879
- .onCancel(() => console.log('Cancelado'))
880
-
881
- // Información (un solo botón)
882
- this.$dialogstyle.info('Operación completada')
883
- .onConfirm(() => console.log('Aceptado'))
884
-
885
- // Carga (bloqueante, se cierra manualmente)
886
- const loader = this.$dialogstyle.loading('Procesando...', 'Espere un momento')
887
- loader.updateMessage('Casi listo...')
888
- loader.close()
889
- ```
890
-
891
- | Método | Retorna | Descripción |
892
- | ------ | ------- | ----------- |
893
- | `confirm(title, description?)` | controlador con `.onAccept(cb)` / `.onCancel(cb)` | Diálogo con botones Aceptar / Cancelar. |
894
- | `info(title, description?)` | controlador con `.onConfirm(cb)` | Diálogo informativo con un solo botón Aceptar. |
895
- | `loading(title, description?)` | `{ close, updateTitle, updateMessage }` | Diálogo de carga con spinner; se cierra con `.close()`. |
896
- | `finish(immediate?)` | `Promise` | Cierra el diálogo abierto manualmente. |
897
-
898
- > 💡 La tecla `Escape` no cierra el diálogo (hace una animación de "shake"), para forzar una decisión explícita del usuario.
899
-
900
- ------
901
-
902
- ## Ejemplo de uso completo
903
-
904
- ```vue
905
- <template>
906
- <div class="p-6 space-y-4 max-w-md">
907
-
908
- <InputTextStyle
909
- v-model="form.nombre"
910
- label="Nombre completo"
911
- placeholder="Ej: Juan Pérez"
912
- :required="true"
913
- :validate="validaciones.nombre"
914
- />
915
-
916
- <InputNumberStyle
917
- v-model="form.monto"
918
- label="Monto"
919
- numeric="10,2"
920
- min="0"
921
- :validate="validaciones.monto"
922
- />
923
-
924
- <InputPasswordStyle
925
- v-model="form.password"
926
- label="Contraseña"
927
- :required="true"
928
- :validate="validaciones.password"
929
- />
930
-
931
- <SelectStyle
932
- v-model="form.pais"
933
- label="País"
934
- :list="paises"
935
- return-value="value"
936
- :validate="validaciones.pais"
937
- />
938
-
939
- <InputDateStyle
940
- v-model="form.fecha"
941
- label="Fecha"
942
- :validate="validaciones.fecha"
943
- />
944
-
945
- <InputDateTimeStyle
946
- v-model="form.fechaHora"
947
- label="Fecha y hora"
948
- :is24h="true"
949
- :validate="validaciones.fechaHora"
950
- />
951
-
952
- <InputTimeStyle
953
- v-model="form.hora"
954
- label="Hora"
955
- :is24h="true"
956
- />
957
-
958
- <InputCheckStyle
959
- v-model="form.acepta"
960
- label="Acepto los términos"
961
- :required="true"
962
- />
963
-
964
- <ButtonStyle
965
- class="w-full px-4 py-2 bg-indigo-500 text-white rounded hover:bg-indigo-600"
966
- @click="enviar"
967
- >
968
- Enviar
969
- </ButtonStyle>
970
-
971
- </div>
972
- </template>
973
-
974
- <script setup>
975
- import { ref } from 'vue'
976
- import {
977
- InputTextStyle, InputNumberStyle, InputPasswordStyle,
978
- SelectStyle, InputDateStyle, InputDateTimeStyle, InputTimeStyle,
979
- InputCheckStyle, ButtonStyle
980
- } from '@luis-angel-martin-dzul/input-styles'
981
-
982
- const form = ref({
983
- nombre: '', monto: '', password: '',
984
- pais: null, fecha: '', fechaHora: '', hora: '', acepta: false
985
- })
986
-
987
- const validaciones = ref({
988
- nombre: null, monto: null, password: null,
989
- pais: null, fecha: null, fechaHora: null
990
- })
991
-
992
- const paises = [
993
- { value: 1, text: 'México' },
994
- { value: 2, text: 'España' },
995
- ]
996
-
997
- function enviar() {
998
- validaciones.value.nombre = form.value.nombre.length > 0
999
- // ... resto de validaciones
1000
- }
1001
- </script>
1002
- ```
1
+ # 📦 Input Style
2
+
3
+ **Librería de componentes y formularios Vue 3**
4
+
5
+ Incluye componentes de inputs, selects, botones, paginación y más, estilizados con TailwindCSS y listos para producción.
6
+
7
+ ------
8
+
9
+ ![Última actualización](https://img.shields.io/badge/Actualizado-28%20de%20Junio%20del%2026-blue)
10
+ ![Versión](https://img.shields.io/badge/versión-0.0.20-indigo)
11
+
12
+ ------
13
+
14
+ # 📦 Instalación
15
+
16
+ La librería se distribuye mediante **npm**:
17
+
18
+ ```
19
+ npm install @luis-angel-martin-dzul/input-styles
20
+ ```
21
+
22
+ ------
23
+
24
+ # 🚀 Uso en Vue + Vite
25
+
26
+ En `main.js` o `main.ts`:
27
+
28
+ ```js
29
+ import { createApp } from 'vue'
30
+ import App from './App.vue'
31
+
32
+ import '@luis-angel-martin-dzul/input-styles/style.css'
33
+ import InputStyle from '@luis-angel-martin-dzul/input-styles'
34
+
35
+ const app = createApp(App)
36
+
37
+ app.use(InputStyle)
38
+ app.mount('#app')
39
+ ```
40
+
41
+ Al instalar el plugin se registran automáticamente todos los componentes y las siguientes **propiedades globales** (disponibles en cualquier componente con la Options API):
42
+
43
+ | Propiedad global | Equivale a | Descripción |
44
+ | ---------------- | ---------- | ----------- |
45
+ | `$alertstyle` | `AlertStyle` | Notificaciones tipo toast (ver [AlertStyle](#alertstyle-notificaciones-tipo-toast)). |
46
+ | `$dialogstyle` | `DialogStyle` | Diálogos de confirmación / info / carga (ver [DialogStyle](#dialogstyle-diálogos-modales)). |
47
+ | `$utilstyle` | `UtilStyle` | Utilidades internas (ej: generación de UUID). |
48
+
49
+ ------
50
+
51
+ # Estilos default
52
+ Para cargar correctamente los estilos debes realizar algo como lo siguiente:
53
+
54
+ Agrega el @source para indicar que el componente requiere de tailwindcss
55
+
56
+ ```css
57
+ @import "tailwindcss";
58
+
59
+ @source "../node_modules/@luis-angel-martin-dzul/input-styles/**/*"
60
+ ```
61
+
62
+ # 🎨 Estilos personalizados (Style Registry)
63
+
64
+ La librería expone un sistema de **tokens de estilo configurables** que permite cambiar la apariencia de todos los componentes desde un solo lugar, sin necesidad de modificar el código fuente.
65
+
66
+ Los estilos se configuran **antes de montar la app**, importando `configureStyles` directamente.
67
+
68
+ ## Opción A — vía `app.use` (recomendado)
69
+
70
+ ```js
71
+ import VueInputYuc from '@luis-angel-martin-dzul/input-styles'
72
+
73
+ app.use(VueInputYuc, {
74
+ styles: {
75
+ INPUT_BASE: 'w-full px-0 py-2 bg-transparent border-b focus:outline-none ...',
76
+ ICON_VALID: 'text-emerald-500',
77
+ // solo los tok ens que quieras cambiar
78
+ }
79
+ })
80
+ ```
81
+
82
+ ## Opción B — importación directa
83
+
84
+ ```js
85
+ import { configureStyles } from '@luis-angel-martin-dzul/input-styles'
86
+
87
+ configureStyles({
88
+ LOADER_ARC_STROKE: '#3B82F6',
89
+ REQUIRED_ASTERISK: 'text-red-500',
90
+ })
91
+ ```
92
+
93
+ ⚠️ `configureStyles()` debe llamarse **antes** de que los componentes se monten. Los cambios no son reactivos después del montaje inicial.
94
+
95
+ ------
96
+
97
+ ## 📋 Referencia completa de tokens
98
+
99
+ ### Tokens auxiliares de label
100
+ _Afecta: todos los componentes que tienen `label` o `description`_
101
+
102
+ Estos tokens controlan partes individuales del label y del texto descriptivo. Al sobrescribir uno, las funciones `inputLabelClass`, `inputCheckLabelClass` e `inputDescriptionClass` lo recogen automáticamente **sin necesidad de sobreescribir la función entera**.
103
+
104
+ | Token | Tipo | Default | Descripción |
105
+ | ----- | ---- | ------- | ----------- |
106
+ | `INPUT_BASE_TITLE` | `string` | `'block text-sm font-medium'` | Clases estructurales del `<label>` de inputs de texto. |
107
+ | `INPUT_CHECK_BASE_TITLE` | `string` | `'block text-sm font-medium'` | Clases estructurales del `<label>` de checkbox, radio y switch. |
108
+ | `LABEL_DISABLED_STATIC` | `string` | `'text-gray-400'` | Color del label y descripción cuando el campo está deshabilitado. |
109
+ | `LABEL_ENABLED_STATIC` | `string` | `'text-gray-700'` | Color del label cuando el campo está habilitado. |
110
+
111
+ ```js
112
+ configureStyles({
113
+ LABEL_DISABLED_STATIC: 'text-gray-400',
114
+ LABEL_ENABLED_STATIC: 'text-gray-900',
115
+ INPUT_BASE_TITLE: 'block font-semibold mb-1 text-sm',
116
+ })
117
+ ```
118
+
119
+ ---
120
+
121
+ ### Inputs de texto
122
+ _Afecta: `InputTextStyle`, `InputNumberStyle`, `InputPasswordStyle`, `TextAreaStyle`, `InputDateStyle`, `InputDateTimeStyle`, `InputTimeStyle`, `SelectStyle`_
123
+
124
+ | Token | Tipo | Default | Descripción |
125
+ | ----- | ---- | ------- | ----------- |
126
+ | `INPUT_BASE` | `string` | `'w-full text-gray-800 bg-white px-3 py-1.5 rounded border ...'` | Clases base del `<input>`. No incluye borde ni ring (eso lo maneja `inputValidateClass`). |
127
+ | `inputValidateClass` | `function(validate)` | Ver abajo | Retorna las clases de borde y ring según el estado de validación. `validate`: `true` / `false` / `null`. |
128
+ | `inputLabelClass` | `function(disabled)` | Ver abajo | Retorna las clases del `<label>`. Internamente usa `INPUT_BASE_TITLE`, `LABEL_DISABLED_STATIC` y `LABEL_ENABLED_STATIC`. |
129
+ | `inputDescriptionClass` | `function(validate, alignRight?)` | Ver abajo | Retorna las clases del texto de descripción. Internamente usa `ICON_VALID`, `ICON_INVALID` y `LABEL_DISABLED_STATIC`. |
130
+
131
+ > 💡 Si solo quieres cambiar un color de validación, basta con sobrescribir `ICON_INVALID` o `ICON_VALID` — `inputDescriptionClass` los recoge automáticamente.
132
+
133
+ ```js
134
+ configureStyles({
135
+ ICON_INVALID: 'text-red-500',
136
+ ICON_VALID: 'text-emerald-500',
137
+ })
138
+
139
+ // Override completo de las funciones (cuando necesitas control total)
140
+ configureStyles({
141
+ inputValidateClass: (validate) => {
142
+ if (validate === false) return 'border-red-500 focus:ring-red-200'
143
+ if (validate === true) return 'border-emerald-500 focus:ring-emerald-200'
144
+ return 'border-gray-200 focus:ring-indigo-200'
145
+ },
146
+ inputLabelClass: (disabled) => ['block font-bold mb-2', disabled ? 'text-gray-400' : 'text-gray-900'],
147
+ inputDescriptionClass: (validate, alignRight = true) => [
148
+ 'block w-full text-sm',
149
+ alignRight && 'text-right',
150
+ validate === false ? 'text-red-500' : validate === true ? 'text-emerald-500' : 'text-gray-500',
151
+ ],
152
+ })
153
+ ```
154
+
155
+ ---
156
+
157
+ ### Select
158
+ _Afecta: `SelectStyle`_
159
+
160
+ | Token | Tipo | Default | Descripción |
161
+ | ----- | ---- | ------- | ----------- |
162
+ | `SELECT_DISABLED` | `string` | `'w-full px-3 py-1.5 rounded bg-gray-100 ...'` | Clases del input en estado disabled. |
163
+ | `SELECT_DROPDOWN` | `string` | `'absolute z-20 w-full bg-white border ...'` | Clases del contenedor `<ul>` del dropdown. |
164
+ | `SELECT_OPTION_BASE` | `string` | `'px-3 py-1 cursor-pointer text-md'` | Clases base de cada `<li>` del dropdown. |
165
+ | `selectOptionClass` | `function(isSelected, isHighlighted)` | Ver abajo | Retorna clases dinámicas por opción según su estado. |
166
+ | `SELECT_OPTGROUP` | `string` | `'px-2 pt-1 pb-0.5 text-xs font-semibold uppercase ...'` | Clases del encabezado de un grupo de opciones (`optgroup`). |
167
+ | `SELECT_EMPTY` | `string` | `'px-3 py-1 text-gray-400 cursor-not-allowed'` | Clases del mensaje "Sin registros". |
168
+
169
+ ```js
170
+ configureStyles({
171
+ selectOptionClass: (isSelected, isHighlighted) => {
172
+ if (isSelected) return 'bg-indigo-100 text-indigo-900 font-medium'
173
+ if (isHighlighted) return 'bg-indigo-50 text-gray-900'
174
+ return 'hover:bg-indigo-50 text-gray-800'
175
+ },
176
+ })
177
+ ```
178
+
179
+ ---
180
+
181
+ ### Checkbox, Radio y Switch
182
+ _Afecta: `InputCheckStyle`, `InputRadioStyle`, `InputSwitchStyle`_
183
+
184
+ | Token | Tipo | Default | Descripción |
185
+ | ----- | ---- | ------- | ----------- |
186
+ | `inputCheckLabelClass` | `function(disabled)` | Ver abajo | Clases del label de checks. |
187
+ | `CHECK_BOX_BASE` | `string` | `'w-5 h-5 flex items-center justify-center rounded border-2 transition'` | Estructura base del cuadro del checkbox. |
188
+ | `RADIO_BASE` | `string` | `'w-5 h-5 flex items-center justify-center rounded-full transition'` | Estructura base del círculo del radio. |
189
+ | `SWITCH_BASE` | `string` | `'w-10 h-5 relative flex items-center rounded-full transition border-2'` | Estructura base de la pista del switch. |
190
+ | `checkBoxClass` | `function(model, disabled)` | Ver abajo | Clases de color del checkbox según estado activo/disabled. |
191
+ | `radioClass` | `function(model, disabled)` | Ver abajo | Clases de color del radio button según estado. |
192
+ | `switchClass` | `function(model, disabled)` | Ver abajo | Clases de color de la pista del switch. |
193
+ | `switchDotClass` | `function(model, disabled)` | Ver abajo | Clases de color de la bola interior del switch. |
194
+
195
+ ```js
196
+ configureStyles({
197
+ checkBoxClass: (model, disabled) => {
198
+ if (disabled) return model
199
+ ? 'bg-indigo-200 border-indigo-200 cursor-not-allowed'
200
+ : 'bg-gray-100 border-gray-200 cursor-not-allowed'
201
+ return model
202
+ ? 'bg-indigo-500 border-indigo-500 peer-focus:ring-2 peer-focus:ring-indigo-200'
203
+ : 'bg-white border-gray-300 peer-focus:ring-2 peer-focus:ring-indigo-200'
204
+ },
205
+ })
206
+ ```
207
+
208
+ ---
209
+
210
+ ### Paginación
211
+ _Afecta: `PaginationStyle`_
212
+
213
+ | Token | Tipo | Default | Descripción |
214
+ | ----- | ---- | ------- | ----------- |
215
+ | `PAGINATION_NAV_PREV` | `string` | `'bg-white hover:bg-indigo-50 border ...'` | Clases del botón de página anterior. |
216
+ | `PAGINATION_NAV_NEXT` | `string` | `'bg-white hover:bg-indigo-50 border ...'` | Clases del botón de página siguiente. |
217
+ | `PAGINATION_PAGE_BASE` | `string` | `'border px-2.5 py-2 cursor-pointer w-8 relative'` | Clases base de cada botón de página. |
218
+ | `paginationPageClass` | `function(page, actualPage, index)` | Ver abajo | Retorna clases dinámicas por botón de página. |
219
+ | `PAGINATION_SUMMARY` | `string` | `'text-sm text-gray-700'` | Clases del texto de resumen de registros. |
220
+
221
+ ---
222
+
223
+ ### Loader
224
+ _Afecta: `LoaderStyle`_
225
+
226
+ | Token | Tipo | Default | Descripción |
227
+ | ----- | ---- | ------- | ----------- |
228
+ | `LOADER_ARC_STROKE` | `string` | `'#6366F1'` | Color hex del arco animado del loader (indigo-500). |
229
+ | `LOADER_MESSAGE` | `string` | `'text-gray-600 text-lg font-medium'` | Clases del mensaje de carga. |
230
+
231
+ ---
232
+
233
+ ### Iconos y asterisco de requerido
234
+ _Afecta: todos los componentes con `required` o iconos de validación_
235
+
236
+ | Token | Tipo | Default | Descripción |
237
+ | ----- | ---- | ------- | ----------- |
238
+ | `REQUIRED_ASTERISK` | `string` | `'text-red-500'` | Clase de color del asterisco `*` en campos requeridos. |
239
+ | `ICON_VALID` | `string` | `'text-emerald-500'` | Clase de color del ícono de validación correcta. |
240
+ | `ICON_INVALID` | `string` | `'text-red-500'` | Clase de color del ícono de validación errónea. |
241
+ | `ICON_STATIC` | `string` | `'text-indigo-400'` | Clase de color de los íconos estáticos (reloj, calendario, ojo, chevron). |
242
+ | `ICON_SIZE_STATIC` | `string` | `'w-5 h-5'` | Clases de tamaño de todos los íconos. |
243
+
244
+ ---
245
+
246
+ ### File Upload y Download
247
+ _Afecta: `InputFileUploadStyle`, `InputFileDownloadStyle`_
248
+
249
+ | Token | Tipo | Default | Descripción |
250
+ | ----- | ---- | ------- | ----------- |
251
+ | `FILE_BUTTON_BASE` | `string` | `'flex items-center justify-center gap-3 px-4 py-2 border-2 rounded-xl ...'` | Clases estructurales del botón (compartido entre upload y download). |
252
+ | `FILE_BUTTON_ACTIVE` | `string` | `'border-gray-200 bg-white text-gray-700 hover:border-indigo-400 ...'` | Clases del botón en estado normal/activo. |
253
+ | `FILE_BUTTON_DISABLED` | `string` | `'border-gray-200 bg-gray-100 text-gray-400'` | Clases del botón en estado disabled. |
254
+ | `FILE_ICON_BG` | `string` | `'bg-indigo-300'` | Fondo del círculo de ícono en estado activo. |
255
+ | `FILE_ICON_BG_COMPLETE` | `string` | `'bg-emerald-500'` | Fondo del círculo de ícono cuando la descarga está completa. |
256
+ | `FILE_ICON_BG_DISABLED` | `string` | `'bg-gray-300'` | Fondo del círculo de ícono en estado disabled. |
257
+
258
+ ---
259
+
260
+ ### Time Picker
261
+ _Afecta: `InputTimeStyle`_
262
+
263
+ | Token | Tipo | Default | Descripción |
264
+ | ----- | ---- | ------- | ----------- |
265
+ | `TIME_PICKER_WRAPPER` | `string` | `'absolute z-20 left-0 top-full rounded-lg bg-white'` | Contenedor flotante del picker. |
266
+ | `TIME_PICKER_CONTAINER` | `string` | `'border border-gray-300 rounded-lg p-3'` | Borde interior del picker. |
267
+ | `TIME_PICKER_COLUMN` | `string` | `'h-48 overflow-y-auto border border-gray-200 rounded-md ...'` | Columna scrollable de horas/minutos. |
268
+ | `TIME_PICKER_AMPM_COLUMN` | `string` | `'h-48 overflow-y-auto border border-gray-200 rounded-md ...'` | Columna scrollable AM/PM. |
269
+ | `TIME_PICKER_COLUMN_LABEL` | `string` | `'block text-xs font-medium text-gray-500 mb-1 text-center'` | Label de cada columna ("Hora", "Minuto", "AM/PM"). |
270
+ | `TIME_PICKER_ITEM` | `string` | `'px-3 py-1 text-center cursor-pointer hover:bg-indigo-50 ...'` | Fila base de cada ítem del picker. |
271
+ | `timePickerItemClass` | `function(isActive)` | Ver abajo | Clases dinámicas de cada ítem según si está seleccionado. |
272
+
273
+ ------
274
+
275
+ # 🧩 Componentes
276
+
277
+ ## Props comunes
278
+
279
+ La mayoría de los componentes comparten las siguientes props:
280
+
281
+ | Prop | Tipo | Default | Descripción |
282
+ | ---- | ---- | ------- | ----------- |
283
+ | `modelValue` | `String \| Number \| Boolean` | — | Valor del campo (v-model). |
284
+ | `label` | `String` | `''` | Texto del label del campo. |
285
+ | `description` | `String` | `''` | Texto de ayuda debajo del campo. Cambia de color según el estado de validación. |
286
+ | `placeholder` | `String` | `''` | Placeholder del input. |
287
+ | `disabled` | `Boolean` | `false` | Deshabilita el campo. |
288
+ | `required` | `Boolean` | `false` | Marca el campo como requerido (muestra asterisco `*`). |
289
+ | `validate` | `Boolean \| null` | `null` | Estado de validación: `true` = válido, `false` = inválido, `null` = sin estado. |
290
+ | `mclass` | `String` | `null` | Clases CSS adicionales aplicadas directamente al elemento del input. |
291
+
292
+ ## Eventos comunes
293
+
294
+ | Evento | Payload | Descripción |
295
+ | ------ | ------- | ----------- |
296
+ | `update:modelValue` | `value` | Se dispara al cambiar el valor (permite `v-model`). |
297
+ | `change` | `value` | Se dispara al cambiar el valor. |
298
+ | `blur` | `event` | Se dispara al perder el foco. |
299
+
300
+ ------
301
+
302
+ ## InputTextStyle
303
+
304
+ Input de texto genérico. Soporta tipos `text`, `email`, `search`, `tel`, `url` y `number`.
305
+
306
+ ```vue
307
+ <InputTextStyle
308
+ v-model="nombre"
309
+ label="Nombre"
310
+ placeholder="Escribe tu nombre"
311
+ :required="true"
312
+ :validate="validate"
313
+ description="Campo requerido"
314
+ />
315
+ ```
316
+
317
+ | Prop | Tipo | Default | Descripción |
318
+ | ---- | ---- | ------- | ----------- |
319
+ | `modelValue` | `String \| Number` | `''` | Valor del campo. |
320
+ | `label` | `String` | `''` | |
321
+ | `description` | `String` | `''` | |
322
+ | `placeholder` | `String` | `''` | |
323
+ | `type` | `String` | `'text'` | Tipo del input: `text`, `email`, `search`, `tel`, `url`, `number`. |
324
+ | `maxlength` | `String` | `null` | Longitud máxima de caracteres. |
325
+ | `minlength` | `String` | `null` | Longitud mínima de caracteres. |
326
+ | `min` | `String` | `null` | Valor mínimo (solo cuando `type="number"`, aplicado al perder el foco). |
327
+ | `max` | `String` | `null` | Valor máximo (solo cuando `type="number"`, aplicado al perder el foco). |
328
+ | `disabled` | `Boolean` | `false` | |
329
+ | `required` | `Boolean` | `false` | |
330
+ | `validate` | `Boolean \| null` | `null` | |
331
+ | `mclass` | `String` | `null` | |
332
+
333
+ ------
334
+
335
+ ## InputNumberStyle
336
+
337
+ Input numérico con control preciso de dígitos, decimales, rango y formato de visualización. El valor se emite siempre como string numérico limpio (ej: `"1234.56"`). Al perder el foco se formatea con `toLocaleString`.
338
+
339
+ ```vue
340
+ <InputNumberStyle
341
+ v-model="monto"
342
+ label="Monto"
343
+ numeric="10,2"
344
+ min="0"
345
+ max="9999999999"
346
+ :required="true"
347
+ :validate="validate"
348
+ />
349
+ ```
350
+
351
+ | Prop | Tipo | Default | Descripción |
352
+ | ---- | ---- | ------- | ----------- |
353
+ | `modelValue` | `String \| Number` | `''` | Valor del campo. |
354
+ | `label` | `String` | `''` | |
355
+ | `description` | `String` | `''` | |
356
+ | `placeholder` | `String` | `''` | |
357
+ | `numeric` | `String` | `null` | Formato tipo SQL: `"10,2"` → 10 dígitos enteros, 2 decimales. Si se omite, usa `10,2` por defecto. |
358
+ | `min` | `String` | `null` | Valor mínimo (aplicado al perder el foco). Si es `null` o negativo, permite negativos. |
359
+ | `max` | `String` | `null` | Valor máximo (aplicado al perder el foco). |
360
+ | `disabled` | `Boolean` | `false` | |
361
+ | `required` | `Boolean` | `false` | |
362
+ | `validate` | `Boolean \| null` | `null` | |
363
+ | `mclass` | `String` | `null` | |
364
+
365
+ **Comportamiento:**
366
+ - Incluye botones **caret ▲ / ▼** alineados a la izquierda del campo para aumentar/disminuir el valor en unidades (respetando `min`/`max`). No tienen separador y usan el color global de los iconos (`ICON_STATIC`).
367
+ - Si **mantienes presionado** un botón, tras una breve pausa el valor cambia de forma **continua** hasta soltar.
368
+ - Puedes escribir `0` y luego `.` para empezar los decimales: el punto se conserva a la espera de los dígitos decimales (solo si los decimales están activados en `numeric`).
369
+ - Al **enfocar** el campo, si el valor es `0` (o está vacío) se limpia, para que al escribir reemplaces el valor en lugar de anteponerlo.
370
+ - Al **perder el foco**, si el campo queda vacío o en `0`, se conserva el valor `0` formateado (ej: `"0.00"`), aplicando además los límites `min`/`max`.
371
+
372
+ **Navegación por teclado:**
373
+ - `↑` / `↓` — aumentar/disminuir el valor en unidades, respetando `min`/`max`.
374
+
375
+ ------
376
+
377
+ ## InputPasswordStyle
378
+
379
+ Input de contraseña con toggle para mostrar/ocultar el texto.
380
+
381
+ ```vue
382
+ <InputPasswordStyle
383
+ v-model="password"
384
+ label="Contraseña"
385
+ placeholder="Mínimo 8 caracteres"
386
+ :required="true"
387
+ :validate="validate"
388
+ />
389
+ ```
390
+
391
+ | Prop | Tipo | Default | Descripción |
392
+ | ---- | ---- | ------- | ----------- |
393
+ | `modelValue` | `String \| Number` | `''` | Valor del campo. |
394
+ | `label` | `String` | `''` | |
395
+ | `description` | `String` | `''` | |
396
+ | `placeholder` | `String` | `''` | |
397
+ | `maxlength` | `String` | `null` | Longitud máxima. |
398
+ | `disabled` | `Boolean` | `false` | |
399
+ | `required` | `Boolean` | `false` | |
400
+ | `validate` | `Boolean \| null` | `null` | |
401
+ | `mclass` | `String` | `null` | |
402
+
403
+ ------
404
+
405
+ ## TextAreaStyle
406
+
407
+ Área de texto multi-línea.
408
+
409
+ ```vue
410
+ <TextAreaStyle
411
+ v-model="comentario"
412
+ label="Comentario"
413
+ placeholder="Escribe aquí..."
414
+ row="5"
415
+ :maxlength="500"
416
+ :validate="validate"
417
+ />
418
+ ```
419
+
420
+ | Prop | Tipo | Default | Descripción |
421
+ | ---- | ---- | ------- | ----------- |
422
+ | `modelValue` | `String` | `''` | Valor del campo. |
423
+ | `label` | `String` | `''` | |
424
+ | `description` | `String` | `''` | |
425
+ | `placeholder` | `String` | `''` | |
426
+ | `row` | `String` | `'3'` | Número de filas visibles del textarea. |
427
+ | `maxlength` | `String` | `null` | Longitud máxima de caracteres. |
428
+ | `disabled` | `Boolean` | `false` | |
429
+ | `required` | `Boolean` | `false` | |
430
+ | `validate` | `Boolean \| null` | `null` | |
431
+ | `mclass` | `String` | `null` | |
432
+
433
+ ------
434
+
435
+ ## SelectStyle
436
+
437
+ Select personalizado con dropdown, navegación por teclado y búsqueda por tipeo. El dropdown se posiciona automáticamente arriba o abajo según el espacio disponible.
438
+
439
+ ```vue
440
+ <SelectStyle
441
+ v-model="pais"
442
+ label="País"
443
+ :list="paises"
444
+ return-value="value"
445
+ :required="true"
446
+ :validate="validate"
447
+ />
448
+ ```
449
+
450
+ ```js
451
+ // Formato del array `list` — lista plana
452
+ const paises = [
453
+ { value: 1, text: 'México' },
454
+ { value: 2, text: 'España' },
455
+ { value: 3, text: 'Argentina' },
456
+ ]
457
+
458
+ // El `value` es opcional: si se omite, se usa el `text` como valor
459
+ const colores = [
460
+ { text: 'Rojo' },
461
+ { text: 'Verde' },
462
+ ]
463
+
464
+ // Lista agrupada — cada grupo tiene un `label` y sus `options`
465
+ const ubicaciones = [
466
+ { label: 'América', options: [{ value: 1, text: 'México' }, { value: 2, text: 'Argentina' }] },
467
+ { label: 'Europa', options: [{ value: 3, text: 'España' }, { value: 4, text: 'Francia' }] },
468
+ ]
469
+ ```
470
+
471
+ | Prop | Tipo | Default | Descripción |
472
+ | ---- | ---- | ------- | ----------- |
473
+ | `modelValue` | `String \| Number \| Object` | — | Valor seleccionado. |
474
+ | `list` | `Array` | `[]` | Array de opciones. Acepta lista plana `{ value, text }` o agrupada `{ label, options: [...] }`. El `value` es opcional: si se omite, se usa el `text` como valor. |
475
+ | `returnValue` | `String` | `'value'` | Qué se emite al seleccionar: `'value'` (item.value), `'text'` (item.text) u `'object'` (el item completo). |
476
+ | `label` | `String` | `''` | |
477
+ | `description` | `String` | `''` | |
478
+ | `wrapOptions` | `Boolean` | `false` | `true` = permite que el texto de cada opción haga salto de línea; `false` = lo trunca con elipsis. |
479
+ | `disabled` | `Boolean` | `false` | |
480
+ | `required` | `Boolean` | `false` | |
481
+ | `validate` | `Boolean \| null` | `null` | |
482
+ | `mclass` | `String` | `null` | |
483
+
484
+ **Navegación por teclado:**
485
+ - `↑` / `↓` — navegar opciones
486
+ - `Enter` — seleccionar la opción resaltada
487
+ - `Escape` — cerrar el dropdown
488
+ - Tipear letras — búsqueda inmediata en la lista
489
+
490
+ ------
491
+
492
+ ## InputCheckStyle
493
+
494
+ Checkbox estilizado con label y descripción.
495
+
496
+ ```vue
497
+ <InputCheckStyle
498
+ v-model="aceptaTerminos"
499
+ label="Acepto los términos y condiciones"
500
+ :required="true"
501
+ />
502
+ ```
503
+
504
+ | Prop | Tipo | Default | Descripción |
505
+ | ---- | ---- | ------- | ----------- |
506
+ | `modelValue` | `Boolean` | `false` | Estado del checkbox. |
507
+ | `label` | `String` | `''` | |
508
+ | `description` | `String` | `''` | |
509
+ | `disabled` | `Boolean` | `false` | |
510
+ | `required` | `Boolean` | `false` | |
511
+ | `validate` | `Boolean \| null` | `null` | |
512
+ | `mclass` | `String` | `null` | Clases adicionales del cuadro del checkbox. |
513
+
514
+ ------
515
+
516
+ ## InputRadioStyle
517
+
518
+ Radio button estilizado con label y descripción.
519
+
520
+ ```vue
521
+ <InputRadioStyle
522
+ v-model="opcionA"
523
+ label="Opción A"
524
+ />
525
+ ```
526
+
527
+ | Prop | Tipo | Default | Descripción |
528
+ | ---- | ---- | ------- | ----------- |
529
+ | `modelValue` | `Boolean` | `false` | Estado del radio. |
530
+ | `label` | `String` | `''` | |
531
+ | `description` | `String` | `''` | |
532
+ | `disabled` | `Boolean` | `false` | |
533
+ | `required` | `Boolean` | `false` | |
534
+ | `validate` | `Boolean \| null` | `null` | |
535
+ | `mclass` | `String` | `null` | Clases adicionales del círculo del radio. |
536
+
537
+ ------
538
+
539
+ ## InputSwitchStyle
540
+
541
+ Toggle switch estilizado.
542
+
543
+ ```vue
544
+ <InputSwitchStyle
545
+ v-model="activo"
546
+ label="Activar notificaciones"
547
+ />
548
+ ```
549
+
550
+ | Prop | Tipo | Default | Descripción |
551
+ | ---- | ---- | ------- | ----------- |
552
+ | `modelValue` | `Boolean` | `false` | Estado del switch. |
553
+ | `label` | `String` | `''` | |
554
+ | `description` | `String` | `''` | |
555
+ | `disabled` | `Boolean` | `false` | |
556
+ | `required` | `Boolean` | `false` | |
557
+ | `validate` | `Boolean \| null` | `null` | |
558
+ | `mclass` | `String` | `null` | Clases adicionales de la pista del switch. |
559
+
560
+ ------
561
+
562
+ ## InputDateStyle
563
+
564
+ Selector de fecha con máscara `dd/mm/aaaa`, navegación por teclado entre secciones y calendario visual nativo (sin dependencias externas). El valor se emite en formato `YYYY-MM-DD`.
565
+
566
+ El calendario incluye vistas de días, meses y años, y un botón **Hoy** para navegar rápidamente a la fecha actual.
567
+
568
+ ```vue
569
+ <InputDateStyle
570
+ v-model="fechaNacimiento"
571
+ label="Fecha de nacimiento"
572
+ min="1900-01-01"
573
+ max="2100-12-31"
574
+ :validate="validate"
575
+ />
576
+ ```
577
+
578
+ | Prop | Tipo | Default | Descripción |
579
+ | ---- | ---- | ------- | ----------- |
580
+ | `modelValue` | `String \| Date` | — | Valor de la fecha en formato `YYYY-MM-DD` o `Date`. |
581
+ | `label` | `String` | `''` | |
582
+ | `description` | `String` | `''` | |
583
+ | `min` | `String` | `'1900-01-01'` | Fecha mínima seleccionable en el calendario (formato `YYYY-MM-DD`). |
584
+ | `max` | `String` | `'2100-12-31'` | Fecha máxima seleccionable en el calendario (formato `YYYY-MM-DD`). |
585
+ | `disabledDates` | `Array` | `[]` | Fechas deshabilitadas. Array de strings `'YYYY-MM-DD'` o objetos `Date`. |
586
+ | `disabled` | `Boolean` | `false` | |
587
+ | `readonly` | `Boolean` | `false` | Solo lectura: muestra el valor pero bloquea la edición por teclado. |
588
+ | `required` | `Boolean` | `false` | |
589
+ | `validate` | `Boolean \| null` | `null` | |
590
+ | `mclass` | `String` | `null` | |
591
+
592
+ **Valor emitido:** `'YYYY-MM-DD'` (string) o `''` si la fecha es inválida o está vacía.
593
+
594
+ **Navegación por teclado:**
595
+ - Escribe los dígitos y el foco avanza automáticamente entre día → mes → año
596
+ - `↑` / `↓` — incrementar/decrementar la sección activa (la fecha resultante se ajusta a los límites `min`/`max`)
597
+ - `←` / `→` — mover entre secciones
598
+ - `Tab` / `Shift + Tab` — mover entre secciones (al llegar al extremo, el foco sale del campo)
599
+ - `Backspace` / `Delete` — limpiar la sección activa
600
+ - Clic en el ícono 📅 — abre/cierra el calendario visual
601
+
602
+ ------
603
+
604
+ ## InputTimeStyle
605
+
606
+ Selector de hora con picker de columnas scrollables. Soporta formato 12h (AM/PM) o 24h. El valor se emite en formato `HH:mm`.
607
+
608
+ ```vue
609
+ <InputTimeStyle
610
+ v-model="horaInicio"
611
+ label="Hora de inicio"
612
+ min="08:00"
613
+ max="18:00"
614
+ :is24h="true"
615
+ :validate="validate"
616
+ />
617
+ ```
618
+
619
+ | Prop | Tipo | Default | Descripción |
620
+ | ---- | ---- | ------- | ----------- |
621
+ | `modelValue` | `String` | — | Valor de la hora en formato `HH:mm`. |
622
+ | `label` | `String` | `''` | |
623
+ | `description` | `String` | `''` | |
624
+ | `min` | `String` | `null` | Hora mínima (ej: `'08:00'`). |
625
+ | `max` | `String` | `null` | Hora máxima (ej: `'18:00'`). |
626
+ | `is24h` | `Boolean` | `false` | `true` = formato 24h, `false` = formato 12h con selector AM/PM. |
627
+ | `disabled` | `Boolean` | `false` | |
628
+ | `required` | `Boolean` | `false` | |
629
+ | `validate` | `Boolean \| null` | `null` | |
630
+ | `mclass` | `String` | `null` | |
631
+
632
+ **Valor emitido:** `'HH:mm'` en formato 24h siempre, independientemente del modo de visualización.
633
+
634
+ ------
635
+
636
+ ## InputDateTimeStyle
637
+
638
+ Selector combinado de fecha y hora con máscara `dd/mm/aaaa hh:mm` (o `dd/mm/aaaa hh:mm a.m.` en modo 12h), calendario visual nativo y picker de columnas scrollables para la hora. El valor se emite en formato `YYYY-MM-DDTHH:mm:00.000`.
639
+
640
+ ```vue
641
+ <InputDateTimeStyle
642
+ v-model="fechaHora"
643
+ label="Fecha y hora"
644
+ min="2000-01-01"
645
+ max="2100-12-31"
646
+ :is24h="true"
647
+ :validate="validate"
648
+ />
649
+ ```
650
+
651
+ | Prop | Tipo | Default | Descripción |
652
+ | ---- | ---- | ------- | ----------- |
653
+ | `modelValue` | `String \| Date` | — | Valor en formato `YYYY-MM-DDTHH:mm:00.000` o `Date`. |
654
+ | `label` | `String` | `''` | |
655
+ | `description` | `String` | `''` | |
656
+ | `min` | `String` | `'1900-01-01'` | Fecha mínima seleccionable (formato `YYYY-MM-DD`). |
657
+ | `max` | `String` | `'2100-12-31'` | Fecha máxima seleccionable (formato `YYYY-MM-DD`). |
658
+ | `disabledDates` | `Array` | `[]` | Fechas deshabilitadas. Array de strings `'YYYY-MM-DD'` o `Date`. |
659
+ | `is24h` | `Boolean` | `false` | `true` = formato 24h, `false` = formato 12h con selector a.m./p.m. |
660
+ | `disabled` | `Boolean` | `false` | |
661
+ | `readonly` | `Boolean` | `false` | Solo lectura. |
662
+ | `required` | `Boolean` | `false` | |
663
+ | `validate` | `Boolean \| null` | `null` | |
664
+ | `mclass` | `String` | `null` | |
665
+
666
+ **Valor emitido:** `'YYYY-MM-DDTHH:mm:00.000'` (string) o `''` si la fecha/hora es inválida o está vacía.
667
+
668
+ **Comportamiento del picker:**
669
+ - El calendario permanece abierto al seleccionar la fecha, para que puedas ajustar también la hora.
670
+ - El selector de hora embebido en el picker usa las mismas columnas scrollables que `InputTimeStyle`.
671
+ - Botón **Ahora** — establece la fecha y hora actuales de un solo clic.
672
+
673
+ **Navegación por teclado:**
674
+ - Escribe los dígitos y el foco avanza automáticamente: día → mes → año → hora → minuto → a.m./p.m.
675
+ - `↑` / `↓` — incrementar/decrementar la sección activa (la fecha se ajusta a los límites `min`/`max`)
676
+ - `←` / `→` — mover entre secciones
677
+ - `Tab` / `Shift + Tab` — mover entre secciones (al llegar al extremo, el foco sale del campo)
678
+ - `Backspace` / `Delete` — limpiar la sección activa
679
+ - `a` / `p` — cambiar entre a.m. y p.m. (solo modo 12h)
680
+ - Clic en el ícono 📅 — abre/cierra el picker
681
+
682
+ ------
683
+
684
+ ## InputFileUploadStyle
685
+
686
+ Botón para adjuntar archivos con validación de tamaño y formato.
687
+
688
+ ```vue
689
+ <InputFileUploadStyle
690
+ v-model="archivo"
691
+ :formats="['pdf', 'docx']"
692
+ max-size-m-b="10"
693
+ :state="uploadState"
694
+ />
695
+ ```
696
+
697
+ | Prop | Tipo | Default | Descripción |
698
+ | ---- | ---- | ------- | ----------- |
699
+ | `modelValue` | `File` | `null` | Archivo seleccionado. |
700
+ | `disabled` | `Boolean` | `false` | Deshabilita el botón. |
701
+ | `formats` | `Array` | `[]` | Extensiones permitidas (ej: `['pdf', 'xlsx', 'png']`). Array vacío = todos los formatos. |
702
+ | `maxSizeMB` | `String \| Number` | `5` | Tamaño máximo del archivo en MB. |
703
+ | `state` | `String` | `'uphold'` | Estado del botón: `'uphold'` (normal) o `'loader'` (cargando, muestra spinner). |
704
+ | `delay` | `String` | `'1.5'` | Segundos de bloqueo del botón tras un clic, para evitar doble envío. |
705
+
706
+ **Eventos:**
707
+ - `update:modelValue` — emite el `File` seleccionado, o `null` si el archivo no es válido.
708
+ - `change` — igual que `update:modelValue`.
709
+
710
+ El componente valida automáticamente el tamaño y el formato, mostrando un mensaje de error inline si la validación falla.
711
+
712
+ ------
713
+
714
+ ## InputFileDownloadStyle
715
+
716
+ Botón para descargar archivos con tres estados visuales.
717
+
718
+ ```vue
719
+ <InputFileDownloadStyle
720
+ :state="downloadState"
721
+ :disabled="false"
722
+ @click="descargarArchivo"
723
+ />
724
+ ```
725
+
726
+ | Prop | Tipo | Default | Descripción |
727
+ | ---- | ---- | ------- | ----------- |
728
+ | `disabled` | `Boolean` | `false` | Deshabilita el botón. |
729
+ | `state` | `String` | `'download'` | Estado: `'download'` (normal), `'loader'` (descargando, spinner), `'complete'` (completado, check verde). |
730
+ | `delay` | `String` | `'1.5'` | Segundos de bloqueo del botón tras un clic. |
731
+
732
+ **Eventos:**
733
+ - `click` — se dispara al hacer clic (solo si no está deshabilitado).
734
+
735
+ ------
736
+
737
+ ## ButtonStyle
738
+
739
+ Botón genérico con bloqueo automático anti-doble-clic.
740
+
741
+ ```vue
742
+ <ButtonStyle
743
+ class="px-4 py-2 bg-indigo-500 text-white rounded"
744
+ :disabled="cargando"
745
+ @click="guardar"
746
+ >
747
+ Guardar
748
+ </ButtonStyle>
749
+ ```
750
+
751
+ | Prop | Tipo | Default | Descripción |
752
+ | ---- | ---- | ------- | ----------- |
753
+ | `type` | `String` | `'button'` | Tipo del botón: `'button'` o `'submit'`. |
754
+ | `class` | `String` | `''` | Clases CSS del botón (TailwindCSS). |
755
+ | `disabled` | `Boolean` | `false` | Deshabilita el botón. |
756
+ | `delay` | `String` | `'1.5'` | Segundos de bloqueo automático tras hacer clic (anti-doble-clic). |
757
+
758
+ **Eventos:**
759
+ - `click` — se dispara al hacer clic (solo si no está deshabilitado ni en periodo de bloqueo).
760
+
761
+ Acepta contenido mediante `<slot>` (texto, íconos, etc.).
762
+
763
+ ------
764
+
765
+ ## LoaderStyle
766
+
767
+ Indicador de carga circular animado.
768
+
769
+ ```vue
770
+ <LoaderStyle message="Procesando datos..." />
771
+ ```
772
+
773
+ | Prop | Tipo | Default | Descripción |
774
+ | ---- | ---- | ------- | ----------- |
775
+ | `message` | `String` | `'Cargando...'` | Texto que se muestra debajo del arco animado. |
776
+
777
+ El color del arco se controla con el token `LOADER_ARC_STROKE`.
778
+
779
+ ------
780
+
781
+ ## PaginationStyle
782
+
783
+ Paginador con navegación anterior/siguiente, botones de página y resumen de registros.
784
+
785
+ ```vue
786
+ <PaginationStyle
787
+ :total-pages="totalPaginas"
788
+ :actual-page="paginaActual"
789
+ :records="totalRegistros"
790
+ :limit="registrosPorPagina"
791
+ :summary="true"
792
+ @page="cambiarPagina"
793
+ />
794
+ ```
795
+
796
+ | Prop | Tipo | Default | Descripción |
797
+ | ---- | ---- | ------- | ----------- |
798
+ | `totalPages` | `Number` | `0` | Total de páginas. |
799
+ | `actualPage` | `Number` | `1` | Página actualmente activa. |
800
+ | `records` | `Number` | `0` | Total de registros (para el resumen). |
801
+ | `limit` | `Number` | `0` | Registros por página (para calcular el rango del resumen). |
802
+ | `summary` | `Boolean` | `false` | `true` = muestra el texto "Mostrando X - Y de Z". |
803
+
804
+ **Eventos:**
805
+ - `page` — emite el número de página a la que se desea navegar.
806
+
807
+ El paginador muestra hasta 7 botones con elipsis (`...`) inteligentes cuando hay más páginas.
808
+
809
+ ------
810
+
811
+ ## ModalStyle
812
+
813
+ Ventana modal con `Teleport` al `body`, animaciones de entrada/salida, bloqueo del scroll de fondo y cierre por tecla `Escape`. El estado de apertura se controla con `v-model`.
814
+
815
+ ```vue
816
+ <ButtonStyle @click="showModal = true">Abrir modal</ButtonStyle>
817
+
818
+ <ModalStyle v-model="showModal" title="Detalles" size="lg">
819
+ <p>Contenido del modal...</p>
820
+
821
+ <template #footer="{ close }">
822
+ <div class="flex justify-end gap-2">
823
+ <ButtonStyle class="px-4 py-2 border rounded" @click="close">Cerrar</ButtonStyle>
824
+ </div>
825
+ </template>
826
+ </ModalStyle>
827
+ ```
828
+
829
+ | Prop | Tipo | Default | Descripción |
830
+ | ---- | ---- | ------- | ----------- |
831
+ | `modelValue` | `Boolean` | `false` | Controla la visibilidad del modal (`v-model`). |
832
+ | `title` | `String` | `'Sin titulo asignado'` | Título mostrado en el header (si no se usa el slot `title`). |
833
+ | `size` | `String` | `'lg'` | Ancho máximo: `'sm'`, `'md'`, `'lg'`, `'xl'`, `'2xl'`, `'3xl'`, `'4xl'`, `'5xl'`, `'full'`. |
834
+ | `showCloseButton` | `Boolean` | `true` | Muestra el botón `✕` de cierre en el header. |
835
+ | `closeOnEscape` | `Boolean` | `true` | Permite cerrar el modal con la tecla `Escape`. |
836
+ | `closeOnOverlayClick` | `Boolean` | `true` | Permite cerrar el modal al hacer clic en el fondo. |
837
+
838
+ **Slots:**
839
+ - `title` — reemplaza el título del header.
840
+ - *default* — contenido principal. Recibe `{ close }` para cerrar desde dentro.
841
+ - `footer` — pie del modal (solo se renderiza si se provee). Recibe `{ close }`.
842
+
843
+ **Eventos:**
844
+ - `update:modelValue` — emite `false` al cerrarse.
845
+
846
+ ------
847
+
848
+ ## AlertStyle (notificaciones tipo toast)
849
+
850
+ Utilidad JavaScript (no es un componente) para mostrar notificaciones flotantes que se auto-descartan. Disponible como propiedad global `$alertstyle` tras instalar el plugin, o mediante importación directa.
851
+
852
+ ```js
853
+ // Vía global property (Options API)
854
+ this.$alertstyle.success('Guardado', 'Los cambios se guardaron correctamente')
855
+
856
+ // Vía importación directa
857
+ import { AlertStyle } from '@luis-angel-martin-dzul/input-styles'
858
+ AlertStyle.error('Error', 'No se pudo completar la operación')
859
+ ```
860
+
861
+ | Método | Parámetros | Descripción |
862
+ | ------ | ---------- | ----------- |
863
+ | `success(title, message?)` | `String, String` | Notificación verde de éxito. |
864
+ | `error(title, message?)` | `String, String` | Notificación roja de error. |
865
+ | `warning(title, message?)` | `String, String` | Notificación de advertencia. |
866
+ | `info(title, message?)` | `String, String` | Notificación informativa. |
867
+ | `neutral(title, message?)` | `String, String` | Notificación neutral. |
868
+
869
+ Cada notificación se cierra automáticamente a los 4 segundos o manualmente con su botón `×`.
870
+
871
+ ------
872
+
873
+ ## DialogStyle (diálogos modales)
874
+
875
+ Utilidad JavaScript para diálogos de confirmación, información y carga, con interfaz fluida (encadenable). Disponible como global `$dialogstyle` o por importación directa.
876
+
877
+ ```js
878
+ // Confirmación
879
+ this.$dialogstyle.confirm('¿Eliminar registro?', 'Esta acción no se puede deshacer')
880
+ .onAccept(() => eliminar())
881
+ .onCancel(() => console.log('Cancelado'))
882
+
883
+ // Información (un solo botón)
884
+ this.$dialogstyle.info('Operación completada')
885
+ .onConfirm(() => console.log('Aceptado'))
886
+
887
+ // Carga (bloqueante, se cierra manualmente)
888
+ const loader = this.$dialogstyle.loading('Procesando...', 'Espere un momento')
889
+ loader.updateMessage('Casi listo...')
890
+ loader.close()
891
+ ```
892
+
893
+ | Método | Retorna | Descripción |
894
+ | ------ | ------- | ----------- |
895
+ | `confirm(title, description?)` | controlador con `.onAccept(cb)` / `.onCancel(cb)` | Diálogo con botones Aceptar / Cancelar. |
896
+ | `info(title, description?)` | controlador con `.onConfirm(cb)` | Diálogo informativo con un solo botón Aceptar. |
897
+ | `loading(title, description?)` | `{ close, updateTitle, updateMessage }` | Diálogo de carga con spinner; se cierra con `.close()`. |
898
+ | `finish(immediate?)` | `Promise` | Cierra el diálogo abierto manualmente. |
899
+
900
+ > 💡 La tecla `Escape` no cierra el diálogo (hace una animación de "shake"), para forzar una decisión explícita del usuario.
901
+
902
+ ------
903
+
904
+ ## UtilStyle (utilidades)
905
+
906
+ Clase con métodos estáticos de utilidad (formateo, validaciones, manejo de archivos, etc.). Disponible como propiedad global `$utilstyle` tras instalar el plugin, o mediante importación directa.
907
+
908
+ ```js
909
+ // Vía global property (Options API)
910
+ this.$utilstyle.formatMoney(1234.5)
911
+
912
+ // Vía importación directa
913
+ import { UtilStyle } from '@luis-angel-martin-dzul/input-styles'
914
+ UtilStyle.getUUID()
915
+ ```
916
+
917
+ | Método | Parámetros | Descripción |
918
+ | ------ | ---------- | ----------- |
919
+ | `getUUID()` | — | Genera un UUID v4 (usa `crypto.randomUUID` con fallback manual). |
920
+ | `formatMoney(valor, dollar?, digit?)` | `Number, String, Number` | Formatea un número como moneda (ej: `$1,234.56`). |
921
+ | `formatNumber(valor, digit?)` | `Number, Number` | Formatea un número con separadores de miles y decimales fijos. |
922
+ | `formatDateShort(dateString)` | `String` | Fecha en formato corto en español (ej: `9 jul 2026`). |
923
+ | `formatDateLong(dateString)` | `String` | Fecha en formato largo en español (ej: `9 de julio de 2026`). |
924
+ | `getDateTimeNow()` | — | Fecha y hora actual (zona `America/Mexico_City`) en formato `YYYY-MM-DDTHH:mm:ss`. |
925
+ | `blockKeyNumber(event)` | `Event` | Bloquea las teclas `e`, `E`, `+`, `-` en un `@keydown`. |
926
+ | `blockKeys(event, keys)` | `Event, Array` | Bloquea las teclas indicadas en un `@keydown`. |
927
+ | `validateTel(value, required?)` | `String, Boolean` | Valida un teléfono con formato `999 9 99 99 99`. Acepta prefijo de región opcional `+XXX 999 9 99 99 99` (XXX = 1 a 3 dígitos). Retorna `true` o mensaje de error. |
928
+ | `validateEmail(value, required?)` | `String, Boolean` | Valida un correo con las condiciones mínimas `xx@xx.x`. Retorna `true` o mensaje de error. |
929
+ | `validateFileExtension(file, allowedExtensions?)` | `File, Array` | Valida la extensión del archivo. Retorna `true` o mensaje de error. |
930
+ | `validateFileType(file, allowedTypes?)` | `File, Array` | Valida el MIME type del archivo. Retorna `true` o mensaje de error. |
931
+ | `validateFileSize(file, maxSizeMB?)` | `File, Number` | Valida el tamaño del archivo. Retorna `true` o mensaje de error. |
932
+ | `downloadBlob(blobData, fileName?, mimeType?)` | `Blob, String, String` | Descarga un blob como archivo. |
933
+ | `fileToBase64(file)` | `File` | Convierte un `File` a base64 (async, retorna `Promise`). |
934
+ | `base64ToBlob(base64, mimeType?)` | `String, String` | Convierte una cadena base64 a `Blob`. |
935
+ | `base64ToFile(base64, fileName?, mimeType?)` | `String, String, String` | Convierte una cadena base64 a `File`. |
936
+
937
+ > 💡 Los métodos de validación (`validateTel`, `validateEmail`, `validateFile*`) devuelven `true` si el valor es válido, o un `String` con el mensaje de error si no lo es.
938
+
939
+ ------
940
+
941
+ ## Ejemplo de uso completo
942
+
943
+ ```vue
944
+ <template>
945
+ <div class="p-6 space-y-4 max-w-md">
946
+
947
+ <InputTextStyle
948
+ v-model="form.nombre"
949
+ label="Nombre completo"
950
+ placeholder="Ej: Juan Pérez"
951
+ :required="true"
952
+ :validate="validaciones.nombre"
953
+ />
954
+
955
+ <InputNumberStyle
956
+ v-model="form.monto"
957
+ label="Monto"
958
+ numeric="10,2"
959
+ min="0"
960
+ :validate="validaciones.monto"
961
+ />
962
+
963
+ <InputPasswordStyle
964
+ v-model="form.password"
965
+ label="Contraseña"
966
+ :required="true"
967
+ :validate="validaciones.password"
968
+ />
969
+
970
+ <SelectStyle
971
+ v-model="form.pais"
972
+ label="País"
973
+ :list="paises"
974
+ return-value="value"
975
+ :validate="validaciones.pais"
976
+ />
977
+
978
+ <InputDateStyle
979
+ v-model="form.fecha"
980
+ label="Fecha"
981
+ :validate="validaciones.fecha"
982
+ />
983
+
984
+ <InputDateTimeStyle
985
+ v-model="form.fechaHora"
986
+ label="Fecha y hora"
987
+ :is24h="true"
988
+ :validate="validaciones.fechaHora"
989
+ />
990
+
991
+ <InputTimeStyle
992
+ v-model="form.hora"
993
+ label="Hora"
994
+ :is24h="true"
995
+ />
996
+
997
+ <InputCheckStyle
998
+ v-model="form.acepta"
999
+ label="Acepto los términos"
1000
+ :required="true"
1001
+ />
1002
+
1003
+ <ButtonStyle
1004
+ class="w-full px-4 py-2 bg-indigo-500 text-white rounded hover:bg-indigo-600"
1005
+ @click="enviar"
1006
+ >
1007
+ Enviar
1008
+ </ButtonStyle>
1009
+
1010
+ </div>
1011
+ </template>
1012
+
1013
+ <script setup>
1014
+ import { ref } from 'vue'
1015
+ import {
1016
+ InputTextStyle, InputNumberStyle, InputPasswordStyle,
1017
+ SelectStyle, InputDateStyle, InputDateTimeStyle, InputTimeStyle,
1018
+ InputCheckStyle, ButtonStyle
1019
+ } from '@luis-angel-martin-dzul/input-styles'
1020
+
1021
+ const form = ref({
1022
+ nombre: '', monto: '', password: '',
1023
+ pais: null, fecha: '', fechaHora: '', hora: '', acepta: false
1024
+ })
1025
+
1026
+ const validaciones = ref({
1027
+ nombre: null, monto: null, password: null,
1028
+ pais: null, fecha: null, fechaHora: null
1029
+ })
1030
+
1031
+ const paises = [
1032
+ { value: 1, text: 'México' },
1033
+ { value: 2, text: 'España' },
1034
+ ]
1035
+
1036
+ function enviar() {
1037
+ validaciones.value.nombre = form.value.nombre.length > 0
1038
+ // ... resto de validaciones
1039
+ }
1040
+ </script>
1041
+ ```