frontend-plus-react 0.4.1 → 0.5.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/CLAUDE.md +28 -1
- package/README.md +8 -0
- package/dist/index.cjs +1297 -693
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -2
- package/dist/index.d.ts +21 -2
- package/dist/index.js +1220 -616
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -61,7 +61,10 @@ Son las de `dmencu` (que aplican a todo proyecto compartido de esta carpeta), es
|
|
|
61
61
|
- Las grillas no deben generar una barra de más: en una página de grilla, `scrollHeight` del contenedor mayor que su `clientHeight` es un error del cálculo de alto de la grilla. Esto no se puede probar en jsdom: se mide en el navegador.
|
|
62
62
|
|
|
63
63
|
### Edición y teclado
|
|
64
|
+
- **La celda está tipada como en `backend-plus`** (`utils/typedControls.ts`). Allá la celda numérica es un `<input type=number>` y `rejectedChar` del typer corta en el teclado lo que el tipo no admite; acá el editor es de texto y `getBestTypedValue` convertía en `null` lo que no era un número, sin avisar: «abc» en un entero borraba el valor o, si ya estaba vacío, «no cambió» y no se guardaba nada (`result_sup` en `edicion_tem`). Ahora el editor no deja escribir los caracteres que el tipo rechaza (`caracterRechazado`: en un entero solo dígitos y «-»; en una referencia abre el menú) ni pegar texto que los tenga (`textoPegadoRechazado`), y al guardar `tiparEntrada` avisa (cartel y celda en rojo) si lo escrito no es un valor del tipo (una fecha que no existe, por ejemplo). Un texto de solo espacios en un número o una fecha es vacío. Con `nullable` sin declarar el vacío de un entero es 0 (`emptyValue` del typer).
|
|
65
|
+
- **El clic afuera de la grilla guarda**, como Enter, Tab y las flechas: es el `blur` del editor (`handleBlur`). Las columnas ponen `commitOnOutsideClick: false`: con el valor por omisión react-data-grid cierra el editor por su cuenta, sin guardar lo que se escribió, y además los menús de F9 (un modal fuera de la grilla) cuentan como «afuera».
|
|
64
66
|
- Al editar con un filtro activo, react-data-grid entrega solo las filas visibles: `handleRowsChange` reemplaza las filas editadas dentro de `tableData`, nunca lo pisa con lo recibido.
|
|
67
|
+
- **Las teclas de una celda en edición reproducen a `backend-plus`** (`captureKeys` de `my-things.js`), en `hooks/grid/teclasDeCelda.ts` (`direccionDeSalida`) y `useGridEvents.handleKeyPressInEditor`. Dejar la celda por cualquier camino la guarda. Enter y Tab van a la celda editable siguiente (al final de la fila, a la primera de la de abajo); Shift+Tab a la anterior; ↑ y ↓ a la misma columna de la fila de arriba o de abajo, siempre; ← solo si el texto está vacío o el cursor está al principio; → solo si el texto está vacío o si la tecla anterior también fue → con el cursor al final (hay que apretarla dos veces: la primera solo llega al final). Con Ctrl, Alt o Meta (o Shift en las flechas) ninguna deja la celda. Una flecha que solo mueve el cursor **no guarda ni cierra el editor** (antes cualquier flecha lo hacía). `handleKeyPressInEditor` devuelve si la resolvió la grilla y el editor entonces cancela el efecto nativo: sin eso el Tab del navegador mandaba el foco a un botón de otra fila.
|
|
65
68
|
- **No esperar la respuesta del servidor para mover el foco** (Enter, Tab, flechas, F4): con la tecla apretada llegan más pulsaciones que respuestas. El cambio de la fila se aplica en el acto y el guardado sigue en segundo plano.
|
|
66
69
|
- **No llamar `onRowChange(fila, true)` del editor después de un `await`**: cierra el editor de la celda que esté seleccionada en ese momento (la siguiente). La fila que devuelve el servidor se aplica sobre los datos (`setTableData`).
|
|
67
70
|
- F4 con la tecla apretada: las pulsaciones que caen en la celda antes de que el editor nuevo tenga el foco se le pasan por `registrarEditor`, o quedan en cola hasta que haya uno. F4 usa el valor que acaba de copiar si la fila de arriba todavía no se redibujó.
|
|
@@ -78,6 +81,29 @@ Son las de `dmencu` (que aplican a todo proyecto compartido de esta carpeta), es
|
|
|
78
81
|
- Lo que aporta la aplicación (`myWScreens`, `myClientSides`, `myResultsOk`, `myConditions`) se registra **antes del primer render**, no en un efecto: las rutas de las wScreens se arman al renderizar a partir del registro.
|
|
79
82
|
- El `ff` de un ítem de menú puede ser un arreglo `[{fieldName, value}]` (lo que documenta backend-plus) o un objeto `{campo: valor}` (lo que usa dmencu); `fixedFieldsDeMenu` entiende los dos.
|
|
80
83
|
|
|
84
|
+
### Guardado en otras tablas (campos con `table`)
|
|
85
|
+
- Una definición puede mostrar campos que se guardan en otra tabla (`table` en el `fieldDef`, con `nameForUpsert` y `sql.otherTableDefs`; ejemplos: `sensibilizado` de `grilla_hoja_ruta` en ggs, las asignaciones de `areas_asignacion_general` en dmencu). Lo reparte el servidor en `table_record_save`; el cliente tiene que mandar **igual que `backend-plus`** (`saveRecord` de `TableConnector`), y acá se rompió de dos maneras (`useTableRecordSave.ts`):
|
|
86
|
+
- `newRow` en un update lleva **solo lo que cambió**; la clave viaja aparte en `primaryKeyValues`. Si `newRow` lleva también la clave, el servidor se la asigna a la tabla principal y esta queda con un update de solo claves: «se detectó una orden de modificación sin columnas a modificar».
|
|
87
|
+
- `table` es `tableDefinition.name` (`depot.def.name`), **no** `tableName`: una definición como `areas_asignacion_general` tiene `tableName: 'areas'` (la física) y el servidor busca la definición por el nombre que recibe; con el de la física no encuentra los campos («field _encu does not exist in areas»).
|
|
88
|
+
- Probado contra ggs (con una copia de `dmencu`): `sensibilizado` (boolean, se escribe `sí`) y el `encu` de la asignación general; los valores de prueba se restauraron.
|
|
89
|
+
|
|
90
|
+
### Expanders (F9 o Alt+↓, como `backend-plus`)
|
|
91
|
+
- En `backend-plus` **F9** o **Alt+↓** en una celda abren el «expander» del tipo (`TypedControls.Expanders`, en `typed-controls`). Acá están todos (`utils/expanders.ts`, `tipoDeExpander`, en el mismo orden en que los prueba backend-plus): JSON de solo lectura, referencias, **booleano** (Sí / No / vacío), **`options`** (menú con los valores y etiquetas declarados), **fecha** (`SelectorDeFecha`: calendario con clic, flechas, Re Pág / Av Pág y Enter; devuelve `AAAA-MM-DD`, que el editor entiende) y la **ventana de texto** (`VentanaDeTexto`: edita el contenido completo con Aceptar o Ctrl+Enter; en una celda que no se edita solo lo muestra, y para un `jsonb` lo muestra con sangría). Un `jsonb` editable usa la ventana de texto: el expander de JSON de backend-plus intenta editarlo con una variable sin definir. Los campos `clientSide` no tienen ventana de texto. El de **referencias** (`myOwn.ExpanderReferences`, my-tables.js) sirve para los campos con `references` (origen de una FK o softFK) salvo `skipReferenceLookup`: un menú con las filas de la tabla referenciada, su clave y sus campos de nombre (`lookupFields` si la tabla los declara y, si no, los `isName`, que el servidor junta en `nameFields`), ordenadas por esa etiqueta, sin las inactivas (`activeRecords`) y, en una FK compuesta, solo las que coinciden con la fila en las columnas anteriores a la última. Elegir guarda el valor como cualquier edición. Acá: `utils/referencias.ts` (opciones, búsqueda y carga con un cache de 2 minutos; backend-plus lo guarda hasta recargar), `MenuDeOpciones` (lo usan referencias, booleano y `options`: buscar escribiendo, ↑↓, Enter, Escape) y `InputRenderer`; con la celda seleccionada y sin editor, `handleCellKeyDown` abre el editor y le pasa la tecla por la misma cola que F4. Escribir un carácter que el tipo rechaza (una letra en un entero) abre el menú ya filtrado (`autoExpand`).
|
|
92
|
+
- `referencesFields`, `skipReferenceLookup`, `nameFields` y `activeRecords` los usa el cliente de backend-plus pero **`backend-plus.d.ts` no los declara**: los tipos locales de `referencias.ts` los describen con intersecciones y guardas, sin casts. Un campo que declara `references` a mano (las asignaciones de dmencu) no tiene `referencesFields`: el valor es la última columna de la clave de la tabla referenciada, como en backend-plus.
|
|
93
|
+
- El menú es un modal de MUI: mientras está abierto el editor pierde el foco y eso **no** guarda (`handleBlur` lo ignora), y al cerrarse el foco se devuelve con un `setTimeout`, porque el modal lo retiene hasta desmontarse.
|
|
94
|
+
- **Una celda que no se edita también se selecciona al hacer clic** (sin editor; `handleCellClick`): `handleCellMouseDown` cancela la selección por defecto y sin selección F9 no le llega a la grilla, así que el visor de solo lectura no se podía abrir.
|
|
95
|
+
- **La lupa** (el icono de `backend-plus` que abre el expander con un clic) es un adorno del editor (`endAdornment` de `InputRenderer`): flecha para los menús, calendario para la fecha y lupa para el texto. Su `onMouseDown` cancela el efecto nativo para que el editor no pierda el foco (perderlo guardaría). En una celda que no se edita no hay lupa: se llega con F9.
|
|
96
|
+
- Sin hacer todavía: `inheritFieldsMode`.
|
|
97
|
+
|
|
98
|
+
### Parámetros de una tabla (dirección y menú, como `backend-plus`)
|
|
99
|
+
- `wScreens.table` de `my-menu.js` abre una tabla con lo que declara el ítem de menú y, **encima**, lo de la dirección. Acá lo hace `utils/parametrosDeTabla.ts` (`parametrosDeTabla(item, direccion)`) y lo usan `MenuTablePage` (`/menu/:nombre`) y `GenericDataGridPage` (`/table/:nombre`): `ff` (fixedFields, arreglo `[{fieldName, value, ...}]` u objeto `{campo: valor}`), `fc` (filtros iniciales), `td` (cambios a la definición de la tabla, mezclados en profundidad con `fusionar`, como `changing` de `best-globals`), `pf` y `pick` (viajan en `table_data` como `paramfun` y `pick`, solo si vienen) y `label` (nombre de la pestaña).
|
|
100
|
+
- Los parámetros `ff`, `fc`, `td` y `pf` van codificados con `json4all` (`JSON4all.toUrl` / `parse`, que acepta también JSON común). La dirección se lee como `UriSearchToObject` (partir por `&` y por el primer `=`, `decodeURIComponent`), **no** con `URLSearchParams`: ahí un `+` sería un espacio.
|
|
101
|
+
- **`fc` / `filterColumns`** (el de la definición del servidor también): `[{column, operator, value}]` es una fila de filtros; con varias filas (arreglo de arreglos, alternativas) **solo se aplica la primera** y se avisa por consola: la grilla tiene una sola fila de filtros. El `.d.ts` de `MenuInfoTable` dice `fieldName` en vez de `column`; el cliente de BP usa `column` y se aceptan las dos. Antes `menuInicial.ts` ponía `td` y `fc` en la dirección y nadie los leía.
|
|
102
|
+
- **fixedFields** (`utils/fixedFields.ts`, igual que `TableConnector`): uno que fija el valor oculta su columna (salvo `show`), no se edita y no se filtra; uno con `range` o `until` es un rango: la columna se ve, se edita y se filtra.
|
|
103
|
+
|
|
104
|
+
### Anchos de columna
|
|
105
|
+
- Como `backend-plus` (`fieldDef.width` en `my-tables.js`, «Width in pixels for the grid» en el `.d.ts`), el `width` que declara el backend para un campo manda sobre el que se calcula con el título y los datos (`calcularAnchosDeColumnas`). Sin `width` se calcula. Las columnas se pueden arrastrar desde el borde del encabezado (`resizable`, con `columnWidths` en el estado de la grilla); en `backend-plus` el `width` también es el mínimo, acá el usuario puede achicarla.
|
|
106
|
+
|
|
81
107
|
### Sesión y arranque
|
|
82
108
|
- Con el estado todavía sin cargar (`idle`), `PrivateRoute` manda a `/login` guardando la ruta pedida; `InitialRedirectHandler` (montado fuera de las rutas privadas) vuelve a esa ruta cuando la sesión resulta válida. Tiene una guarda para no caer en la pantalla de inicio mientras la ruta guardada se limpia.
|
|
83
109
|
- **No hay `HomePage` ni ruta `/home`: la pantalla de inicio la define el backend, como en `backend-plus`.** `/` (`PantallaInicial`) redirige al ítem de menú con `selectedByDefault` (y, si es un grupo, al que lo declare adentro; `itemInicial`, `rutaDeItemDeMenu` en `utils/menuInicial.ts`, que también arma las rutas del menú lateral). Si el backend no declara ninguno, `/` queda vacía, igual que en `backend-plus`. El login y `InitialRedirectHandler` mandan a `/`. Una aplicación que quiera otra pantalla de inicio la declara en su menú.
|
|
@@ -94,8 +120,9 @@ Para no perder lo que la grilla de `backend-plus` ya hace y que acá todavía no
|
|
|
94
120
|
- Tres cosas que el cliente de `backend-plus` necesita y que el puente hace: un `<base href>` en la base-url (arma sus direcciones relativas a la página), el global `my` igual a `myOwn`, y llamar a `myOwn.autoSetup()` a mano (él lo hace al evento `load`, que ya pasó).
|
|
95
121
|
- Una wScreen de `backend-plus` puede ser una función, un objeto con `mainFunction`, o un objeto con `parameters` y `mainAction` (como `abrir_encuesta` de dmencu: muestra el formulario de parámetros). `resolverPantallaBP` reproduce el `showPage` de my-menu.js para las tres.
|
|
96
122
|
- **Las pantallas de `backend-plus` viven en una sola ruta, `/bp/menu#w=<pantalla>&...`** (`PaginaBP`; `/wScreens-fallback/:nombre` redirige ahí). Se parece a `<base-url>/menu` **a propósito**: BP y las aplicaciones arman direcciones a partir de la ubicación (`location.origin + location.pathname + '/../menu#w=...'` en un `history.replaceState` + `location.reload()`, como el «Cerrar» de una encuesta de dmencu; `location.pathname + '#w=...'` para abrir en otra pestaña) y así caen en esta misma ruta, que lee los parámetros del hash (o de la query) como BP. Con otra ruta, esas direcciones salen de `/react`. En producción, el servidor tiene que devolver el `index.html` también para `/react/bp/menu`.
|
|
97
|
-
- **Lo que BP manda a páginas del backend se resuelve adentro** (`capturaDeNavegacion.ts`,
|
|
123
|
+
- **Lo que BP manda a páginas del backend se resuelve adentro** (`capturaDeNavegacion.ts`, montado una sola vez por `CapturaBP` mientras el puente está encendido, **no** solo mientras hay una pantalla de BP: su cliente sigue vivo después de salir de ella y sus avisos quedan en el `body`; por ejemplo el de «Iniciar sesión» de `my-inform-net-status.js`, un `<a href="login">` que sin la captura llevaba a `<base-url>/login` y, con la sesión abierta, a `<base-url>/menu`, el BP nativo): clics en `<a>` (a nivel documento, también en diálogos) y, donde existe la Navigation API, cualquier `location.href = ...`. `rutaDeEnlaceBP` traduce `<base-url>/menu#w=...` a `/bp/menu#w=...`, y `login`/`logout` a los de la aplicación. El clic se corta con `stopPropagation`: el botón «proceder» de BP tiene además un `onclick` propio (`history.pushState` a `<base-url>/menu#...` y `showPage`) que dejaría la URL fuera de la aplicación y daría un error de BP. También se traduce `history.replaceState`: al editar un parámetro de una pantalla (`myOwn.replaceAddrParams`) BP reescribe la dirección a `menu#...` y, con el `<base>`, la aplicación quedaba en `<base-url>/menu` (un F5 y se salía de `/react`); `replaceState` no pasa por la Navigation API. Los enlaces `menu#i=...` (ítems del menú de BP, sin `w`) no se traducen. Los parámetros se leen con `myOwn.UriSearchToObject`, no con `URLSearchParams`: `up`, `ff` y `detailing` tienen su propio formato y llegan como objetos.
|
|
98
124
|
- **Los scripts de `backend-plus` se inyectan recién cuando la ventana terminó de cargar** (`esperarCargaDeVentana`): su `load` ejecuta `showPage`, que acá falla (no hay `#total-layout`). Y su manejador de errores (`myOwn.log`) deja el mensaje como primer hijo del `body` cuando no hay un `#mini-console`, lo que **corre toda la aplicación hacia abajo** durante 30 s. Era el «espacio entre el menú y el contenido» que aparecía con F5 y a veces no.
|
|
125
|
+
- **La aplicación no está atada a la ruta `/react`.** `my-menu.js` escucha `popstate` y, si la dirección no termina en `/react`, ejecuta `showPage()` (falla igual que el `load`); lo marca como `//TODO: desacoplar de BP`. El puente lo evita sin tocar `backend-plus`: mientras inyecta los scripts, `sinEscuchasDeVentana(['popstate'], ...)` ignora que registren ese evento (la navegación es del router). Si backend-plus agrega otro enganche así, se ve como un error en `ConsolaBP`. Sin el puente (`puenteBP` apagado) no se carga nada de `backend-plus` y la base-url de la aplicación es la que quiera. **Sin probar en navegador** (se probó el filtro en Vitest; falta atrás/adelante contra un backend real).
|
|
99
126
|
- **`ConsolaBP`** ofrece ese `#mini-console` (dentro de `PantallaBP` y `GrillaBP`): los mensajes y errores de `backend-plus` se ven como avisos flotantes arriba a la derecha, debajo de la AppBar, sin ocupar lugar. BP registra algunos errores **dos veces** (el de una llamada ajax lo registra `my-ajax.js` y el `catch` de «proceder», en `my-menu.js`, lo repite; pasa también en BP nativo): si llega un aviso igual a uno que llegó hace menos de 1 s, se descarta (`descartarRepetidos`). Sin contador: el error se ve una vez. Las hojas de estilo del backend y de las aplicaciones también estilizan `#mini-console` (por id, con `top`, `width: 100%`, `max-height`...): por eso `ConsolaBP` usa `&#mini-console` y repone esos valores.
|
|
100
127
|
- Las wScreens de `backend-plus` dibujan en `#main_layout`: `PantallaBP` le da ese contenedor y le pasa lo que declara su ítem de menú más la query string. Mientras está montada no puede haber otro elemento con ese id.
|
|
101
128
|
- `rutaBase` (`src/rutaBase.ts`) toma el `base` del bundler: es el `basename` del router y el prefijo de las pestañas nuevas (Ctrl+click en un detalle `wScreen`).
|
package/README.md
CHANGED
|
@@ -24,3 +24,11 @@ export default () => (
|
|
|
24
24
|
|
|
25
25
|
Las variables de entorno `VITE_BACKEND_URL` (o `REACT_APP_BACKEND_URL`) indican dónde está el backend. Ver `CLAUDE.md` para
|
|
26
26
|
las decisiones de diseño y cómo probar con un backend real.
|
|
27
|
+
|
|
28
|
+
## La grilla, como la de backend-plus
|
|
29
|
+
|
|
30
|
+
- **Teclado en una celda en edición**: Enter y Tab guardan y pasan a la celda siguiente, Shift+Tab a la anterior, ↑ y ↓ a la de arriba y abajo; ← y → solo salen de la celda al llegar al borde del texto. El clic fuera de la grilla también guarda.
|
|
31
|
+
- **F9 o Alt+↓** (y la lupa junto al editor) abren el selector del tipo de campo: las filas de la tabla referenciada (FK y softFK, con sus campos `isName`), Sí/No, las `options`, un calendario o el contenido completo.
|
|
32
|
+
- **Parámetros de la tabla** en la dirección y en el menú, como en backend-plus: `ff` (fixedFields, también con `show`, `range` y `until`), `fc` (filtros iniciales), `td`, `pf`, `pick` y `label`.
|
|
33
|
+
- Un campo con `table` se guarda en esa tabla, y el `width` del campo manda sobre el ancho calculado.
|
|
34
|
+
- Los detalles completos están en `CLAUDE.md`.
|