frontend-plus-react 0.2.0 → 0.4.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/dist/index.css CHANGED
@@ -1,3 +1,66 @@
1
+ /* src/components/grid/GenericDataGrid.css */
2
+ .fpr-celda {
3
+ position: absolute;
4
+ inset: 0;
5
+ display: flex;
6
+ align-items: center;
7
+ justify-content: flex-start;
8
+ padding: 0 12px 0 8px;
9
+ box-sizing: border-box;
10
+ font-size: 0.875rem;
11
+ white-space: nowrap;
12
+ overflow: hidden;
13
+ text-overflow: ellipsis;
14
+ transition: background-color 0.3s ease-in-out;
15
+ }
16
+ .fpr-celda-numerica {
17
+ justify-content: flex-end;
18
+ }
19
+ .fpr-fila-saliendo {
20
+ opacity: 0;
21
+ transition: opacity 0.4s ease-in-out;
22
+ }
23
+ .fpr-fila-detalle > .rdg-cell {
24
+ padding: 0;
25
+ background-color: #f3f6fa;
26
+ white-space: normal;
27
+ overflow: visible;
28
+ }
29
+ .fpr-detalle {
30
+ position: sticky;
31
+ left: 0;
32
+ height: 100%;
33
+ width: max-content;
34
+ box-sizing: border-box;
35
+ padding: 4px 8px 4px 28px;
36
+ border-left: 3px solid #90caf9;
37
+ background-color: #f3f6fa;
38
+ }
39
+ .fpr-detalle-wscreen {
40
+ height: 100%;
41
+ overflow: auto;
42
+ }
43
+ .fpr-detalle-wscreen-contenido {
44
+ width: max-content;
45
+ max-width: 100%;
46
+ }
47
+ .fpr-boton-detalle {
48
+ display: flex;
49
+ justify-content: center;
50
+ align-items: center;
51
+ height: 100%;
52
+ }
53
+ .typed-controls-signal-no-data {
54
+ background-color: #fff9c4 !important;
55
+ color: #fbc02d;
56
+ font-weight: bold;
57
+ }
58
+ .typed-controls-signal-unknown-data {
59
+ background-color: #ffe0b2 !important;
60
+ color: #fb8c00;
61
+ font-weight: bold;
62
+ }
63
+
1
64
  /* src/App.css */
2
65
  html,
3
66
  body,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/App.css"],"sourcesContent":["/* src/app.css */\n\n/* Estilos globales para asegurar que la aplicación ocupe el 100% del viewport\n y que el scroll sea gestionado por componentes específicos, no por la página. */\n html, body, #root {\n height: 100%; /* Ocupa el 100% del alto del viewport */\n margin: 0; /* Elimina márgenes por defecto del navegador */\n padding: 0; /* Elimina rellenos por defecto del navegador */\n overflow: hidden; /* ¡CLAVE! Previene scrolls a nivel de la página (horizontal y vertical) */\n box-sizing: border-box; /* Asegura que padding y border se incluyan en el width/height */\n}\n\nbody {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n/* El contenedor principal de tu aplicación (si es <App />) */\n.App {\n height: 100%; /* Asegura que la App ocupe el 100% del alto de #root */\n display: flex; /* Utiliza flexbox para su contenido (MainLayout) */\n flex-direction: column; /* Los hijos se apilan verticalmente */\n}\n\n/* Otros estilos que tenías en tu archivo */\n.App-logo {\n height: 40vmin;\n pointer-events: none;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n .App-logo {\n animation: App-logo-spin infinite 20s linear;\n }\n}\n\n.App-header {\n background-color: #282c34;\n min-height: 100vh; /* Revisa si esto es necesario o si causa desbordamiento */\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n font-size: calc(10px + 2vmin);\n color: white;\n}\n\n.App-link {\n color: #61dafb;\n}\n\n@keyframes App-logo-spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',\n monospace;\n}\n\n/* Estilos base para las filas de react-data-grid */\n.rdg-row {\n /* La transición debe ser lo suficientemente larga para ser visible */\n transition: opacity 0.5s ease-out, transform 0.5s ease-out;\n}\n\n/* Esta clase se aplica a la fila cuando se va a borrar */\n.deleting-row {\n opacity: 0; /* Hace que la fila se desvanezca */\n transform: translateX(-100%); /* Mueve la fila hacia la izquierda */\n pointer-events: none; /* Evita interacciones mientras se desvanece */\n}"],"mappings":";AAIE;AAAM;AAAM,CAAC;AACb,UAAQ;AACR,UAAQ;AACR,WAAS;AACT,YAAU;AACV,cAAY;AACd;AAEA;AACE;AAAA,IAAa,aAAa;AAAA,IAAE,kBAAkB;AAAA,IAAE,UAAU;AAAA,IAAE,QAAQ;AAAA,IAAE,QAAQ;AAAA,IAC5E,QAAQ;AAAA,IAAE,WAAW;AAAA,IAAE,WAAW;AAAA,IAAE,YAAY;AAAA,IAAE,gBAAgB;AAAA,IAClE;AACF,0BAAwB;AACxB,2BAAyB;AAC3B;AAGA,CAAC;AACC,UAAQ;AACR,WAAS;AACT,kBAAgB;AAClB;AAGA,CAAC;AACC,UAAQ;AACR,kBAAgB;AAClB;AAEA,OAAO,CAAC,sBAAsB,EAAE;AAC9B,GAND;AAOG,eAAW,cAAc,SAAS,IAAI;AACxC;AACF;AAEA,CAAC;AACC,oBAAkB;AAClB,cAAY;AACZ,WAAS;AACT,kBAAgB;AAChB,eAAa;AACb,mBAAiB;AACjB,aAAW,KAAK,KAAK,EAAE;AACvB,SAAO;AACT;AAEA,CAAC;AACC,SAAO;AACT;AAEA,WAnBe;AAoBb;AACE,eAAW,OAAO;AACpB;AACA;AACE,eAAW,OAAO;AACpB;AACF;AAEA;AACE;AAAA,IAAa,eAAe;AAAA,IAAE,KAAK;AAAA,IAAE,MAAM;AAAA,IAAE,QAAQ;AAAA,IAAE,aAAa;AAAA,IAClE;AACJ;AAGA,CAAC;AAEC,cAAY,QAAQ,KAAK,QAAQ,EAAE,UAAU,KAAK;AACpD;AAGA,CAAC;AACC,WAAS;AACT,aAAW,WAAW;AACtB,kBAAgB;AAClB;","names":[]}
