feat(nakui-ui): atajo Esc para cancelar el modal de delete

`capture_key_down` en el root div: si event.keystroke.key=="escape" y
hay pending_delete, lo limpia y emite toast "delete cancelado (X)
[esc]". Capture phase (no bubble) intercepta el Esc antes de que
cualquier TextInput descendiente lo consuma. Sin pending el handler
es no-op, el evento sigue su flujo.

Hint visual en el banner: subtítulo amber tenue
"Esc para cancelar · click [Confirmar] para borrar" para que el
usuario descubra el atajo sin RTFM.

35 tests verdes. El handler son 8 líneas no-testeables sin GPUI cx;
type-check garantiza wireup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sergio
2026-05-09 21:58:15 +00:00
parent bcccf3b953
commit 9951307fd9
2 changed files with 53 additions and 2 deletions
+28
View File
@@ -6,6 +6,34 @@ ratio/diff ver `git show <sha>`.
## 2026-05-09
### feat(nakui-ui): atajo Esc para cancelar el modal de delete
Cierra otro pendiente de UX. El banner de confirmación de delete
ya tenía botones [Cancelar] / [Confirmar], pero la acción más
natural para cancelar un dialog es Esc — y no la teníamos wireada.
Cambios:
- **`capture_key_down` en el root div** del `MetaUi::render`. Capture
phase (no bubble) para interceptar el Esc *antes* que cualquier
TextInput descendiente lo consuma. Sin pending el handler es
no-op y el evento sigue su flujo normal.
- **Match `event.keystroke.key == "escape"`** + `pending_delete.take()`
→ toast `"delete cancelado (Entity) [esc]"` (sufijo `[esc]` para
diferenciar visualmente del botón). Si no hay pending, return
temprano sin tocar nada.
- **Hint visual en el banner**: subtítulo en amber tenue debajo del
título: `"Esc para cancelar · click [Confirmar] para borrar"`.
Que el usuario descubra el atajo sin RTFM.
35 tests verdes — el handler de Esc es 8 líneas no-testeables sin
GPUI cx (la lógica de pending_delete + toast vive dentro del
listener); el wireup compila por type-check.
Pendientes restantes:
- **`FieldOp::Clear`** — para soportar borrar un value vía form vacío.
- **Snapshot durante runtime** (cada N writes, no sólo al startup).
- **Validación cross-field** (UUID del EntityRef existe en la
entity referida).
### feat(nakui-ui): EntityRef validation en parse_field_value (UUID al submit)
Cierra otro pendiente: `parse_field_value(FieldKind::EntityRef, raw)`
devolvía `Ok(json!(raw))` blindly — el value entraba al log/store