@onpe/ui 1.0.25 → 1.0.27

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.
Files changed (2) hide show
  1. package/README.md +1313 -27
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -13,31 +13,115 @@ Librería completa de componentes de interfaz de usuario para aplicaciones de la
13
13
 
14
14
  ## 🚀 Instalación
15
15
 
16
+ ### Instalación Completa de la Librería
16
17
  ```bash
17
18
  npm install @onpe/ui
18
19
  ```
19
20
 
20
- ## ⚠️ Importante sobre TailwindCSS
21
+ ### Instalación Individual de Componentes (CLI)
22
+ ```bash
23
+ # Instalar la CLI globalmente
24
+ npm install -g @onpe/ui
25
+
26
+ # O usar directamente con npx
27
+ npx onpe-ui add <componente>
28
+ ```
29
+
30
+ ## ⚠️ Importante sobre Estilos
21
31
 
22
- Esta librería **NO requiere** que instales TailwindCSS en tu proyecto. Los estilos ya están compilados y optimizados. Solo necesitas importar los estilos:
32
+ ### Opción 1: Librería Completa (Recomendado)
33
+ Esta librería **NO requiere** que instales TailwindCSS en tu proyecto. Los estilos ya están compilados y optimizados. Solo necesitas crear tu propio archivo CSS con las variables personalizadas:
34
+
35
+ #### Crear archivo CSS personalizado
36
+ Crea un archivo llamado `onpe-ui.css` en la raíz de tu proyecto:
23
37
 
24
38
  ```css
25
- @import "@onpe/ui/styles";
39
+ /* Solo definimos nuestras variables y estilos personalizados */
40
+
41
+
42
+
43
+ :root {
44
+ --blue: #003770;
45
+ --skyblue: #0073cf;
46
+ --skyblue-light: #69b2e8;
47
+ --yellow: #ffb81c;
48
+ --light-skyblue: #aaeff6;
49
+ --gray: #bcbcbc;
50
+ --gray-light: #bdbdbd;
51
+ --gray-extra-light: #f2f2f2;
52
+ --red: #e3002b;
53
+ --dark-gray: #4f4f4f;
54
+ --green: #76bd43;
55
+ --yellow-light: #FFF1D2;
56
+ }
57
+
58
+ @theme {
59
+ --color-onpe-ui-blue: var(--blue);
60
+ --color-onpe-ui-skyblue: var(--skyblue);
61
+ --color-onpe-ui-skyblue-light: var(--skyblue-light);
62
+ --color-onpe-ui-yellow: var(--yellow);
63
+ --color-onpe-ui-light-skyblue: var(--light-skyblue);
64
+ --color-onpe-ui-gray: var(--gray);
65
+ --color-onpe-ui-gray-light: var(--gray-light);
66
+ --color-onpe-ui-gray-extra-light: var(--gray-extra-light);
67
+ --color-onpe-ui-red: var(--red);
68
+ --color-onpe-ui-dark-gray: var(--dark-gray);
69
+ --color-onpe-ui-green: var(--green);
70
+ --color-onpe-ui-yellow-light: var(--yellow-light);
71
+ }
72
+
73
+
74
+ button{
75
+ cursor: pointer;
76
+ }
77
+
78
+
79
+ @keyframes fastBlink {
80
+ 0% {
81
+ opacity: 1;
82
+ }
83
+ 25% {
84
+ opacity: 0.8;
85
+ }
86
+ 50% {
87
+ opacity: 0.4;
88
+ }
89
+ 75% {
90
+ opacity: 0.8;
91
+ }
92
+ 100% {
93
+ opacity: 1;
94
+ }
95
+ }
96
+ .fast-blink {
97
+ animation: fastBlink 0.8s ease-in-out infinite;
98
+ }
26
99
  ```
27
100
 
28
- Si quieres extender los estilos con tus propias clases de TailwindCSS, entonces sí necesitarías instalar TailwindCSS en tu proyecto.
101
+ #### Importar el archivo CSS en tu proyecto
102
+ ```tsx
103
+ // En tu archivo principal (index.tsx, App.tsx, etc.)
104
+ import './onpe-ui.css';
105
+ import { Button } from '@onpe/ui/components';
106
+ ```
107
+
108
+ ### Opción 2: Componentes Individuales
109
+ Si instalas componentes individualmente con la CLI, **SÍ necesitas** instalar y configurar TailwindCSS en tu proyecto.
29
110
 
30
111
  ## 📖 Uso Básico
31
112
 
32
- ### Importar estilos
113
+ ### Opción 1: Usar la Librería Completa
33
114
 
34
- ```css
35
- @import "@onpe/ui/styles";
115
+ #### Importar estilos
116
+ ```tsx
117
+ // Importar tu archivo CSS personalizado
118
+ import './onpe-ui.css';
119
+ import { Button } from '@onpe/ui/components';
36
120
  ```
37
121
 
38
- ### Usar componentes
39
-
122
+ #### Usar componentes
40
123
  ```tsx
124
+ import './onpe-ui.css';
41
125
  import { Button } from '@onpe/ui/components';
42
126
 
43
127
  function App() {
@@ -50,6 +134,122 @@ function App() {
50
134
  }
51
135
  ```
52
136
 
137
+ ### Opción 2: Instalar Componentes Individualmente con CLI
138
+
139
+ #### Instalar un componente específico
140
+ ```bash
141
+ # Instalar Button
142
+ npx onpe-ui add button
143
+
144
+ # Instalar Modal (instala automáticamente Portal y Overlay)
145
+ npx onpe-ui add modal
146
+
147
+ # Instalar Portal
148
+ npx onpe-ui add portal
149
+
150
+ # Instalar Overlay
151
+ npx onpe-ui add overlay
152
+
153
+ # Instalar Show
154
+ npx onpe-ui add show
155
+ ```
156
+
157
+ #### Usar componentes instalados individualmente
158
+ ```tsx
159
+ // Después de instalar con CLI
160
+ import { Button } from './components/ui/Button';
161
+ import { Modal } from './components/ui/Modal';
162
+ import { useState } from 'react';
163
+
164
+ function App() {
165
+ const [isOpen, setIsOpen] = useState(false);
166
+
167
+ return (
168
+ <div className="p-4">
169
+ <Button
170
+ color="primary"
171
+ title="Abrir Modal"
172
+ onClick={() => setIsOpen(true)}
173
+ />
174
+
175
+ <Modal
176
+ isOpen={isOpen}
177
+ onClose={() => setIsOpen(false)}
178
+ closeButton={true}
179
+ overlayColor="blue"
180
+ >
181
+ <div className="p-6">
182
+ <h2 className="text-xl font-bold mb-4">Contenido del Modal</h2>
183
+ <p className="mb-4">Este es un ejemplo de modal con contenido.</p>
184
+ <Button
185
+ color="green"
186
+ title="Cerrar"
187
+ onClick={() => setIsOpen(false)}
188
+ />
189
+ </div>
190
+ </Modal>
191
+ </div>
192
+ );
193
+ }
194
+ ```
195
+
196
+ #### Configuración requerida para componentes individuales
197
+
198
+ **1. Instalar Tailwind CSS:**
199
+ ```bash
200
+ npm install -D tailwindcss postcss autoprefixer
201
+ npx tailwindcss init -p
202
+ ```
203
+
204
+ **2. Configurar tailwind.config.js:**
205
+ ```javascript
206
+ /** @type {import('tailwindcss').Config} */
207
+ module.exports = {
208
+ content: ["./src/**/*.{js,jsx,ts,tsx}"],
209
+ theme: {
210
+ extend: {
211
+ colors: {
212
+ 'onpe-ui-blue': '#003770',
213
+ 'onpe-ui-skyblue': '#0073cf',
214
+ 'onpe-ui-skyblue-light': '#69b2e8',
215
+ 'onpe-ui-yellow': '#ffb81c',
216
+ 'onpe-ui-light-skyblue': '#aaeff6',
217
+ 'onpe-ui-gray': '#bcbcbc',
218
+ 'onpe-ui-gray-light': '#bdbdbd',
219
+ 'onpe-ui-gray-extra-light': '#f2f2f2',
220
+ 'onpe-ui-red': '#e3002b',
221
+ 'onpe-ui-dark-gray': '#4f4f4f',
222
+ 'onpe-ui-green': '#76bd43',
223
+ 'onpe-ui-yellow-light': '#FFF1D2',
224
+ }
225
+ },
226
+ },
227
+ plugins: [],
228
+ }
229
+ ```
230
+
231
+ **3. Agregar estilos base en index.css:**
232
+ ```css
233
+ @tailwind base;
234
+ @tailwind components;
235
+ @tailwind utilities;
236
+ ```
237
+
238
+ **4. Para componentes que usan Portal, agregar en public/index.html:**
239
+ ```html
240
+ <!DOCTYPE html>
241
+ <html lang="es">
242
+ <head>
243
+ <meta charset="utf-8" />
244
+ <title>Mi App</title>
245
+ </head>
246
+ <body>
247
+ <div id="root"></div>
248
+ <div id="portal"></div>
249
+ </body>
250
+ </html>
251
+ ```
252
+
53
253
  ## 🎨 Paleta de Colores ONPE
54
254
 
55
255
  ### Colores Principales
@@ -69,32 +269,156 @@ function App() {
69
269
  - **Gray Light**: `#bdbdbd` - Texto terciario
70
270
  - **Gray Extra Light**: `#f2f2f2` - Fondos suaves
71
271
 
272
+ ## 🔗 Dependencias entre Componentes
273
+
274
+ ### Mapa de Dependencias
275
+ ```
276
+ Modal
277
+ ├── Portal (requerido)
278
+ ├── Overlay (requerido)
279
+ └── IconClose (requerido)
280
+
281
+ Portal
282
+ └── react-dom (createPortal)
283
+
284
+ Overlay
285
+ └── (sin dependencias externas)
286
+
287
+ Button
288
+ └── (sin dependencias externas)
289
+
290
+ Show
291
+ └── (sin dependencias externas)
292
+ ```
293
+
294
+ ### Instalación Automática de Dependencias
295
+
296
+ **Modal** - Instala automáticamente sus dependencias:
297
+ ```bash
298
+ npx onpe-ui add modal
299
+ # Esto instalará automáticamente:
300
+ # - Portal.tsx
301
+ # - Overlay.tsx
302
+ # - IconClose.tsx (si está disponible)
303
+ ```
304
+
305
+ **Otros componentes** - Instalación independiente:
306
+ ```bash
307
+ npx onpe-ui add button # Sin dependencias
308
+ npx onpe-ui add portal # Sin dependencias
309
+ npx onpe-ui add overlay # Sin dependencias
310
+ npx onpe-ui add show # Sin dependencias
311
+ ```
312
+
313
+ ### Estructura de Archivos Después de la Instalación
314
+
315
+ ```
316
+ src/
317
+ └── components/
318
+ └── ui/
319
+ ├── Button.tsx
320
+ ├── Modal.tsx
321
+ ├── Overlay.tsx
322
+ ├── Portal.tsx
323
+ └── Show.tsx
324
+ ```
325
+
72
326
  ## 🧩 Componentes Disponibles
73
327
 
74
328
  ### Button
75
329
 
76
330
  Botón versátil con múltiples colores y tamaños.
77
331
 
78
- Botón versátil con múltiples variantes y tamaños.
332
+ #### Ejemplo Básico
333
+ ```tsx
334
+ import { Button } from '@onpe/ui/components';
79
335
 
336
+ function App() {
337
+ return (
338
+ <div className="space-y-4 p-4">
339
+ <h2 className="text-2xl font-bold">Botones ONPE</h2>
340
+
341
+ {/* Colores disponibles */}
342
+ <div className="space-y-2">
343
+ <h3 className="text-lg font-semibold">Colores:</h3>
344
+ <div className="flex flex-wrap gap-2">
345
+ <Button color="primary" title="Primario" />
346
+ <Button color="blue" title="Azul" />
347
+ <Button color="skyblue" title="Sky Blue" />
348
+ <Button color="green" title="Verde" />
349
+ <Button color="yellow" title="Amarillo" />
350
+ <Button color="red" title="Rojo" />
351
+ </div>
352
+ </div>
353
+
354
+ {/* Tamaños */}
355
+ <div className="space-y-2">
356
+ <h3 className="text-lg font-semibold">Tamaños:</h3>
357
+ <div className="flex items-center gap-2">
358
+ <Button color="primary" title="Pequeño" size="small" />
359
+ <Button color="primary" title="Mediano" size="normal" />
360
+ <Button color="primary" title="Grande" size="large" />
361
+ </div>
362
+ </div>
363
+
364
+ {/* Estados */}
365
+ <div className="space-y-2">
366
+ <h3 className="text-lg font-semibold">Estados:</h3>
367
+ <div className="flex gap-2">
368
+ <Button color="primary" title="Normal" />
369
+ <Button color="primary" title="Deshabilitado" disabled />
370
+ </div>
371
+ </div>
372
+ </div>
373
+ );
374
+ }
375
+ ```
376
+
377
+ #### Ejemplo con Funcionalidad
80
378
  ```tsx
81
379
  import { Button } from '@onpe/ui/components';
380
+ import { useState } from 'react';
82
381
 
83
- // Colores disponibles
84
- <Button color="primary" title="Primario" />
85
- <Button color="blue" title="Azul" />
86
- <Button color="skyblue" title="Sky Blue" />
87
- <Button color="green" title="Verde" />
88
- <Button color="yellow" title="Amarillo" />
89
- <Button color="red" title="Rojo" />
382
+ function VotingApp() {
383
+ const [voted, setVoted] = useState(false);
384
+ const [loading, setLoading] = useState(false);
90
385
 
91
- // Tamaños
92
- <Button color="primary" title="Pequeño" size="small" />
93
- <Button color="primary" title="Mediano" size="normal" />
94
- <Button color="primary" title="Grande" size="large" />
386
+ const handleVote = async () => {
387
+ setLoading(true);
388
+ // Simular llamada a API
389
+ await new Promise(resolve => setTimeout(resolve, 2000));
390
+ setVoted(true);
391
+ setLoading(false);
392
+ };
95
393
 
96
- // Estados
97
- <Button color="primary" title="Deshabilitado" disabled />
394
+ return (
395
+ <div className="max-w-md mx-auto p-6 bg-white rounded-lg shadow-lg">
396
+ <h2 className="text-xl font-bold mb-4">Sistema de Votación</h2>
397
+
398
+ {!voted ? (
399
+ <div className="space-y-4">
400
+ <p className="text-gray-600">¿Desea votar por esta opción?</p>
401
+ <Button
402
+ color="primary"
403
+ title={loading ? "Procesando..." : "Votar Ahora"}
404
+ onClick={handleVote}
405
+ disabled={loading}
406
+ size="large"
407
+ />
408
+ </div>
409
+ ) : (
410
+ <div className="text-center">
411
+ <p className="text-green-600 font-semibold mb-4">¡Voto registrado exitosamente!</p>
412
+ <Button
413
+ color="green"
414
+ title="Ver Resultados"
415
+ onClick={() => setVoted(false)}
416
+ />
417
+ </div>
418
+ )}
419
+ </div>
420
+ );
421
+ }
98
422
  ```
99
423
 
100
424
  ### Props del Button
@@ -111,17 +435,223 @@ import { Button } from '@onpe/ui/components';
111
435
 
112
436
  Componente modal para mostrar contenido en overlay.
113
437
 
438
+ #### Ejemplo Básico
114
439
  ```tsx
115
440
  import { Modal } from '@onpe/ui/components';
441
+ import { useState } from 'react';
116
442
 
117
443
  function App() {
118
444
  const [isOpen, setIsOpen] = useState(false);
119
445
 
120
446
  return (
121
- <Modal isOpen={isOpen} onClose={() => setIsOpen(false)}>
122
- <h2>Contenido del Modal</h2>
123
- <p>Este es el contenido del modal.</p>
124
- </Modal>
447
+ <div className="p-4">
448
+ <button
449
+ onClick={() => setIsOpen(true)}
450
+ className="bg-blue-500 text-white px-4 py-2 rounded"
451
+ >
452
+ Abrir Modal
453
+ </button>
454
+
455
+ <Modal
456
+ isOpen={isOpen}
457
+ onClose={() => setIsOpen(false)}
458
+ closeButton={true}
459
+ overlayColor="blue"
460
+ >
461
+ <div className="p-6">
462
+ <h2 className="text-xl font-bold mb-4">Título del Modal</h2>
463
+ <p className="mb-4">Este es el contenido del modal.</p>
464
+ <button
465
+ onClick={() => setIsOpen(false)}
466
+ className="bg-gray-500 text-white px-4 py-2 rounded"
467
+ >
468
+ Cerrar
469
+ </button>
470
+ </div>
471
+ </Modal>
472
+ </div>
473
+ );
474
+ }
475
+ ```
476
+
477
+ #### Ejemplo Avanzado - Modal de Confirmación
478
+ ```tsx
479
+ import { Modal } from '@onpe/ui/components';
480
+ import { useState } from 'react';
481
+
482
+ function DeleteConfirmation() {
483
+ const [showModal, setShowModal] = useState(false);
484
+ const [itemToDelete, setItemToDelete] = useState('');
485
+
486
+ const handleDelete = (itemName) => {
487
+ setItemToDelete(itemName);
488
+ setShowModal(true);
489
+ };
490
+
491
+ const confirmDelete = () => {
492
+ // Lógica para eliminar el elemento
493
+ console.log(`Eliminando: ${itemToDelete}`);
494
+ setShowModal(false);
495
+ setItemToDelete('');
496
+ };
497
+
498
+ return (
499
+ <div className="p-4">
500
+ <div className="space-y-2">
501
+ <button
502
+ onClick={() => handleDelete('Usuario 1')}
503
+ className="bg-red-500 text-white px-4 py-2 rounded mr-2"
504
+ >
505
+ Eliminar Usuario 1
506
+ </button>
507
+ <button
508
+ onClick={() => handleDelete('Documento 2')}
509
+ className="bg-red-500 text-white px-4 py-2 rounded mr-2"
510
+ >
511
+ Eliminar Documento 2
512
+ </button>
513
+ </div>
514
+
515
+ <Modal
516
+ isOpen={showModal}
517
+ onClose={() => setShowModal(false)}
518
+ closeButton={true}
519
+ overlayColor="red"
520
+ closeDisabled={false}
521
+ >
522
+ <div className="p-6 text-center">
523
+ <div className="mb-4">
524
+ <div className="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-red-100 mb-4">
525
+ <svg className="h-6 w-6 text-red-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
526
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z" />
527
+ </svg>
528
+ </div>
529
+ <h3 className="text-lg font-medium text-gray-900 mb-2">
530
+ Confirmar Eliminación
531
+ </h3>
532
+ <p className="text-sm text-gray-500">
533
+ ¿Está seguro de que desea eliminar <strong>{itemToDelete}</strong>?
534
+ Esta acción no se puede deshacer.
535
+ </p>
536
+ </div>
537
+
538
+ <div className="flex justify-center space-x-3">
539
+ <button
540
+ onClick={() => setShowModal(false)}
541
+ className="bg-gray-300 text-gray-700 px-4 py-2 rounded-md text-sm font-medium"
542
+ >
543
+ Cancelar
544
+ </button>
545
+ <button
546
+ onClick={confirmDelete}
547
+ className="bg-red-600 text-white px-4 py-2 rounded-md text-sm font-medium"
548
+ >
549
+ Eliminar
550
+ </button>
551
+ </div>
552
+ </div>
553
+ </Modal>
554
+ </div>
555
+ );
556
+ }
557
+ ```
558
+
559
+ #### Ejemplo - Modal de Formulario
560
+ ```tsx
561
+ import { Modal } from '@onpe/ui/components';
562
+ import { useState } from 'react';
563
+
564
+ function UserForm() {
565
+ const [isOpen, setIsOpen] = useState(false);
566
+ const [formData, setFormData] = useState({
567
+ name: '',
568
+ email: '',
569
+ phone: ''
570
+ });
571
+
572
+ const handleSubmit = (e) => {
573
+ e.preventDefault();
574
+ console.log('Datos del formulario:', formData);
575
+ setIsOpen(false);
576
+ setFormData({ name: '', email: '', phone: '' });
577
+ };
578
+
579
+ return (
580
+ <div className="p-4">
581
+ <button
582
+ onClick={() => setIsOpen(true)}
583
+ className="bg-green-500 text-white px-4 py-2 rounded"
584
+ >
585
+ Agregar Usuario
586
+ </button>
587
+
588
+ <Modal
589
+ isOpen={isOpen}
590
+ onClose={() => setIsOpen(false)}
591
+ closeButton={true}
592
+ overlayColor="skyblue"
593
+ >
594
+ <div className="p-6">
595
+ <h2 className="text-xl font-bold mb-4">Nuevo Usuario</h2>
596
+
597
+ <form onSubmit={handleSubmit} className="space-y-4">
598
+ <div>
599
+ <label className="block text-sm font-medium text-gray-700 mb-1">
600
+ Nombre Completo
601
+ </label>
602
+ <input
603
+ type="text"
604
+ value={formData.name}
605
+ onChange={(e) => setFormData({...formData, name: e.target.value})}
606
+ className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
607
+ required
608
+ />
609
+ </div>
610
+
611
+ <div>
612
+ <label className="block text-sm font-medium text-gray-700 mb-1">
613
+ Correo Electrónico
614
+ </label>
615
+ <input
616
+ type="email"
617
+ value={formData.email}
618
+ onChange={(e) => setFormData({...formData, email: e.target.value})}
619
+ className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
620
+ required
621
+ />
622
+ </div>
623
+
624
+ <div>
625
+ <label className="block text-sm font-medium text-gray-700 mb-1">
626
+ Teléfono
627
+ </label>
628
+ <input
629
+ type="tel"
630
+ value={formData.phone}
631
+ onChange={(e) => setFormData({...formData, phone: e.target.value})}
632
+ className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
633
+ />
634
+ </div>
635
+
636
+ <div className="flex justify-end space-x-3 pt-4">
637
+ <button
638
+ type="button"
639
+ onClick={() => setIsOpen(false)}
640
+ className="bg-gray-300 text-gray-700 px-4 py-2 rounded-md"
641
+ >
642
+ Cancelar
643
+ </button>
644
+ <button
645
+ type="submit"
646
+ className="bg-blue-600 text-white px-4 py-2 rounded-md"
647
+ >
648
+ Guardar Usuario
649
+ </button>
650
+ </div>
651
+ </form>
652
+ </div>
653
+ </Modal>
654
+ </div>
125
655
  );
126
656
  }
127
657
  ```
@@ -420,6 +950,762 @@ MIT © ONPE - Oficina Nacional de Procesos Electorales
420
950
  4. Push a la rama (`git push origin feature/nueva-funcionalidad`)
421
951
  5. Abre un Pull Request
422
952
 
953
+ ## 🚀 Ejemplos Completos de Aplicaciones
954
+
955
+ ### Aplicación de Votación Electrónica
956
+ ```tsx
957
+ import { Button, Modal, Show } from '@onpe/ui/components';
958
+ import { useState } from 'react';
959
+
960
+ function VotingApp() {
961
+ const [currentStep, setCurrentStep] = useState(1);
962
+ const [selectedCandidate, setSelectedCandidate] = useState('');
963
+ const [showConfirmModal, setShowConfirmModal] = useState(false);
964
+ const [voted, setVoted] = useState(false);
965
+
966
+ const candidates = [
967
+ { id: '1', name: 'Juan Pérez', party: 'Partido Democrático' },
968
+ { id: '2', name: 'María García', party: 'Partido Progresista' },
969
+ { id: '3', name: 'Carlos López', party: 'Partido Independiente' }
970
+ ];
971
+
972
+ const handleVote = () => {
973
+ setVoted(true);
974
+ setShowConfirmModal(false);
975
+ setCurrentStep(4);
976
+ };
977
+
978
+ return (
979
+ <div className="min-h-screen bg-gray-50 py-8">
980
+ <div className="max-w-2xl mx-auto bg-white rounded-lg shadow-lg p-6">
981
+ <div className="text-center mb-8">
982
+ <h1 className="text-3xl font-bold text-gray-900 mb-2">
983
+ Sistema de Votación ONPE
984
+ </h1>
985
+ <p className="text-gray-600">
986
+ Seleccione su candidato preferido
987
+ </p>
988
+ </div>
989
+
990
+ <Show when={currentStep === 1}>
991
+ <div className="space-y-4">
992
+ <h2 className="text-xl font-semibold mb-4">Candidatos Disponibles:</h2>
993
+ {candidates.map((candidate) => (
994
+ <div
995
+ key={candidate.id}
996
+ className={`p-4 border-2 rounded-lg cursor-pointer transition-colors ${
997
+ selectedCandidate === candidate.id
998
+ ? 'border-blue-500 bg-blue-50'
999
+ : 'border-gray-200 hover:border-gray-300'
1000
+ }`}
1001
+ onClick={() => setSelectedCandidate(candidate.id)}
1002
+ >
1003
+ <div className="flex items-center justify-between">
1004
+ <div>
1005
+ <h3 className="font-semibold text-lg">{candidate.name}</h3>
1006
+ <p className="text-gray-600">{candidate.party}</p>
1007
+ </div>
1008
+ {selectedCandidate === candidate.id && (
1009
+ <div className="w-6 h-6 bg-blue-500 rounded-full flex items-center justify-center">
1010
+ <svg className="w-4 h-4 text-white" fill="currentColor" viewBox="0 0 20 20">
1011
+ <path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
1012
+ </svg>
1013
+ </div>
1014
+ )}
1015
+ </div>
1016
+ </div>
1017
+ ))}
1018
+
1019
+ <div className="pt-4">
1020
+ <Button
1021
+ color="primary"
1022
+ title="Continuar"
1023
+ size="large"
1024
+ disabled={!selectedCandidate}
1025
+ onClick={() => setCurrentStep(2)}
1026
+ />
1027
+ </div>
1028
+ </div>
1029
+ </Show>
1030
+
1031
+ <Show when={currentStep === 2}>
1032
+ <div className="text-center">
1033
+ <h2 className="text-xl font-semibold mb-4">Confirmar Voto</h2>
1034
+ <div className="bg-gray-50 p-4 rounded-lg mb-6">
1035
+ <p className="text-gray-600 mb-2">Ha seleccionado:</p>
1036
+ <p className="font-semibold text-lg">
1037
+ {candidates.find(c => c.id === selectedCandidate)?.name}
1038
+ </p>
1039
+ <p className="text-gray-600">
1040
+ {candidates.find(c => c.id === selectedCandidate)?.party}
1041
+ </p>
1042
+ </div>
1043
+
1044
+ <div className="flex justify-center space-x-4">
1045
+ <Button
1046
+ color="gray"
1047
+ title="Volver"
1048
+ onClick={() => setCurrentStep(1)}
1049
+ />
1050
+ <Button
1051
+ color="primary"
1052
+ title="Confirmar Voto"
1053
+ onClick={() => setShowConfirmModal(true)}
1054
+ />
1055
+ </div>
1056
+ </div>
1057
+ </Show>
1058
+
1059
+ <Show when={currentStep === 4}>
1060
+ <div className="text-center">
1061
+ <div className="mb-6">
1062
+ <div className="mx-auto flex items-center justify-center h-16 w-16 rounded-full bg-green-100 mb-4">
1063
+ <svg className="h-8 w-8 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
1064
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
1065
+ </svg>
1066
+ </div>
1067
+ <h2 className="text-2xl font-bold text-green-600 mb-2">
1068
+ ¡Voto Registrado Exitosamente!
1069
+ </h2>
1070
+ <p className="text-gray-600">
1071
+ Su voto ha sido procesado correctamente. Gracias por participar en la democracia.
1072
+ </p>
1073
+ </div>
1074
+
1075
+ <Button
1076
+ color="green"
1077
+ title="Ver Resultados"
1078
+ size="large"
1079
+ onClick={() => window.location.reload()}
1080
+ />
1081
+ </div>
1082
+ </Show>
1083
+
1084
+ {/* Modal de Confirmación */}
1085
+ <Modal
1086
+ isOpen={showConfirmModal}
1087
+ onClose={() => setShowConfirmModal(false)}
1088
+ closeButton={true}
1089
+ overlayColor="blue"
1090
+ >
1091
+ <div className="p-6 text-center">
1092
+ <div className="mb-4">
1093
+ <div className="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 mb-4">
1094
+ <svg className="h-6 w-6 text-blue-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
1095
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
1096
+ </svg>
1097
+ </div>
1098
+ <h3 className="text-lg font-medium text-gray-900 mb-2">
1099
+ Confirmar Voto
1100
+ </h3>
1101
+ <p className="text-sm text-gray-500">
1102
+ ¿Está seguro de que desea votar por{' '}
1103
+ <strong>{candidates.find(c => c.id === selectedCandidate)?.name}</strong>?
1104
+ Esta acción no se puede deshacer.
1105
+ </p>
1106
+ </div>
1107
+
1108
+ <div className="flex justify-center space-x-3">
1109
+ <Button
1110
+ color="gray"
1111
+ title="Cancelar"
1112
+ onClick={() => setShowConfirmModal(false)}
1113
+ />
1114
+ <Button
1115
+ color="primary"
1116
+ title="Confirmar Voto"
1117
+ onClick={handleVote}
1118
+ />
1119
+ </div>
1120
+ </div>
1121
+ </Modal>
1122
+ </div>
1123
+ </div>
1124
+ );
1125
+ }
1126
+
1127
+ export default VotingApp;
1128
+ ```
1129
+
1130
+ ### Dashboard de Administración
1131
+ ```tsx
1132
+ import { Button, Modal, Show } from '@onpe/ui/components';
1133
+ import { useState } from 'react';
1134
+
1135
+ function AdminDashboard() {
1136
+ const [activeTab, setActiveTab] = useState('users');
1137
+ const [showUserModal, setShowUserModal] = useState(false);
1138
+ const [users, setUsers] = useState([
1139
+ { id: 1, name: 'Juan Pérez', email: 'juan@onpe.gob.pe', role: 'Admin' },
1140
+ { id: 2, name: 'María García', email: 'maria@onpe.gob.pe', role: 'Usuario' },
1141
+ { id: 3, name: 'Carlos López', email: 'carlos@onpe.gob.pe', role: 'Usuario' }
1142
+ ]);
1143
+
1144
+ const [newUser, setNewUser] = useState({
1145
+ name: '',
1146
+ email: '',
1147
+ role: 'Usuario'
1148
+ });
1149
+
1150
+ const addUser = () => {
1151
+ const user = {
1152
+ id: users.length + 1,
1153
+ ...newUser
1154
+ };
1155
+ setUsers([...users, user]);
1156
+ setNewUser({ name: '', email: '', role: 'Usuario' });
1157
+ setShowUserModal(false);
1158
+ };
1159
+
1160
+ return (
1161
+ <div className="min-h-screen bg-gray-100">
1162
+ {/* Header */}
1163
+ <header className="bg-white shadow-sm border-b">
1164
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
1165
+ <div className="flex justify-between items-center py-4">
1166
+ <div>
1167
+ <h1 className="text-2xl font-bold text-gray-900">Dashboard ONPE</h1>
1168
+ <p className="text-gray-600">Panel de administración del sistema electoral</p>
1169
+ </div>
1170
+ <div className="flex items-center space-x-4">
1171
+ <span className="text-sm text-gray-500">Administrador</span>
1172
+ <Button color="primary" title="Cerrar Sesión" size="small" />
1173
+ </div>
1174
+ </div>
1175
+ </div>
1176
+ </header>
1177
+
1178
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
1179
+ {/* Navigation Tabs */}
1180
+ <div className="mb-8">
1181
+ <nav className="flex space-x-8">
1182
+ <button
1183
+ onClick={() => setActiveTab('users')}
1184
+ className={`py-2 px-1 border-b-2 font-medium text-sm ${
1185
+ activeTab === 'users'
1186
+ ? 'border-blue-500 text-blue-600'
1187
+ : 'border-transparent text-gray-500 hover:text-gray-700'
1188
+ }`}
1189
+ >
1190
+ Usuarios
1191
+ </button>
1192
+ <button
1193
+ onClick={() => setActiveTab('elections')}
1194
+ className={`py-2 px-1 border-b-2 font-medium text-sm ${
1195
+ activeTab === 'elections'
1196
+ ? 'border-blue-500 text-blue-600'
1197
+ : 'border-transparent text-gray-500 hover:text-gray-700'
1198
+ }`}
1199
+ >
1200
+ Elecciones
1201
+ </button>
1202
+ <button
1203
+ onClick={() => setActiveTab('reports')}
1204
+ className={`py-2 px-1 border-b-2 font-medium text-sm ${
1205
+ activeTab === 'reports'
1206
+ ? 'border-blue-500 text-blue-600'
1207
+ : 'border-transparent text-gray-500 hover:text-gray-700'
1208
+ }`}
1209
+ >
1210
+ Reportes
1211
+ </button>
1212
+ </nav>
1213
+ </div>
1214
+
1215
+ {/* Users Tab */}
1216
+ <Show when={activeTab === 'users'}>
1217
+ <div className="bg-white rounded-lg shadow">
1218
+ <div className="px-6 py-4 border-b border-gray-200">
1219
+ <div className="flex justify-between items-center">
1220
+ <h2 className="text-lg font-medium text-gray-900">Gestión de Usuarios</h2>
1221
+ <Button
1222
+ color="green"
1223
+ title="Agregar Usuario"
1224
+ onClick={() => setShowUserModal(true)}
1225
+ />
1226
+ </div>
1227
+ </div>
1228
+
1229
+ <div className="overflow-x-auto">
1230
+ <table className="min-w-full divide-y divide-gray-200">
1231
+ <thead className="bg-gray-50">
1232
+ <tr>
1233
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
1234
+ Nombre
1235
+ </th>
1236
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
1237
+ Email
1238
+ </th>
1239
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
1240
+ Rol
1241
+ </th>
1242
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
1243
+ Acciones
1244
+ </th>
1245
+ </tr>
1246
+ </thead>
1247
+ <tbody className="bg-white divide-y divide-gray-200">
1248
+ {users.map((user) => (
1249
+ <tr key={user.id}>
1250
+ <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
1251
+ {user.name}
1252
+ </td>
1253
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
1254
+ {user.email}
1255
+ </td>
1256
+ <td className="px-6 py-4 whitespace-nowrap">
1257
+ <span className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
1258
+ user.role === 'Admin'
1259
+ ? 'bg-red-100 text-red-800'
1260
+ : 'bg-green-100 text-green-800'
1261
+ }`}>
1262
+ {user.role}
1263
+ </span>
1264
+ </td>
1265
+ <td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
1266
+ <div className="flex space-x-2">
1267
+ <Button color="skyblue" title="Editar" size="small" />
1268
+ <Button color="red" title="Eliminar" size="small" />
1269
+ </div>
1270
+ </td>
1271
+ </tr>
1272
+ ))}
1273
+ </tbody>
1274
+ </table>
1275
+ </div>
1276
+ </div>
1277
+ </Show>
1278
+
1279
+ {/* Elections Tab */}
1280
+ <Show when={activeTab === 'elections'}>
1281
+ <div className="bg-white rounded-lg shadow p-6">
1282
+ <h2 className="text-lg font-medium text-gray-900 mb-4">Gestión de Elecciones</h2>
1283
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
1284
+ <div className="bg-blue-50 p-4 rounded-lg">
1285
+ <h3 className="font-semibold text-blue-900">Elecciones Generales</h3>
1286
+ <p className="text-blue-700 text-sm mt-1">14 de abril, 2024</p>
1287
+ <Button color="blue" title="Ver Detalles" size="small" />
1288
+ </div>
1289
+ <div className="bg-green-50 p-4 rounded-lg">
1290
+ <h3 className="font-semibold text-green-900">Elecciones Regionales</h3>
1291
+ <p className="text-green-700 text-sm mt-1">28 de octubre, 2024</p>
1292
+ <Button color="green" title="Ver Detalles" size="small" />
1293
+ </div>
1294
+ <div className="bg-yellow-50 p-4 rounded-lg">
1295
+ <h3 className="font-semibold text-yellow-900">Elecciones Municipales</h3>
1296
+ <p className="text-yellow-700 text-sm mt-1">15 de diciembre, 2024</p>
1297
+ <Button color="yellow" title="Ver Detalles" size="small" />
1298
+ </div>
1299
+ </div>
1300
+ </div>
1301
+ </Show>
1302
+
1303
+ {/* Reports Tab */}
1304
+ <Show when={activeTab === 'reports'}>
1305
+ <div className="bg-white rounded-lg shadow p-6">
1306
+ <h2 className="text-lg font-medium text-gray-900 mb-4">Reportes del Sistema</h2>
1307
+ <div className="space-y-4">
1308
+ <div className="flex justify-between items-center p-4 bg-gray-50 rounded-lg">
1309
+ <div>
1310
+ <h3 className="font-medium">Reporte de Participación Electoral</h3>
1311
+ <p className="text-sm text-gray-600">Generado el 15 de abril, 2024</p>
1312
+ </div>
1313
+ <Button color="primary" title="Descargar PDF" size="small" />
1314
+ </div>
1315
+ <div className="flex justify-between items-center p-4 bg-gray-50 rounded-lg">
1316
+ <div>
1317
+ <h3 className="font-medium">Estadísticas de Usuarios</h3>
1318
+ <p className="text-sm text-gray-600">Generado el 14 de abril, 2024</p>
1319
+ </div>
1320
+ <Button color="primary" title="Descargar PDF" size="small" />
1321
+ </div>
1322
+ </div>
1323
+ </div>
1324
+ </Show>
1325
+
1326
+ {/* Add User Modal */}
1327
+ <Modal
1328
+ isOpen={showUserModal}
1329
+ onClose={() => setShowUserModal(false)}
1330
+ closeButton={true}
1331
+ overlayColor="skyblue"
1332
+ >
1333
+ <div className="p-6">
1334
+ <h2 className="text-xl font-bold mb-4">Agregar Nuevo Usuario</h2>
1335
+
1336
+ <div className="space-y-4">
1337
+ <div>
1338
+ <label className="block text-sm font-medium text-gray-700 mb-1">
1339
+ Nombre Completo
1340
+ </label>
1341
+ <input
1342
+ type="text"
1343
+ value={newUser.name}
1344
+ onChange={(e) => setNewUser({...newUser, name: e.target.value})}
1345
+ className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
1346
+ placeholder="Ingrese el nombre completo"
1347
+ />
1348
+ </div>
1349
+
1350
+ <div>
1351
+ <label className="block text-sm font-medium text-gray-700 mb-1">
1352
+ Correo Electrónico
1353
+ </label>
1354
+ <input
1355
+ type="email"
1356
+ value={newUser.email}
1357
+ onChange={(e) => setNewUser({...newUser, email: e.target.value})}
1358
+ className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
1359
+ placeholder="usuario@onpe.gob.pe"
1360
+ />
1361
+ </div>
1362
+
1363
+ <div>
1364
+ <label className="block text-sm font-medium text-gray-700 mb-1">
1365
+ Rol
1366
+ </label>
1367
+ <select
1368
+ value={newUser.role}
1369
+ onChange={(e) => setNewUser({...newUser, role: e.target.value})}
1370
+ className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
1371
+ >
1372
+ <option value="Usuario">Usuario</option>
1373
+ <option value="Admin">Administrador</option>
1374
+ </select>
1375
+ </div>
1376
+
1377
+ <div className="flex justify-end space-x-3 pt-4">
1378
+ <Button
1379
+ color="gray"
1380
+ title="Cancelar"
1381
+ onClick={() => setShowUserModal(false)}
1382
+ />
1383
+ <Button
1384
+ color="green"
1385
+ title="Agregar Usuario"
1386
+ onClick={addUser}
1387
+ />
1388
+ </div>
1389
+ </div>
1390
+ </div>
1391
+ </Modal>
1392
+ </div>
1393
+ </div>
1394
+ );
1395
+ }
1396
+
1397
+ export default AdminDashboard;
1398
+ ```
1399
+
1400
+ ## 🐛 Solución de Problemas
1401
+
1402
+ ### Problemas con la CLI
1403
+
1404
+ **Error: "Componente no encontrado"**
1405
+ ```bash
1406
+ # Verificar componentes disponibles
1407
+ npx onpe-ui add --help
1408
+
1409
+ # Componentes válidos:
1410
+ # button, modal, overlay, portal, show
1411
+ ```
1412
+
1413
+ **Error: "No se pudo descargar el componente"**
1414
+ ```bash
1415
+ # Verificar conexión a internet
1416
+ ping github.com
1417
+
1418
+ # Verificar que el repositorio esté disponible
1419
+ curl https://raw.githubusercontent.com/ricardosv46/onpe-ui/main/src/components/Button/Button.tsx
1420
+ ```
1421
+
1422
+ **Los estilos no se aplican**
1423
+ ```bash
1424
+ # Verificar que Tailwind esté instalado
1425
+ npm list tailwindcss
1426
+
1427
+ # Verificar configuración
1428
+ cat tailwind.config.js
1429
+ ```
1430
+
1431
+ **Portal no funciona**
1432
+ ```bash
1433
+ # Verificar que tengas el elemento portal en HTML
1434
+ grep -r "id=\"portal\"" public/
1435
+ ```
1436
+
1437
+ ### Problemas con la Librería Completa
1438
+
1439
+ **Error: "Module not found"**
1440
+ ```bash
1441
+ # Verificar instalación
1442
+ npm list @onpe/ui
1443
+
1444
+ # Reinstalar si es necesario
1445
+ npm uninstall @onpe/ui
1446
+ npm install @onpe/ui
1447
+ ```
1448
+
1449
+ **Estilos no se cargan**
1450
+ ```tsx
1451
+ /* Verificar que tengas la importación correcta */
1452
+ import './onpe-ui.css';
1453
+ import { Button } from '@onpe/ui/components';
1454
+ ```
1455
+
1456
+ **Solución: Verificar rutas de importación**
1457
+ ```tsx
1458
+ // ✅ CORRECTO: Importar tu archivo CSS personalizado
1459
+ import './onpe-ui.css';
1460
+ import { Button } from '@onpe/ui/components';
1461
+
1462
+ // ❌ INCORRECTO: No importar el archivo CSS
1463
+ import { Button } from '@onpe/ui/components';
1464
+ ```
1465
+
1466
+ **Solución: Verificar configuración de bundler**
1467
+ ```javascript
1468
+ // webpack.config.js
1469
+ module.exports = {
1470
+ resolve: {
1471
+ alias: {
1472
+ '@onpe/ui': path.resolve(__dirname, 'node_modules/@onpe/ui')
1473
+ }
1474
+ }
1475
+ };
1476
+
1477
+ // vite.config.js
1478
+ export default {
1479
+ resolve: {
1480
+ alias: {
1481
+ '@onpe/ui': path.resolve(__dirname, 'node_modules/@onpe/ui')
1482
+ }
1483
+ }
1484
+ };
1485
+ ```
1486
+
1487
+ **Solución: Verificar orden de importación**
1488
+ ```tsx
1489
+ // ✅ CORRECTO - Importar estilos ANTES de los componentes
1490
+ import '@onpe/ui/styles';
1491
+ import { Button } from '@onpe/ui/components';
1492
+
1493
+ // ❌ INCORRECTO - Importar estilos DESPUÉS de los componentes
1494
+ import { Button } from '@onpe/ui/components';
1495
+ import '@onpe/ui/styles';
1496
+ ```
1497
+
1498
+ ### Problemas con Estilos
1499
+
1500
+ **Los componentes se ven sin estilos**
1501
+ ```bash
1502
+ # Verificar que la librería esté instalada
1503
+ npm list @onpe/ui
1504
+
1505
+ # Verificar que los estilos estén en node_modules
1506
+ ls node_modules/@onpe/ui/dist/
1507
+ ```
1508
+
1509
+ **Solución: Verificar importación de estilos**
1510
+ ```tsx
1511
+ // En tu archivo principal (index.tsx o App.tsx)
1512
+ import React from 'react';
1513
+ import ReactDOM from 'react-dom/client';
1514
+ import '@onpe/ui/styles'; // ← IMPORTANTE: Importar estilos primero
1515
+ import App from './App';
1516
+
1517
+ const root = ReactDOM.createRoot(document.getElementById('root'));
1518
+ root.render(<App />);
1519
+ ```
1520
+
1521
+ **Solución: Verificar configuración de CSS**
1522
+ ```css
1523
+ /* En tu archivo CSS principal */
1524
+ @import "@onpe/ui/styles";
1525
+
1526
+ /* O si usas CSS modules */
1527
+ @import "~@onpe/ui/styles";
1528
+ ```
1529
+
1530
+ **Los colores personalizados no funcionan**
1531
+ ```tsx
1532
+ // Verificar que estés usando las clases correctas
1533
+ <div className="bg-onpe-ui-blue text-white p-4">
1534
+ Contenido con colores ONPE
1535
+ </div>
1536
+
1537
+ // Verificar que los estilos estén importados
1538
+ import '@onpe/ui/styles';
1539
+ ```
1540
+
1541
+ **Solución: Verificar configuración de Tailwind (para componentes individuales)**
1542
+ ```javascript
1543
+ // tailwind.config.js
1544
+ module.exports = {
1545
+ content: ["./src/**/*.{js,jsx,ts,tsx}"],
1546
+ theme: {
1547
+ extend: {
1548
+ colors: {
1549
+ 'onpe-ui-blue': '#003770',
1550
+ 'onpe-ui-skyblue': '#0073cf',
1551
+ // ... resto de colores
1552
+ }
1553
+ },
1554
+ },
1555
+ }
1556
+ ```
1557
+
1558
+ ### Problemas con Storybook
1559
+
1560
+ **Error: "Failed to fetch dynamically imported module"**
1561
+ - Verificar que el archivo `preview.ts` importe correctamente `../src/styles.css`
1562
+ - Asegurar que Tailwind CSS esté configurado correctamente
1563
+ - Verificar que PostCSS esté configurado
1564
+
1565
+ **Solución: Configuración completa de Storybook**
1566
+ ```typescript
1567
+ // .storybook/main.ts
1568
+ import type { StorybookConfig } from "@storybook/react-vite";
1569
+
1570
+ const config: StorybookConfig = {
1571
+ stories: ["../src/**/*.stories.@(js|jsx|ts|tsx)"],
1572
+ addons: ["@storybook/addon-links", "@storybook/addon-essentials", "@storybook/addon-interactions"],
1573
+ framework: {
1574
+ name: "@storybook/react-vite",
1575
+ options: {},
1576
+ },
1577
+ viteFinal: async (config) => {
1578
+ config.css = {
1579
+ ...config.css,
1580
+ postcss: {
1581
+ plugins: [require("tailwindcss"), require("autoprefixer")],
1582
+ },
1583
+ };
1584
+ return config;
1585
+ },
1586
+ };
1587
+
1588
+ export default config;
1589
+ ```
1590
+
1591
+ ```typescript
1592
+ // .storybook/preview.ts
1593
+ import type { Preview } from "@storybook/react";
1594
+ import "../src/styles.css"; // ← Importar estilos
1595
+
1596
+ const preview: Preview = {
1597
+ parameters: {
1598
+ actions: { argTypesRegex: "^on[A-Z].*" },
1599
+ controls: {
1600
+ matchers: {
1601
+ color: /(background|color)$/i,
1602
+ date: /Date$/,
1603
+ },
1604
+ },
1605
+ },
1606
+ };
1607
+
1608
+ export default preview;
1609
+ ```
1610
+
1611
+ ## 🚀 Guía Rápida de Solución
1612
+
1613
+ ### ¿Los componentes no se ven con estilos?
1614
+
1615
+ **Paso 1: Verificar instalación**
1616
+ ```bash
1617
+ npm list @onpe/ui
1618
+ ```
1619
+
1620
+ **Paso 2: Crear archivo CSS personalizado**
1621
+ ```css
1622
+ /* Crear archivo onpe-ui.css en la raíz del proyecto */
1623
+ :root {
1624
+ --blue: #003770;
1625
+ --skyblue: #0073cf;
1626
+ /* ... resto de variables */
1627
+ }
1628
+
1629
+ @theme {
1630
+ --color-onpe-ui-blue: var(--blue);
1631
+ /* ... resto de colores */
1632
+ }
1633
+ ```
1634
+
1635
+ **Paso 3: Importar estilos**
1636
+ ```tsx
1637
+ // En tu archivo principal (index.tsx)
1638
+ import './onpe-ui.css';
1639
+ import { Button } from '@onpe/ui/components';
1640
+ ```
1641
+
1642
+ **Paso 4: Verificar orden de importación**
1643
+ ```tsx
1644
+ // ✅ CORRECTO
1645
+ import './onpe-ui.css';
1646
+ import { Button } from '@onpe/ui/components';
1647
+
1648
+ // ❌ INCORRECTO
1649
+ import { Button } from '@onpe/ui/components';
1650
+ // Falta importar el archivo CSS
1651
+ ```
1652
+
1653
+ ### ¿Los colores no funcionan?
1654
+
1655
+ **Solución: Usar clases correctas**
1656
+ ```tsx
1657
+ // ✅ CORRECTO
1658
+ <div className="bg-onpe-ui-blue text-white p-4">
1659
+ Contenido
1660
+ </div>
1661
+
1662
+ // ❌ INCORRECTO
1663
+ <div className="bg-blue-500 text-white p-4">
1664
+ Contenido
1665
+ </div>
1666
+ ```
1667
+
1668
+ ### ¿Storybook no funciona?
1669
+
1670
+ **Solución: Configuración completa**
1671
+ ```typescript
1672
+ // .storybook/preview.ts
1673
+ import "../src/styles.css";
1674
+
1675
+ // .storybook/main.ts
1676
+ viteFinal: async (config) => {
1677
+ config.css = {
1678
+ ...config.css,
1679
+ postcss: {
1680
+ plugins: [require("tailwindcss"), require("autoprefixer")],
1681
+ },
1682
+ };
1683
+ return config;
1684
+ },
1685
+ ```
1686
+
1687
+ ### ¿CLI no instala componentes?
1688
+
1689
+ **Solución: Verificar comandos**
1690
+ ```bash
1691
+ # ✅ CORRECTO
1692
+ npx onpe-ui add button
1693
+ npx onpe-ui add modal
1694
+
1695
+ # ❌ INCORRECTO
1696
+ npx onpe-ui add Button
1697
+ npx onpe-ui add Modal
1698
+ ```
1699
+
1700
+ ### ¿Portal no funciona?
1701
+
1702
+ **Solución: Agregar elemento HTML**
1703
+ ```html
1704
+ <!-- En public/index.html -->
1705
+ <div id="root"></div>
1706
+ <div id="portal"></div>
1707
+ ```
1708
+
423
1709
  ## 📞 Soporte
424
1710
 
425
1711
  - 📧 Email: desarrollo@onpe.gob.pe
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onpe/ui",
3
- "version": "1.0.25",
3
+ "version": "1.0.27",
4
4
  "type": "module",
5
5
  "description": "Librería completa de UI para ONPE - Componentes, Hooks, Utils y Librerías",
6
6
  "main": "dist/index.js",