Skip to content

feat: core agnóstico de framework con bindings para DOM, React y Angular - #3

Merged
renearias merged 1 commit into
masterfrom
feat-framework-agnostic-rewrite
Sep 24, 2026
Merged

renearias merged 1 commit into
masterfrom
feat-framework-agnostic-rewrite

Conversation

@renearias

Copy link
Copy Markdown
Owner

Qué cambia

Reescribe gtvzone como un core de navegación espacial sin DOM ni framework más bindings finos, en un monorepo pnpm. Sale el port jQuery del Google TV UI Library (2010).

Paquete Contenido
@arxis/gtvzone-core Registro de elementos y zonas, estrategias geometry / list / grid (o una propia), capas con prioridad y restauración del foco, nextFocus / nextZone / trap, store con getState + subscribe y eventos. Cero dependencias.
@arxis/gtvzone-dom Adapter de navegador (medición con getRect enchufable para polyfills, orden por posición en el DOM), mapas de teclas Tizen / webOS / VIDAA / navegador y modo selectores (bindZone) con MutationObserver para apps sin framework.
@arxis/gtvzone-react NavigationProvider, useFocusable, <FocusZone> (reenvía ref), <FocusLayer>. Usa useSyncExternalStore con shim, así que funciona desde React 16.8/17.
@arxis/gtvzone-angular Directivas standalone gtvFocusable / gtvZone / gtvLayer, GtvNavigationService con signals y state$, provideGtvNavigation(). Angular ≥ 16.1.

Decisiones de diseño

  • El estado del foco vive solo en el core. Los bindings registran elementos y se suscriben; ninguno guarda una copia propia. Por eso React y Angular pueden volver a renderizar sin perder el foco.
  • Registro en lugar de selectores en React y Angular. El ciclo de vida del framework da de alta y de baja cada elemento. Los selectores quedan solo para el modo vanilla, donde nadie más es dueño del DOM.
  • La resolución del árbol es lazy. Un hijo puede registrarse antes que su zona padre (React corre los efectos de hijos primero) sin romper nada.
  • list y grid no miden el layout al moverse dentro de la zona. Solo geometry y la entrada a una zona desde un costado llaman a getBoundingClientRect, y cada operación mide cada nodo una sola vez.
  • Teclas fuera de la zona de Angular. El listener corre con runOutsideAngular y solo se entra a la zona cuando el foco cambia.
  • Salida para TV: core, dom y react se compilan con target chrome63 (Tizen 5); verifiqué que el bundle no tiene ?. ni ??. El paquete Angular queda con minVersion: 16.1.0 en la compilación parcial.

Cómo lo probé

  • pnpm test: 43 tests con vitest.
    • core (node): geometría, zonas, list/grid, capas, overrides, contrato del store y orden estable al re-registrar.
    • dom (jsdom): adapter, teclas, bindZone con mutaciones, zonas anidadas y cleanup.
    • react (jsdom): auto-foco, StrictMode, capas, desmontar el elemento enfocado.
    • angular (TestBed zoneless): directivas, focused() como signal, capas, provider con teclas.
  • pnpm typecheck y pnpm build en verde para los cuatro paquetes.
  • Recorrí examples/vanilla en Chrome real contra los dist. Probé menú → filas, bajar entre filas cayendo en la tarjeta alineada, volver a la última enfocada, abrir un modal como capa (las flechas no salen de él) y cerrarlo con Esc, que devuelve el foco a la tarjeta de origen.

Los tests encontraron dos bugs que ya quedaron corregidos en esta rama:

  • En el modo selectores, una zona padre desregistraba los ítems que una zona anidada acababa de tomar. Ahora cada ítem guarda qué zona es su dueña.
  • En Angular, focused() quedaba suscrito a un signal viejo. Ahora es un computed estable.

Breaking change

Es una reescritura completa y no hay API compatible con la 0.x. El README trae una tabla de migración (KeyBehaviorZone → bindZone, KeyZoneLayer → openLayer, etc.). Las 0.x siguen en npm como @arxis/gtvzone. Los paquetes nuevos quedan en 1.0.0-alpha.0 y esta PR no publica nada.

Pendiente, fuera de esta PR

  • Probar en dispositivos Tizen y webOS reales (solo se probó en Chrome de escritorio y jsdom).
  • Decidir si @arxis/gtvzone pasa a ser un paquete paraguas (core + dom) o se depreca.
  • Un flujo de publicación (changesets o similar).
  • Nota: en npm existe la 0.1.0, pero master estaba en 0.0.6; esa versión se publicó desde código que no está en el repo.

…bindings

Replace the jQuery port of the Google TV UI library with a pnpm monorepo:

- @arxis/gtvzone-core: DOM-free navigation engine (zones, geometry/list/grid
  strategies, prioritized layers, overrides, store with subscribe)
- @arxis/gtvzone-dom: browser adapter, platform key maps and a selector mode
  with MutationObserver for apps without a framework
- @arxis/gtvzone-react: useFocusable, FocusZone and FocusLayer on top of
  useSyncExternalStore
- @arxis/gtvzone-angular: standalone directives and a signal-based service,
  with key handling outside the Angular zone

Builds target chrome63 (Tizen 5) and CI replaces Travis.
@renearias
renearias merged commit d6e8f7d into master Sep 24, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant