janelkit-router
Navegación entre pantallas para aplicaciones JanelKit: pilas de páginas que conservan su estado, secciones paralelas (barra lateral, pestañas), resultados con await, preguntar antes de salir y transiciones.
pip install janelkit-router
En un minuto
from janelkit_router import pages, Pages
from pages.Inicio import Inicio
from pages.Detalle import Detalle
nav = pages(Inicio)
def abrir(item):
nav.push(Detalle, item=item, on_borrado=borrar)
<row class="barra">
<button on:click={nav.pop()} enabled={nav.can_go_back}>← Volver</button>
<text>{nav.current}</text>
</row>
<Pages stack={nav} />
Pages y Sections son elementos de este paquete: se importan en el script, como un componente, y se usan con mayúscula.
Pilas de páginas
-
<Pages stack={nav} />muestra la página de arriba de la pila y ocupa el espacio libre (flex: 1). -
pushmuestra una página nueva encima; la de debajo sigue viva y, al volver conpop(), conserva su estado. Mientras está debajo, lo que muestra espera, como en{#hidden}: se pone al día al volver a verse.replacesustituye la actual;resetvacía la pila y empieza de nuevo. Las páginas descartadas se liberan (suson_destroyse ejecutan). -
Cada página ocupa todo su
<Pages>(y cada sección, su<Sections>) y se estrecha con la ventana hasta el mínimo de su contenido; lo que deba desplazarse, conoverflow: auto. La raíz de la página puede cambiarlo con CSS, como un bloque en una columna:.pagina { max-width: 720px; margin: 0 auto; } /* una columna centrada */ .pagina { width: 400px; height: 300px; } /* tamaño fijo, arriba a la izquierda */ .pagina { align-self: center; } /* su ancho natural, centrada */ .pagina { margin: 24px; } /* llena, con margen alrededor */
-
Las palabras clave son props y eventos (
on_...) de la página, como enopen_window. Las páginas heredan el contexto (inject) del componente que declara la pila. -
nav.current(nombre de la página visible),nav.depthynav.can_go_backson reactivos;nav.pageda la instancia visible. -
Transiciones:
<Pages stack={nav} transition:slide />anima la página que entra: al avanzar llega desde la derecha y al volver desde la izquierda. Tambiénfadeyscale, y la duración:transition:fade={150}. En<Sections>, la transición se aplica al cambiar de sección y a las páginas de cada una. -
Atrás con el teclado y el ratón: Alt+← y el botón «atrás» del ratón vuelven a la página anterior, en la pila visible más interna. Alt+← actúa cuando el foco está dentro de la pila (si está, por ejemplo, en un botón de la barra lateral, no hace nada); el ratón, al pulsar sobre ella. Si un elemento de la página ya usa Alt+← (por ejemplo, para mover una tarjeta), gana el elemento.
Navegar desde una página
Dentro de una página (o de cualquier componente que contenga), navigation() da su pila, sin pasarla como prop:
from janelkit_router import navigation
from pages.Detalle import Detalle
nav = navigation()
def abrir(item):
nav.push(Detalle, item=item)
Devolver un resultado
push se puede esperar: await devuelve el valor con el que la página vuelve con nav.back(valor) (o None si se descarta de otra forma, con pop, replace o reset).
async def elegir_responsable(): # en la página que pregunta
persona = await nav.push(ElegirPersona)
if persona is not None:
tarjeta["responsable"] = persona["id"]
<!-- en ElegirPersona.janel -->
<button on:click={nav.back(p)}>{p["nombre"]}</button>
Secciones
Una aplicación con barra lateral o pestañas tiene varias secciones, y cada una necesita su pila. sections() las crea; <Sections of={...} /> muestra la actual:
from janelkit_router import sections, Sections
from pages.Proyectos import Proyectos
from pages.Personas import Personas
secciones = sections(proyectos=Proyectos, personas=Personas)
<flex>
<flex direction="column" class="barra">
<button on:click={secciones.show("proyectos")} class:activa={secciones.current == "proyectos"}>Proyectos</button>
<button on:click={secciones.show("personas")} class:activa={secciones.current == "personas"}>Personas</button>
</flex>
<Sections of={secciones} />
</flex>
- Cada sección es una pila (
secciones["personas"], osecciones.stackpara la visible) y empieza en el componente indicado. Dentro,navigation()da la pila de su sección. - Una sección se crea la primera vez que se muestra. Al cambiar a otra se conserva entera, con todas sus páginas y su estado; volver a ella cuesta lo mismo que mostrarla. Mientras no se ve, lo que muestra espera (como en
{#hidden}): un cambio de datos en otra sección no la recalcula. secciones.current(el nombre de la visible) es reactivo;secciones.nameslista los nombres.
Preguntar antes de salir
before_leave(fn) (del núcleo) en el script de un componente registra una pregunta: si fn() devuelve False, no se sale. Puede ser async, para preguntar con un diálogo:
from janelkit import before_leave
from janelkit_desktop import dialogs
@before_leave
async def salir():
return not cambios.value or await dialogs.confirm("Hay cambios sin guardar. ¿Salir igualmente?")
-
Se consulta al descartar la página que contiene el componente (
pop,back,replace,reset, Alt+←) y al cerrar con la X la ventana que lo muestra. -
Cambiar de sección no pregunta: la sección se conserva y no se pierde nada.
-
Para encadenar navegaciones, espera la salida:
pop,back,replaceyresetdevuelvenTruesi se salió yFalsesi unbefore_leavelo impidió (la respuesta puede llegar después de un diálogo):async def ir_a_tarjeta(pid, tid): if await nav.reset(Proyectos): # un detalle con cambios puede pedir quedarse nav.push(Proyecto, ident=pid, enfocar=tid)
Referencia
| Nombre | Descripción |
|---|---|
pages |
nav = pages(Inicio, **props) crea una pila de páginas que empieza en Inicio (un componente importado). |
PageStack |
Tipo de pages: .push(C, **props_y_on_eventos) (se puede esperar: await devuelve el valor de .back(valor)), .pop(), .back(valor=None), .replace(...), .reset(...) (los cuatro se pueden esperar: True si se salió, False si un before_leave lo impidió); reactivos .current, .depth, .can_go_back; .page (instancia visible). |
navigation |
nav = navigation() en el script de una página (o de un componente dentro de ella) da su pila. |
sections |
secciones = sections(a=A, b=B) crea secciones paralelas, cada una con su pila. |
SectionSet |
Tipo de sections: .show(nombre), [nombre] (su pila), .stack (la de la visible), .names; reactivo .current. |
<Pages stack={nav} /> |
Muestra la página de arriba. Admite transition: y las propiedades comunes (flex, width, class…). |
<Sections of={secciones} /> |
Muestra la sección visible. Igual que <Pages>. |
En pruebas
Con janelkit.inspect, las páginas se buscan por el nombre de su componente (app.find("Detalle")) y la pila se maneja desde el script (app.get("nav").push(...)). La página de debajo y las secciones ocultas no salen en find.
Ejemplo: apps/challenges/gestor (secciones, páginas, resultados con await y preguntas antes de salir).
Cómo está hecho
Solo usa la API pública del núcleo, como cualquier paquete externo: Pages.qml y Sections.qml son elementos cuya raíz es JkDeck y que declaran una propiedad JkHost; PageStack y SectionSet son anfitriones (janelkit.ext.Host) que montan las páginas con Container.mount. Ver arquitectura.md.
Compatibilidad
| janelkit-router | janelkit | Python |
|---|---|---|
| 0.2.x.x | 0.2.x.x | ≥ 3.10 |
Comparte la serie con el núcleo: janelkit-router 0.2.x.x va con janelkit 0.2.x.x. Los dos últimos números son suyos (política de versiones).
Cambios
0.2.0.0 (2026-11-05)
- Primera versión, en la serie 0.2. Antes era la navegación del núcleo (
janelkit.pages): cambiafrom janelkit import pages, navigation, sectionsporfrom janelkit_router import pages, navigation, sectionse importa los elementos que uses (from janelkit_router import Pages, Sections). El tipo desections()pasa a llamarseSectionSet.janel migratehace el cambio.
Licencia
MIT.
Metadata
Release files for janelkit-router 0.2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| janelkit_router-0.2.0.0.tar.gz | 20.0 kB | With provenance | Details for janelkit_router-0.2.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| janelkit_router-0.2.0.0-py3-none-any.whl | Python 3 | none | any | Details for janelkit_router-0.2.0.0-py3-none-any.whl |
Total release size: 31.5 kB
Release files / janelkit_router-0.2.0.0.tar.gz
| Download URL | janelkit_router-0.2.0.0.tar.gz |
|---|---|
| Size | 20.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f078d970b7ed82248b61724411cd922713890a1c9479ba0382660483603aa308
|
|
BLAKE2b-256 checksum How to use checksums |
befd83fa4b5e878d07a074452ad50a5a534f0d4e94a577c68f471ff344252885
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 5, 2026.
Transparency logRelease files / janelkit_router-0.2.0.0-py3-none-any.whl
| Download URL | janelkit_router-0.2.0.0-py3-none-any.whl |
|---|---|
| Size | 11.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d0ee4961245078849e5566672c81b796888ddbd121743a438b366aee7183c935
|
|
BLAKE2b-256 checksum How to use checksums |
468cf331fae54e3316cd484bf6bed5e150b35d0527b2025ad53a9ffaed5b1afa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 5, 2026.
Transparency logSecurity for janelkit-router 0.2.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for janelkit_router-0.2.0.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 5, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.