@sfperusacdev/sf-ui 0.1.34 → 0.1.35

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 (62) hide show
  1. package/dist/sf-ui.css +1 -1
  2. package/dist/sf-ui.js +2291 -1499
  3. package/dist/sf-ui.js.map +1 -1
  4. package/dist/sf-ui.umd.cjs +7 -7
  5. package/dist/sf-ui.umd.cjs.map +1 -1
  6. package/dist/theme/sf-tokens.css +1 -0
  7. package/dist/types/src/components/cards/KPICard.d.ts +44 -1
  8. package/dist/types/src/components/cards/MetricBar.test.d.ts +1 -0
  9. package/dist/types/src/components/cards/SplitBar.d.ts +17 -0
  10. package/dist/types/src/components/cards/SplitBar.test.d.ts +1 -0
  11. package/dist/types/src/components/cards/index.d.ts +4 -2
  12. package/dist/types/src/components/cards/types.d.ts +36 -0
  13. package/dist/types/src/components/charts/Chart.d.ts +1 -1
  14. package/dist/types/src/components/charts/Chart.test.d.ts +1 -0
  15. package/dist/types/src/components/charts/DeviationBand.d.ts +7 -0
  16. package/dist/types/src/components/charts/DeviationBand.test.d.ts +1 -0
  17. package/dist/types/src/components/charts/DiagnosticRadar.d.ts +12 -0
  18. package/dist/types/src/components/charts/DiagnosticRadar.test.d.ts +1 -0
  19. package/dist/types/src/components/charts/Sparkline.d.ts +1 -1
  20. package/dist/types/src/components/charts/Sparkline.test.d.ts +1 -0
  21. package/dist/types/src/components/charts/chartUtils.d.ts +16 -0
  22. package/dist/types/src/components/charts/chartUtils.test.d.ts +1 -0
  23. package/dist/types/src/components/charts/index.d.ts +4 -2
  24. package/dist/types/src/components/charts/types.d.ts +95 -0
  25. package/dist/types/src/components/dashboard/AlertSummaryList.d.ts +31 -0
  26. package/dist/types/src/components/dashboard/AlertSummaryList.test.d.ts +1 -0
  27. package/dist/types/src/components/dashboard/ComparisonTable.d.ts +24 -0
  28. package/dist/types/src/components/dashboard/ComparisonTable.test.d.ts +1 -0
  29. package/dist/types/src/components/dashboard/DashboardCard.d.ts +31 -4
  30. package/dist/types/src/components/dashboard/DashboardCard.test.d.ts +1 -0
  31. package/dist/types/src/components/dashboard/EventLog.d.ts +6 -2
  32. package/dist/types/src/components/dashboard/EvidenceFootnote.d.ts +19 -0
  33. package/dist/types/src/components/dashboard/EvidenceFootnote.test.d.ts +1 -0
  34. package/dist/types/src/components/dashboard/StatCard.d.ts +5 -1
  35. package/dist/types/src/components/dashboard/StatCard.test.d.ts +1 -0
  36. package/dist/types/src/components/dashboard/Stepper.test.d.ts +1 -0
  37. package/dist/types/src/components/dashboard/index.d.ts +8 -1
  38. package/dist/types/src/components/dashboard/types.d.ts +22 -1
  39. package/dist/types/src/components/display/MediaPanel.d.ts +29 -0
  40. package/dist/types/src/components/display/MediaPanel.test.d.ts +1 -0
  41. package/dist/types/src/components/display/index.d.ts +2 -0
  42. package/dist/types/src/components/feedback/Alert.actions.test.d.ts +1 -0
  43. package/dist/types/src/components/feedback/Alert.d.ts +8 -1
  44. package/dist/types/src/components/feedback/index.d.ts +1 -1
  45. package/dist/types/src/components/inputs/Input.d.ts +7 -1
  46. package/dist/types/src/components/inputs/Input.test.d.ts +1 -0
  47. package/dist/types/src/components/inputs/index.d.ts +1 -0
  48. package/dist/types/src/components/interactive-inputs/AllocationBar.d.ts +25 -0
  49. package/dist/types/src/components/interactive-inputs/AllocationBar.test.d.ts +1 -0
  50. package/dist/types/src/components/interactive-inputs/CardRadioGroup.d.ts +5 -0
  51. package/dist/types/src/components/interactive-inputs/CardRadioGroup.test.d.ts +1 -0
  52. package/dist/types/src/components/interactive-inputs/SemanticSlider.d.ts +21 -0
  53. package/dist/types/src/components/interactive-inputs/SemanticSlider.test.d.ts +1 -0
  54. package/dist/types/src/components/interactive-inputs/TemporalScrub.d.ts +29 -0
  55. package/dist/types/src/components/interactive-inputs/TemporalScrub.test.d.ts +1 -0
  56. package/dist/types/src/components/interactive-inputs/index.d.ts +6 -0
  57. package/dist/types/src/components/layout/PageHeader.d.ts +7 -3
  58. package/dist/types/src/components/layout/PageHeader.test.d.ts +1 -0
  59. package/dist/types/src/components/layout/index.d.ts +1 -0
  60. package/dist/types/src/components/table/EditableDataGrid.cellTone.test.d.ts +1 -0
  61. package/dist/types/src/components/table/EditableDataGrid.d.ts +6 -0
  62. package/package.json +1 -1
