@sfperusacdev/sf-ui 0.2.5 → 0.2.6

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.
@@ -77,6 +77,7 @@
77
77
  --sf-modal-close-hover-text: #f1f5f9;
78
78
 
79
79
  --sf-card-bg: #1e293b;
80
+ --sf-section-panel-bg: #334155;
80
81
  --sf-card-border: #334155;
81
82
  --sf-card-title: #f1f5f9;
82
83
  --sf-card-subtitle: #94a3b8;
@@ -111,6 +111,7 @@
111
111
 
112
112
  /* Cards / layout */
113
113
  --sf-card-bg: #ffffff;
114
+ --sf-section-panel-bg: #e5e7eb;
114
115
  --sf-card-border: #e5e7eb;
115
116
  --sf-card-shadow: 0 1px 2px rgb(0 0 0 / 0.05);
116
117
  --sf-card-shadow-hover: 0 4px 6px rgb(0 0 0 / 0.07);
@@ -0,0 +1,13 @@
1
+ import { ReactNode } from 'react';
2
+ export type CardNoteTone = "info" | "warning";
3
+ export type CardNoteProps = {
4
+ /** Cómo leer el dato de la tarjeta, en una frase. */
5
+ text: ReactNode;
6
+ tone?: CardNoteTone;
7
+ className?: string;
8
+ };
9
+ /**
10
+ * Nota al pie de una tarjeta o de un panel: explica cómo leer el dato con el mismo tintado que el pie de
11
+ * `MetricBarList`. Para procedencia de datos (período, fuentes, confianza) está `EvidenceFootnote`.
12
+ */
13
+ export declare const CardNote: ({ text, tone, className }: CardNoteProps) => import("react").JSX.Element;
@@ -0,0 +1,37 @@
1
+ import { ReactNode } from 'react';
2
+ /** Una fila del tablero: bloque ancho a la izquierda y, si viene, uno angosto a la derecha (2/3 · 1/3). */
3
+ export type DashboardBoardBlock = {
4
+ left?: ReactNode;
5
+ right?: ReactNode;
6
+ };
7
+ /** Pieza que no aplica a esta empresa o a este contexto, con el motivo que se le explica al usuario. */
8
+ export type DashboardBoardHiddenPiece = {
9
+ title: string;
10
+ reason: string;
11
+ };
12
+ export type DashboardBoardProps = {
13
+ /** Indicadores de la fila superior; los `null`/`false` se descartan y la grilla se ajusta a 3, 4 o 5 columnas. */
14
+ kpis?: ReactNode[];
15
+ /** Avisos informativos que van arriba de todo (uno por `Alert`). */
16
+ notes?: string[];
17
+ /** Piezas ocultas: se listan en un solo aviso de advertencia. */
18
+ hidden?: DashboardBoardHiddenPiece[];
19
+ hiddenTitle?: (count: number) => string;
20
+ /** Panel de detalle abierto (p. ej. `DetailPanel`); se pinta entre los indicadores y el bloque principal. */
21
+ detail?: ReactNode;
22
+ primary?: DashboardBoardBlock;
23
+ secondary?: DashboardBoardBlock;
24
+ };
25
+ /**
26
+ * Disposición de un tablero: avisos, fila de indicadores, panel de detalle y hasta dos filas de bloques
27
+ * (principal y secundario) con la proporción 2/3 · 1/3 cuando la fila tiene dos bloques. Va dentro de
28
+ * `DashboardLayout` o de `DashboardPageFrame`, que ponen el espacio vertical entre piezas.
29
+ */
30
+ export declare const DashboardBoard: ({ kpis, notes, hidden, hiddenTitle, detail, primary, secondary, }: DashboardBoardProps) => import("react").JSX.Element;
31
+ export type DashboardCardRowProps = {
32
+ /** Tarjetas de la fila; los `null`/`false` se descartan. Con tres o más van a 3 columnas, si no a 2. */
33
+ children: ReactNode[];
34
+ className?: string;
35
+ };
36
+ /** Tarjetas del mismo alto dentro de una sección (p. ej. Sexo · Edad · Antigüedad). */
37
+ export declare const DashboardCardRow: ({ children, className }: DashboardCardRowProps) => import("react").JSX.Element;
@@ -15,6 +15,11 @@ export type DashboardCardProps = {
15
15
  * una sola fila, en vez de recortar con `truncate` (acabado actual, `false` por defecto). Útil con
16
16
  * títulos largos + `headerAction` ancho (ej. un selector de rango de fechas). */
17
17
  headerWrap?: boolean;
18
+ /** `false` quita la línea entre la cabecera y el cuerpo (tarjetas de tablero con título grande). */
19
+ headerDivider?: boolean;
20
+ /** "board" = la tarjeta de los tableros: cabecera `md` que pasa a dos líneas y sin divisor. Cada una de esas
21
+ * tres props se puede seguir fijando aparte; `look` solo cambia sus valores por defecto. */
22
+ look?: "default" | "board";
18
23
  /** Contenido opcional bajo el cuerpo, separado por un borde — ej. un resumen o un link "ver todo". */
19
24
  footer?: ReactNode;
20
25
  /** Estado del cuerpo. "idle" (por defecto) = `children` tal cual. Ver `DashboardCardState`. */
@@ -34,7 +39,7 @@ export type DashboardCardProps = {
34
39
  className?: string;
35
40
  children?: ReactNode;
36
41
  };
37
- export declare const DashboardCard: ({ title, subtitle, icon, headerAction, variant, span, padding, headerSize, headerWrap, footer, state, loadingMessage, errorMessage, emptyMessage, onRetry, retryLabel, minHeight, bodyOverflow, className, children, }: DashboardCardProps) => import("react").JSX.Element;
42
+ export declare const DashboardCard: ({ title, subtitle, icon, headerAction, variant, span, padding, look, headerSize, headerWrap, headerDivider, footer, state, loadingMessage, errorMessage, emptyMessage, onRetry, retryLabel, minHeight, bodyOverflow, className, children, }: DashboardCardProps) => import("react").JSX.Element;
38
43
  type DashboardGridProps = {
39
44
  cols?: 2 | 3 | 4 | 5 | 6;
40
45
  gap?: "sm" | "md" | "lg";
@@ -58,10 +63,13 @@ type DashboardSectionProps = {
58
63
  icon?: ReactNode;
59
64
  /** Tamaño del tile de `icon`. "md" (por defecto) = 1.75rem, acabado actual. "lg" = 2.5rem. */
60
65
  iconSize?: "md" | "lg";
66
+ /** "plain" (por defecto) = sin fondo, acabado actual. "panel" la encierra en un recuadro gris redondeado
67
+ * (`--sf-section-panel-bg`) que agrupa visualmente sus tarjetas, como las secciones de los tableros. */
68
+ variant?: "plain" | "panel";
61
69
  className?: string;
62
70
  children?: ReactNode;
63
71
  };
64
- export declare const DashboardSection: ({ title, subtitle, action, icon, iconSize, className, children, }: DashboardSectionProps) => import("react").JSX.Element;
72
+ export declare const DashboardSection: ({ title, subtitle, action, icon, iconSize, variant, className, children, }: DashboardSectionProps) => import("react").JSX.Element;
65
73
  type DashboardStatBarProps = {
66
74
  items: StatBarItem[];
67
75
  className?: string;
@@ -0,0 +1,43 @@
1
+ import { ReactNode } from 'react';
2
+ export type DashboardDateRange = {
3
+ from: string;
4
+ to: string;
5
+ };
6
+ export type DashboardPageFrameProps = {
7
+ title: string;
8
+ /** La pregunta que responde el tablero, debajo del título. */
9
+ subtitle?: string;
10
+ /** Rango aplicado (YYYY-MM-DD). El marco guarda un borrador aparte hasta que el usuario pulsa «Aplicar». */
11
+ range: DashboardDateRange;
12
+ onApply: (range: DashboardDateRange) => void;
13
+ /** Devuelve el mensaje de error si el borrador no es válido; `null` si se puede aplicar. */
14
+ validate?: (range: DashboardDateRange) => string | null;
15
+ /** Error del rango ya aplicado (p. ej. venía inválido en la URL): se muestra y no se pintan los hijos. */
16
+ error?: string | null;
17
+ /** Hay consultas en curso: el botón «Aplicar» gira y el chip de estado cuenta los segundos. */
18
+ loading?: boolean;
19
+ /** Momento (epoch ms) del dato más antiguo en pantalla; alimenta el chip «datos de las HH:MM». */
20
+ updatedAt?: number | null;
21
+ /** El corte es el día de hoy: el chip dice «datos en vivo». */
22
+ live?: boolean;
23
+ /** Texto del corte a la derecha del chip, ya formateado (p. ej. «corte 30 set. 2026»). */
24
+ cutoffLabel?: string;
25
+ onRefresh?: () => void;
26
+ refreshLabel?: string;
27
+ /** Texto del tercer campo del filtro cuando el borrador coincide con lo aplicado. */
28
+ compareLabel?: string;
29
+ /** Texto del tercer campo cuando el borrador cambió y falta pulsar «Aplicar». */
30
+ pendingLabel?: string;
31
+ applyLabel?: string;
32
+ filterAriaLabel?: string;
33
+ /** Segundos de espera antes de mostrar el contador en el chip. */
34
+ slowAfterSeconds?: number;
35
+ className?: string;
36
+ children?: ReactNode;
37
+ };
38
+ /**
39
+ * Marco de una página de tablero: título y pregunta, chip de estado (en vivo / calculando / hora del dato),
40
+ * botón de actualizar y el filtro de rango de fechas con borrador y validación. Los hijos van dentro de
41
+ * `DashboardLayout`, así que `DashboardBoard` encaja directamente.
42
+ */
43
+ export declare const DashboardPageFrame: ({ title, subtitle, range, onApply, validate, error, loading, updatedAt, live, cutoffLabel, onRefresh, refreshLabel, compareLabel, pendingLabel, applyLabel, filterAriaLabel, slowAfterSeconds, className, children, }: DashboardPageFrameProps) => import("react").JSX.Element;
@@ -0,0 +1,18 @@
1
+ export type DashboardPlaceholderShape = "kpi" | "bars" | "chart" | "list";
2
+ export type DashboardPlaceholderState = "loading" | "error";
3
+ export type DashboardPlaceholderProps = {
4
+ /** Título de la pieza que ocupará este lugar cuando llegue su dato. */
5
+ title: string;
6
+ /** Silueta que se dibuja mientras carga: la misma forma que tendrá la pieza, para que la página no salte. */
7
+ shape?: DashboardPlaceholderShape;
8
+ state?: DashboardPlaceholderState;
9
+ errorMessage?: string;
10
+ onRetry?: () => void;
11
+ className?: string;
12
+ };
13
+ /**
14
+ * Lugar de una pieza de tablero que todavía no tiene dato: en carga dibuja una silueta con la forma de la
15
+ * pieza (la de `shape="kpi"` mide lo mismo que `KPICard size="lg"`); en error muestra el mensaje y el botón de
16
+ * reintento de `DashboardCard`.
17
+ */
18
+ export declare const DashboardPlaceholder: ({ title, shape, state, errorMessage, onRetry, className, }: DashboardPlaceholderProps) => import("react").JSX.Element;
@@ -0,0 +1,38 @@
1
+ import { ReactNode } from 'react';
2
+ import { DetailController, DetailSpec } from './detailTypes';
3
+ export type { DetailColumn, DetailController, DetailGroup, DetailSpec } from './detailTypes';
4
+ /** Estado del `DetailPanel` de un tablero: una sola cifra abierta a la vez; volver a pulsarla la cierra. */
5
+ export declare const useDetailPanel: <Row>() => DetailController<Row>;
6
+ type FlatRow = Record<string, unknown>;
7
+ export type DetailPanelProps<Row> = {
8
+ detail: DetailController<Row>;
9
+ /** Convierte cada fila al objeto plano que leen las columnas (`column.key`). Por defecto la fila tal cual. */
10
+ rowMapper?: (row: Row) => FlatRow;
11
+ subtitle?: string;
12
+ /** Texto del contador de la cabecera, con el total ya formateado por quien llama. */
13
+ countLabel?: (total: number) => string;
14
+ /** Filas que se muestran en pantalla; el resto sale por Excel, para no convertir el tablero en una tabla. */
15
+ previewRows?: number;
16
+ /** Nota bajo la tabla cuando hay más filas que `previewRows`. */
17
+ previewNote?: (shown: number, total: number) => string;
18
+ /** Nota bajo la tabla cuando caben todas. */
19
+ totalNote?: (total: number) => string;
20
+ emptyMessage?: string;
21
+ closeLabel?: string;
22
+ exportLabel?: string;
23
+ /** Nombre del archivo exportado, sin extensión. */
24
+ exportFilename?: (spec: DetailSpec<Row>, groupId: string | null) => string;
25
+ /** Acciones extra junto a «Exportar Excel» (p. ej. abrir la lista en otro módulo); reciben las filas visibles. */
26
+ actions?: (context: {
27
+ spec: DetailSpec<Row>;
28
+ groupId: string | null;
29
+ groupTitle: string | null;
30
+ rows: Row[];
31
+ }) => ReactNode;
32
+ };
33
+ /**
34
+ * Panel de «qué hay detrás de la cifra»: título, contador, frase que explica la lista, una pestaña por caso
35
+ * con su acción, las primeras filas en una `CustomTable` y la exportación completa a Excel. Se abre desde
36
+ * `DrilldownKPICard`, `DrilldownAlertList` o cualquier pieza que llame a `detail.toggle(spec)`.
37
+ */
38
+ export declare const DetailPanel: <Row>({ detail, rowMapper, subtitle, countLabel, previewRows, previewNote, totalNote, emptyMessage, closeLabel, exportLabel, exportFilename, actions, }: DetailPanelProps<Row>) => import("react").JSX.Element | null;
@@ -0,0 +1,68 @@
1
+ import { ReactNode } from 'react';
2
+ import { KPICardAccentTone, KPICardTrendDirection } from '../cards/KPICard';
3
+ import { DetailController, DetailSpec } from './detailTypes';
4
+ import { EventStatus } from './types';
5
+ /** Hay algo que mostrar: filas sueltas o filas dentro de algún caso. */
6
+ export declare const detailHasRows: <Row>(spec: DetailSpec<Row> | null | undefined) => spec is DetailSpec<Row>;
7
+ export type DrilldownKPICardProps<Row> = {
8
+ label: string;
9
+ /** Valor ya formateado (la librería no decide el separador de miles). */
10
+ value: string | number;
11
+ sublabel?: string;
12
+ icon?: ReactNode;
13
+ accentTone?: KPICardAccentTone;
14
+ /** El indicador destacado de la fila (`KPICard variant="filled"`). */
15
+ primary?: boolean;
16
+ /** Variación porcentual contra el período anterior; `null` cuando no hay base para comparar. */
17
+ trend?: number | null;
18
+ trendDirection?: KPICardTrendDirection;
19
+ trendLabel?: string;
20
+ detail: DetailController<Row>;
21
+ /** Lo que se abre al hacer clic; sin filas, la tarjeta no es clicable. */
22
+ spec?: DetailSpec<Row> | null;
23
+ };
24
+ /** `KPICard` de tablero que abre su detalle en el `DetailPanel` compartido y se marca cuando está abierto. */
25
+ export declare const DrilldownKPICard: <Row>({ label, value, sublabel, icon, accentTone, primary, trend, trendDirection, trendLabel, detail, spec, }: DrilldownKPICardProps<Row>) => import("react").JSX.Element;
26
+ export type DrilldownAlertItem<Row> = {
27
+ label: string;
28
+ /** Valor ya formateado; con `count` en 0 la fila toma `zeroTone`. */
29
+ value: ReactNode;
30
+ count: number;
31
+ tone: EventStatus;
32
+ description?: string;
33
+ spec?: DetailSpec<Row> | null;
34
+ };
35
+ export type DrilldownAlertListProps<Row> = {
36
+ items: DrilldownAlertItem<Row>[];
37
+ detail: DetailController<Row>;
38
+ /** Tono de una fila cuya cifra es 0 (nada pendiente). */
39
+ zeroTone?: EventStatus;
40
+ emptyMessage?: ReactNode;
41
+ };
42
+ /** Lista de pendientes en una tarjeta: cada fila abre su detalle en el `DetailPanel` compartido. */
43
+ export declare const DrilldownAlertList: <Row>({ items, detail, zeroTone, emptyMessage, }: DrilldownAlertListProps<Row>) => import("react").JSX.Element;
44
+ export type DetailPreviewItem = {
45
+ key: string;
46
+ /** Código o documento, en monoespaciada delante del texto principal. */
47
+ code?: string;
48
+ primary: string;
49
+ /** Texto corto a la derecha (p. ej. la situación o los días). */
50
+ meta?: string;
51
+ };
52
+ export type DetailPreviewListProps = {
53
+ items: DetailPreviewItem[];
54
+ /** Total real de la lista; se muestra grande arriba de la vista previa. */
55
+ total: number;
56
+ /** Total ya formateado; por defecto `String(total)`. */
57
+ totalLabel?: string;
58
+ /** Sustantivo junto al total (p. ej. «personas»). */
59
+ unit?: string;
60
+ /** Cuántos elementos se muestran antes del botón. */
61
+ previewCount?: number;
62
+ emptyText: string;
63
+ /** Abre el detalle completo; el botón dice «Ver las N» o `openLabel`. */
64
+ onOpen: () => void;
65
+ openLabel?: string;
66
+ };
67
+ /** Las primeras filas de una lista con su total y el acceso al detalle completo. */
68
+ export declare const DetailPreviewList: ({ items, total, totalLabel, unit, previewCount, emptyText, onOpen, openLabel, }: DetailPreviewListProps) => import("react").JSX.Element;
@@ -0,0 +1,31 @@
1
+ /** Columna del panel de detalle: `key` indexa la fila ya aplanada (ver `DetailPanel.rowMapper`). */
2
+ export type DetailColumn = {
3
+ key: string;
4
+ header: string;
5
+ };
6
+ /** Un caso distinto dentro de la misma cifra: su pestaña, la acción que se sugiere y sus filas. */
7
+ export type DetailGroup<Row> = {
8
+ id: string;
9
+ title: string;
10
+ note?: string;
11
+ rows: Row[];
12
+ };
13
+ /** Lo que hay detrás de una cifra: las filas, cómo se leen (frase, columnas) y, si hay casos, una pestaña por caso. */
14
+ export type DetailSpec<Row> = {
15
+ id: string;
16
+ title: string;
17
+ rows: Row[];
18
+ columns: DetailColumn[];
19
+ /** Qué es la lista y qué hacer con ella, en una frase. */
20
+ description?: string;
21
+ groups?: DetailGroup<Row>[];
22
+ /** Nota al pie, p. ej. quiénes quedaron fuera de la lista y por qué. */
23
+ footnote?: string;
24
+ };
25
+ /** Estado del panel de detalle compartido por las piezas de un tablero: qué cifra está abierta. */
26
+ export type DetailController<Row> = {
27
+ open: DetailSpec<Row> | null;
28
+ /** Abre esa cifra, o la cierra si ya estaba abierta. */
29
+ toggle: (spec: DetailSpec<Row>) => void;
30
+ close: () => void;
31
+ };
@@ -22,6 +22,18 @@ export { DashboardCard, DashboardGrid, DashboardSection, DashboardStatBar, Dashb
22
22
  export type { DashboardCardProps } from './DashboardCard';
23
23
  export { DetailSidePanel } from './DetailSidePanel';
24
24
  export type { DetailSidePanelProps, DetailSidePanelRow, DetailSidePanelSecondaryItem } from './DetailSidePanel';
25
+ export { CardNote } from './CardNote';
26
+ export type { CardNoteProps, CardNoteTone } from './CardNote';
27
+ export { DashboardBoard, DashboardCardRow } from './DashboardBoard';
28
+ export type { DashboardBoardBlock, DashboardBoardHiddenPiece, DashboardBoardProps, DashboardCardRowProps } from './DashboardBoard';
29
+ export { DashboardPageFrame } from './DashboardPageFrame';
30
+ export type { DashboardDateRange, DashboardPageFrameProps } from './DashboardPageFrame';
31
+ export { DashboardPlaceholder } from './DashboardPlaceholder';
32
+ export type { DashboardPlaceholderProps, DashboardPlaceholderShape, DashboardPlaceholderState } from './DashboardPlaceholder';
33
+ export { DetailPanel, useDetailPanel } from './DetailPanel';
34
+ export type { DetailPanelProps, DetailColumn, DetailController, DetailGroup, DetailSpec } from './DetailPanel';
35
+ export { DrilldownKPICard, DrilldownAlertList, DetailPreviewList, detailHasRows } from './DrilldownCards';
36
+ export type { DrilldownKPICardProps, DrilldownAlertItem, DrilldownAlertListProps, DetailPreviewItem, DetailPreviewListProps, } from './DrilldownCards';
25
37
  export { DetailSection } from './DetailSection';
26
38
  export type { DetailSectionProps, DetailSectionRow, DetailSectionTone, DetailSectionDensity } from './DetailSection';
27
39
  export type { StepStatus, StepItem, CalendarBadgeVariant, CalendarDayBadge, DonutGaugeSize, DashboardCardVariant, DashboardCardState, EventStatus, EventItem, AlertRowItem, AlertRowSeverity, AlertRowListHeaderLabels, AlertActionItem, AlertDensity, StatBarItem, } from './types';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sfperusacdev/sf-ui",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "SF UI — librería de componentes React (inputs, tablas, hooks) para aplicaciones SF Perú.",
5
5
  "type": "module",
6
6
  "main": "./dist/sf-ui.umd.cjs",