@luis-angel-martin-dzul/input-styles 0.0.1 → 0.0.3

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,3 +1,812 @@
1
1
  # 📦 Input Style
2
2
 
3
- **Librería de componentes y formularios Vue 3**
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-31%20de%20Marzo%20del%2026-blue)
10
+
11
+ ------
12
+
13
+ # 📦 Instalación
14
+
15
+ La librería se distribuye mediante **npm**:
16
+
17
+ ```
18
+ npm install @luis-angel-martin-dzul/input-styles
19
+ ```
20
+
21
+ ### Dependencias peer requeridas
22
+
23
+ ```
24
+ npm install vue@^3.3.0 v-calendar@^3.0.0
25
+ ```
26
+
27
+ ------
28
+
29
+ # 🚀 Uso en Vue + Vite
30
+
31
+ En `main.js` o `main.ts`:
32
+
33
+ ```js
34
+ import { createApp } from 'vue'
35
+ import App from './App.vue'
36
+
37
+ import 'v-calendar/style.css'
38
+ import { library } from '@fortawesome/fontawesome-svg-core'
39
+ import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
40
+ import {
41
+ faCircleCheck, faCircleXmark, faEye, faEyeSlash,
42
+ faCalendar, faClock, faChevronDown,
43
+ faCloudArrowUp, faCloudArrowDown, faSpinner, faCheck
44
+ } from '@fortawesome/free-solid-svg-icons'
45
+ import VCalendar from 'v-calendar'
46
+
47
+ library.add(
48
+ faCircleCheck, faCircleXmark, faEye, faEyeSlash,
49
+ faCalendar, faClock, faChevronDown,
50
+ faCloudArrowUp, faCloudArrowDown, faSpinner, faCheck
51
+ )
52
+
53
+ const app = createApp(App)
54
+
55
+ app.component('font-awesome-icon', FontAwesomeIcon)
56
+ app.use(VCalendar, { locale: 'es', firstDayOfWeek: 1 })
57
+ app.mount('#app')
58
+ ```
59
+
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
+ ```js
69
+ import { configureStyles } from '@luis-angel-martin-dzul/input-styles'
70
+
71
+ configureStyles({
72
+ LOADER_ARC_STROKE: '#3B82F6',
73
+ REQUIRED_ASTERISK: 'text-red-500',
74
+ // solo los tokens que quieras cambiar
75
+ })
76
+ ```
77
+
78
+ > ⚠️ `configureStyles()` debe llamarse **antes** de que los componentes se monten. Los cambios no son reactivos después del montaje inicial.
79
+
80
+ ------
81
+
82
+ ## 📋 Referencia completa de tokens
83
+
84
+ ### Tokens auxiliares de label
85
+ _Afecta: todos los componentes que tienen `label` o `description`_
86
+
87
+ 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**.
88
+
89
+ | Token | Tipo | Default | Descripción |
90
+ | ----- | ---- | ------- | ----------- |
91
+ | `INPUT_BASE_TITLE` | `string` | `'block text-sm font-medium'` | Clases estructurales del `<label>` de inputs de texto. |
92
+ | `INPUT_CHECK_BASE_TITLE` | `string` | `'block text-sm font-medium'` | Clases estructurales del `<label>` de checkbox, radio y switch. |
93
+ | `LABEL_DISABLED_STATIC` | `string` | `'text-gray-400'` | Color del label y descripción cuando el campo está deshabilitado. |
94
+ | `LABEL_ENABLED_STATIC` | `string` | `'text-gray-700'` | Color del label cuando el campo está habilitado. |
95
+
96
+ ```js
97
+ configureStyles({
98
+ LABEL_DISABLED_STATIC: 'text-gray-400',
99
+ LABEL_ENABLED_STATIC: 'text-gray-900',
100
+ INPUT_BASE_TITLE: 'block font-semibold mb-1 text-sm',
101
+ })
102
+ ```
103
+
104
+ ---
105
+
106
+ ### Inputs de texto
107
+ _Afecta: `InputTextStyle`, `InputNumberStyle`, `InputPasswordStyle`, `TextAreaStyle`, `InputDateStyle`, `InputTimeStyle`, `SelectStyle`_
108
+
109
+ | Token | Tipo | Default | Descripción |
110
+ | ----- | ---- | ------- | ----------- |
111
+ | `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`). |
112
+ | `inputValidateClass` | `function(validate)` | Ver abajo | Retorna las clases de borde y ring según el estado de validación. `validate`: `true` / `false` / `null`. |
113
+ | `inputLabelClass` | `function(disabled)` | Ver abajo | Retorna las clases del `<label>`. Internamente usa `INPUT_BASE_TITLE`, `LABEL_DISABLED_STATIC` y `LABEL_ENABLED_STATIC`. |
114
+ | `inputDescriptionClass` | `function(validate, alignRight?)` | Ver abajo | Retorna las clases del texto de descripción. Internamente usa `ICON_VALID`, `ICON_INVALID` y `LABEL_DISABLED_STATIC`. |
115
+
116
+ > 💡 Si solo quieres cambiar un color de validación, basta con sobrescribir `ICON_INVALID` o `ICON_VALID` — `inputDescriptionClass` los recoge automáticamente.
117
+
118
+ ```js
119
+ configureStyles({
120
+ ICON_INVALID: 'text-red-500',
121
+ ICON_VALID: 'text-emerald-500',
122
+ })
123
+
124
+ // Override completo de las funciones (cuando necesitas control total)
125
+ configureStyles({
126
+ inputValidateClass: (validate) => {
127
+ if (validate === false) return 'border-red-500 focus:ring-red-200'
128
+ if (validate === true) return 'border-emerald-500 focus:ring-emerald-200'
129
+ return 'border-gray-200 focus:ring-indigo-200'
130
+ },
131
+ inputLabelClass: (disabled) => ['block font-bold mb-2', disabled ? 'text-gray-400' : 'text-gray-900'],
132
+ inputDescriptionClass: (validate, alignRight = true) => [
133
+ 'block w-full text-sm',
134
+ alignRight && 'text-right',
135
+ validate === false ? 'text-red-500' : validate === true ? 'text-emerald-500' : 'text-gray-500',
136
+ ],
137
+ })
138
+ ```
139
+
140
+ ---
141
+
142
+ ### Select
143
+ _Afecta: `SelectStyle`_
144
+
145
+ | Token | Tipo | Default | Descripción |
146
+ | ----- | ---- | ------- | ----------- |
147
+ | `SELECT_DISABLED` | `string` | `'w-full px-3 py-1.5 rounded bg-gray-100 ...'` | Clases del input en estado disabled. |
148
+ | `SELECT_DROPDOWN` | `string` | `'absolute z-20 w-full bg-white border ...'` | Clases del contenedor `<ul>` del dropdown. |
149
+ | `SELECT_OPTION_BASE` | `string` | `'px-3 py-1 cursor-pointer text-md'` | Clases base de cada `<li>` del dropdown. |
150
+ | `selectOptionClass` | `function(isSelected, isHighlighted)` | Ver abajo | Retorna clases dinámicas por opción según su estado. |
151
+ | `SELECT_EMPTY` | `string` | `'px-3 py-1 text-gray-400 cursor-not-allowed'` | Clases del mensaje "Sin registros". |
152
+
153
+ ```js
154
+ configureStyles({
155
+ selectOptionClass: (isSelected, isHighlighted) => {
156
+ if (isSelected) return 'bg-indigo-100 text-indigo-900 font-medium'
157
+ if (isHighlighted) return 'bg-indigo-50 text-gray-900'
158
+ return 'hover:bg-indigo-50 text-gray-800'
159
+ },
160
+ })
161
+ ```
162
+
163
+ ---
164
+
165
+ ### Checkbox, Radio y Switch
166
+ _Afecta: `InputCheckStyle`, `InputRadioStyle`, `InputSwitchStyle`_
167
+
168
+ | Token | Tipo | Default | Descripción |
169
+ | ----- | ---- | ------- | ----------- |
170
+ | `inputCheckLabelClass` | `function(disabled)` | Ver abajo | Clases del label de checks. |
171
+ | `CHECK_BOX_BASE` | `string` | `'w-5 h-5 flex items-center justify-center rounded border-2 transition'` | Estructura base del cuadro del checkbox. |
172
+ | `RADIO_BASE` | `string` | `'w-5 h-5 flex items-center justify-center rounded-full transition'` | Estructura base del círculo del radio. |
173
+ | `SWITCH_BASE` | `string` | `'w-10 h-5 relative flex items-center rounded-full transition border-2'` | Estructura base de la pista del switch. |
174
+ | `checkBoxClass` | `function(model, disabled)` | Ver abajo | Clases de color del checkbox según estado activo/disabled. |
175
+ | `radioClass` | `function(model, disabled)` | Ver abajo | Clases de color del radio button según estado. |
176
+ | `switchClass` | `function(model, disabled)` | Ver abajo | Clases de color de la pista del switch. |
177
+ | `switchDotClass` | `function(model, disabled)` | Ver abajo | Clases de color de la bola interior del switch. |
178
+
179
+ ```js
180
+ configureStyles({
181
+ checkBoxClass: (model, disabled) => {
182
+ if (disabled) return model
183
+ ? 'bg-indigo-200 border-indigo-200 cursor-not-allowed'
184
+ : 'bg-gray-100 border-gray-200 cursor-not-allowed'
185
+ return model
186
+ ? 'bg-indigo-500 border-indigo-500 peer-focus:ring-2 peer-focus:ring-indigo-200'
187
+ : 'bg-white border-gray-300 peer-focus:ring-2 peer-focus:ring-indigo-200'
188
+ },
189
+ })
190
+ ```
191
+
192
+ ---
193
+
194
+ ### Paginación
195
+ _Afecta: `PaginationStyle`_
196
+
197
+ | Token | Tipo | Default | Descripción |
198
+ | ----- | ---- | ------- | ----------- |
199
+ | `PAGINATION_NAV_PREV` | `string` | `'bg-white hover:bg-indigo-50 border ...'` | Clases del botón de página anterior. |
200
+ | `PAGINATION_NAV_NEXT` | `string` | `'bg-white hover:bg-indigo-50 border ...'` | Clases del botón de página siguiente. |
201
+ | `PAGINATION_PAGE_BASE` | `string` | `'border px-2.5 py-2 cursor-pointer w-8 relative'` | Clases base de cada botón de página. |
202
+ | `paginationPageClass` | `function(page, actualPage, index)` | Ver abajo | Retorna clases dinámicas por botón de página. |
203
+ | `PAGINATION_SUMMARY` | `string` | `'text-sm text-gray-700'` | Clases del texto de resumen de registros. |
204
+
205
+ ---
206
+
207
+ ### Loader
208
+ _Afecta: `LoaderStyle`_
209
+
210
+ | Token | Tipo | Default | Descripción |
211
+ | ----- | ---- | ------- | ----------- |
212
+ | `LOADER_ARC_STROKE` | `string` | `'#6366F1'` | Color hex del arco animado del loader (indigo-500). |
213
+ | `LOADER_MESSAGE` | `string` | `'text-gray-600 text-lg font-medium'` | Clases del mensaje de carga. |
214
+
215
+ ---
216
+
217
+ ### Iconos y asterisco de requerido
218
+ _Afecta: todos los componentes con `required` o iconos de validación_
219
+
220
+ | Token | Tipo | Default | Descripción |
221
+ | ----- | ---- | ------- | ----------- |
222
+ | `REQUIRED_ASTERISK` | `string` | `'text-red-500'` | Clase de color del asterisco `*` en campos requeridos. |
223
+ | `ICON_VALID` | `string` | `'text-emerald-500'` | Clase de color del ícono de validación correcta. |
224
+ | `ICON_INVALID` | `string` | `'text-red-500'` | Clase de color del ícono de validación errónea. |
225
+ | `ICON_STATIC` | `string` | `'text-indigo-400'` | Clase de color de los íconos estáticos (reloj, calendario, ojo, chevron). |
226
+ | `ICON_SIZE_STATIC` | `string` | `'w-5 h-5'` | Clases de tamaño de todos los íconos. |
227
+
228
+ ---
229
+
230
+ ### File Upload y Download
231
+ _Afecta: `InputFileUploadStyle`, `InputFileDownloadStyle`_
232
+
233
+ | Token | Tipo | Default | Descripción |
234
+ | ----- | ---- | ------- | ----------- |
235
+ | `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). |
236
+ | `FILE_BUTTON_ACTIVE` | `string` | `'border-gray-200 bg-white text-gray-700 hover:border-indigo-400 ...'` | Clases del botón en estado normal/activo. |
237
+ | `FILE_BUTTON_DISABLED` | `string` | `'border-gray-200 bg-gray-100 text-gray-400'` | Clases del botón en estado disabled. |
238
+ | `FILE_ICON_BG` | `string` | `'bg-indigo-300'` | Fondo del círculo de ícono en estado activo. |
239
+ | `FILE_ICON_BG_COMPLETE` | `string` | `'bg-emerald-500'` | Fondo del círculo de ícono cuando la descarga está completa. |
240
+ | `FILE_ICON_BG_DISABLED` | `string` | `'bg-gray-300'` | Fondo del círculo de ícono en estado disabled. |
241
+
242
+ ---
243
+
244
+ ### Time Picker
245
+ _Afecta: `InputTimeStyle`_
246
+
247
+ | Token | Tipo | Default | Descripción |
248
+ | ----- | ---- | ------- | ----------- |
249
+ | `TIME_PICKER_WRAPPER` | `string` | `'absolute z-20 left-0 top-full rounded-lg bg-white'` | Contenedor flotante del picker. |
250
+ | `TIME_PICKER_CONTAINER` | `string` | `'border border-gray-300 rounded-lg p-3'` | Borde interior del picker. |
251
+ | `TIME_PICKER_COLUMN` | `string` | `'h-48 overflow-y-auto border border-gray-200 rounded-md ...'` | Columna scrollable de horas/minutos. |
252
+ | `TIME_PICKER_AMPM_COLUMN` | `string` | `'h-48 overflow-y-auto border border-gray-200 rounded-md ...'` | Columna scrollable AM/PM. |
253
+ | `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"). |
254
+ | `TIME_PICKER_ITEM` | `string` | `'px-3 py-1 text-center cursor-pointer hover:bg-indigo-50 ...'` | Fila base de cada ítem del picker. |
255
+ | `timePickerItemClass` | `function(isActive)` | Ver abajo | Clases dinámicas de cada ítem según si está seleccionado. |
256
+
257
+ ------
258
+
259
+ # 🧩 Componentes
260
+
261
+ ## Props comunes
262
+
263
+ La mayoría de los componentes comparten las siguientes props:
264
+
265
+ | Prop | Tipo | Default | Descripción |
266
+ | ---- | ---- | ------- | ----------- |
267
+ | `modelValue` | `String \| Number \| Boolean` | — | Valor del campo (v-model). |
268
+ | `label` | `String` | `''` | Texto del label del campo. |
269
+ | `description` | `String` | `''` | Texto de ayuda debajo del campo. Cambia de color según el estado de validación. |
270
+ | `placeholder` | `String` | `''` | Placeholder del input. |
271
+ | `disabled` | `Boolean` | `false` | Deshabilita el campo. |
272
+ | `required` | `Boolean` | `false` | Marca el campo como requerido (muestra asterisco `*`). |
273
+ | `validate` | `Boolean \| null` | `null` | Estado de validación: `true` = válido, `false` = inválido, `null` = sin estado. |
274
+ | `mclass` | `String` | `null` | Clases CSS adicionales aplicadas directamente al elemento del input. |
275
+
276
+ ## Eventos comunes
277
+
278
+ | Evento | Payload | Descripción |
279
+ | ------ | ------- | ----------- |
280
+ | `update:modelValue` | `value` | Se dispara al cambiar el valor (permite `v-model`). |
281
+ | `change` | `value` | Se dispara al cambiar el valor. |
282
+ | `blur` | `event` | Se dispara al perder el foco. |
283
+
284
+ ------
285
+
286
+ ## InputTextStyle
287
+
288
+ Input de texto genérico. Soporta tipos `text`, `email`, `search`, `tel`, `url` y `number`.
289
+
290
+ ```vue
291
+ <InputTextStyle
292
+ v-model="nombre"
293
+ label="Nombre"
294
+ placeholder="Escribe tu nombre"
295
+ :required="true"
296
+ :validate="validate"
297
+ description="Campo requerido"
298
+ />
299
+ ```
300
+
301
+ | Prop | Tipo | Default | Descripción |
302
+ | ---- | ---- | ------- | ----------- |
303
+ | `modelValue` | `String \| Number` | `''` | Valor del campo. |
304
+ | `label` | `String` | `''` | |
305
+ | `description` | `String` | `''` | |
306
+ | `placeholder` | `String` | `''` | |
307
+ | `type` | `String` | `'text'` | Tipo del input: `text`, `email`, `search`, `tel`, `url`, `number`. |
308
+ | `maxlength` | `String` | `null` | Longitud máxima de caracteres. |
309
+ | `minlength` | `String` | `null` | Longitud mínima de caracteres. |
310
+ | `min` | `String` | `null` | Valor mínimo (solo cuando `type="number"`, aplicado al perder el foco). |
311
+ | `max` | `String` | `null` | Valor máximo (solo cuando `type="number"`, aplicado al perder el foco). |
312
+ | `disabled` | `Boolean` | `false` | |
313
+ | `required` | `Boolean` | `false` | |
314
+ | `validate` | `Boolean \| null` | `null` | |
315
+ | `mclass` | `String` | `null` | |
316
+
317
+ ------
318
+
319
+ ## InputNumberStyle
320
+
321
+ 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`.
322
+
323
+ ```vue
324
+ <InputNumberStyle
325
+ v-model="monto"
326
+ label="Monto"
327
+ numeric="10,2"
328
+ min="0"
329
+ max="9999999999"
330
+ :required="true"
331
+ :validate="validate"
332
+ />
333
+ ```
334
+
335
+ | Prop | Tipo | Default | Descripción |
336
+ | ---- | ---- | ------- | ----------- |
337
+ | `modelValue` | `String \| Number` | `''` | Valor del campo. |
338
+ | `label` | `String` | `''` | |
339
+ | `description` | `String` | `''` | |
340
+ | `placeholder` | `String` | `''` | |
341
+ | `numeric` | `String` | `null` | Formato tipo SQL: `"10,2"` → 10 dígitos enteros, 2 decimales. Si se omite, usa `10,2` por defecto. |
342
+ | `min` | `String` | `null` | Valor mínimo (aplicado al perder el foco). Si es `null` o negativo, permite negativos. |
343
+ | `max` | `String` | `null` | Valor máximo (aplicado al perder el foco). |
344
+ | `disabled` | `Boolean` | `false` | |
345
+ | `required` | `Boolean` | `false` | |
346
+ | `validate` | `Boolean \| null` | `null` | |
347
+ | `mclass` | `String` | `null` | |
348
+
349
+ ------
350
+
351
+ ## InputPasswordStyle
352
+
353
+ Input de contraseña con toggle para mostrar/ocultar el texto.
354
+
355
+ ```vue
356
+ <InputPasswordStyle
357
+ v-model="password"
358
+ label="Contraseña"
359
+ placeholder="Mínimo 8 caracteres"
360
+ :required="true"
361
+ :validate="validate"
362
+ />
363
+ ```
364
+
365
+ | Prop | Tipo | Default | Descripción |
366
+ | ---- | ---- | ------- | ----------- |
367
+ | `modelValue` | `String \| Number` | `''` | Valor del campo. |
368
+ | `label` | `String` | `''` | |
369
+ | `description` | `String` | `''` | |
370
+ | `placeholder` | `String` | `''` | |
371
+ | `maxlength` | `String` | `null` | Longitud máxima. |
372
+ | `disabled` | `Boolean` | `false` | |
373
+ | `required` | `Boolean` | `false` | |
374
+ | `validate` | `Boolean \| null` | `null` | |
375
+ | `mclass` | `String` | `null` | |
376
+
377
+ ------
378
+
379
+ ## TextAreaStyle
380
+
381
+ Área de texto multi-línea.
382
+
383
+ ```vue
384
+ <TextAreaStyle
385
+ v-model="comentario"
386
+ label="Comentario"
387
+ placeholder="Escribe aquí..."
388
+ row="5"
389
+ :maxlength="500"
390
+ :validate="validate"
391
+ />
392
+ ```
393
+
394
+ | Prop | Tipo | Default | Descripción |
395
+ | ---- | ---- | ------- | ----------- |
396
+ | `modelValue` | `String` | `''` | Valor del campo. |
397
+ | `label` | `String` | `''` | |
398
+ | `description` | `String` | `''` | |
399
+ | `placeholder` | `String` | `''` | |
400
+ | `row` | `String` | `'3'` | Número de filas visibles del textarea. |
401
+ | `maxlength` | `String` | `null` | Longitud máxima de caracteres. |
402
+ | `disabled` | `Boolean` | `false` | |
403
+ | `required` | `Boolean` | `false` | |
404
+ | `validate` | `Boolean \| null` | `null` | |
405
+ | `mclass` | `String` | `null` | |
406
+
407
+ ------
408
+
409
+ ## SelectStyle
410
+
411
+ 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.
412
+
413
+ ```vue
414
+ <SelectStyle
415
+ v-model="pais"
416
+ label="País"
417
+ :list="paises"
418
+ return-value="value"
419
+ :required="true"
420
+ :validate="validate"
421
+ />
422
+ ```
423
+
424
+ ```js
425
+ // Formato requerido del array `list`
426
+ const paises = [
427
+ { value: 1, text: 'México' },
428
+ { value: 2, text: 'España' },
429
+ { value: 3, text: 'Argentina' },
430
+ ]
431
+ ```
432
+
433
+ | Prop | Tipo | Default | Descripción |
434
+ | ---- | ---- | ------- | ----------- |
435
+ | `modelValue` | `String \| Number \| Object` | — | Valor seleccionado. |
436
+ | `list` | `Array` | `[]` | Array de opciones con formato `{ value, text }`. |
437
+ | `returnValue` | `String` | `'value'` | Qué se emite al seleccionar: `'value'` (item.value), `'text'` (item.text) u `'object'` (el item completo). |
438
+ | `label` | `String` | `''` | |
439
+ | `description` | `String` | `''` | |
440
+ | `disabled` | `Boolean` | `false` | |
441
+ | `required` | `Boolean` | `false` | |
442
+ | `validate` | `Boolean \| null` | `null` | |
443
+ | `mclass` | `String` | `null` | |
444
+
445
+ **Navegación por teclado:**
446
+ - `↑` / `↓` — navegar opciones
447
+ - `Enter` — seleccionar la opción resaltada
448
+ - `Escape` — cerrar el dropdown
449
+ - Tipear letras — búsqueda inmediata en la lista
450
+
451
+ ------
452
+
453
+ ## InputCheckStyle
454
+
455
+ Checkbox estilizado con label y descripción.
456
+
457
+ ```vue
458
+ <InputCheckStyle
459
+ v-model="aceptaTerminos"
460
+ label="Acepto los términos y condiciones"
461
+ :required="true"
462
+ />
463
+ ```
464
+
465
+ | Prop | Tipo | Default | Descripción |
466
+ | ---- | ---- | ------- | ----------- |
467
+ | `modelValue` | `Boolean` | `false` | Estado del checkbox. |
468
+ | `label` | `String` | `''` | |
469
+ | `description` | `String` | `''` | |
470
+ | `disabled` | `Boolean` | `false` | |
471
+ | `required` | `Boolean` | `false` | |
472
+ | `validate` | `Boolean \| null` | `null` | |
473
+ | `mclass` | `String` | `null` | Clases adicionales del cuadro del checkbox. |
474
+
475
+ ------
476
+
477
+ ## InputRadioStyle
478
+
479
+ Radio button estilizado con label y descripción.
480
+
481
+ ```vue
482
+ <InputRadioStyle
483
+ v-model="opcionA"
484
+ label="Opción A"
485
+ />
486
+ ```
487
+
488
+ | Prop | Tipo | Default | Descripción |
489
+ | ---- | ---- | ------- | ----------- |
490
+ | `modelValue` | `Boolean` | `false` | Estado del radio. |
491
+ | `label` | `String` | `''` | |
492
+ | `description` | `String` | `''` | |
493
+ | `disabled` | `Boolean` | `false` | |
494
+ | `required` | `Boolean` | `false` | |
495
+ | `validate` | `Boolean \| null` | `null` | |
496
+ | `mclass` | `String` | `null` | Clases adicionales del círculo del radio. |
497
+
498
+ ------
499
+
500
+ ## InputSwitchStyle
501
+
502
+ Toggle switch estilizado.
503
+
504
+ ```vue
505
+ <InputSwitchStyle
506
+ v-model="activo"
507
+ label="Activar notificaciones"
508
+ />
509
+ ```
510
+
511
+ | Prop | Tipo | Default | Descripción |
512
+ | ---- | ---- | ------- | ----------- |
513
+ | `modelValue` | `Boolean` | `false` | Estado del switch. |
514
+ | `label` | `String` | `''` | |
515
+ | `description` | `String` | `''` | |
516
+ | `disabled` | `Boolean` | `false` | |
517
+ | `required` | `Boolean` | `false` | |
518
+ | `validate` | `Boolean \| null` | `null` | |
519
+ | `mclass` | `String` | `null` | Clases adicionales de la pista del switch. |
520
+
521
+ ------
522
+
523
+ ## InputDateStyle
524
+
525
+ Selector de fecha con máscara `dd/mm/aaaa`, navegación por teclado entre secciones y calendario visual (v-calendar). El valor se emite en formato `YYYY-MM-DD`.
526
+
527
+ ```vue
528
+ <InputDateStyle
529
+ v-model="fechaNacimiento"
530
+ label="Fecha de nacimiento"
531
+ min="1900-01-01"
532
+ max="2100-12-31"
533
+ :validate="validate"
534
+ />
535
+ ```
536
+
537
+ | Prop | Tipo | Default | Descripción |
538
+ | ---- | ---- | ------- | ----------- |
539
+ | `modelValue` | `String \| Date` | — | Valor de la fecha en formato `YYYY-MM-DD` o `Date`. |
540
+ | `label` | `String` | `''` | |
541
+ | `description` | `String` | `''` | |
542
+ | `min` | `String` | `'1900-01-01'` | Fecha mínima seleccionable en el calendario (formato `YYYY-MM-DD`). |
543
+ | `max` | `String` | `'2100-12-31'` | Fecha máxima seleccionable en el calendario (formato `YYYY-MM-DD`). |
544
+ | `disabledDates` | `Array \| Object` | `[]` | Fechas deshabilitadas en el calendario. Acepta el formato de `v-calendar`. |
545
+ | `disabled` | `Boolean` | `false` | |
546
+ | `required` | `Boolean` | `false` | |
547
+ | `validate` | `Boolean \| null` | `null` | |
548
+ | `mclass` | `String` | `null` | |
549
+
550
+ **Valor emitido:** `'YYYY-MM-DD'` (string) o `''` si la fecha es inválida o está vacía.
551
+
552
+ **Navegación por teclado:**
553
+ - Escribe los dígitos y el foco avanza automáticamente entre día → mes → año
554
+ - `←` / `→` — mover entre secciones
555
+ - `Backspace` / `Delete` — limpiar la sección activa
556
+ - Clic en el ícono 📅 — abre/cierra el calendario visual
557
+
558
+ ------
559
+
560
+ ## InputTimeStyle
561
+
562
+ Selector de hora con picker de columnas scrollables. Soporta formato 12h (AM/PM) o 24h. El valor se emite en formato `HH:mm`.
563
+
564
+ ```vue
565
+ <InputTimeStyle
566
+ v-model="horaInicio"
567
+ label="Hora de inicio"
568
+ min="08:00"
569
+ max="18:00"
570
+ :is24h="true"
571
+ :validate="validate"
572
+ />
573
+ ```
574
+
575
+ | Prop | Tipo | Default | Descripción |
576
+ | ---- | ---- | ------- | ----------- |
577
+ | `modelValue` | `String` | — | Valor de la hora en formato `HH:mm`. |
578
+ | `label` | `String` | `''` | |
579
+ | `description` | `String` | `''` | |
580
+ | `min` | `String` | `null` | Hora mínima (ej: `'08:00'`). |
581
+ | `max` | `String` | `null` | Hora máxima (ej: `'18:00'`). |
582
+ | `is24h` | `Boolean` | `false` | `true` = formato 24h, `false` = formato 12h con selector AM/PM. |
583
+ | `disabled` | `Boolean` | `false` | |
584
+ | `required` | `Boolean` | `false` | |
585
+ | `validate` | `Boolean \| null` | `null` | |
586
+ | `mclass` | `String` | `null` | |
587
+
588
+ **Valor emitido:** `'HH:mm'` en formato 24h siempre, independientemente del modo de visualización.
589
+
590
+ ------
591
+
592
+ ## InputFileUploadStyle
593
+
594
+ Botón para adjuntar archivos con validación de tamaño y formato.
595
+
596
+ ```vue
597
+ <InputFileUploadStyle
598
+ v-model="archivo"
599
+ :formats="['pdf', 'docx']"
600
+ max-size-m-b="10"
601
+ :state="uploadState"
602
+ />
603
+ ```
604
+
605
+ | Prop | Tipo | Default | Descripción |
606
+ | ---- | ---- | ------- | ----------- |
607
+ | `modelValue` | `File` | `null` | Archivo seleccionado. |
608
+ | `disabled` | `Boolean` | `false` | Deshabilita el botón. |
609
+ | `formats` | `Array` | `[]` | Extensiones permitidas (ej: `['pdf', 'xlsx', 'png']`). Array vacío = todos los formatos. |
610
+ | `maxSizeMB` | `String \| Number` | `5` | Tamaño máximo del archivo en MB. |
611
+ | `state` | `String` | `'uphold'` | Estado del botón: `'uphold'` (normal) o `'loader'` (cargando, muestra spinner). |
612
+ | `delay` | `String` | `'1.5'` | Segundos de bloqueo del botón tras un clic, para evitar doble envío. |
613
+
614
+ **Eventos:**
615
+ - `update:modelValue` — emite el `File` seleccionado, o `null` si el archivo no es válido.
616
+ - `change` — igual que `update:modelValue`.
617
+
618
+ El componente valida automáticamente el tamaño y el formato, mostrando un mensaje de error inline si la validación falla.
619
+
620
+ ------
621
+
622
+ ## InputFileDownloadStyle
623
+
624
+ Botón para descargar archivos con tres estados visuales.
625
+
626
+ ```vue
627
+ <InputFileDownloadStyle
628
+ :state="downloadState"
629
+ :disabled="false"
630
+ @click="descargarArchivo"
631
+ />
632
+ ```
633
+
634
+ | Prop | Tipo | Default | Descripción |
635
+ | ---- | ---- | ------- | ----------- |
636
+ | `disabled` | `Boolean` | `false` | Deshabilita el botón. |
637
+ | `state` | `String` | `'download'` | Estado: `'download'` (normal), `'loader'` (descargando, spinner), `'complete'` (completado, check verde). |
638
+ | `delay` | `String` | `'1.5'` | Segundos de bloqueo del botón tras un clic. |
639
+
640
+ **Eventos:**
641
+ - `click` — se dispara al hacer clic (solo si no está deshabilitado).
642
+
643
+ ------
644
+
645
+ ## ButtonStyle
646
+
647
+ Botón genérico con bloqueo automático anti-doble-clic.
648
+
649
+ ```vue
650
+ <ButtonStyle
651
+ class="px-4 py-2 bg-indigo-500 text-white rounded"
652
+ :disabled="cargando"
653
+ @click="guardar"
654
+ >
655
+ Guardar
656
+ </ButtonStyle>
657
+ ```
658
+
659
+ | Prop | Tipo | Default | Descripción |
660
+ | ---- | ---- | ------- | ----------- |
661
+ | `type` | `String` | `'button'` | Tipo del botón: `'button'` o `'submit'`. |
662
+ | `class` | `String` | `''` | Clases CSS del botón (TailwindCSS). |
663
+ | `disabled` | `Boolean` | `false` | Deshabilita el botón. |
664
+ | `delay` | `String` | `'1.5'` | Segundos de bloqueo automático tras hacer clic (anti-doble-clic). |
665
+
666
+ **Eventos:**
667
+ - `click` — se dispara al hacer clic (solo si no está deshabilitado ni en periodo de bloqueo).
668
+
669
+ Acepta contenido mediante `<slot>` (texto, íconos, etc.).
670
+
671
+ ------
672
+
673
+ ## LoaderStyle
674
+
675
+ Indicador de carga circular animado.
676
+
677
+ ```vue
678
+ <LoaderStyle message="Procesando datos..." />
679
+ ```
680
+
681
+ | Prop | Tipo | Default | Descripción |
682
+ | ---- | ---- | ------- | ----------- |
683
+ | `message` | `String` | `'Cargando...'` | Texto que se muestra debajo del arco animado. |
684
+
685
+ El color del arco se controla con el token `LOADER_ARC_STROKE`.
686
+
687
+ ------
688
+
689
+ ## PaginationStyle
690
+
691
+ Paginador con navegación anterior/siguiente, botones de página y resumen de registros.
692
+
693
+ ```vue
694
+ <PaginationStyle
695
+ :total-pages="totalPaginas"
696
+ :actual-page="paginaActual"
697
+ :records="totalRegistros"
698
+ :limit="registrosPorPagina"
699
+ :summary="true"
700
+ @page="cambiarPagina"
701
+ />
702
+ ```
703
+
704
+ | Prop | Tipo | Default | Descripción |
705
+ | ---- | ---- | ------- | ----------- |
706
+ | `totalPages` | `Number` | `0` | Total de páginas. |
707
+ | `actualPage` | `Number` | `1` | Página actualmente activa. |
708
+ | `records` | `Number` | `0` | Total de registros (para el resumen). |
709
+ | `limit` | `Number` | `0` | Registros por página (para calcular el rango del resumen). |
710
+ | `summary` | `Boolean` | `false` | `true` = muestra el texto "Mostrando X - Y de Z". |
711
+
712
+ **Eventos:**
713
+ - `page` — emite el número de página a la que se desea navegar.
714
+
715
+ El paginador muestra hasta 7 botones con elipsis (`...`) inteligentes cuando hay más páginas.
716
+
717
+ ------
718
+
719
+ ## Ejemplo de uso completo
720
+
721
+ ```vue
722
+ <template>
723
+ <div class="p-6 space-y-4 max-w-md">
724
+
725
+ <InputTextStyle
726
+ v-model="form.nombre"
727
+ label="Nombre completo"
728
+ placeholder="Ej: Juan Pérez"
729
+ :required="true"
730
+ :validate="validaciones.nombre"
731
+ />
732
+
733
+ <InputNumberStyle
734
+ v-model="form.monto"
735
+ label="Monto"
736
+ numeric="10,2"
737
+ min="0"
738
+ :validate="validaciones.monto"
739
+ />
740
+
741
+ <InputPasswordStyle
742
+ v-model="form.password"
743
+ label="Contraseña"
744
+ :required="true"
745
+ :validate="validaciones.password"
746
+ />
747
+
748
+ <SelectStyle
749
+ v-model="form.pais"
750
+ label="País"
751
+ :list="paises"
752
+ return-value="value"
753
+ :validate="validaciones.pais"
754
+ />
755
+
756
+ <InputDateStyle
757
+ v-model="form.fecha"
758
+ label="Fecha"
759
+ :validate="validaciones.fecha"
760
+ />
761
+
762
+ <InputTimeStyle
763
+ v-model="form.hora"
764
+ label="Hora"
765
+ :is24h="true"
766
+ />
767
+
768
+ <InputCheckStyle
769
+ v-model="form.acepta"
770
+ label="Acepto los términos"
771
+ :required="true"
772
+ />
773
+
774
+ <ButtonStyle
775
+ class="w-full px-4 py-2 bg-indigo-500 text-white rounded hover:bg-indigo-600"
776
+ @click="enviar"
777
+ >
778
+ Enviar
779
+ </ButtonStyle>
780
+
781
+ </div>
782
+ </template>
783
+
784
+ <script setup>
785
+ import { ref } from 'vue'
786
+ import {
787
+ InputTextStyle, InputNumberStyle, InputPasswordStyle,
788
+ SelectStyle, InputDateStyle, InputTimeStyle,
789
+ InputCheckStyle, ButtonStyle
790
+ } from '@luis-angel-martin-dzul/input-styles'
791
+
792
+ const form = ref({
793
+ nombre: '', monto: '', password: '',
794
+ pais: null, fecha: '', hora: '', acepta: false
795
+ })
796
+
797
+ const validaciones = ref({
798
+ nombre: null, monto: null, password: null,
799
+ pais: null, fecha: null
800
+ })
801
+
802
+ const paises = [
803
+ { value: 1, text: 'México' },
804
+ { value: 2, text: 'España' },
805
+ ]
806
+
807
+ function enviar() {
808
+ validaciones.value.nombre = form.value.nombre.length > 0
809
+ // ... resto de validaciones
810
+ }
811
+ </script>
812
+ ```