1
+ {"version":3,"sources":["../src/components/grid/GenericDataGrid.css","../src/App.css"],"sourcesContent":["/* src/components/grid/GenericDataGrid.css */\n\n/* Celda de datos (reemplaza al Box + Typography de MUI que se armaba en cada celda) */\n.fpr-celda {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: flex-start;\n padding: 0 12px 0 8px;\n box-sizing: border-box;\n font-size: 0.875rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n transition: background-color 0.3s ease-in-out;\n}\n\n.fpr-celda-numerica {\n justify-content: flex-end;\n}\n\n/* Fila que se está borrando (antes era un style copiado en cada fila) */\n.fpr-fila-saliendo {\n opacity: 0;\n transition: opacity 0.4s ease-in-out;\n}\n\n/* Fila de detalle desplegada: una sola celda (colSpan) con la grilla anidada adentro */\n.fpr-fila-detalle > .rdg-cell {\n padding: 0;\n background-color: #f3f6fa;\n white-space: normal;\n /* react-data-grid recorta las celdas (overflow: clip, en un @layer): la grilla anidada\n puede ser más ancha que las columnas del padre, como el td de detalle de backend-plus */\n overflow: visible;\n}\n\n.fpr-detalle {\n position: sticky; /* queda a la vista aunque la grilla de arriba esté scrolleada a la derecha */\n left: 0;\n height: 100%;\n /* el ancho lo da el contenido (las columnas de la grilla anidada), hasta lo visible */\n width: max-content;\n box-sizing: border-box;\n padding: 4px 8px 4px 28px; /* sangría: se ve de qué fila es el detalle */\n border-left: 3px solid #90caf9;\n background-color: #f3f6fa;\n}\n\n/* Detalle que es una pantalla propia (wScreen): el contenido se mide y la fila toma su alto */\n.fpr-detalle-wscreen {\n height: 100%;\n overflow: auto;\n}\n\n.fpr-detalle-wscreen-contenido {\n width: max-content;\n max-width: 100%;\n}\n\n.fpr-boton-detalle {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n}\n\n/* Estas clases estaban solo en index.css, que la librería no importa */\n.typed-controls-signal-no-data {\n background-color: #fff9c4 !important;\n color: #fbc02d;\n font-weight: bold;\n}\n\n.typed-controls-signal-unknown-data {\n background-color: #ffe0b2 !important;\n color: #fb8c00;\n font-weight: bold;\n}\n","/* src/app.css */\n\n/* Estilos globales para asegurar que la aplicación ocupe el 100% del viewport\n y que el scroll sea gestionado por componentes específicos, no por la página. */\n html, body, #root {\n height: 100%; /* Ocupa el 100% del alto del viewport */\n margin: 0; /* Elimina márgenes por defecto del navegador */\n padding: 0; /* Elimina rellenos por defecto del navegador */\n overflow: hidden; /* ¡CLAVE! Previene scrolls a nivel de la página (horizontal y vertical) */\n box-sizing: border-box; /* Asegura que padding y border se incluyan en el width/height */\n}\n\nbody {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n/* El contenedor principal de tu aplicación (si es <App />) */\n.App {\n height: 100%; /* Asegura que la App ocupe el 100% del alto de #root */\n display: flex; /* Utiliza flexbox para su contenido (MainLayout) */\n flex-direction: column; /* Los hijos se apilan verticalmente */\n}\n\n/* Otros estilos que tenías en tu archivo */\n.App-logo {\n height: 40vmin;\n pointer-events: none;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n .App-logo {\n animation: App-logo-spin infinite 20s linear;\n }\n}\n\n.App-header {\n background-color: #282c34;\n min-height: 100vh; /* Revisa si esto es necesario o si causa desbordamiento */\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n font-size: calc(10px + 2vmin);\n color: white;\n}\n\n.App-link {\n color: #61dafb;\n}\n\n@keyframes App-logo-spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',\n monospace;\n}\n\n/* Estilos base para las filas de react-data-grid */\n.rdg-row {\n /* La transición debe ser lo suficientemente larga para ser visible */\n transition: opacity 0.5s ease-out, transform 0.5s ease-out;\n}\n\n/* Esta clase se aplica a la fila cuando se va a borrar */\n.deleting-row {\n opacity: 0; /* Hace que la fila se desvanezca */\n transform: translateX(-100%); /* Mueve la fila hacia la izquierda */\n pointer-events: none; /* Evita interacciones mientras se desvanece */\n}"],"mappings":";AAGA,CAAC;AACC,YAAU;AACV,SAAO;AACP,WAAS;AACT,eAAa;AACb,mBAAiB;AACjB,WAAS,EAAE,KAAK,EAAE;AAClB,cAAY;AACZ,aAAW;AACX,eAAa;AACb,YAAU;AACV,iBAAe;AACf,cAAY,iBAAiB,KAAK;AACpC;AAEA,CAAC;AACC,mBAAiB;AACnB;AAGA,CAAC;AACC,WAAS;AACT,cAAY,QAAQ,KAAK;AAC3B;AAGA,CAAC,iBAAiB,EAAE,CAAC;AACnB,WAAS;AACT,oBAAkB;AAClB,eAAa;AAGb,YAAU;AACZ;AAEA,CAAC;AACC,YAAU;AACV,QAAM;AACN,UAAQ;AAER,SAAO;AACP,cAAY;AACZ,WAAS,IAAI,IAAI,IAAI;AACrB,eAAa,IAAI,MAAM;AACvB,oBAAkB;AACpB;AAGA,CAAC;AACC,UAAQ;AACR,YAAU;AACZ;AAEA,CAAC;AACC,SAAO;AACP,aAAW;AACb;AAEA,CAAC;AACC,WAAS;AACT,mBAAiB;AACjB,eAAa;AACb,UAAQ;AACV;AAGA,CAAC;AACC,oBAAkB;AAClB,SAAO;AACP,eAAa;AACf;AAEA,CAAC;AACC,oBAAkB;AAClB,SAAO;AACP,eAAa;AACf;;;AC3EE;AAAM;AAAM,CAAC;AACb,UAAQ;AACR,UAAQ;AACR,WAAS;AACT,YAAU;AACV,cAAY;AACd;AAEA;AACE;AAAA,IAAa,aAAa;AAAA,IAAE,kBAAkB;AAAA,IAAE,UAAU;AAAA,IAAE,QAAQ;AAAA,IAAE,QAAQ;AAAA,IAC5E,QAAQ;AAAA,IAAE,WAAW;AAAA,IAAE,WAAW;AAAA,IAAE,YAAY;AAAA,IAAE,gBAAgB;AAAA,IAClE;AACF,0BAAwB;AACxB,2BAAyB;AAC3B;AAGA,CAAC;AACC,UAAQ;AACR,WAAS;AACT,kBAAgB;AAClB;AAGA,CAAC;AACC,UAAQ;AACR,kBAAgB;AAClB;AAEA,OAAO,CAAC,sBAAsB,EAAE;AAC9B,GAND;AAOG,eAAW,cAAc,SAAS,IAAI;AACxC;AACF;AAEA,CAAC;AACC,oBAAkB;AAClB,cAAY;AACZ,WAAS;AACT,kBAAgB;AAChB,eAAa;AACb,mBAAiB;AACjB,aAAW,KAAK,KAAK,EAAE;AACvB,SAAO;AACT;AAEA,CAAC;AACC,SAAO;AACT;AAEA,WAnBe;AAoBb;AACE,eAAW,OAAO;AACpB;AACA;AACE,eAAW,OAAO;AACpB;AACF;AAEA;AACE;AAAA,IAAa,eAAe;AAAA,IAAE,KAAK;AAAA,IAAE,MAAM;AAAA,IAAE,QAAQ;AAAA,IAAE,aAAa;AAAA,IAClE;AACJ;AAGA,CAAC;AAEC,cAAY,QAAQ,KAAK,QAAQ,EAAE,UAAU,KAAK;AACpD;AAGA,CAAC;AACC,WAAS;AACT,aAAW,WAAW;AACtB,kBAAgB;AAClB;","names":[]}
package/dist/index.d.cts CHANGED
@@ -11,16 +11,34 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
11
11
  */
12
12
  declare const extractPathsFromRoutes: (node: ReactNode) => string[];
13
13
 
14
+ /**
15
+ * Marca con la que una fila nueva conserva su identidad en la grilla mientras se edita (antes de tener su clave primaria).
16
+ * Es un Symbol: no aparece en Object.keys ni en JSON.stringify, pero el spread de la fila (...fila) la conserva.
17
+ */
18
+ declare const DOM_TEMP_ID: unique symbol;
19
+ /** Una fila de datos de una tabla: los valores salen del servidor ya decodificados (JSON4all) */
20
+ type FilaDeDatos = Record<string, unknown> & {
21
+ [DOM_TEMP_ID]?: string;
22
+ };
14
23
  interface WScreenProps {
15
24
  screenName: string;
25
+ /**
26
+ * Parámetros de la pantalla como objeto plano (en backend-plus, el primer argumento de `mainAction`).
27
+ * Como detalle de una grilla son los fixedFields del detalle; en una ruta, la query string.
28
+ */
29
+ params?: Record<string, unknown>;
16
30
  }
17
- interface ClientSideProps extends RenderCellProps<any, any> {
31
+ /** Decide, para una fila, si se muestra el botón de un detalle (`condition` del DetailTable) */
32
+ type CondicionDeDetalle = (fila: FilaDeDatos) => boolean;
33
+ type CondicionesMap = Record<string, CondicionDeDetalle>;
34
+ interface ClientSideProps extends RenderCellProps<FilaDeDatos, FilaDeResumen> {
18
35
  fieldDefinition: FieldDefinition;
19
36
  tableDefinition: TableDefinition;
20
37
  primaryKey: string[];
21
38
  }
22
39
  interface ResultOkProps {
23
- data: any;
40
+ /** Lo que devolvió el procedimiento (la forma depende de cada procedimiento) */
41
+ data: unknown;
24
42
  }
25
43
  type WScreenMap = Record<string, FC<WScreenProps>>;
26
44
  type ClientSidesMap = Record<string, FC<ClientSideProps>>;
@@ -32,6 +50,8 @@ interface AppProps {
32
50
  myWScreens?: WScreenMap;
33
51
  myClientSides?: ClientSidesMap;
34
52
  myResultsOk?: ResultsOksMap;
53
+ /** Condiciones de los detalles, por nombre (la `condition` de un DetailTable) */
54
+ myConditions?: CondicionesMap;
35
55
  }
36
56
 
37
57
  interface CellFeedback {
@@ -41,24 +61,24 @@ interface CellFeedback {
41
61
  message?: string;
42
62
  }
43
63
  type CellFeedbackMap = Map<string, CellFeedback>;
44
- interface InputRendererProps<R extends Record<string, any>, S> {
64
+ /** Lo que el manejo de teclas del editor necesita saber de la tecla (sirve para un evento real o uno encolado) */
65
+ type TeclaDeEditor = Pick<React.KeyboardEvent, 'key' | 'target'>;
66
+ interface InputRendererProps<R extends FilaDeDatos, S> {
45
67
  column: Column<R, S>;
46
68
  row: R;
47
69
  rowIdx: number;
48
70
  onRowChange: (row: R, commitChanges?: boolean) => void;
49
71
  onClose: (commitChanges?: boolean, shouldFocusCell?: boolean) => void;
50
72
  tableDefinition: TableDefinition;
51
- cellFeedbackMap: CellFeedbackMap;
52
- setCellFeedbackMap: React.Dispatch<React.SetStateAction<CellFeedbackMap>>;
53
- onKeyPress?: (rowIndex: number, columnKey: string, event: React.KeyboardEvent, handleCommit: (currentValue: any, closeEditor: boolean, focusNextCell: boolean) => Promise<void>) => void;
54
- setTableData: React.Dispatch<React.SetStateAction<any[]>>;
55
- setLocalCellChanges: React.Dispatch<React.SetStateAction<Map<string, Set<string>>>>;
56
- localCellChanges: Map<string, Set<string>>;
73
+ onKeyPress?: (rowIndex: number, columnKey: string, event: TeclaDeEditor, handleCommit: (currentValue: unknown, closeEditor: boolean, focusNextCell: boolean) => Promise<void>) => void;
74
+ /** El editor se registra para recibir las teclas (F4) que caen en la celda antes de que él tenga el foco; devuelve la baja */
75
+ registrarEditor?: (procesar: (key: string) => void) => () => void;
76
+ setTableData: React.Dispatch<React.SetStateAction<FilaDeDatos[]>>;
57
77
  primaryKey: string[];
58
78
  }
59
79
  interface Ancestor {
60
80
  tableName: string;
61
- row: any;
81
+ row: FilaDeDatos;
62
82
  tableDefinition: TableDefinition;
63
83
  fixedFields?: FixedField[];
64
84
  }
@@ -71,8 +91,8 @@ interface GenericDataGridProps$1 {
71
91
  }
72
92
  interface FixedField {
73
93
  fieldName: string;
74
- value: any;
75
- until?: any;
94
+ value: unknown;
95
+ until?: unknown;
76
96
  }
77
97
  interface ConfirmDialogProps {
78
98
  open: boolean;
@@ -80,13 +100,37 @@ interface ConfirmDialogProps {
80
100
  title: string;
81
101
  message: string;
82
102
  }
83
- interface FilterRendererProps<R extends Record<string, any>, S> {
103
+ /** El filtro de una columna: un texto (busca que contenga) o un operador con su valor */
104
+ type FiltroDeColumna = string | {
105
+ operator?: string;
106
+ value?: string;
107
+ };
108
+ type FiltrosDeGrilla = Record<string, FiltroDeColumna>;
109
+ /** Una entrada de la fila de totales: qué se calculó (suma, promedio...) y su valor */
110
+ type EntradaDeTotal = {
111
+ type: string;
112
+ value: number;
113
+ };
114
+ /** Fila de resumen con los filtros de cada columna (arriba de la grilla, cuando se muestran los filtros) */
115
+ type FilaDeFiltros = {
116
+ id: 'filterRow';
117
+ filters: FiltrosDeGrilla;
118
+ setFilters: React.Dispatch<React.SetStateAction<FiltrosDeGrilla>>;
119
+ };
120
+ /** Fila de resumen con los totales de las columnas que los tienen (pie de la grilla) */
121
+ type FilaDeTotales = {
122
+ id: 'bottomSummaryRow';
123
+ totales: Record<string, EntradaDeTotal>;
124
+ };
125
+ /** Las filas de resumen de react-data-grid: filtros o totales */
126
+ type FilaDeResumen = FilaDeFiltros | FilaDeTotales;
127
+ interface FilterRendererProps<R extends FilaDeDatos, S> {
84
128
  column: Column<R, S>;
85
- filters: Record<string, any>;
86
- setFilters: React.Dispatch<React.SetStateAction<Record<string, any>>>;
129
+ filters: FiltrosDeGrilla;
130
+ setFilters: React.Dispatch<React.SetStateAction<FiltrosDeGrilla>>;
87
131
  }
88
132
  interface GenericDataGridForDetailProps {
89
- parentRow: any;
133
+ parentRow: FilaDeDatos;
90
134
  detailTableDefinition: DetailTable;
91
135
  parentTableName: string;
92
136
  }
@@ -98,7 +142,7 @@ interface MenuListItemProps {
98
142
  level: number;
99
143
  onMenuItemClick?: () => void;
100
144
  }
101
- interface CustomCellProps<R extends Record<string, any>, SR> extends RenderCellProps<R, SR> {
145
+ interface CustomCellProps<R extends FilaDeDatos, SR> extends RenderCellProps<R, SR> {
102
146
  column: RenderCellProps<R, SR>['column'] & {
103
147
  isPK?: boolean;
104
148
  };
@@ -142,9 +186,12 @@ interface SnackbarProviderProps {
142
186
  interface CallApiOptions {
143
187
  isCritical?: boolean;
144
188
  }
145
- interface UseApiCallResult<T> {
146
- callApi: (procedureName: string, params: Record<string, any>, opts?: CallApiOptions) => Promise<T | undefined>;
147
- callApiUpload: (procedureName: string, file: File, params: Record<string, any>, opts?: CallApiOptions) => Promise<T | undefined>;
189
+ /** Los parámetros de un procedimiento por nombre; se codifican según la definición que manda el servidor */
190
+ type ParametrosDeProcedimiento = Record<string, unknown>;
191
+ interface UseApiCallResult {
192
+ /** R es la forma del resultado de ese procedimiento (el servidor puede no devolver nada: undefined) */
193
+ callApi: <R = unknown>(procedureName: string, params: ParametrosDeProcedimiento, opts?: CallApiOptions) => Promise<R | undefined>;
194
+ callApiUpload: <R = unknown>(procedureName: string, file: File, params: ParametrosDeProcedimiento, opts?: CallApiOptions) => Promise<R | undefined>;
148
195
  loading: boolean;
149
196
  error: Error | null;
150
197
  }
@@ -172,6 +219,8 @@ interface RouterState {
172
219
  redirectPath: string | null;
173
220
  }
174
221
  interface FetchApiOptions extends RequestInit {
222
+ /** No poner Content-Type: lo arma el navegador (por ejemplo con un FormData, que lleva su boundary) */
223
+ sinContentType?: boolean;
175
224
  }
176
225
  interface DataGridOption {
177
226
  id: string;
@@ -184,15 +233,37 @@ interface DataGridOption {
184
233
  interface GenericDataGridProps {
185
234
  tableName: string;
186
235
  fixedFields?: FixedField[];
236
+ /** Abre un detalle en otra pestaña (Ctrl+click o botón del medio en el botón del detalle) */
187
237
  onOpenDetail?: (tableName: string, fixedFields: FixedField[], label: string, ancestors: Ancestor[]) => void;
188
238
  gridStyles?: React$1.CSSProperties;
189
239
  ancestors?: Ancestor[];
240
+ /** Es una grilla de detalle desplegada dentro de otra grilla */
241
+ anidada?: boolean;
242
+ /** Título a mostrar en lugar del de la tabla (por omisión, el título o nombre de la tabla, igual que la grilla principal) */
243
+ titulo?: string;
244
+ /** Clave en el CacheGrillasContext para volver a mostrarse sin recargar */
245
+ claveCache?: string;
246
+ /** La grilla anidada informa el alto que necesita para mostrarse completa */
247
+ onAltoDeseado?: (alto: number) => void;
248
+ /** La grilla anidada informa el ancho que necesita (sus columnas y los detalles que tenga abiertos) */
249
+ onAnchoDeseado?: (ancho: number) => void;
250
+ /** Alto máximo que puede tener esta grilla anidada (lo calcula la de arriba a partir de la ventana) */
251
+ altoDisponible?: number;
252
+ /**
253
+ * Tope opcional para el alto de las filas de detalle (en px). Sin tope (lo normal) cada detalle
254
+ * se muestra completo, como en backend-plus; con tope, el detalle más alto scrollea por dentro
255
+ * y virtualiza sus propias filas (útil si hay detalles de miles de filas).
256
+ */
257
+ altoMaximoDetalle?: number;
190
258
  }
259
+
191
260
  declare const GenericDataGrid: React$1.FC<GenericDataGridProps>;
192
261
 
193
262
  declare let clientSides: Record<string, React$1.FC<ClientSideProps>>;
194
263
  declare const extendClientSides: (newRenderers: Record<string, React$1.FC<ClientSideProps>>) => void;
195
264
 
265
+ declare const extendConditions: (nuevas: CondicionesMap) => void;
266
+
196
267
  declare const ConfirmDialog: React.FC<ConfirmDialogProps>;
197
268
 
198
269
  interface RoutesProps {
@@ -206,7 +277,10 @@ declare const FrontendPlusProviders: React$1.FC<{
206
277
  children: React$1.ReactNode;
207
278
  publicPaths: string[];
208
279
  }>;
209
- declare const App: ({ myRoutes, myUnloggedRoutes, unloggedLayout, myWScreens, myClientSides, myResultsOk }: AppProps) => react_jsx_runtime.JSX.Element;
280
+ declare const App: ({ myRoutes, myUnloggedRoutes, unloggedLayout, myWScreens, myClientSides, myResultsOk, myConditions, puenteBP }: AppProps & {
281
+ /** Las wScreens que la aplicación no registró se piden al cliente de backend-plus (mismo origen que el backend) */
282
+ puenteBP?: boolean;
283
+ }) => react_jsx_runtime.JSX.Element;
210
284
 
211
285
  declare const MainLayout: React$1.FC;
212
286
 
@@ -224,8 +298,8 @@ declare const useApp: () => {
224
298
  };
225
299
  declare const AppProvider: React$1.FC<AppProviderProps>;
226
300
 
227
- declare const useApiCall: <T = any>() => UseApiCallResult<T>;
228
- declare const useApiCallWithoutSnackbar: <T = any>() => UseApiCallResult<T>;
301
+ declare const useApiCall: () => UseApiCallResult;
302
+ declare const useApiCallWithoutSnackbar: () => UseApiCallResult;
229
303
 
230
304
  /**
231
305
  * Hook personalizado para manejar la lógica de cierre de sesión.
@@ -245,4 +319,13 @@ type ResultComponentMap = {
245
319
  declare let resultsOk: ResultComponentMap;
246
320
  declare const extendResultsOk: (newComponents: ResultComponentMap) => void;
247
321
 
248
- export { type Ancestor, App, type AppContextType, type AppProps, AppProvider, type AppProviderProps, type CallApiOptions, type CellFeedback, type CellFeedbackMap, type ClientContextState, type ClientSideProps, type ClientSidesMap, ConfirmDialog, type ConfirmDialogProps, type CustomCellProps, type CustomHeaderCellProps, type DataGridOption, type FetchApiOptions, type FilterRendererProps, type FixedField, FrontendPlusProviders, FrontendPlusReactRoutes, GenericDataGrid, type GenericDataGridForDetailProps, type GenericDataGridProps$1 as GenericDataGridProps, type InputRendererProps, MainLayout, type MenuListItemProps, type MenuUiState, type PrivateRouteProps, type ResultOkProps, type ResultsOksMap, type RouterState, type SideMenuProps, type SnackbarContextType, SnackbarProvider, type SnackbarProviderProps, type SnackbarState, type UseApiCallResult, type WScreenMap, type WScreenProps, cambiarGuionesBajosPorEspacios, clientSides, extendClientSides, extendResultsOk, extendWScreens, extractPathsFromRoutes, resultsOk as resultComponents, useApiCall, useApiCallWithoutSnackbar, useApp, useLogout, useSnackbar, wScreens };
322
+ interface GrillaBPProps {
323
+ tabla: string;
324
+ fixedFields?: FixedField[];
325
+ }
326
+ /** La grilla de backend-plus (`myOwn.tableGrid`) dentro de una página React: conserva todo lo que ya hace esa grilla */
327
+ declare const GrillaBP: React$1.FC<GrillaBPProps>;
328
+
329
+ declare const habilitarPuenteBP: () => void;
330
+
331
+ export { type Ancestor, App, type AppContextType, type AppProps, AppProvider, type AppProviderProps, type CallApiOptions, type CellFeedback, type CellFeedbackMap, type ClientContextState, type ClientSideProps, type ClientSidesMap, type CondicionDeDetalle, type CondicionesMap, ConfirmDialog, type ConfirmDialogProps, type CustomCellProps, type CustomHeaderCellProps, DOM_TEMP_ID, type DataGridOption, type EntradaDeTotal, type FetchApiOptions, type FilaDeDatos, type FilaDeFiltros, type FilaDeResumen, type FilaDeTotales, type FilterRendererProps, type FiltroDeColumna, type FiltrosDeGrilla, type FixedField, FrontendPlusProviders, FrontendPlusReactRoutes, GenericDataGrid, type GenericDataGridForDetailProps, type GenericDataGridProps$1 as GenericDataGridProps, GrillaBP, type InputRendererProps, MainLayout, type MenuListItemProps, type MenuUiState, type ParametrosDeProcedimiento, type PrivateRouteProps, type ResultOkProps, type ResultsOksMap, type RouterState, type SideMenuProps, type SnackbarContextType, SnackbarProvider, type SnackbarProviderProps, type SnackbarState, type TeclaDeEditor, type UseApiCallResult, type WScreenMap, type WScreenProps, cambiarGuionesBajosPorEspacios, clientSides, extendClientSides, extendConditions, extendResultsOk, extendWScreens, extractPathsFromRoutes, habilitarPuenteBP, resultsOk as resultComponents, useApiCall, useApiCallWithoutSnackbar, useApp, useLogout, useSnackbar, wScreens };
package/dist/index.d.ts CHANGED
@@ -11,16 +11,34 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
11
11
  */
12
12
  declare const extractPathsFromRoutes: (node: ReactNode) => string[];
13
13
 
14
+ /**
15
+ * Marca con la que una fila nueva conserva su identidad en la grilla mientras se edita (antes de tener su clave primaria).
16
+ * Es un Symbol: no aparece en Object.keys ni en JSON.stringify, pero el spread de la fila (...fila) la conserva.
17
+ */
18
+ declare const DOM_TEMP_ID: unique symbol;
19
+ /** Una fila de datos de una tabla: los valores salen del servidor ya decodificados (JSON4all) */
20
+ type FilaDeDatos = Record<string, unknown> & {
21
+ [DOM_TEMP_ID]?: string;
22
+ };
14
23
  interface WScreenProps {
15
24
  screenName: string;
25
+ /**
26
+ * Parámetros de la pantalla como objeto plano (en backend-plus, el primer argumento de `mainAction`).
27
+ * Como detalle de una grilla son los fixedFields del detalle; en una ruta, la query string.
28
+ */
29
+ params?: Record<string, unknown>;
16
30
  }
17
- interface ClientSideProps extends RenderCellProps<any, any> {
31
+ /** Decide, para una fila, si se muestra el botón de un detalle (`condition` del DetailTable) */
32
+ type CondicionDeDetalle = (fila: FilaDeDatos) => boolean;
33
+ type CondicionesMap = Record<string, CondicionDeDetalle>;
34
+ interface ClientSideProps extends RenderCellProps<FilaDeDatos, FilaDeResumen> {
18
35
  fieldDefinition: FieldDefinition;
19
36
  tableDefinition: TableDefinition;
20
37
  primaryKey: string[];
21
38
  }
22
39
  interface ResultOkProps {
23
- data: any;
40
+ /** Lo que devolvió el procedimiento (la forma depende de cada procedimiento) */
41
+ data: unknown;
24
42
  }
25
43
  type WScreenMap = Record<string, FC<WScreenProps>>;
26
44
  type ClientSidesMap = Record<string, FC<ClientSideProps>>;
@@ -32,6 +50,8 @@ interface AppProps {
32
50
  myWScreens?: WScreenMap;
33
51
  myClientSides?: ClientSidesMap;
34
52
  myResultsOk?: ResultsOksMap;
53
+ /** Condiciones de los detalles, por nombre (la `condition` de un DetailTable) */
54
+ myConditions?: CondicionesMap;
35
55
  }
36
56
 
37
57
  interface CellFeedback {
@@ -41,24 +61,24 @@ interface CellFeedback {
41
61
  message?: string;
42
62
  }
43
63
  type CellFeedbackMap = Map<string, CellFeedback>;
44
- interface InputRendererProps<R extends Record<string, any>, S> {
64
+ /** Lo que el manejo de teclas del editor necesita saber de la tecla (sirve para un evento real o uno encolado) */
65
+ type TeclaDeEditor = Pick<React.KeyboardEvent, 'key' | 'target'>;
66
+ interface InputRendererProps<R extends FilaDeDatos, S> {
45
67
  column: Column<R, S>;
46
68
  row: R;
47
69
  rowIdx: number;
48
70
  onRowChange: (row: R, commitChanges?: boolean) => void;
49
71
  onClose: (commitChanges?: boolean, shouldFocusCell?: boolean) => void;
50
72
  tableDefinition: TableDefinition;
51
- cellFeedbackMap: CellFeedbackMap;
52
- setCellFeedbackMap: React.Dispatch<React.SetStateAction<CellFeedbackMap>>;
53
- onKeyPress?: (rowIndex: number, columnKey: string, event: React.KeyboardEvent, handleCommit: (currentValue: any, closeEditor: boolean, focusNextCell: boolean) => Promise<void>) => void;
54
- setTableData: React.Dispatch<React.SetStateAction<any[]>>;
55
- setLocalCellChanges: React.Dispatch<React.SetStateAction<Map<string, Set<string>>>>;
56
- localCellChanges: Map<string, Set<string>>;
73
+ onKeyPress?: (rowIndex: number, columnKey: string, event: TeclaDeEditor, handleCommit: (currentValue: unknown, closeEditor: boolean, focusNextCell: boolean) => Promise<void>) => void;
74
+ /** El editor se registra para recibir las teclas (F4) que caen en la celda antes de que él tenga el foco; devuelve la baja */
75
+ registrarEditor?: (procesar: (key: string) => void) => () => void;
76
+ setTableData: React.Dispatch<React.SetStateAction<FilaDeDatos[]>>;
57
77
  primaryKey: string[];
58
78
  }
59
79
  interface Ancestor {
60
80
  tableName: string;
61
- row: any;
81
+ row: FilaDeDatos;
62
82
  tableDefinition: TableDefinition;
63
83
  fixedFields?: FixedField[];
64
84
  }
@@ -71,8 +91,8 @@ interface GenericDataGridProps$1 {
71
91
  }
72
92
  interface FixedField {
73
93
  fieldName: string;
74
- value: any;
75
- until?: any;
94
+ value: unknown;
95
+ until?: unknown;
76
96
  }
77
97
  interface ConfirmDialogProps {
78
98
  open: boolean;
@@ -80,13 +100,37 @@ interface ConfirmDialogProps {
80
100
  title: string;
81
101
  message: string;
82
102
  }
83
- interface FilterRendererProps<R extends Record<string, any>, S> {
103
+ /** El filtro de una columna: un texto (busca que contenga) o un operador con su valor */
104
+ type FiltroDeColumna = string | {
105
+ operator?: string;
106
+ value?: string;
107
+ };
108
+ type FiltrosDeGrilla = Record<string, FiltroDeColumna>;
109
+ /** Una entrada de la fila de totales: qué se calculó (suma, promedio...) y su valor */
110
+ type EntradaDeTotal = {
111
+ type: string;
112
+ value: number;
113
+ };
114
+ /** Fila de resumen con los filtros de cada columna (arriba de la grilla, cuando se muestran los filtros) */
115
+ type FilaDeFiltros = {
116
+ id: 'filterRow';
117
+ filters: FiltrosDeGrilla;
118
+ setFilters: React.Dispatch<React.SetStateAction<FiltrosDeGrilla>>;
119
+ };
120
+ /** Fila de resumen con los totales de las columnas que los tienen (pie de la grilla) */
121
+ type FilaDeTotales = {
122
+ id: 'bottomSummaryRow';
123
+ totales: Record<string, EntradaDeTotal>;
124
+ };
125
+ /** Las filas de resumen de react-data-grid: filtros o totales */
126
+ type FilaDeResumen = FilaDeFiltros | FilaDeTotales;
127
+ interface FilterRendererProps<R extends FilaDeDatos, S> {
84
128
  column: Column<R, S>;
85
- filters: Record<string, any>;
86
- setFilters: React.Dispatch<React.SetStateAction<Record<string, any>>>;
129
+ filters: FiltrosDeGrilla;
130
+ setFilters: React.Dispatch<React.SetStateAction<FiltrosDeGrilla>>;
87
131
  }
88
132
  interface GenericDataGridForDetailProps {
89
- parentRow: any;
133
+ parentRow: FilaDeDatos;
90
134
  detailTableDefinition: DetailTable;
91
135
  parentTableName: string;
92
136
  }
@@ -98,7 +142,7 @@ interface MenuListItemProps {
98
142
  level: number;
99
143
  onMenuItemClick?: () => void;
100
144
  }
101
- interface CustomCellProps<R extends Record<string, any>, SR> extends RenderCellProps<R, SR> {
145
+ interface CustomCellProps<R extends FilaDeDatos, SR> extends RenderCellProps<R, SR> {
102
146
  column: RenderCellProps<R, SR>['column'] & {
103
147
  isPK?: boolean;
104
148
  };
@@ -142,9 +186,12 @@ interface SnackbarProviderProps {
142
186
  interface CallApiOptions {
143
187
  isCritical?: boolean;
144
188
  }
145
- interface UseApiCallResult<T> {
146
- callApi: (procedureName: string, params: Record<string, any>, opts?: CallApiOptions) => Promise<T | undefined>;
147
- callApiUpload: (procedureName: string, file: File, params: Record<string, any>, opts?: CallApiOptions) => Promise<T | undefined>;
189
+ /** Los parámetros de un procedimiento por nombre; se codifican según la definición que manda el servidor */
190
+ type ParametrosDeProcedimiento = Record<string, unknown>;
191
+ interface UseApiCallResult {
192
+ /** R es la forma del resultado de ese procedimiento (el servidor puede no devolver nada: undefined) */
193
+ callApi: <R = unknown>(procedureName: string, params: ParametrosDeProcedimiento, opts?: CallApiOptions) => Promise<R | undefined>;
194
+ callApiUpload: <R = unknown>(procedureName: string, file: File, params: ParametrosDeProcedimiento, opts?: CallApiOptions) => Promise<R | undefined>;
148
195
  loading: boolean;
149
196
  error: Error | null;
150
197
  }
@@ -172,6 +219,8 @@ interface RouterState {
172
219
  redirectPath: string | null;
173
220
  }
174
221
  interface FetchApiOptions extends RequestInit {
222
+ /** No poner Content-Type: lo arma el navegador (por ejemplo con un FormData, que lleva su boundary) */
223
+ sinContentType?: boolean;
175
224
  }
176
225
  interface DataGridOption {
177
226
  id: string;
@@ -184,15 +233,37 @@ interface DataGridOption {
184
233
  interface GenericDataGridProps {
185
234
  tableName: string;
186
235
  fixedFields?: FixedField[];
236
+ /** Abre un detalle en otra pestaña (Ctrl+click o botón del medio en el botón del detalle) */
187
237
  onOpenDetail?: (tableName: string, fixedFields: FixedField[], label: string, ancestors: Ancestor[]) => void;
188
238
  gridStyles?: React$1.CSSProperties;
189
239
  ancestors?: Ancestor[];
240
+ /** Es una grilla de detalle desplegada dentro de otra grilla */
241
+ anidada?: boolean;
242
+ /** Título a mostrar en lugar del de la tabla (por omisión, el título o nombre de la tabla, igual que la grilla principal) */
243
+ titulo?: string;
244
+ /** Clave en el CacheGrillasContext para volver a mostrarse sin recargar */
245
+ claveCache?: string;
246
+ /** La grilla anidada informa el alto que necesita para mostrarse completa */
247
+ onAltoDeseado?: (alto: number) => void;
248
+ /** La grilla anidada informa el ancho que necesita (sus columnas y los detalles que tenga abiertos) */
249
+ onAnchoDeseado?: (ancho: number) => void;
250
+ /** Alto máximo que puede tener esta grilla anidada (lo calcula la de arriba a partir de la ventana) */
251
+ altoDisponible?: number;
252
+ /**
253
+ * Tope opcional para el alto de las filas de detalle (en px). Sin tope (lo normal) cada detalle
254
+ * se muestra completo, como en backend-plus; con tope, el detalle más alto scrollea por dentro
255
+ * y virtualiza sus propias filas (útil si hay detalles de miles de filas).
256
+ */
257
+ altoMaximoDetalle?: number;
190
258
  }
259
+
191
260
  declare const GenericDataGrid: React$1.FC<GenericDataGridProps>;
192
261
 
193
262
  declare let clientSides: Record<string, React$1.FC<ClientSideProps>>;
194
263
  declare const extendClientSides: (newRenderers: Record<string, React$1.FC<ClientSideProps>>) => void;
195
264
 
265
+ declare const extendConditions: (nuevas: CondicionesMap) => void;
266
+
196
267
  declare const ConfirmDialog: React.FC<ConfirmDialogProps>;
197
268
 
198
269
  interface RoutesProps {
@@ -206,7 +277,10 @@ declare const FrontendPlusProviders: React$1.FC<{
206
277
  children: React$1.ReactNode;
207
278
  publicPaths: string[];
208
279
  }>;
209
- declare const App: ({ myRoutes, myUnloggedRoutes, unloggedLayout, myWScreens, myClientSides, myResultsOk }: AppProps) => react_jsx_runtime.JSX.Element;
280
+ declare const App: ({ myRoutes, myUnloggedRoutes, unloggedLayout, myWScreens, myClientSides, myResultsOk, myConditions, puenteBP }: AppProps & {
281
+ /** Las wScreens que la aplicación no registró se piden al cliente de backend-plus (mismo origen que el backend) */
282
+ puenteBP?: boolean;
283
+ }) => react_jsx_runtime.JSX.Element;
210
284
 
211
285
  declare const MainLayout: React$1.FC;
212
286
 
@@ -224,8 +298,8 @@ declare const useApp: () => {
224
298
  };
225
299
  declare const AppProvider: React$1.FC<AppProviderProps>;
226
300
 
227
- declare const useApiCall: <T = any>() => UseApiCallResult<T>;
228
- declare const useApiCallWithoutSnackbar: <T = any>() => UseApiCallResult<T>;
301
+ declare const useApiCall: () => UseApiCallResult;
302
+ declare const useApiCallWithoutSnackbar: () => UseApiCallResult;
229
303
 
230
304
  /**
231
305
  * Hook personalizado para manejar la lógica de cierre de sesión.
@@ -245,4 +319,13 @@ type ResultComponentMap = {
245
319
  declare let resultsOk: ResultComponentMap;
246
320
  declare const extendResultsOk: (newComponents: ResultComponentMap) => void;
247
321
 
248
- export { type Ancestor, App, type AppContextType, type AppProps, AppProvider, type AppProviderProps, type CallApiOptions, type CellFeedback, type CellFeedbackMap, type ClientContextState, type ClientSideProps, type ClientSidesMap, ConfirmDialog, type ConfirmDialogProps, type CustomCellProps, type CustomHeaderCellProps, type DataGridOption, type FetchApiOptions, type FilterRendererProps, type FixedField, FrontendPlusProviders, FrontendPlusReactRoutes, GenericDataGrid, type GenericDataGridForDetailProps, type GenericDataGridProps$1 as GenericDataGridProps, type InputRendererProps, MainLayout, type MenuListItemProps, type MenuUiState, type PrivateRouteProps, type ResultOkProps, type ResultsOksMap, type RouterState, type SideMenuProps, type SnackbarContextType, SnackbarProvider, type SnackbarProviderProps, type SnackbarState, type UseApiCallResult, type WScreenMap, type WScreenProps, cambiarGuionesBajosPorEspacios, clientSides, extendClientSides, extendResultsOk, extendWScreens, extractPathsFromRoutes, resultsOk as resultComponents, useApiCall, useApiCallWithoutSnackbar, useApp, useLogout, useSnackbar, wScreens };
322
+ interface GrillaBPProps {
323
+ tabla: string;
324
+ fixedFields?: FixedField[];
325
+ }
326
+ /** La grilla de backend-plus (`myOwn.tableGrid`) dentro de una página React: conserva todo lo que ya hace esa grilla */
327
+ declare const GrillaBP: React$1.FC<GrillaBPProps>;
328
+
329
+ declare const habilitarPuenteBP: () => void;
330
+
331
+ export { type Ancestor, App, type AppContextType, type AppProps, AppProvider, type AppProviderProps, type CallApiOptions, type CellFeedback, type CellFeedbackMap, type ClientContextState, type ClientSideProps, type ClientSidesMap, type CondicionDeDetalle, type CondicionesMap, ConfirmDialog, type ConfirmDialogProps, type CustomCellProps, type CustomHeaderCellProps, DOM_TEMP_ID, type DataGridOption, type EntradaDeTotal, type FetchApiOptions, type FilaDeDatos, type FilaDeFiltros, type FilaDeResumen, type FilaDeTotales, type FilterRendererProps, type FiltroDeColumna, type FiltrosDeGrilla, type FixedField, FrontendPlusProviders, FrontendPlusReactRoutes, GenericDataGrid, type GenericDataGridForDetailProps, type GenericDataGridProps$1 as GenericDataGridProps, GrillaBP, type InputRendererProps, MainLayout, type MenuListItemProps, type MenuUiState, type ParametrosDeProcedimiento, type PrivateRouteProps, type ResultOkProps, type ResultsOksMap, type RouterState, type SideMenuProps, type SnackbarContextType, SnackbarProvider, type SnackbarProviderProps, type SnackbarState, type TeclaDeEditor, type UseApiCallResult, type WScreenMap, type WScreenProps, cambiarGuionesBajosPorEspacios, clientSides, extendClientSides, extendConditions, extendResultsOk, extendWScreens, extractPathsFromRoutes, habilitarPuenteBP, resultsOk as resultComponents, useApiCall, useApiCallWithoutSnackbar, useApp, useLogout, useSnackbar, wScreens };