{"_id":"@dotrino/nav","_rev":"3-5696b8411d2412a851997907d6c2e7f9","name":"@dotrino/nav","dist-tags":{"latest":"0.3.0"},"versions":{"0.2.1":{"name":"@dotrino/nav","version":"0.2.1","keywords":["dotrino","web-component","custom-element","back","navigation","history","popstate","android-back","pwa","cross-app"],"author":{"name":"seyacat"},"license":"MIT","_id":"@dotrino/nav@0.2.1","maintainers":[{"name":"imdotrino","email":"imdotrino@gmail.com"}],"homepage":"https://github.com/imdotrino/dotrino-nav#readme","bugs":{"url":"https://github.com/imdotrino/dotrino-nav/issues"},"dist":{"shasum":"4fa3d6b08a4724a55234fbef9e759faecad9a30a","tarball":"https://registry.npmjs.org/@dotrino/nav/-/nav-0.2.1.tgz","fileCount":7,"integrity":"sha512-GSoYDseNVKjdyjW3DfF0R+oefaMySoBICioBK6bayvWwbHiB0Wc0CMrpL3ZwWI9V1l6YnynrJ2242R72+DmvGA==","signatures":[{"sig":"MEYCIQCyK5XR4G/ABkqOseH2Y/EbKV2PXke07+hz56WBufAyiAIhAPgbLE75O3zn5mlJ/L+A7Lixi9FSCs7xL/gI/yumRAcW","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":23440},"main":"src/index.js","type":"module","types":"src/index.d.ts","module":"src/index.js","exports":{".":{"types":"./src/index.d.ts","import":"./src/index.js"},"./vue":{"types":"./src/vue.d.ts","import":"./src/vue.js"}},"gitHead":"08aaec4ae18d487f233d783dd6e25c51e89bffb9","scripts":{"test":"node test/smoke.mjs"},"_npmUser":{"name":"imdotrino","email":"imdotrino@gmail.com"},"repository":{"url":"git+https://github.com/imdotrino/dotrino-nav.git","type":"git"},"_npmVersion":"11.12.1","description":"Navegación \"volver\" unificada del ecosistema Dotrino: controlador de pila (modal → pestaña → página anterior → dotrino.com) que intercepta el botón físico de Android / gesto de iOS / atrás del navegador, más el Web Component <dotrino-back> (chevron de hea","directories":{},"_nodeVersion":"25.9.0","_hasShrinkwrap":false,"peerDependencies":{"vue":">=3.3.0"},"peerDependenciesMeta":{"vue":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/nav_0.2.1_1781796118035_0.621267412965878","host":"s3://npm-registry-packages-npm-production"}},"0.2.2":{"name":"@dotrino/nav","version":"0.2.2","keywords":["dotrino","web-component","custom-element","back","navigation","history","popstate","android-back","pwa","cross-app"],"author":{"name":"seyacat"},"license":"MIT","_id":"@dotrino/nav@0.2.2","maintainers":[{"name":"imdotrino","email":"imdotrino@gmail.com"}],"homepage":"https://github.com/imdotrino/dotrino-nav#readme","bugs":{"url":"https://github.com/imdotrino/dotrino-nav/issues"},"dist":{"shasum":"06ca25df9ab8f1106cff54176e1444ee423b711c","tarball":"https://registry.npmjs.org/@dotrino/nav/-/nav-0.2.2.tgz","fileCount":7,"integrity":"sha512-1b1Z/X9ZW3RBLU+FDGwCjQU09AfwCdtGachI8PyvArAXK7jGmkA2uMwPF8bKstVvZCznXJaP4R5xPZ7O+4PhkQ==","signatures":[{"sig":"MEQCICMTF8ABa9rajPm/rpE4a4QQs0rqLr66Opgxl/ASTxnxAiBRoU8sfqOmAyROFptY51hXWMiHf16/+B92fw0wOihnZg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":25045},"main":"src/index.js","type":"module","types":"src/index.d.ts","module":"src/index.js","exports":{".":{"types":"./src/index.d.ts","import":"./src/index.js"},"./vue":{"types":"./src/vue.d.ts","import":"./src/vue.js"}},"gitHead":"b0c4293b3ea95e456252b5edf8dffc24c86bdfd2","scripts":{"test":"node test/smoke.mjs"},"_npmUser":{"name":"imdotrino","email":"imdotrino@gmail.com"},"repository":{"url":"git+https://github.com/imdotrino/dotrino-nav.git","type":"git"},"_npmVersion":"11.12.1","description":"Navegación \"volver\" unificada del ecosistema Dotrino: controlador de pila (modal → pestaña → página anterior → dotrino.com) que intercepta el botón físico de Android / gesto de iOS / atrás del navegador, más el Web Component <dotrino-back> (chevron de hea","directories":{},"_nodeVersion":"25.9.0","_hasShrinkwrap":false,"peerDependencies":{"vue":">=3.3.0"},"peerDependenciesMeta":{"vue":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/nav_0.2.2_1783724609112_0.10448321379427505","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@dotrino/nav","version":"0.3.0","description":"Navegación \"volver\" unificada del ecosistema Dotrino: controlador de pila (modal → pestaña → página anterior → dotrino.com) que intercepta el botón físico de Android / gesto de iOS / atrás del navegador, más el Web Component <dotrino-back> (chevron de hea","type":"module","main":"src/index.js","module":"src/index.js","types":"src/index.d.ts","exports":{".":{"types":"./src/index.d.ts","import":"./src/index.js"},"./vue":{"types":"./src/vue.d.ts","import":"./src/vue.js"}},"scripts":{"test":"node test/smoke.mjs","type-check":"tsc --noEmit"},"peerDependencies":{"vue":">=3.3.0"},"peerDependenciesMeta":{"vue":{"optional":true}},"keywords":["dotrino","web-component","custom-element","back","navigation","history","popstate","android-back","pwa","cross-app"],"author":{"name":"seyacat"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/imdotrino/dotrino-nav.git"},"devDependencies":{"typescript":"5.9.3","@types/node":"^22.0.0"},"gitHead":"3fad4d69a38e80d9872ad3bd1de6e45273d15b4f","_id":"@dotrino/nav@0.3.0","bugs":{"url":"https://github.com/imdotrino/dotrino-nav/issues"},"homepage":"https://github.com/imdotrino/dotrino-nav#readme","_nodeVersion":"25.9.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-P56LWqGc2OB7oWLWB/EODcAr5zXv+5DzN76Am+R8MEg2hxz/b3CjzdefQgB7HLsZhEWBRRWI+bilw4aS34uY4A==","shasum":"542d7294aa03fe1db79378233c69574cd5732392","tarball":"https://registry.npmjs.org/@dotrino/nav/-/nav-0.3.0.tgz","fileCount":7,"unpackedSize":28796,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDd1VhAz3ilSNmDF5DeyV7LZvPhEVQPnGjoNMn6g/QKuwIhALN2MOIDyN3DJu9T54VanImS+RQpOgohZLHyuPPr7aWD"}]},"_npmUser":{"name":"imdotrino","email":"imdotrino@gmail.com"},"directories":{},"maintainers":[{"name":"imdotrino","email":"imdotrino@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/nav_0.3.0_1787407426419_0.40485828980510496"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-18T15:21:57.665Z","modified":"2026-08-22T14:03:46.711Z","0.2.1":"2026-06-18T15:21:58.175Z","0.2.2":"2026-07-10T23:03:29.250Z","0.3.0":"2026-08-22T14:03:46.567Z"},"bugs":{"url":"https://github.com/imdotrino/dotrino-nav/issues"},"author":{"name":"seyacat"},"license":"MIT","homepage":"https://github.com/imdotrino/dotrino-nav#readme","keywords":["dotrino","web-component","custom-element","back","navigation","history","popstate","android-back","pwa","cross-app"],"repository":{"type":"git","url":"git+https://github.com/imdotrino/dotrino-nav.git"},"description":"Navegación \"volver\" unificada del ecosistema Dotrino: controlador de pila (modal → pestaña → página anterior → dotrino.com) que intercepta el botón físico de Android / gesto de iOS / atrás del navegador, más el Web Component <dotrino-back> (chevron de hea","maintainers":[{"name":"imdotrino","email":"imdotrino@gmail.com"}],"readme":"# @dotrino/nav\n\n> **Parte del ecosistema [Dotrino](https://dotrino.com).** Dotrino es un ecosistema de aplicaciones centradas en la privacidad de los datos: tu información es tuya, y las decisiones sobre ella también — qué compartes, con quién, cuándo y por qué. Sin anuncios, sin cookies, sin rastreo de datos, sin vender tu identidad a nadie.\n\nNavegación **\"volver\"** unificada para todo el ecosistema [Dotrino](https://dotrino.com).\n\nResuelve un problema real: en iOS / PWA standalone no había forma de volver a\n`dotrino.com`, y cada app inventaba (o no) su propio botón atrás. Este paquete\nunifica el **botón físico de volver de Android**, el **gesto de volver de iOS** y\nel **botón atrás del navegador** con un **chevron de UI**, y aplica la misma\ncascada en todas las apps:\n\n```\nmodal abierto            →  ciérralo\nhistorial de pestañas    →  vuelve a la vista anterior\nsin historial interno    →  página anterior del navegador\nsin página anterior      →  dotrino.com\n```\n\nSin JS de terceros, sin cookies, autohosteado (Shadow DOM). Funciona en apps\nVue y vanilla.\n\n## Por qué un paquete y no copiar el código\n\nEl chevron es trivial; lo que **no** lo es —y por eso se centraliza— es la\nlógica de `popstate`: el botón físico de Android/iOS en una PWA no dispara un\nevento propio, sino `popstate`. Para interceptarlo hay que mantener un\n**centinela de history** (`pushState` base + uno por capa) y contar entradas\npara no salirse de la app antes de tiempo ni quedar atrapado. Reimplementado por\napp, son 15 variantes con bugs sutiles distintos. Aquí está en un solo lugar y\ntesteado (`npm test`, Playwright).\n\n## Dos piezas\n\n### 1. Controlador `createBackNav()`\n\n```js\nimport { createBackNav } from '@dotrino/nav'\n\nconst nav = createBackNav({ home: 'https://dotrino.com' }) // crea e instala el singleton\n\n// Modal: empuja una capa; onClose define cómo cerrarlo.\nconst layer = nav.open(() => { mostrarModal.value = false })\n// …y al cerrarlo desde su propio botón X / backdrop:\nlayer.close()\n\n// Pestaña/vista: deja \"miga\" para volver a la anterior.\nnav.open(() => setTab(tabAnterior))\n\n// Routing real (opcional): refleja una URL/path enlazable mientras la capa\n// esté abierta. Al cerrar (volver físico o layer.close()) el navegador\n// restaura la URL anterior automáticamente.\nconst vista = nav.open(() => cerrarVista(), { url: '/que-es' })\n```\n\nAPI: `open(onClose, opts?) → { close(), active }` —con `opts.url` opcional para\n**routing real** (URL enlazable/indexable por capa; sin `url` = modal clásico que\nno toca la barra de direcciones)—, `back()` (volver programático, idéntico al\nfísico), `size()`, `home`, `destroy()`. `getBackNav()` devuelve el controlador\nactivo.\n\n> Routing real en GitHub Pages: para que un deep-link a `/que-es` cargue, genera\n> una página estática real (p. ej. copia `index.html` → `que-es.html`, que Pages\n> sirve con 200) y, al arrancar, deriva la vista inicial de `location.pathname`.\n\n### 2. Web Component `<dotrino-back>` (el chevron)\n\n```html\n<!-- vanilla -->\n<script type=\"module\" src=\".../@dotrino/nav/src/index.js\"></script>\n<dotrino-back></dotrino-back>\n```\n\n```js\n// Vue: importa el paquete una vez y usa el tag en el header.\nimport '@dotrino/nav'\n```\n```html\n<header class=\"topbar\">\n  <dotrino-back lang=\"es\" />\n  <div class=\"brand\">…</div>\n</header>\n```\n\nAl hacer click hace lo mismo que el botón físico de volver. Atributos: `lang`\n(`es`|`en`|auto), `label` (override), `floating` (posición fija arriba-izquierda\npara apps sin header donde anclar), `home` (fallback si no hay controlador).\nEmite el evento cancelable `cc-back`. Variables CSS: `--cc-back-size`,\n`--cc-back-color`, `--cc-back-bg`, `--cc-back-radius`, `--cc-back-icon`, …\n\n### Helper Vue (opcional)\n\nSincroniza un `ref` de modal con la pila sin cablear `open`/`close` a mano:\n\n```js\nimport { useBackLayer } from '@dotrino/nav/vue'\n\nconst showNotifs = ref(false)\nuseBackLayer(showNotifs) // el volver cierra el modal en vez de salir de la app\n\n// Con routing real: la vista pasa a tener URL propia enlazable/indexable.\nconst aboutOpen = ref(false)\nuseBackLayer(aboutOpen, { url: '/que-es' }) // string o función que lo devuelva\n```\n\n## Cómo decide (y por qué no cuenta eventos)\n\nEl controlador **no lleva la cuenta de los `popstate`**: lee `history.state.ccNav` de\nla entrada en la que acaba de caer (`base`, o el número de capa) y ajusta la pila a\neso. `history.back()` es asíncrono, y contar eventos se desfasaba en cuanto una capa se\nabría mientras el `back()` de otra estaba en vuelo — la capa nueva quedaba «adelante» y\nel siguiente volver salía de la app (visto en eco, 2026-08-22). Tres reglas:\n\n- abrir/cerrar mientras un `back()` nuestro está en vuelo **se encola** hasta que llega\n  su `popstate`;\n- cerrar una capa solo retira entradas de history si **de verdad estamos sobre la\n  suya** (`history.state` lo dice); si no, se cierra en la pila y el historial se deja\n  en paz;\n- una entrada sin estado nuestro con **otra URL** (un `location.hash = …` de la app) se\n  adopta como capa transparente, en vez de tomarla por «el usuario quiere salir».\n\n## Test\n\n```\nnpm test   # smoke con Playwright: cascada popstate, LIFO, cierre programático, fallback a home\n```\n","readmeFilename":"README.md"}