@@ -1,9 +1,14 @@
1
- export type StepStatus = "completed" | "active" | "pending" | "error";
1
+ /** "warning" y "opportunity" son aditivos (ej. cadena de decisiones tipo Domino Flow: un paso puede
2
+ * señalar un riesgo detectado o una oportunidad, no solo "hecho/pendiente/error"). */
3
+ export type StepStatus = "completed" | "active" | "pending" | "error" | "warning" | "opportunity";
2
4
  export type StepItem = {
3
5
  key: string;
4
6
  label: string;
5
7
  sublabel?: string;
6
8
  status?: StepStatus;
9
+ /** Valor o consecuencia de este paso (ej. "EXCEL", "+20 usuarios") — se pinta junto al label cuando
10
+ * viene. Sin `value`, el render es idéntico al de antes. */
11
+ value?: string;
7
12
  };
8
13
  export type CalendarBadgeVariant = "primary" | "warning" | "danger" | "success" | "neutral";
9
14
  export type CalendarDayBadge = {
@@ -13,7 +18,20 @@ export type CalendarDayBadge = {
13
18
  };
14
19
  export type DonutGaugeSize = "xs" | "sm" | "md" | "lg";
15
20
  export type DashboardCardVariant = "default" | "accent" | "critical" | "warning" | "success";
21
+ /** Estado del contenido de `DashboardCard`. "idle" (por defecto) = renderiza `children` tal cual (acabado
22
+ * actual). Precedencia cuando se combinan por error del consumidor: "error" > "loading" > "empty" > "idle". */
23
+ export type DashboardCardState = "idle" | "loading" | "error" | "empty";
16
24
  export type EventStatus = "success" | "warning" | "danger" | "info" | "default";
25
+ /** Densidad de filas en `AlertSummaryList`/`AlertRowList`. "comfortable" (por defecto) = acabado
26
+ * actual. "compact" reduce el padding vertical de cada fila. */
27
+ export type AlertDensity = "comfortable" | "compact";
28
+ /** Acción explícita de una fila (ej. "Ver detalle", "Marcar leído"), renderizada como botón propio.
29
+ * Mutuamente excluyente con que la fila entera sea clickeable (`onClick` de la fila): con `actions`
30
+ * presente, la fila deja de ser un `role="button"` — evita anidar un botón dentro de otro botón. */
31
+ export type AlertActionItem = {
32
+ label: string;
33
+ onClick: () => void;
34
+ };
17
35
  export type EventItem = {
18
36
  id?: string;
19
37
  time: string;
@@ -38,6 +56,9 @@ export type AlertRowItem = {
38
56
  /** Icono de severidad antes de la placa. Solo se muestra con `AlertRowList columns`. */
39
57
  severity?: AlertRowSeverity;
40
58
  onClick?: () => void;
59
+ /** Ver `AlertActionItem`. Si viene con 1+ elementos, gana sobre `onClick` (la fila deja de ser
60
+ * clickeable como un todo y cada acción es su propio botón). */
61
+ actions?: AlertActionItem[];
41
62
  };
42
63
  /** Textos de la cabecera de `AlertRowList columns` (por defecto "Hora", "Vehículo", "Descripción"). */
43
64
  export type AlertRowListHeaderLabels = {
@@ -0,0 +1,29 @@
1
+ import { ImgHTMLAttributes, ReactNode } from 'react';
2
+ export type MediaPanelOverlay = "none" | "gradient-bottom" | "gradient-top" | "dark";
3
+ export type MediaPanelFit = "cover" | "contain";
4
+ export type MediaPanelProps = {
5
+ src: string;
6
+ alt: string;
7
+ /** Relación de aspecto CSS (`width/height`), ej. "16/9", "4/3", "1/1". Por defecto "16/9". */
8
+ aspectRatio?: string;
9
+ fit?: MediaPanelFit;
10
+ /** Capa de degradado/oscurecimiento sobre la imagen, para que texto superpuesto (vía `children`) se
11
+ * siga leyendo. "none" (por defecto) = sin capa. */
12
+ overlay?: MediaPanelOverlay;
13
+ /** Contenido superpuesto (ej. un título editorial) — se ancla abajo a la izquierda por defecto. */
14
+ children?: ReactNode;
15
+ rounded?: boolean;
16
+ loading?: "lazy" | "eager";
17
+ /** Se muestra si `src` falla al cargar (ej. un icono o texto). Sin `fallback`, un error de carga
18
+ * deja el panel vacío con el `overlay`/`children` igual encima, sin romper el layout. */
19
+ fallback?: ReactNode;
20
+ className?: string;
21
+ imgProps?: Omit<ImgHTMLAttributes<HTMLImageElement>, "src" | "alt" | "loading">;
22
+ };
23
+ /**
24
+ * Imagen/visual dominante a proporción fija, con degradado opcional y contenido superpuesto —
25
+ * para una intro o portada de estilo editorial (no existía nada equivalente en la librería: lo más
26
+ * cercano era `Avatar`, pensado para fotos de perfil pequeñas, y `Carousel`, que es un slideshow
27
+ * genérico sin tratamiento de imagen).
28
+ */
29
+ export declare const MediaPanel: ({ src, alt, aspectRatio, fit, overlay, children, rounded, loading, fallback, className, imgProps, }: MediaPanelProps) => import("react").JSX.Element;
@@ -14,3 +14,5 @@ export { Timeline } from './Timeline';
14
14
  export type { TimelineItem, TimelineProps, TimelineStatus } from './Timeline';
15
15
  export { Carousel } from './Carousel';
16
16
  export type { CarouselProps } from './Carousel';
17
+ export { MediaPanel } from './MediaPanel';
18
+ export type { MediaPanelFit, MediaPanelOverlay, MediaPanelProps } from './MediaPanel';
@@ -1,5 +1,9 @@
1
1
  import { ReactNode } from 'react';
2
2
  export type AlertVariant = "info" | "success" | "warning" | "danger";
3
+ export type AlertAction = {
4
+ label: string;
5
+ onClick: () => void;
6
+ };
3
7
  type AlertProps = {
4
8
  variant?: AlertVariant;
5
9
  title?: string;
@@ -7,7 +11,10 @@ type AlertProps = {
7
11
  dismissible?: boolean;
8
12
  onDismiss?: () => void;
9
13
  icon?: ReactNode;
14
+ /** Acciones explícitas (ej. "Confirmar de todos modos", "Revisar") — para alertas contextuales que
15
+ * ofrecen una salida además de informar. Sin `actions`, el render es idéntico al de antes. */
16
+ actions?: AlertAction[];
10
17
  className?: string;
11
18
  };
12
- export declare const Alert: ({ variant, title, message, dismissible, onDismiss, icon, className, }: AlertProps) => import("react").JSX.Element | null;
19
+ export declare const Alert: ({ variant, title, message, dismissible, onDismiss, icon, actions, className, }: AlertProps) => import("react").JSX.Element | null;
13
20
  export {};
@@ -1,5 +1,5 @@
1
1
  export { Alert } from './Alert';
2
- export type { AlertVariant } from './Alert';
2
+ export type { AlertVariant, AlertAction } from './Alert';
3
3
  export { EmptyState } from './EmptyState';
4
4
  export { ErrorView } from './ErrorView';
5
5
  export { LoadingView } from './LoadingView';
@@ -1,6 +1,7 @@
1
1
  import { default as React } from 'react';
2
2
  import { FieldValues, Path, UseFormReturn } from 'react-hook-form';
3
3
  import { IconType } from 'react-icons';
4
+ export type InputVariant = "default" | "editorial";
4
5
  type InputProps<TFormValues extends FieldValues> = {
5
6
  form: UseFormReturn<TFormValues>;
6
7
  name: Path<TFormValues>;
@@ -20,6 +21,11 @@ type InputProps<TFormValues extends FieldValues> = {
20
21
  small?: boolean;
21
22
  onIconClick?: () => Promise<void> | void;
22
23
  onRefresh?: () => Promise<void> | void;
24
+ /** "default" (por defecto) = acabado actual, con `fieldClassName` (compartido por los ~24 inputs
25
+ * de la librería, sin tocar). "editorial" = texto grande, sin caja, solo línea inferior — pensado
26
+ * para experiencias de una pregunta por pantalla. Usa clases propias de este componente; NO
27
+ * modifica `fieldClassName.ts` ni afecta a ningún otro input. */
28
+ variant?: InputVariant;
23
29
  };
24
- export declare const Input: <TFormValues extends FieldValues>({ form, name, label, info, required, type, className, placeholder, readOnly, min, max, step, onChange, icon, small, onIconClick, onRefresh, }: InputProps<TFormValues>) => React.JSX.Element;
30
+ export declare const Input: <TFormValues extends FieldValues>({ form, name, label, info, required, type, className, placeholder, readOnly, min, max, step, onChange, icon, small, onIconClick, onRefresh, variant, }: InputProps<TFormValues>) => React.JSX.Element;
25
31
  export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -3,6 +3,7 @@ export { fieldClassName } from './fieldClassName';
3
3
  export { inputFocusTransition } from './inputFocusTransition';
4
4
  export { textareaClassName } from './textareaClassName';
5
5
  export { Input } from './Input';
6
+ export type { InputVariant } from './Input';
6
7
  export { TextArea } from './TextArea';
7
8
  export { NumberInput } from './NumberInput';
8
9
  export { DateInput } from './DateInput';
@@ -0,0 +1,25 @@
1
+ import { MetricBarTone } from '../cards/types';
2
+ export type AllocationSegment = {
3
+ key: string;
4
+ label: string;
5
+ value: number;
6
+ tone?: MetricBarTone;
7
+ };
8
+ export type AllocationBarProps = {
9
+ /** Cada segmento con su valor absoluto (no %) — el ancho se calcula sobre la suma de todos. */
10
+ segments: AllocationSegment[];
11
+ /** Sin `onChange`, la barra es de solo lectura (sin manijas de arrastre) — para mostrar una
12
+ * distribución fija. Con `onChange`, arrastrar/teclado entre dos segmentos vecinos redistribuye
13
+ * SOLO ese par (la suma total no cambia), y se llama con el array completo actualizado. */
14
+ onChange?: (segments: AllocationSegment[]) => void;
15
+ unit?: string;
16
+ valueFormat?: (value: number) => string;
17
+ className?: string;
18
+ };
19
+ /**
20
+ * Barra proporcional editable para repartir un total entre categorías (presupuesto, horas, recursos):
21
+ * arrastrar el límite entre dos segmentos vecinos redistribuye solo ese par, la suma total se
22
+ * mantiene siempre — no hace falta re-normalizar nada. Cada manija es `role="slider"` con flechas de
23
+ * teclado como alternativa al drag (Shift+flecha = paso grande).
24
+ */
25
+ export declare const AllocationBar: ({ segments, onChange, unit, valueFormat, className }: AllocationBarProps) => import("react").JSX.Element;
@@ -1,7 +1,12 @@
1
+ import { ReactNode } from 'react';
1
2
  export type CardRadioOption = {
2
3
  value: string;
3
4
  label: string;
4
5
  detail?: string;
6
+ /** Icono opcional sobre el label (ej. un glifo de sf-ui o de `react-icons`). Sin `icon`, el render
7
+ * es idéntico al de antes. No reemplaza el marcador de selección (el `role="radiogroup"` lo necesita
8
+ * para que el estado seleccionado sea perceptible sin depender solo del color del borde). */
9
+ icon?: ReactNode;
5
10
  disabled?: boolean;
6
11
  };
7
12
  export type CardRadioGroupProps = {
@@ -0,0 +1,21 @@
1
+ export type SemanticSliderZone = {
2
+ value: string;
3
+ label: string;
4
+ };
5
+ export type SemanticSliderProps = {
6
+ value: string;
7
+ onChange: (value: string) => void;
8
+ /** Zonas ordenadas de menor a mayor (ej. "Deficiente" → "Excelente"). Mínimo 2. */
9
+ zones: SemanticSliderZone[];
10
+ label?: string;
11
+ disabled?: boolean;
12
+ className?: string;
13
+ };
14
+ /**
15
+ * Slider de escala semántica: en vez de un número, el usuario elige una zona nombrada
16
+ * (calidad, severidad, riesgo, avance...). Es un `<input type="range">` nativo con `step=1` sobre el
17
+ * índice de `zones` — teclado (flechas), mouse y touch funcionan gratis por ser nativo, igual que
18
+ * `SliderInput`. Distinto de `SliderInput`: ese es numérico con valor libre; este es una escala
19
+ * ordinal fija con significado en cada posición.
20
+ */
21
+ export declare const SemanticSlider: ({ value, onChange, zones, label, disabled, className }: SemanticSliderProps) => import("react").JSX.Element;
@@ -0,0 +1,29 @@
1
+ export type TemporalScrubMark = {
2
+ /** Mismo dominio que `min`/`max`/`value` (ej. timestamp en ms). */
3
+ value: number;
4
+ label: string;
5
+ };
6
+ export type TemporalScrubProps = {
7
+ /** Momento actual, en el mismo dominio que `min`/`max` (ej. `Date.now()` o un timestamp cualquiera). */
8
+ value: number;
9
+ onChange: (value: number) => void;
10
+ min: number;
11
+ max: number;
12
+ step?: number;
13
+ /** Puntos de referencia con etiqueta sobre la pista (ej. "Ayer", "Hoy", "Ahora"). */
14
+ marks?: TemporalScrubMark[];
15
+ /** Formatea el valor mostrado sobre el control. Por defecto, hora local (`toLocaleTimeString`). */
16
+ formatValue?: (value: number) => string;
17
+ label?: string;
18
+ disabled?: boolean;
19
+ className?: string;
20
+ };
21
+ /**
22
+ * Selector de un instante sobre una línea de tiempo continua (arrastre + teclado), con marcas de
23
+ * referencia con etiqueta. Distinto de `DatePicker` (que es un calendario para elegir una fecha, no
24
+ * un punto continuo en un rango) — pensado para "¿cuándo pasó esto?" dentro de un rango acotado
25
+ * (ej. las últimas 48 horas), no para elegir cualquier fecha del calendario.
26
+ * Es un `<input type="range">` nativo, igual que `SliderInput`/`SemanticSlider` — teclado y accesibilidad
27
+ * vienen gratis por ser nativo.
28
+ */
29
+ export declare const TemporalScrub: ({ value, onChange, min, max, step, marks, formatValue, label, disabled, className, }: TemporalScrubProps) => import("react").JSX.Element;
@@ -20,5 +20,11 @@ export { SortableList } from './SortableList';
20
20
  export type { SortableListItem, SortableListProps } from './SortableList';
21
21
  export { CardRadioGroup } from './CardRadioGroup';
22
22
  export type { CardRadioGroupProps, CardRadioOption } from './CardRadioGroup';
23
+ export { SemanticSlider } from './SemanticSlider';
24
+ export type { SemanticSliderProps, SemanticSliderZone } from './SemanticSlider';
25
+ export { AllocationBar } from './AllocationBar';
26
+ export type { AllocationBarProps, AllocationSegment } from './AllocationBar';
27
+ export { TemporalScrub } from './TemporalScrub';
28
+ export type { TemporalScrubMark, TemporalScrubProps } from './TemporalScrub';
23
29
  export { clampNumber, formatSliderLabel, emptyTags, normalizeTags, emptyKeyValuePairs, normalizeKeyValuePairs, emptyNumberRange, normalizeNumberRange, normalizeHexColor, progressPercent, } from './interactiveUtils';
24
30
  export type { NumberRange, KeyValuePair } from './types';
@@ -1,13 +1,17 @@
1
1
  import { ReactNode } from 'react';
2
- type PageHeaderProps = {
2
+ export type PageHeaderProps = {
3
3
  title: string;
4
4
  subtitle?: string;
5
5
  eyebrow?: string;
6
+ /** Indicador de paso (ej. "01/05"), separado de `eyebrow` — para un formulario multietapa donde el
7
+ * número de paso y la etiqueta de sección son dos datos distintos que no deben concatenarse a mano
8
+ * en un solo string. Se pinta como una píldora antes de `eyebrow`/`title`. Sin `stepLabel`, el render
9
+ * es idéntico al de antes. */
10
+ stepLabel?: string;
6
11
  icon?: ReactNode;
7
12
  actions?: ReactNode;
8
13
  className?: string;
9
14
  tone?: "default" | "app";
10
15
  sticky?: boolean;
11
16
  };
12
- export declare const PageHeader: ({ title, subtitle, eyebrow, icon, actions, className, tone, sticky, }: PageHeaderProps) => import("react").JSX.Element;
13
- export {};
17
+ export declare const PageHeader: ({ title, subtitle, eyebrow, stepLabel, icon, actions, className, tone, sticky, }: PageHeaderProps) => import("react").JSX.Element;
@@ -1,6 +1,7 @@
1
1
  export { PageContainer } from './PageContainer';
2
2
  export { TablePageLayout } from './TablePageLayout';
3
3
  export { PageHeader } from './PageHeader';
4
+ export type { PageHeaderProps } from './PageHeader';
4
5
  export { SidePanel } from './SidePanel';
5
6
  export { Tabs } from './Tabs';
6
7
  export type { TabItem } from './Tabs';
@@ -1,4 +1,5 @@
1
1
  import { ReactNode } from 'react';
2
+ import { MetricBarTone } from '../cards/types';
2
3
  export type EditableColumn<T extends Record<string, unknown>> = {
3
4
  id: keyof T & string;
4
5
  header: string;
@@ -12,6 +13,11 @@ export type EditableColumn<T extends Record<string, unknown>> = {
12
13
  width?: string;
13
14
  /** celda custom en modo form (p.ej. botón PDF); recibe la fila y su índice real */
14
15
  render?: (row: T, originalIndex: number) => ReactNode;
16
+ /** Colorea el fondo de la celda según su valor (ej. matriz de intensidad tipo heatmap: productividad,
17
+ * ocupación, calidad). Devuelve `undefined` para dejar la celda sin colorear. El color es solo un
18
+ * refuerzo visual — el valor y el `tooltip`/`title` siguen siendo texto, nunca depende únicamente
19
+ * del color (regla de accesibilidad del resto de la librería). */
20
+ cellTone?: (value: T[keyof T & string], row: T) => MetricBarTone | undefined;
15
21
  };
16
22
  export type EditableDataGridProps<T extends Record<string, unknown>> = {
17
23
  data: T[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sfperusacdev/sf-ui",
3
- "version": "0.1.34",
3
+ "version": "0.1.35",
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",