innoboxrr-form-core 1.0.0 → 2.0.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "innoboxrr-form-core",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "El nucleo agnostico de los formularios innoboxrr: tema, archivos y zonas horarias.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -9,13 +9,21 @@
9
9
  "./theme": "./src/theme.js",
10
10
  "./files": "./src/files.js",
11
11
  "./timezone": "./src/timezone.js",
12
+ "./styles": "./styles.css",
13
+ "./styles.css": "./styles.css",
14
+ "./tokens.css": "./src/tokens.css",
15
+ "./components.css": "./src/components.css",
16
+ "./layout.css": "./src/layout.css",
12
17
  "./src/*": "./src/*"
13
18
  },
14
19
  "files": [
15
20
  "index.js",
21
+ "styles.css",
16
22
  "src"
17
23
  ],
18
- "sideEffects": false,
24
+ "sideEffects": [
25
+ "*.css"
26
+ ],
19
27
  "scripts": {
20
28
  "test": "vitest run",
21
29
  "test:watch": "vitest"
@@ -0,0 +1,543 @@
1
+ /**
2
+ * Las clases del sistema de diseño innoboxrr, construidas sobre las
3
+ * primitivas de tokens.css.
4
+ *
5
+ * Sustituyen a lo que antes venía de fuera sin declararse: 305 clases de
6
+ * UIkit y 551 de Tailwind repartidas por los paquetes, ninguna de las dos
7
+ * declarada como dependencia. Un módulo generado solo se montaba dentro de
8
+ * una aplicación que ya trajera UIkit cargado, y nada lo decía.
9
+ *
10
+ * Todo lo de aquí depende únicamente de las variables de tokens.css, así que
11
+ * el paquete ya no exige ningún framework de CSS y el aspecto de todo el
12
+ * ecosistema se cambia en un sitio.
13
+ */
14
+
15
+ /* ============================================================
16
+ CAMPOS
17
+ ============================================================ */
18
+
19
+ .fe-field {
20
+ display: flex;
21
+ flex-direction: column;
22
+ gap: var(--fe-space-1);
23
+ margin-bottom: var(--fe-space-4);
24
+ }
25
+
26
+ .fe-field-inner {
27
+ position: relative;
28
+ width: 100%;
29
+ }
30
+
31
+ .fe-label {
32
+ display: inline-flex;
33
+ align-items: center;
34
+ gap: var(--fe-space-1);
35
+ font-size: var(--fe-text-sm);
36
+ font-weight: 500;
37
+ color: var(--fe-text);
38
+ }
39
+
40
+ .fe-help {
41
+ cursor: help;
42
+ color: var(--fe-text-subtle);
43
+ }
44
+
45
+ /* Dibujado, no una fuente de iconos. El token `helpIcon` valía
46
+ `fa-solid fa-circle-question`, así que el paquete exigía Font Awesome sin
47
+ declararlo: si la aplicación no lo cargaba, el icono no aparecía y nadie
48
+ se enteraba. */
49
+ .fe-help-icon {
50
+ display: inline-flex;
51
+ align-items: center;
52
+ justify-content: center;
53
+ width: 1rem;
54
+ height: 1rem;
55
+ font-size: 0.6875rem;
56
+ font-weight: 600;
57
+ line-height: 1;
58
+ color: var(--fe-text-inverse);
59
+ background: var(--fe-text-subtle);
60
+ border-radius: var(--fe-radius-full);
61
+ cursor: help;
62
+ user-select: none;
63
+ }
64
+
65
+ .fe-help-icon::before {
66
+ content: '?';
67
+ }
68
+
69
+ .fe-error {
70
+ font-size: var(--fe-text-xs);
71
+ font-weight: 500;
72
+ color: var(--fe-danger);
73
+ }
74
+
75
+ /* ============================================================
76
+ CONTROLES
77
+ ============================================================ */
78
+
79
+ .fe-input,
80
+ .fe-select,
81
+ .fe-textarea,
82
+ .fe-file {
83
+ width: 100%;
84
+ min-height: var(--fe-control-height);
85
+ padding: 0 var(--fe-control-padding-x);
86
+ font-family: var(--fe-font);
87
+ font-size: var(--fe-text-base);
88
+ color: var(--fe-text);
89
+ background: var(--fe-surface);
90
+ border: 1px solid var(--fe-border);
91
+ border-radius: var(--fe-radius);
92
+ transition: border-color var(--fe-transition), box-shadow var(--fe-transition);
93
+ appearance: none;
94
+ }
95
+
96
+ .fe-textarea {
97
+ padding: var(--fe-space-2) var(--fe-control-padding-x);
98
+ min-height: calc(var(--fe-control-height) * 3);
99
+ line-height: 1.5;
100
+ resize: vertical;
101
+ }
102
+
103
+ .fe-select {
104
+ /* La flecha va en el CSS y no en un icono suelto: así el control no
105
+ depende de que la aplicación haya cargado una fuente de iconos. */
106
+ padding-right: calc(var(--fe-control-padding-x) * 2.5);
107
+ background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
108
+ linear-gradient(135deg, currentColor 50%, transparent 50%);
109
+ background-position: right var(--fe-control-padding-x) center,
110
+ calc(100% - var(--fe-control-padding-x) + 5px) center;
111
+ background-size: 5px 5px, 5px 5px;
112
+ background-repeat: no-repeat;
113
+ }
114
+
115
+ .fe-input::placeholder,
116
+ .fe-textarea::placeholder {
117
+ color: var(--fe-text-subtle);
118
+ }
119
+
120
+ .fe-input:hover,
121
+ .fe-select:hover,
122
+ .fe-textarea:hover {
123
+ border-color: var(--fe-border-strong);
124
+ }
125
+
126
+ .fe-input:focus,
127
+ .fe-select:focus,
128
+ .fe-textarea:focus,
129
+ .fe-file:focus {
130
+ outline: none;
131
+ border-color: var(--fe-focus);
132
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--fe-focus) 22%, transparent);
133
+ }
134
+
135
+ .fe-input:disabled,
136
+ .fe-select:disabled,
137
+ .fe-textarea:disabled {
138
+ background: var(--fe-surface-sunk);
139
+ color: var(--fe-text-subtle);
140
+ cursor: not-allowed;
141
+ }
142
+
143
+ /* El componente de validación marca el campo inválido con esta clase. */
144
+ .fe-input-error,
145
+ .fe-input[aria-invalid='true'],
146
+ .fe-select[aria-invalid='true'],
147
+ .fe-textarea[aria-invalid='true'] {
148
+ border-color: var(--fe-danger);
149
+ }
150
+
151
+ .fe-checkbox,
152
+ .fe-radio {
153
+ width: 1rem;
154
+ height: 1rem;
155
+ accent-color: var(--fe-primary);
156
+ border: 1px solid var(--fe-border-strong);
157
+ cursor: pointer;
158
+ }
159
+
160
+ .fe-radio {
161
+ border-radius: var(--fe-radius-full);
162
+ }
163
+
164
+ .fe-checkbox {
165
+ border-radius: var(--fe-radius-sm);
166
+ }
167
+
168
+ .fe-checkbox:focus-visible,
169
+ .fe-radio:focus-visible {
170
+ outline: 2px solid var(--fe-focus);
171
+ outline-offset: 2px;
172
+ }
173
+
174
+ /* ============================================================
175
+ BOTONES
176
+ ============================================================ */
177
+
178
+ .fe-button,
179
+ .fe-button-secondary,
180
+ .fe-button-danger,
181
+ .fe-button-link {
182
+ display: inline-flex;
183
+ align-items: center;
184
+ justify-content: center;
185
+ gap: var(--fe-space-2);
186
+ min-height: var(--fe-control-height);
187
+ padding: 0 calc(var(--fe-control-padding-x) * 1.25);
188
+ font-family: var(--fe-font);
189
+ font-size: var(--fe-text-base);
190
+ font-weight: 500;
191
+ line-height: 1;
192
+ border: 1px solid transparent;
193
+ border-radius: var(--fe-radius);
194
+ cursor: pointer;
195
+ transition: background var(--fe-transition), border-color var(--fe-transition), color var(--fe-transition);
196
+ }
197
+
198
+ .fe-button {
199
+ color: var(--fe-primary-text);
200
+ background: var(--fe-primary);
201
+ }
202
+
203
+ .fe-button:hover:not(:disabled) {
204
+ background: var(--fe-primary-hover);
205
+ }
206
+
207
+ .fe-button-secondary {
208
+ color: var(--fe-text);
209
+ background: var(--fe-surface);
210
+ border-color: var(--fe-border);
211
+ }
212
+
213
+ .fe-button-secondary:hover:not(:disabled) {
214
+ background: var(--fe-surface-hover);
215
+ border-color: var(--fe-border-strong);
216
+ }
217
+
218
+ .fe-button-danger {
219
+ color: var(--fe-danger-text);
220
+ background: var(--fe-danger);
221
+ }
222
+
223
+ .fe-button-danger:hover:not(:disabled) {
224
+ background: var(--fe-danger-hover);
225
+ }
226
+
227
+ .fe-button-link {
228
+ min-height: 0;
229
+ padding: 0;
230
+ color: var(--fe-primary);
231
+ background: none;
232
+ }
233
+
234
+ .fe-button-sm {
235
+ min-height: calc(1.75rem * var(--fe-density));
236
+ padding: 0 var(--fe-space-2);
237
+ font-size: var(--fe-text-sm);
238
+ }
239
+
240
+ /* El icono que va dentro del control, a la izquierda. Sustituye a
241
+ `uk-form-icon`, que además de la clase traía el componente de iconos de
242
+ UIkit. */
243
+ .fe-field-icon {
244
+ position: absolute;
245
+ top: 50%;
246
+ left: var(--fe-space-3);
247
+ transform: translateY(-50%);
248
+ display: inline-flex;
249
+ align-items: center;
250
+ color: var(--fe-text-subtle);
251
+ pointer-events: none;
252
+ }
253
+
254
+ .fe-field-icon ~ .fe-input,
255
+ .fe-field-icon ~ * .fe-input {
256
+ padding-left: calc(var(--fe-control-padding-x) * 2.5);
257
+ }
258
+
259
+ .fe-button-link:hover:not(:disabled) {
260
+ text-decoration: underline;
261
+ }
262
+
263
+ .fe-button:focus-visible,
264
+ .fe-button-secondary:focus-visible,
265
+ .fe-button-danger:focus-visible,
266
+ .fe-button-link:focus-visible {
267
+ outline: 2px solid var(--fe-focus);
268
+ outline-offset: 2px;
269
+ }
270
+
271
+ .fe-button:disabled,
272
+ .fe-button-secondary:disabled,
273
+ .fe-button-danger:disabled,
274
+ .fe-button-link:disabled {
275
+ opacity: 0.55;
276
+ cursor: not-allowed;
277
+ }
278
+
279
+ /* ============================================================
280
+ NAVEGACION Y ACCIONES
281
+ ============================================================ */
282
+
283
+ .fe-breadcrumb {
284
+ display: flex;
285
+ align-items: center;
286
+ flex-wrap: wrap;
287
+ gap: var(--fe-space-2);
288
+ margin-bottom: var(--fe-space-4);
289
+ font-size: var(--fe-text-sm);
290
+ }
291
+
292
+ .fe-breadcrumb-link {
293
+ color: var(--fe-text-muted);
294
+ text-decoration: none;
295
+ }
296
+
297
+ .fe-breadcrumb-link:hover {
298
+ color: var(--fe-text);
299
+ text-decoration: underline;
300
+ }
301
+
302
+ .fe-breadcrumb-current {
303
+ font-weight: 500;
304
+ color: var(--fe-text);
305
+ }
306
+
307
+ .fe-breadcrumb-separator {
308
+ color: var(--fe-text-subtle);
309
+ user-select: none;
310
+ }
311
+
312
+ .fe-action-menu {
313
+ display: inline-flex;
314
+ align-items: center;
315
+ gap: var(--fe-space-3);
316
+ }
317
+
318
+ .fe-action-item {
319
+ font-size: var(--fe-text-sm);
320
+ color: var(--fe-primary);
321
+ background: none;
322
+ border: none;
323
+ padding: 0;
324
+ cursor: pointer;
325
+ }
326
+
327
+ .fe-action-item:hover {
328
+ text-decoration: underline;
329
+ }
330
+
331
+ .fe-action-danger {
332
+ font-size: var(--fe-text-sm);
333
+ color: var(--fe-danger);
334
+ background: none;
335
+ border: none;
336
+ padding: 0;
337
+ cursor: pointer;
338
+ }
339
+
340
+ .fe-action-danger:hover {
341
+ text-decoration: underline;
342
+ }
343
+
344
+ /* ============================================================
345
+ SUPERFICIES
346
+ ============================================================ */
347
+
348
+ .fe-surface {
349
+ background: var(--fe-surface);
350
+ border: 1px solid var(--fe-border);
351
+ border-radius: var(--fe-radius-lg);
352
+ }
353
+
354
+ .fe-surface-raised {
355
+ background: var(--fe-surface-raised);
356
+ border: 1px solid var(--fe-border);
357
+ border-radius: var(--fe-radius-lg);
358
+ box-shadow: var(--fe-shadow);
359
+ }
360
+
361
+ /* ============================================================
362
+ PIEZAS DE APLICACION
363
+ ============================================================ */
364
+
365
+ .fe-toolbar {
366
+ display: flex;
367
+ align-items: center;
368
+ gap: var(--fe-space-3);
369
+ padding: var(--fe-space-3) var(--fe-space-4);
370
+ border-bottom: 1px solid var(--fe-border);
371
+ }
372
+
373
+ .fe-badge {
374
+ display: inline-flex;
375
+ align-items: center;
376
+ padding: 0 var(--fe-space-2);
377
+ height: 1.375rem;
378
+ font-size: var(--fe-text-xs);
379
+ font-weight: 500;
380
+ border-radius: var(--fe-radius-full);
381
+ background: var(--fe-surface-sunk);
382
+ color: var(--fe-text-muted);
383
+ }
384
+
385
+ /* Un esqueleto en vez de un spinner: mantiene la forma de lo que va a
386
+ llegar, así que la página no salta al cargar. */
387
+ .fe-skeleton {
388
+ background: linear-gradient(
389
+ 90deg,
390
+ var(--fe-surface-sunk) 25%,
391
+ var(--fe-surface-hover) 37%,
392
+ var(--fe-surface-sunk) 63%
393
+ );
394
+ background-size: 400% 100%;
395
+ border-radius: var(--fe-radius-sm);
396
+ animation: fe-skeleton 1.4s ease infinite;
397
+ }
398
+
399
+ @keyframes fe-skeleton {
400
+ 0% {
401
+ background-position: 100% 50%;
402
+ }
403
+ 100% {
404
+ background-position: 0 50%;
405
+ }
406
+ }
407
+
408
+ .fe-overlay {
409
+ position: fixed;
410
+ inset: 0;
411
+ background: rgba(16, 18, 27, 0.45);
412
+ }
413
+
414
+ .fe-dialog {
415
+ background: var(--fe-surface-raised);
416
+ border: 1px solid var(--fe-border);
417
+ border-radius: var(--fe-radius-lg);
418
+ box-shadow: var(--fe-shadow-lg);
419
+ max-height: 85vh;
420
+ overflow: auto;
421
+ }
422
+
423
+ .fe-drawer {
424
+ position: fixed;
425
+ top: 0;
426
+ right: 0;
427
+ bottom: 0;
428
+ width: min(32rem, 100vw);
429
+ background: var(--fe-surface);
430
+ border-left: 1px solid var(--fe-border);
431
+ box-shadow: var(--fe-shadow-lg);
432
+ display: flex;
433
+ flex-direction: column;
434
+ overflow: auto;
435
+ }
436
+
437
+ .fe-menu {
438
+ min-width: 11rem;
439
+ padding: var(--fe-space-1);
440
+ background: var(--fe-surface-raised);
441
+ border: 1px solid var(--fe-border);
442
+ border-radius: var(--fe-radius);
443
+ box-shadow: var(--fe-shadow);
444
+ }
445
+
446
+ .fe-menu-item {
447
+ display: flex;
448
+ align-items: center;
449
+ gap: var(--fe-space-2);
450
+ width: 100%;
451
+ padding: var(--fe-space-2) var(--fe-space-3);
452
+ font-size: var(--fe-text-sm);
453
+ color: var(--fe-text);
454
+ text-align: left;
455
+ background: none;
456
+ border: none;
457
+ border-radius: var(--fe-radius-sm);
458
+ cursor: pointer;
459
+ }
460
+
461
+ .fe-menu-item:hover,
462
+ .fe-menu-item[data-highlighted] {
463
+ background: var(--fe-surface-hover);
464
+ }
465
+
466
+ .fe-toast {
467
+ display: flex;
468
+ align-items: flex-start;
469
+ gap: var(--fe-space-3);
470
+ padding: var(--fe-space-3) var(--fe-space-4);
471
+ font-size: var(--fe-text-sm);
472
+ background: var(--fe-surface-raised);
473
+ border: 1px solid var(--fe-border);
474
+ border-left: 3px solid var(--fe-primary);
475
+ border-radius: var(--fe-radius);
476
+ box-shadow: var(--fe-shadow);
477
+ }
478
+
479
+ .fe-toast-danger {
480
+ border-left-color: var(--fe-danger);
481
+ }
482
+
483
+ .fe-toast-success {
484
+ border-left-color: var(--fe-success);
485
+ }
486
+
487
+ /* ============================================================
488
+ TABLA
489
+ ============================================================ */
490
+
491
+ .fe-table {
492
+ width: 100%;
493
+ border-collapse: collapse;
494
+ font-size: var(--fe-text-base);
495
+ }
496
+
497
+ .fe-table th {
498
+ padding: var(--fe-space-2) var(--fe-space-3);
499
+ font-size: var(--fe-text-xs);
500
+ font-weight: 500;
501
+ text-transform: uppercase;
502
+ letter-spacing: 0.05em;
503
+ color: var(--fe-text-subtle);
504
+ text-align: left;
505
+ white-space: nowrap;
506
+ border-bottom: 1px solid var(--fe-border);
507
+ }
508
+
509
+ .fe-table td {
510
+ padding: var(--fe-space-3);
511
+ color: var(--fe-text);
512
+ border-bottom: 1px solid var(--fe-border);
513
+ }
514
+
515
+ .fe-table tbody tr:hover {
516
+ background: var(--fe-surface-hover);
517
+ }
518
+
519
+ .fe-table tbody tr[data-selected='true'] {
520
+ background: var(--fe-primary-soft);
521
+ }
522
+
523
+ /* Las columnas de números se leen mucho peor si los dígitos no se alinean. */
524
+ .fe-table .fe-numeric {
525
+ font-variant-numeric: tabular-nums;
526
+ text-align: right;
527
+ }
528
+
529
+ @media (prefers-reduced-motion: reduce) {
530
+ .fe-skeleton {
531
+ animation: none;
532
+ }
533
+
534
+ .fe-input,
535
+ .fe-select,
536
+ .fe-textarea,
537
+ .fe-button,
538
+ .fe-button-secondary,
539
+ .fe-button-danger,
540
+ .fe-button-link {
541
+ transition: none;
542
+ }
543
+ }
package/src/layout.css ADDED
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Las utilidades de maquetación que los componentes tomaban prestadas de
3
+ * UIkit.
4
+ *
5
+ * Eran 280 usos de 68 clases distintas —`uk-margin`, `uk-grid`, `uk-flex`,
6
+ * `uk-width-expand`…— repartidos por form-elements, react-form-elements y los
7
+ * dos datatables, y **UIkit no estaba declarado como dependencia en ninguno**.
8
+ * Se consumía como CSS global del anfitrión: el paquete solo se veía bien
9
+ * dentro de una aplicación que ya lo trajera.
10
+ *
11
+ * Esto es deliberadamente pequeño. No es un framework de utilidades: es
12
+ * exactamente lo que los componentes usaban, y nada más. Si hace falta algo
13
+ * nuevo, se añade aquí y así queda a la vista lo que el ecosistema necesita
14
+ * de verdad.
15
+ */
16
+
17
+ /* ---------- ESPACIO ---------- */
18
+
19
+ .fe-mb { margin-bottom: var(--fe-space-4); }
20
+ .fe-mb-sm { margin-bottom: var(--fe-space-2); }
21
+ .fe-mt { margin-top: var(--fe-space-4); }
22
+ .fe-mt-sm { margin-top: var(--fe-space-2); }
23
+ .fe-mt-md { margin-top: var(--fe-space-5); }
24
+ .fe-mr-sm { margin-right: var(--fe-space-2); }
25
+
26
+ .fe-p-sm { padding: var(--fe-space-3); }
27
+ .fe-p-0 { padding: 0; }
28
+ .fe-pt-0 { padding-top: 0; }
29
+
30
+ /* ---------- CAJA ---------- */
31
+
32
+ .fe-inline {
33
+ position: relative;
34
+ display: inline-block;
35
+ }
36
+
37
+ .fe-inline-clip {
38
+ position: relative;
39
+ display: inline-block;
40
+ overflow: hidden;
41
+ }
42
+
43
+ .fe-relative { position: relative; }
44
+
45
+ .fe-center-abs {
46
+ position: absolute;
47
+ top: 50%;
48
+ left: 50%;
49
+ transform: translate(-50%, -50%);
50
+ }
51
+
52
+ /* ---------- ANCHO ---------- */
53
+
54
+ .fe-w-full { width: 100%; }
55
+ .fe-w-auto { width: auto; }
56
+ .fe-w-expand { flex: 1 1 0; min-width: 0; }
57
+
58
+ @media (min-width: 960px) {
59
+ .fe-w-quarter { width: 25%; }
60
+ }
61
+
62
+ /* ---------- FLEX Y REJILLA ---------- */
63
+
64
+ .fe-flex { display: flex; }
65
+ .fe-items-center { align-items: center; }
66
+ .fe-justify-start { justify-content: flex-start; }
67
+ .fe-justify-center { justify-content: center; }
68
+ .fe-justify-end { justify-content: flex-end; }
69
+
70
+ .fe-grid {
71
+ display: flex;
72
+ flex-wrap: wrap;
73
+ gap: var(--fe-space-4);
74
+ }
75
+
76
+ .fe-grid-sm {
77
+ display: flex;
78
+ flex-wrap: wrap;
79
+ gap: var(--fe-space-2);
80
+ }
81
+
82
+ .fe-grid-divider > * + * {
83
+ border-left: 1px solid var(--fe-border);
84
+ padding-left: var(--fe-space-4);
85
+ }
86
+
87
+ .fe-children-expand > * { flex: 1 1 0; min-width: 0; }
88
+
89
+ /* ---------- TEXTO ---------- */
90
+
91
+ .fe-text-center { text-align: center; }
92
+ .fe-text-right { text-align: right; }
93
+ .fe-text-sm { font-size: var(--fe-text-sm); }
94
+ .fe-text-muted { font-size: var(--fe-text-sm); color: var(--fe-text-muted); }
95
+ .fe-text-success { color: var(--fe-success); }
96
+
97
+ /* ---------- CONTENEDOR ---------- */
98
+
99
+ .fe-container {
100
+ width: 100%;
101
+ max-width: 72rem;
102
+ margin: 0 auto;
103
+ padding: 0 var(--fe-space-4);
104
+ }
105
+
106
+ .fe-container-wide {
107
+ width: 100%;
108
+ margin: 0 auto;
109
+ padding: 0 var(--fe-space-4);
110
+ }
111
+
112
+ /* ---------- TARJETA ---------- */
113
+
114
+ .fe-card {
115
+ background: var(--fe-surface);
116
+ border: 1px solid var(--fe-border);
117
+ border-radius: var(--fe-radius-lg);
118
+ }
119
+
120
+ .fe-card-body { padding: var(--fe-space-5); }
121
+ .fe-card-sm .fe-card-body { padding: var(--fe-space-3); }
122
+
123
+ /* ---------- MAQUETACION DE LAS VISTAS GENERADAS ---------- */
124
+
125
+ /*
126
+ * Estas tres no son utilidades sueltas: son la forma que tienen las vistas que
127
+ * genera larapack. Antes se componian con la rejilla fraccionaria de UIkit
128
+ * —`uk-width-1-3@m uk-width-1-1@s`— repetida en cada stub. Nombrar la
129
+ * intencion en vez de las fracciones deja que la maquetacion cambie en un
130
+ * sitio.
131
+ */
132
+
133
+ .fe-section {
134
+ padding: var(--fe-space-5) 0;
135
+ }
136
+
137
+ .fe-section-sm {
138
+ padding: var(--fe-space-3) 0;
139
+ }
140
+
141
+ /* La vista de detalle: ficha a un lado, contenido al otro. */
142
+ .fe-split {
143
+ display: grid;
144
+ gap: var(--fe-space-4);
145
+ grid-template-columns: 1fr;
146
+ }
147
+
148
+ @media (min-width: 960px) {
149
+ .fe-split {
150
+ grid-template-columns: minmax(16rem, 1fr) 2fr;
151
+ align-items: start;
152
+ }
153
+ }
154
+
155
+ /* Los filtros: tantas columnas como quepan, sin decidir un numero fijo. */
156
+ .fe-filter-grid {
157
+ display: grid;
158
+ gap: var(--fe-space-3);
159
+ grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
160
+ }
161
+
162
+ .fe-filter-actions {
163
+ display: flex;
164
+ flex-wrap: wrap;
165
+ justify-content: flex-end;
166
+ gap: var(--fe-space-2);
167
+ margin-top: var(--fe-space-3);
168
+ }
169
+
170
+ /* ---------- LISTAS ---------- */
171
+
172
+ .fe-list {
173
+ list-style: none;
174
+ margin: 0;
175
+ padding: 0;
176
+ }
177
+
178
+ .fe-list > li + li { margin-top: var(--fe-space-2); }
179
+
180
+ /* ---------- PAGINACION ---------- */
181
+
182
+ .fe-pagination {
183
+ display: flex;
184
+ align-items: center;
185
+ gap: var(--fe-space-1);
186
+ list-style: none;
187
+ margin: 0;
188
+ padding: 0;
189
+ }
190
+
191
+ .fe-pagination a,
192
+ .fe-pagination button {
193
+ display: inline-flex;
194
+ align-items: center;
195
+ justify-content: center;
196
+ min-width: 2rem;
197
+ height: 2rem;
198
+ padding: 0 var(--fe-space-2);
199
+ font-size: var(--fe-text-sm);
200
+ color: var(--fe-text-muted);
201
+ background: none;
202
+ border: 1px solid transparent;
203
+ border-radius: var(--fe-radius-sm);
204
+ cursor: pointer;
205
+ text-decoration: none;
206
+ }
207
+
208
+ .fe-pagination a:hover,
209
+ .fe-pagination button:hover:not(:disabled) {
210
+ background: var(--fe-surface-hover);
211
+ color: var(--fe-text);
212
+ }
213
+
214
+ .fe-pagination .fe-active {
215
+ color: var(--fe-primary-text);
216
+ background: var(--fe-primary);
217
+ }
218
+
219
+ /* Las flechas se dibujan, para no depender de la fuente de iconos de UIkit
220
+ (`uk-pagination-previous` es un `uk-icon`, es decir JS de UIkit). */
221
+ .fe-page-prev::before { content: '‹'; font-size: 1.125rem; line-height: 1; }
222
+ .fe-page-next::before { content: '›'; font-size: 1.125rem; line-height: 1; }
223
+
224
+ /* ---------- ESTADOS ---------- */
225
+
226
+ .fe-disabled,
227
+ .fe-disabled a {
228
+ opacity: 0.5;
229
+ pointer-events: none;
230
+ }
231
+
232
+ .fe-shrink { width: 1px; white-space: nowrap; }
233
+
234
+ /* ---------- APARICION ---------- */
235
+
236
+ .fe-fade-in { animation: fe-fade-in 160ms ease; }
237
+
238
+ @keyframes fe-fade-in {
239
+ from { opacity: 0; }
240
+ to { opacity: 1; }
241
+ }
242
+
243
+ .fe-reveal > .fe-reveal-target { opacity: 0; transition: opacity var(--fe-transition); }
244
+ .fe-reveal:hover > .fe-reveal-target,
245
+ .fe-reveal:focus-within > .fe-reveal-target { opacity: 1; }
246
+
247
+ @media (prefers-reduced-motion: reduce) {
248
+ .fe-fade-in { animation: none; }
249
+ .fe-reveal > .fe-reveal-target { transition: none; }
250
+ }
package/src/theme.js CHANGED
@@ -1,13 +1,6 @@
1
1
  /**
2
- * Las clases CSS de los formularios, en un solo sitio.
3
- *
4
- * Antes cada componente llevaba su cadena incrustada como valor por defecto de
5
- * `customClass`, repetida en los dos paquetes; y el codigo generado por
6
- * larapack esperaba `inputClass` y `buttonClass` de un mixin global que la
7
- * aplicacion anfitriona tenia que registrar sin que nada lo dijera.
8
- *
9
- * Ahora hay un tema: un mapa de tokens que la aplicacion ajusta una vez, al
10
- * arrancar, y que leen los dos paquetes de componentes.
2
+ * El tema del ecosistema innoboxrr: un mapa de tokens que la aplicacion
3
+ * ajusta una vez, al arrancar, y que leen los dos paquetes de componentes.
11
4
  *
12
5
  * import { setTheme } from 'innoboxrr-form-core'
13
6
  *
@@ -21,46 +14,72 @@
21
14
  * @typedef {Record<string, string>} Theme
22
15
  */
23
16
 
24
- /** @type {Theme} */
17
+ /**
18
+ * Los tokens apuntan a las clases del sistema de diseno propio, no a las de
19
+ * un framework ajeno.
20
+ *
21
+ * Antes cada valor era una cadena que mezclaba UIkit, Tailwind y Font Awesome
22
+ * —`uk-input uk-form-large uk-border-rounded`— y ninguno de esos frameworks
23
+ * estaba declarado como dependencia: el paquete solo funcionaba dentro de una
24
+ * aplicacion que ya los trajera cargados, y nada lo decia. Ademas el modo
25
+ * oscuro iba incrustado en cada cadena, repetido treinta veces.
26
+ *
27
+ * Ahora las clases son nuestras y su aspecto sale de las variables de
28
+ * `tokens.css`, donde el modo oscuro se define una sola vez. Cambiar el color
29
+ * principal del ecosistema vuelve a ser editar una linea.
30
+ *
31
+ * @type {Theme}
32
+ */
25
33
  export const defaultTheme = {
26
34
  // ENVOLTORIOS
27
- field: 'uk-margin',
28
- fieldInner: 'uk-inline uk-width-1-1',
29
- label: 'ml-2 text-sm font-medium text-gray-900 dark:text-white',
30
- help: 'cursor-pointer',
31
- helpIcon: 'fa-solid fa-circle-question',
32
- error: 'fe-input-error text-red-600 font-bold',
35
+ field: 'fe-field',
36
+ fieldInner: 'fe-field-inner',
37
+ label: 'fe-label',
38
+ help: 'fe-help',
39
+ helpIcon: 'fe-help-icon',
40
+ error: 'fe-error',
33
41
 
34
42
  // CONTROLES
35
- input: 'uk-input uk-form-large uk-border-rounded',
36
- select: 'uk-select uk-form-large uk-border-rounded',
37
- textarea: 'uk-textarea uk-form-large uk-border-rounded',
38
- checkbox: 'uk-checkbox',
39
- radio: 'uk-radio',
40
- file: 'uk-input uk-form-large uk-border-rounded',
43
+ input: 'fe-input',
44
+ select: 'fe-select',
45
+ textarea: 'fe-textarea',
46
+ checkbox: 'fe-checkbox',
47
+ radio: 'fe-radio',
48
+ file: 'fe-file',
41
49
 
42
50
  // BOTONES
43
- button: 'uk-button uk-width-1-1 button',
44
- buttonSecondary: 'uk-button uk-button-default',
45
- buttonDanger: 'uk-button uk-button-danger',
46
- buttonLink: 'uk-button uk-button-link',
47
-
48
- // NAVEGACION
49
- //
50
- // Estos no vienen de UIkit a proposito. El codigo generado por larapack
51
- // pintaba las migas con un <BreadcrumbsComponent> que no existe en ningun
52
- // paquete del ecosistema: cada aplicacion tenia que registrarlo
53
- // globalmente sin que nada lo declarara, y la rama React directamente no
54
- // tenia migas. Con estos tokens el componente se genera dentro del modulo
55
- // y se estiliza desde aqui, como todo lo demas.
56
- actionMenu: 'flex items-center gap-3',
57
- actionMenuItem: 'text-sm text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300',
58
- actionMenuDanger: 'text-sm text-red-600 hover:text-red-800 dark:text-red-400 dark:hover:text-red-300',
59
-
60
- breadcrumb: 'flex items-center flex-wrap gap-2 text-sm mb-4',
61
- breadcrumbLink: 'text-slate-500 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-100',
62
- breadcrumbCurrent: 'font-medium text-slate-900 dark:text-slate-100',
63
- breadcrumbSeparator: 'text-slate-300 dark:text-slate-600 select-none',
51
+ button: 'fe-button',
52
+ buttonSecondary: 'fe-button-secondary',
53
+ buttonDanger: 'fe-button-danger',
54
+ buttonLink: 'fe-button-link',
55
+
56
+ // NAVEGACION Y ACCIONES
57
+ breadcrumb: 'fe-breadcrumb',
58
+ breadcrumbLink: 'fe-breadcrumb-link',
59
+ breadcrumbCurrent: 'fe-breadcrumb-current',
60
+ breadcrumbSeparator: 'fe-breadcrumb-separator',
61
+ actionMenu: 'fe-action-menu',
62
+ actionMenuItem: 'fe-action-item',
63
+ actionMenuDanger: 'fe-action-danger',
64
+
65
+ // SUPERFICIES Y PIEZAS DE APLICACION
66
+ surface: 'fe-surface',
67
+ surfaceRaised: 'fe-surface-raised',
68
+ toolbar: 'fe-toolbar',
69
+ badge: 'fe-badge',
70
+ skeleton: 'fe-skeleton',
71
+ overlay: 'fe-overlay',
72
+ dialog: 'fe-dialog',
73
+ drawer: 'fe-drawer',
74
+ menu: 'fe-menu',
75
+ menuItem: 'fe-menu-item',
76
+ toast: 'fe-toast',
77
+ toastDanger: 'fe-toast-danger',
78
+ toastSuccess: 'fe-toast-success',
79
+
80
+ // TABLA
81
+ table: 'fe-table',
82
+ tableNumeric: 'fe-numeric',
64
83
  }
65
84
 
66
85
  /** @type {Theme} */
package/src/tokens.css ADDED
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Las primitivas del sistema de diseño innoboxrr.
3
+ *
4
+ * Antes no había primitivas: el tema eran 23 cadenas de clases sueltas que
5
+ * mezclaban UIkit, Tailwind y Font Awesome, con el modo oscuro incrustado en
6
+ * cada una (`dark:text-white` repetido treinta veces). Cambiar el color
7
+ * principal del ecosistema no era una edición, era una búsqueda.
8
+ *
9
+ * Aquí cada decisión vive una vez. El modo oscuro redefine solo los colores,
10
+ * y en tres estados, porque el visitante tiene tres:
11
+ *
12
+ * - `:root` → el tema claro completo
13
+ * - `prefers-color-scheme: dark` → lo que ve quien no ha elegido nada
14
+ * - `[data-theme="dark"]` → la elección explícita, que manda sobre el SO
15
+ *
16
+ * La densidad es una variable, no un conjunto de clases: `--fe-density: 0.875`
17
+ * en un contenedor compacta todo lo que hay dentro sin tocar un componente.
18
+ */
19
+
20
+ :root {
21
+ /* ---------- COLOR ---------- */
22
+
23
+ /* Neutros con sesgo frío: un gris puro se lee como falta de decisión. */
24
+ --fe-bg: #f7f8fa;
25
+ --fe-surface: #ffffff;
26
+ --fe-surface-raised: #ffffff;
27
+ --fe-surface-sunk: #f1f2f6;
28
+ --fe-surface-hover: #f1f2f6;
29
+
30
+ --fe-border: #e3e5eb;
31
+ --fe-border-strong: #c8cbd4;
32
+
33
+ --fe-text: #1b1c22;
34
+ --fe-text-muted: #5c5f6b;
35
+ --fe-text-subtle: #8b8e9a;
36
+ --fe-text-inverse: #ffffff;
37
+
38
+ --fe-primary: #4b5bd7;
39
+ --fe-primary-hover: #3f4dc0;
40
+ --fe-primary-text: #ffffff;
41
+ --fe-primary-soft: #eceefb;
42
+
43
+ --fe-danger: #c0392f;
44
+ --fe-danger-hover: #a32e26;
45
+ --fe-danger-text: #ffffff;
46
+ --fe-danger-soft: #fbeceb;
47
+
48
+ --fe-success: #17714d;
49
+ --fe-success-soft: #e6f2ec;
50
+
51
+ --fe-warning: #8a6212;
52
+ --fe-warning-soft: #f8f0dd;
53
+
54
+ --fe-focus: #4b5bd7;
55
+
56
+ /* ---------- FORMA ---------- */
57
+
58
+ --fe-radius-sm: 4px;
59
+ --fe-radius: 6px;
60
+ --fe-radius-lg: 10px;
61
+ --fe-radius-full: 9999px;
62
+
63
+ /* ---------- ESPACIO ---------- */
64
+
65
+ --fe-space-1: 0.25rem;
66
+ --fe-space-2: 0.5rem;
67
+ --fe-space-3: 0.75rem;
68
+ --fe-space-4: 1rem;
69
+ --fe-space-5: 1.5rem;
70
+ --fe-space-6: 2rem;
71
+
72
+ /* ---------- TIPOGRAFIA ---------- */
73
+
74
+ --fe-font: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
75
+ --fe-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
76
+
77
+ --fe-text-xs: 0.75rem;
78
+ --fe-text-sm: 0.8125rem;
79
+ --fe-text-base: 0.875rem;
80
+ --fe-text-lg: 1rem;
81
+ --fe-text-xl: 1.25rem;
82
+
83
+ /* ---------- ELEVACION ---------- */
84
+
85
+ --fe-shadow-sm: 0 1px 2px rgba(16, 18, 27, 0.06);
86
+ --fe-shadow: 0 4px 12px rgba(16, 18, 27, 0.08);
87
+ --fe-shadow-lg: 0 16px 40px rgba(16, 18, 27, 0.16);
88
+
89
+ /* ---------- DENSIDAD ---------- */
90
+
91
+ /* Multiplica alturas y padding de los controles. 1 es cómodo; 0.875
92
+ compacta una tabla sin que sus celdas dejen de ser pulsables. */
93
+ --fe-density: 1;
94
+ --fe-control-height: calc(2.25rem * var(--fe-density));
95
+ --fe-control-padding-x: calc(0.75rem * var(--fe-density));
96
+
97
+ /* ---------- MOVIMIENTO ---------- */
98
+
99
+ --fe-transition: 120ms ease;
100
+ }
101
+
102
+ /* La elección explícita de claro gana sobre un sistema en oscuro, de ahí el
103
+ :not(). Sin él, un usuario que pide claro seguiría viendo oscuro. */
104
+ @media (prefers-color-scheme: dark) {
105
+ :root:not([data-theme='light']) {
106
+ --fe-bg: #101116;
107
+ --fe-surface: #181a21;
108
+ --fe-surface-raised: #1f222b;
109
+ --fe-surface-sunk: #14161c;
110
+ --fe-surface-hover: #22252f;
111
+
112
+ --fe-border: #2b2e39;
113
+ --fe-border-strong: #3d4150;
114
+
115
+ --fe-text: #e8e9ef;
116
+ --fe-text-muted: #a7aab8;
117
+ --fe-text-subtle: #767a89;
118
+ --fe-text-inverse: #101116;
119
+
120
+ --fe-primary: #8b95f0;
121
+ --fe-primary-hover: #a0a8f5;
122
+ --fe-primary-text: #101116;
123
+ --fe-primary-soft: #21243a;
124
+
125
+ --fe-danger: #e08b83;
126
+ --fe-danger-hover: #eda29a;
127
+ --fe-danger-text: #101116;
128
+ --fe-danger-soft: #2c1d1c;
129
+
130
+ --fe-success: #5ec79c;
131
+ --fe-success-soft: #142b23;
132
+
133
+ --fe-warning: #d9ac53;
134
+ --fe-warning-soft: #2a2417;
135
+
136
+ --fe-focus: #8b95f0;
137
+
138
+ --fe-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
139
+ --fe-shadow: 0 4px 12px rgba(0, 0, 0, 0.45);
140
+ --fe-shadow-lg: 0 16px 40px rgba(0, 0, 0, 0.55);
141
+ }
142
+ }
143
+
144
+ /* Y el interruptor manda en las dos direcciones. */
145
+ :root[data-theme='dark'] {
146
+ --fe-bg: #101116;
147
+ --fe-surface: #181a21;
148
+ --fe-surface-raised: #1f222b;
149
+ --fe-surface-sunk: #14161c;
150
+ --fe-surface-hover: #22252f;
151
+
152
+ --fe-border: #2b2e39;
153
+ --fe-border-strong: #3d4150;
154
+
155
+ --fe-text: #e8e9ef;
156
+ --fe-text-muted: #a7aab8;
157
+ --fe-text-subtle: #767a89;
158
+ --fe-text-inverse: #101116;
159
+
160
+ --fe-primary: #8b95f0;
161
+ --fe-primary-hover: #a0a8f5;
162
+ --fe-primary-text: #101116;
163
+ --fe-primary-soft: #21243a;
164
+
165
+ --fe-danger: #e08b83;
166
+ --fe-danger-hover: #eda29a;
167
+ --fe-danger-text: #101116;
168
+ --fe-danger-soft: #2c1d1c;
169
+
170
+ --fe-success: #5ec79c;
171
+ --fe-success-soft: #142b23;
172
+
173
+ --fe-warning: #d9ac53;
174
+ --fe-warning-soft: #2a2417;
175
+
176
+ --fe-focus: #8b95f0;
177
+
178
+ --fe-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
179
+ --fe-shadow: 0 4px 12px rgba(0, 0, 0, 0.45);
180
+ --fe-shadow-lg: 0 16px 40px rgba(0, 0, 0, 0.55);
181
+ }
182
+
183
+ /* Una variante compacta lista para usar: se pone en un contenedor y todo lo
184
+ de dentro encoge, sin que ningún componente sepa nada. */
185
+ .fe-dense {
186
+ --fe-density: 0.875;
187
+ }
package/styles.css ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * La hoja de estilos del ecosistema innoboxrr.
3
+ *
4
+ * import 'innoboxrr-form-core/styles'
5
+ *
6
+ * Una sola linea en el arranque de la aplicacion, y los componentes de
7
+ * innoboxrr-form-elements, innoboxrr-react-form-elements y todo lo que genera
8
+ * larapack tienen aspecto sin depender de UIkit, Tailwind ni Font Awesome.
9
+ *
10
+ * Si la aplicacion ya tiene su propio sistema visual, puede no importar esto y
11
+ * apuntar los tokens a sus clases con `setTheme`.
12
+ */
13
+
14
+ @import './src/tokens.css';
15
+ @import './src/layout.css';
16
+ @import './src/components.css';