@sfperusacdev/sf-ui 0.3.1 → 0.3.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/dist/sf-ui.css +1 -1
- package/dist/sf-ui.js +2567 -2339
- package/dist/sf-ui.js.map +1 -1
- package/dist/sf-ui.umd.cjs +8 -7
- package/dist/sf-ui.umd.cjs.map +1 -1
- package/dist/theme/dark.css +8 -0
- package/dist/theme/densidad.css +0 -1
- package/dist/theme/temas.css +116 -0
- package/dist/types/src/components/dashboard/MetricTile.sinDesborde.test.d.ts +1 -0
- package/dist/types/src/components/display/CheckList.srOnly.test.d.ts +1 -0
- package/dist/types/src/components/inputs/SearchSelect.escape.test.d.ts +1 -0
- package/dist/types/src/components/layout/InPageDetail.d.ts +36 -0
- package/dist/types/src/components/layout/InPageDetail.test.d.ts +1 -0
- package/dist/types/src/components/layout/index.d.ts +2 -0
- package/dist/types/src/components/overlays/UnsavedChanges.d.ts +35 -0
- package/dist/types/src/components/overlays/UnsavedChanges.test.d.ts +1 -0
- package/dist/types/src/components/overlays/index.d.ts +2 -0
- package/dist/types/src/components/visualization/IntegrationFlow.sinRecorte.test.d.ts +1 -0
- package/dist/types/src/theme/detailSectionOscuro.test.d.ts +1 -0
- package/dist/types/src/utils/exportTableXlsx.d.ts +7 -1
- package/dist/types/src/utils/exportTableXlsx.notas.test.d.ts +1 -0
- package/dist/types/src/utils/index.d.ts +1 -0
- package/dist/types/src/utils/queryParams.test.d.ts +1 -0
- package/dist/types/src/utils/restoreFocus.d.ts +8 -0
- package/dist/types/src/utils/restoreFocus.test.d.ts +1 -0
- package/package.json +1 -1
package/dist/theme/dark.css
CHANGED
|
@@ -195,6 +195,14 @@
|
|
|
195
195
|
--sf-success: #16a34a;
|
|
196
196
|
--sf-warning: #d97706;
|
|
197
197
|
|
|
198
|
+
/* Título e icono de la cabecera de DetailSection con tono. Los tonos de base dan de 2,5:1 a 3,8:1 sobre la tarjeta
|
|
199
|
+
* con el fondo tenue del tono; estos, de 4,9:1 a 7,2:1. No se aclaran --sf-info y compañía: también son fondo de
|
|
200
|
+
* botones y barras con texto blanco. */
|
|
201
|
+
--sf-detail-section-info-text: #60a5fa;
|
|
202
|
+
--sf-detail-section-danger-text: #f87171;
|
|
203
|
+
--sf-detail-section-warning-text: #fbbf24;
|
|
204
|
+
--sf-detail-section-success-text: #4ade80;
|
|
205
|
+
|
|
198
206
|
/* Paleta de categorías sobre fondo oscuro: los mismos 10 tonos de sf-tokens.css, aclarados para dar al menos
|
|
199
207
|
* 3:1 sobre la tarjeta (#1e293b) y sobre la superficie alterna (#0f172a). */
|
|
200
208
|
--sf-cat-1: #7aa2f0;
|
package/dist/theme/densidad.css
CHANGED
package/dist/theme/temas.css
CHANGED
|
@@ -155,6 +155,17 @@
|
|
|
155
155
|
color: color-mix(in srgb, var(--sf-donut-label-color) 48%, #0b1f29);
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
/* Íconos de baldosa (procesados, en espera, validadas…): el centrado se oscurece como el texto; en el «leading», el
|
|
159
|
+
círculo de color lleva el ícono en blanco, así que es el círculo el que se oscurece. */
|
|
160
|
+
:root[data-skin]:not([data-theme='dark']) .sf-metric-tile--centered .sf-metric-tile__icon {
|
|
161
|
+
color: color-mix(in srgb, var(--sf-metric-tile-color) 50%, #0b1f29);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
:root[data-skin]:not([data-theme='dark']) .sf-metric-tile--leading .sf-metric-tile__icon {
|
|
165
|
+
background: color-mix(in srgb, var(--sf-metric-tile-color) 55%, #0b1f29);
|
|
166
|
+
color: #ffffff;
|
|
167
|
+
}
|
|
168
|
+
|
|
158
169
|
:root[data-skin]:not([data-theme='dark']) .sf-metric-row__tile-icon {
|
|
159
170
|
border-color: color-mix(in srgb, var(--sf-metric-color, var(--sf-accent)) 60%, #0b1f29);
|
|
160
171
|
color: color-mix(in srgb, var(--sf-metric-color, var(--sf-accent)) 48%, #0b1f29);
|
|
@@ -192,6 +203,111 @@
|
|
|
192
203
|
color: var(--sf-accent-text);
|
|
193
204
|
}
|
|
194
205
|
|
|
206
|
+
/* Página activa del paginador en claro: en classic, soft y neon el número va en blanco sobre el acento, y el de ERP
|
|
207
|
+
(#ec7211) o Neón (#0d9488) no llega a 4,5:1. premium y matrix tienen sus propios colores y no se tocan. */
|
|
208
|
+
:root[data-skin='erp']:not([data-theme='dark']) .sf-pagination--classic .sf-pagination__page--active,
|
|
209
|
+
:root[data-skin='erp']:not([data-theme='dark']) .sf-pagination--classic .sf-pagination__page--active:hover,
|
|
210
|
+
:root[data-skin='erp']:not([data-theme='dark']) .sf-pagination--soft .sf-pagination__page--active,
|
|
211
|
+
:root[data-skin='erp']:not([data-theme='dark']) .sf-pagination--soft .sf-pagination__page--active:hover,
|
|
212
|
+
:root[data-skin='erp']:not([data-theme='dark']) .sf-pagination--neon .sf-pagination__page--active,
|
|
213
|
+
:root[data-skin='erp']:not([data-theme='dark']) .sf-pagination--neon .sf-pagination__page--active:hover {
|
|
214
|
+
background: #b35400;
|
|
215
|
+
border-color: #b35400;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
:root[data-skin='neon']:not([data-theme='dark']) .sf-pagination--classic .sf-pagination__page--active,
|
|
219
|
+
:root[data-skin='neon']:not([data-theme='dark']) .sf-pagination--classic .sf-pagination__page--active:hover,
|
|
220
|
+
:root[data-skin='neon']:not([data-theme='dark']) .sf-pagination--soft .sf-pagination__page--active,
|
|
221
|
+
:root[data-skin='neon']:not([data-theme='dark']) .sf-pagination--soft .sf-pagination__page--active:hover,
|
|
222
|
+
:root[data-skin='neon']:not([data-theme='dark']) .sf-pagination--neon .sf-pagination__page--active,
|
|
223
|
+
:root[data-skin='neon']:not([data-theme='dark']) .sf-pagination--neon .sf-pagination__page--active:hover {
|
|
224
|
+
background: #0f766e;
|
|
225
|
+
border-color: #0f766e;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* —— Detalles y capas (medido en el prototipo ADN con capas-temas, 6 temas) ——
|
|
229
|
+
Botones de estado: el texto no sale de --sf-btn-primary-text, que en los oscuros es casi negro. */
|
|
230
|
+
:root[data-skin] .sf-btn--success {
|
|
231
|
+
background: #15803d;
|
|
232
|
+
color: #ffffff;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
:root[data-skin] .sf-btn--success:hover:not(:disabled) {
|
|
236
|
+
background: #166534;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
:root[data-skin] .sf-btn--danger-solid {
|
|
240
|
+
background: #b91c1c;
|
|
241
|
+
color: #ffffff;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
:root[data-skin] .sf-btn--danger-solid:hover:not(:disabled) {
|
|
245
|
+
background: #991b1b;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/* Claro: rojo, acentos de aviso, asterisco y cierre de los avisos a 4,5:1 sobre blanco o su tinte. */
|
|
249
|
+
:root[data-skin]:not([data-theme='dark']) {
|
|
250
|
+
--sf-alert-info-accent: #1d4ed8;
|
|
251
|
+
--sf-alert-success-accent: #15803d;
|
|
252
|
+
--sf-alert-warning-accent: #b45309;
|
|
253
|
+
--sf-alert-danger-accent: #b91c1c;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
:root[data-skin]:not([data-theme='dark']) .sf-btn--danger {
|
|
257
|
+
background: #ffffff;
|
|
258
|
+
color: #b91c1c;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
:root[data-skin]:not([data-theme='dark']) .sf-field-required {
|
|
262
|
+
color: #b91c1c;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
:root[data-skin]:not([data-theme='dark']) .sf-table thead th:not([class*='sf-table__th--']) {
|
|
266
|
+
color: #475569;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
:root[data-skin]:not([data-theme='dark']) .sf-toast__close {
|
|
270
|
+
color: #475569;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
:root[data-skin]:not([data-theme='dark']) .sf-toast--warning .sf-toast__icon {
|
|
274
|
+
color: #b45309;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/* El encabezado sin variante no tiene fondo propio: el subtítulo va sobre el blanco del modal, no sobre el marino. */
|
|
278
|
+
:root[data-skin]:not([data-theme='dark']) .sf-modal-header:not(.sf-modal-header--dark):not(.sf-modal-header--brand) .sf-modal-header__subtitle {
|
|
279
|
+
color: var(--sf-text-muted);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/* Oscuro: los mismos elementos con los tonos claros de su color, sobre el fondo pizarra o negro. */
|
|
283
|
+
:root[data-skin][data-theme='dark'] {
|
|
284
|
+
--sf-alert-info-accent: #60a5fa;
|
|
285
|
+
--sf-alert-success-accent: #4ade80;
|
|
286
|
+
--sf-alert-warning-accent: #fbbf24;
|
|
287
|
+
--sf-alert-danger-accent: #f87171;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
:root[data-skin][data-theme='dark'] .sf-btn--danger {
|
|
291
|
+
background: transparent;
|
|
292
|
+
color: #f87171;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
:root[data-skin][data-theme='dark'] .sf-field-required {
|
|
296
|
+
color: #f87171;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
:root[data-skin][data-theme='dark'] .sf-table thead th:not([class*='sf-table__th--']) {
|
|
300
|
+
color: var(--sf-text-muted);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
:root[data-skin][data-theme='dark'] .sf-badge--info:not(.sf-badge--solid) {
|
|
304
|
+
color: #93c5fd;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
:root[data-skin][data-theme='dark'] .sf-toast__close {
|
|
308
|
+
color: var(--sf-text-muted);
|
|
309
|
+
}
|
|
310
|
+
|
|
195
311
|
/* Modo oscuro con cualquier tema: el texto pintado con un color de categoría se aclara para leerse sobre el fondo
|
|
196
312
|
pizarra o negro; el amarillo de advertencia suave y los rellenos de barra también suben de luz. */
|
|
197
313
|
:root[data-skin][data-theme='dark'] .sf-relation-graph--orbit .sf-relation-graph__relation {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
export type InPageDetailState<T> = {
|
|
3
|
+
origin: string;
|
|
4
|
+
value: T;
|
|
5
|
+
};
|
|
6
|
+
export type InPageDetailController<T> = {
|
|
7
|
+
current: InPageDetailState<T> | null;
|
|
8
|
+
/** Abre el detalle de `value`, originado en `origin` (la tarjeta o cifra). Si el abierto tiene cambios, pregunta. */
|
|
9
|
+
open: (origin: string, value: T) => void;
|
|
10
|
+
/** Cierra y devuelve el foco al control que abrió el detalle (o a su tarjeta). Si hay cambios, pregunta. */
|
|
11
|
+
close: () => void;
|
|
12
|
+
/** Cambia el detalle sin preguntar: para pasar a la ficha de lo que se acaba de guardar. */
|
|
13
|
+
replace: (origin: string, value: T) => void;
|
|
14
|
+
/** El valor abierto si su origen es `origin`; si no, `null`. */
|
|
15
|
+
valueFor: (origin: string) => T | null;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Estado de un detalle que se abre en la página, bajo la tarjeta que lo originó (regla de ventanas §6). Abrir otro o
|
|
19
|
+
* cerrar pasa por `useProtectedExit` con el ámbito `scope`; la captura del detalle se registra en ese mismo ámbito con
|
|
20
|
+
* `useUnsavedChanges`.
|
|
21
|
+
*/
|
|
22
|
+
export declare const useInPageDetail: <T>(scope?: string) => InPageDetailController<T>;
|
|
23
|
+
export type InPageDetailZoneProps = {
|
|
24
|
+
/** Cambia cuando cambia lo que se muestra: la zona vuelve a recibir el foco. */
|
|
25
|
+
zoneKey: string;
|
|
26
|
+
/** Nombre accesible de la región («Evento Lote 12»). */
|
|
27
|
+
label: string;
|
|
28
|
+
onClose: () => void;
|
|
29
|
+
children: ReactNode;
|
|
30
|
+
className?: string;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Contenedor del detalle: se desplaza a la vista, recibe el foco con su nombre y Escape lo cierra. Si el control
|
|
34
|
+
* enfocado dentro desaparece o se deshabilita (Reprocesar, Validar), el foco vuelve a la zona en vez de caer a <body>.
|
|
35
|
+
*/
|
|
36
|
+
export declare const InPageDetailZone: ({ zoneKey, label, onClose, children, className }: InPageDetailZoneProps) => import('react').JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -44,3 +44,5 @@ export type { WeekdayToggleStripProps, WeekdayOption } from './WeekdayToggleStri
|
|
|
44
44
|
export type { UnitSelectorOption, UnitSelectorProps } from './UnitSelector';
|
|
45
45
|
export { AppShell, AppBarContainer } from './AppShell';
|
|
46
46
|
export type { AppShellCategory, AppShellNavItem, AppShellProps, AppShellBranchSelector, AppShellExitToModulesAction, AppShellHelpAction, AppShellLayout, AppShellModuleLauncher, AppShellModuleLauncherItem, AppShellThemeToggle, AppShellTopBarContext, AppShellUserMenu, AppBarContainerProps, } from './AppShell';
|
|
47
|
+
export { InPageDetailZone, useInPageDetail } from './InPageDetail';
|
|
48
|
+
export type { InPageDetailController, InPageDetailState, InPageDetailZoneProps } from './InPageDetail';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
export type UnsavedChangesEntry = {
|
|
3
|
+
/** Qué se pierde, en palabras del usuario: «la edición del conductor». */
|
|
4
|
+
description: string;
|
|
5
|
+
/** Deja la captura como estaba antes de editar. */
|
|
6
|
+
discard: () => void;
|
|
7
|
+
};
|
|
8
|
+
export type ProtectedExit = {
|
|
9
|
+
/**
|
|
10
|
+
* Ejecuta `action`, o pregunta antes si el ámbito (`null`: cualquiera) tiene cambios sin guardar. «Descartar
|
|
11
|
+
* cambios» descarta esos ámbitos y ejecuta `action`; «Seguir editando», Escape o el fondo llaman a `onCancel` y dejan
|
|
12
|
+
* la captura como está. Mientras la pregunta está abierta, otra petición se rechaza (su `onCancel`).
|
|
13
|
+
*/
|
|
14
|
+
protect: (scope: string | null, action: () => void, onCancel?: () => void) => void;
|
|
15
|
+
/** Hay cambios sin guardar en el ámbito (`null` o sin argumento: en cualquiera). */
|
|
16
|
+
hasChanges: (scope?: string | null) => boolean;
|
|
17
|
+
};
|
|
18
|
+
export type UnsavedChangesProviderProps = {
|
|
19
|
+
children: ReactNode;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Un solo lugar que sabe qué capturas tienen cambios sin guardar y una sola pregunta para salir de ellas. Las
|
|
23
|
+
* pantallas registran su captura con `useUnsavedChanges`; los cierres, cambios de registro y el adaptador del
|
|
24
|
+
* enrutador de la app piden salir con `useProtectedExit().protect`. Mientras haya cambios, recargar o cerrar la
|
|
25
|
+
* pestaña muestra el aviso nativo del navegador.
|
|
26
|
+
*/
|
|
27
|
+
export declare const UnsavedChangesProvider: ({ children }: UnsavedChangesProviderProps) => import('react').JSX.Element;
|
|
28
|
+
/**
|
|
29
|
+
* Registra una captura mientras `dirty` sea verdadero; al guardar, descartar o desmontar se borra sola. Un ámbito por
|
|
30
|
+
* captura visible (la ficha, una edición en línea, un paso): dos capturas abiertas son dos ámbitos.
|
|
31
|
+
*/
|
|
32
|
+
export declare const useUnsavedChanges: (scope: string, { dirty, description, discard }: UnsavedChangesEntry & {
|
|
33
|
+
dirty: boolean;
|
|
34
|
+
}) => void;
|
|
35
|
+
export declare const useProtectedExit: () => ProtectedExit;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -25,3 +25,5 @@ export { CommandPalette } from './CommandPalette';
|
|
|
25
25
|
export type { CommandPaletteItem, CommandPaletteProps } from './CommandPalette';
|
|
26
26
|
export { ProductTour } from './ProductTour';
|
|
27
27
|
export type { ProductTourProps, ProductTourStep } from './ProductTour';
|
|
28
|
+
export { UnsavedChangesProvider, useProtectedExit, useUnsavedChanges } from './UnsavedChanges';
|
|
29
|
+
export type { ProtectedExit, UnsavedChangesEntry, UnsavedChangesProviderProps } from './UnsavedChanges';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { ExportTableColumn, ExportTableCsvOptions } from './exportTableCsv';
|
|
2
|
-
export type ExportTableXlsxOptions<T> = ExportTableCsvOptions<T
|
|
2
|
+
export type ExportTableXlsxOptions<T> = ExportTableCsvOptions<T> & {
|
|
3
|
+
/**
|
|
4
|
+
* Líneas que van antes del encabezado, una por fila (alcance, filtros, fecha). Sin `notes` el archivo no cambia.
|
|
5
|
+
* Sirve para que el archivo diga qué conjunto exporta cuando la pantalla muestra solo una parte.
|
|
6
|
+
*/
|
|
7
|
+
notes?: string[];
|
|
8
|
+
};
|
|
3
9
|
/** Exporta filas a Excel (.xls SpreadsheetML) sin peer externo. */
|
|
4
10
|
export declare const exportTableXlsx: <T>(rows: T[], options: ExportTableXlsxOptions<T>) => void;
|
|
5
11
|
export type { ExportTableColumn };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { toStringArray } from './toStringArray';
|
|
2
|
+
export { restoreFocus } from './restoreFocus';
|
|
2
3
|
export { readQueryValue, updateQueryValue, removeQueryValue } from './queryParams';
|
|
3
4
|
export { exportTableCsv } from './exportTableCsv';
|
|
4
5
|
export type { ExportTableColumn, ExportTableCsvOptions } from './exportTableCsv';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Devuelve el foco al cerrar un detalle o un panel: al control que lo abrió si sigue usable; si no, a otro control de
|
|
3
|
+
* su tarjeta (`context`), al encabezado de esa tarjeta y, por último, al `h1` de la página. Nunca mueve el foco a
|
|
4
|
+
* `<body>`; si no encuentra destino, no lo mueve (y si ya estaba en `<body>`, ahí sigue). El encabezado que recibe el
|
|
5
|
+
* foco queda con `tabindex="-1"`. Ignora lo que esté dentro de una zona de detalle, que se cierra.
|
|
6
|
+
* Devuelve el elemento enfocado, o `null`.
|
|
7
|
+
*/
|
|
8
|
+
export declare const restoreFocus: (trigger: HTMLElement | null, context?: HTMLElement | null) => HTMLElement | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED