@alwatr/page-ready 9.14.0 → 9.16.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/README.md +77 -12
- package/dist/main.d.ts +13 -8
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +3 -3
- package/dist/main.js.map +3 -3
- package/dist/page-ready.d.ts +36 -2
- package/dist/page-ready.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/main.ts +13 -8
- package/src/page-ready.ts +44 -3
package/README.md
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
**Lightweight page identity signal for multi page applications routing.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Reads the `page-id` HTML attribute from the document and notifies subscribers using a dedicated O(1) channel signal. Designed for SSG/SSR setups where each generated page has a different `page-id` baked into the HTML.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
## Why `@alwatr/page-ready`?
|
|
10
10
|
|
|
11
|
-
In SSG/SSR apps, the server renders a different HTML page for each route. Each page has a `page-id` attribute
|
|
11
|
+
In SSG/SSR apps, the server renders a different HTML page for each route. Each page has a `page-id` attribute somewhere in the document. Instead of a full client-side router, you just need to know _which page is currently loaded_ so you can run page-specific initialization logic.
|
|
12
12
|
|
|
13
13
|
`@alwatr/page-ready` solves exactly this — nothing more.
|
|
14
14
|
|
|
@@ -29,7 +29,7 @@ npm i @alwatr/page-ready
|
|
|
29
29
|
### 1. Add `page-id` to your HTML
|
|
30
30
|
|
|
31
31
|
```html
|
|
32
|
-
<!-- Each SSG-generated page has a unique page-id -->
|
|
32
|
+
<!-- Each SSG-generated page has a unique page-id — can be on any element -->
|
|
33
33
|
<body page-id="home">
|
|
34
34
|
…
|
|
35
35
|
</body>
|
|
@@ -38,15 +38,22 @@ npm i @alwatr/page-ready
|
|
|
38
38
|
### 2. Subscribe and dispatch
|
|
39
39
|
|
|
40
40
|
```ts
|
|
41
|
-
import {onPageReady, dispatchPageReady} from '@alwatr/page-ready';
|
|
41
|
+
import {onPageReady, subscribePageReady, dispatchPageReady} from '@alwatr/page-ready';
|
|
42
42
|
|
|
43
43
|
// Optionally constrain valid page IDs with a type alias
|
|
44
44
|
type PageId = 'home' | 'about' | 'product-detail';
|
|
45
45
|
|
|
46
|
+
// Subscribe to a specific page
|
|
46
47
|
onPageReady<PageId>('home', () => initHomePage());
|
|
47
48
|
onPageReady<PageId>('about', () => initAboutPage());
|
|
48
49
|
|
|
49
|
-
//
|
|
50
|
+
// Or subscribe to ALL pages and receive the page ID in the handler
|
|
51
|
+
subscribePageReady<PageId>((pageId) => {
|
|
52
|
+
console.log('Page ready:', pageId);
|
|
53
|
+
analytics.trackPageView(pageId);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Call once at bootstrap — finds [page-id] anywhere in the document and notifies subscribers
|
|
50
57
|
dispatchPageReady();
|
|
51
58
|
```
|
|
52
59
|
|
|
@@ -61,11 +68,13 @@ dispatchPageReady()
|
|
|
61
68
|
│
|
|
62
69
|
└─ pageReadyChannel_.dispatch('home')
|
|
63
70
|
│
|
|
64
|
-
|
|
71
|
+
├─ Map.get('home') → O(1) → invoke only 'home' handlers (onPageReady)
|
|
72
|
+
└─ global subscribers → invoked for every page ID (subscribePageReady)
|
|
65
73
|
```
|
|
66
74
|
|
|
67
|
-
- `dispatchPageReady`
|
|
68
|
-
- `ChannelSignal` routes in O(1):
|
|
75
|
+
- `dispatchPageReady` scans the entire document for the first `[page-id]` element — no argument needed.
|
|
76
|
+
- `ChannelSignal` routes in O(1): `onPageReady` handlers for non-matching page IDs are never invoked.
|
|
77
|
+
- `subscribePageReady` handlers receive the dispatched page ID and are called on every dispatch.
|
|
69
78
|
|
|
70
79
|
---
|
|
71
80
|
|
|
@@ -73,10 +82,10 @@ dispatchPageReady()
|
|
|
73
82
|
|
|
74
83
|
### `onPageReady(pageId, handler)`
|
|
75
84
|
|
|
76
|
-
Subscribes to a specific page becoming ready.
|
|
85
|
+
Subscribes to a **specific** page becoming ready. The handler is only invoked when the dispatched page ID matches `pageId`.
|
|
77
86
|
|
|
78
87
|
```ts
|
|
79
|
-
function onPageReady<T extends string>(pageId: T, handler: () => void): SubscribeResult;
|
|
88
|
+
function onPageReady<T extends string>(pageId: T, handler: () => Awaitable<void>): SubscribeResult;
|
|
80
89
|
```
|
|
81
90
|
|
|
82
91
|
Pass a string literal union as the generic to constrain valid page IDs:
|
|
@@ -90,15 +99,71 @@ sub.unsubscribe(); // stop listening when no longer needed
|
|
|
90
99
|
|
|
91
100
|
---
|
|
92
101
|
|
|
102
|
+
### `subscribePageReady(handler)`
|
|
103
|
+
|
|
104
|
+
Subscribes to **all** page-ready events. The handler is invoked on every `dispatchPageReady()` call, regardless of which page ID is dispatched. The current page ID is passed as the first argument.
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
function subscribePageReady<T extends string>(handler: (pageId: T) => Awaitable<void>): SubscribeResult;
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Useful for cross-cutting concerns like analytics, logging, or layout transitions that need to react to every page change:
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
type PageId = 'home' | 'about' | 'product-detail';
|
|
114
|
+
|
|
115
|
+
const sub = subscribePageReady<PageId>((pageId) => {
|
|
116
|
+
analytics.trackPageView(pageId);
|
|
117
|
+
updateActiveNavLink(pageId);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
sub.unsubscribe(); // stop listening when no longer needed
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
93
125
|
### `dispatchPageReady()`
|
|
94
126
|
|
|
95
|
-
Finds the first `[page-id]` element in the document and notifies matching subscribers.
|
|
127
|
+
Finds the first `[page-id]` element anywhere in the document and notifies all matching subscribers.
|
|
96
128
|
|
|
97
129
|
```ts
|
|
98
130
|
function dispatchPageReady(): void;
|
|
99
131
|
```
|
|
100
132
|
|
|
101
|
-
Call once at application bootstrap. Logs an accident if no `[page-id]` element is found.
|
|
133
|
+
Call once at application bootstrap. Logs an accident if no `[page-id]` element is found in the document. An empty attribute value (`page-id=""`) is treated as a valid identifier and dispatched normally.
|
|
134
|
+
|
|
135
|
+
The lookup uses `document.querySelector('[page-id]')`, so the attribute can be placed on any element (`<body>`, `<main>`, `<div>`, etc.).
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Choosing Between `onPageReady` and `subscribePageReady`
|
|
140
|
+
|
|
141
|
+
| Use case | API |
|
|
142
|
+
| ------------------------------------------- | -------------------- |
|
|
143
|
+
| Initialize logic for one specific page | `onPageReady` |
|
|
144
|
+
| React to every page change (analytics, nav) | `subscribePageReady` |
|
|
145
|
+
| Multiple pages, each with their own init | `onPageReady` × N |
|
|
146
|
+
| Single handler that branches on the page ID | `subscribePageReady` |
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 🌊 Part of Alwatr Flux
|
|
153
|
+
|
|
154
|
+
`@alwatr/page-ready` is the **Routing Layer** of the [Alwatr Flux](https://github.com/Alwatr/alwatr/tree/next/pkg/flux) architecture — a complete Unidirectional Data Flow system for building scalable Progressive Web Applications.
|
|
155
|
+
|
|
156
|
+
In the Flux architecture, `@alwatr/page-ready` handles **page identity** for Multi-Page Applications (MPA). It reads the `page-id` attribute from the HTML and notifies the application which page is currently active — enabling page-specific initialization without a full client-side router.
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
// Use @alwatr/flux for the complete architecture
|
|
160
|
+
import {onPageReady, subscribePageReady, dispatchPageReady, setupActionDelegation} from '@alwatr/flux';
|
|
161
|
+
|
|
162
|
+
// Or use @alwatr/page-ready standalone
|
|
163
|
+
import {onPageReady, subscribePageReady, dispatchPageReady} from '@alwatr/page-ready';
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
→ [View the complete Flux documentation](https://github.com/Alwatr/alwatr/tree/next/pkg/flux)
|
|
102
167
|
|
|
103
168
|
---
|
|
104
169
|
|
package/dist/main.d.ts
CHANGED
|
@@ -1,30 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @alwatr/page-ready — Lightweight page identity signal for MPA routing.
|
|
3
3
|
*
|
|
4
|
-
* Reads the `page-id` attribute from
|
|
5
|
-
* signal so any part of the application can react to the
|
|
6
|
-
* coupling to a full router.
|
|
4
|
+
* Reads the `page-id` attribute from the first matching element in the document
|
|
5
|
+
* and dispatches a named signal so any part of the application can react to the
|
|
6
|
+
* current page without coupling to a full router.
|
|
7
7
|
*
|
|
8
8
|
* ## Usage
|
|
9
9
|
*
|
|
10
10
|
* ```html
|
|
11
|
+
* <!-- page-id can be placed on any element, not just <body> -->
|
|
11
12
|
* <body page-id="home">…</body>
|
|
12
13
|
* ```
|
|
13
14
|
*
|
|
14
15
|
* ```ts
|
|
15
|
-
* import {onPageReady, dispatchPageReady} from '@alwatr/page-ready';
|
|
16
|
+
* import {onPageReady, subscribePageReady, dispatchPageReady} from '@alwatr/page-ready';
|
|
16
17
|
*
|
|
17
|
-
* // Subscribe
|
|
18
|
+
* // Subscribe to a specific page before dispatching.
|
|
18
19
|
* onPageReady('home', () => initHomePage());
|
|
19
20
|
*
|
|
20
|
-
* //
|
|
21
|
+
* // Or subscribe to ALL pages — handler receives the page ID.
|
|
22
|
+
* subscribePageReady((pageId) => analytics.trackPageView(pageId));
|
|
23
|
+
*
|
|
24
|
+
* // Call once at bootstrap — finds [page-id] anywhere in the document and notifies subscribers.
|
|
21
25
|
* dispatchPageReady();
|
|
22
26
|
* ```
|
|
23
27
|
*
|
|
24
28
|
* ## Public API
|
|
25
29
|
*
|
|
26
|
-
* - `onPageReady(pageId, handler)` — subscribe to a specific page becoming ready
|
|
27
|
-
* - `
|
|
30
|
+
* - `onPageReady(pageId, handler)` — subscribe to a **specific** page becoming ready
|
|
31
|
+
* - `subscribePageReady(handler)` — subscribe to **all** page-ready events; handler receives the page ID
|
|
32
|
+
* - `dispatchPageReady()` — find `[page-id]` via `querySelector` and notify subscribers
|
|
28
33
|
*/
|
|
29
34
|
export * from './page-ready.js';
|
|
30
35
|
//# sourceMappingURL=main.d.ts.map
|
package/dist/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,cAAc,iBAAiB,CAAC"}
|
package/dist/main.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/* 📦 @alwatr/page-ready v9.
|
|
2
|
-
import{createLogger as
|
|
1
|
+
/* 📦 @alwatr/page-ready v9.16.0 */
|
|
2
|
+
import{createLogger as o}from"@alwatr/logger";import{createChannelSignal as c}from"@alwatr/signal";var a=o("page-ready"),i=c({name:"page-ready"});function d(e,t){return a.logMethodArgs?.("onPageReady",{pageId:e}),i.on(e,t)}function b(e){return a.logMethod?.("subscribePageReady"),i.subscribe((t)=>{e(t.name)})}function n(){a.logMethod?.("dispatchPageReady");let e=document.querySelector("[page-id]")?.getAttribute("page-id")?.trim();if(e==null){a.accident("dispatchPageReady","page_id_not_found");return}i.dispatch(e)}export{b as subscribePageReady,d as onPageReady,n as dispatchPageReady};
|
|
3
3
|
|
|
4
|
-
//# debugId=
|
|
4
|
+
//# debugId=8902EDA064F1D27364756E2164756E21
|
|
5
5
|
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/page-ready.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * @file page-ready.ts\n *\n * Lightweight page identity signal for MPA routing.\n *\n * ## Design\n *\n * Uses a dedicated `ChannelSignal` keyed by page identifier. This gives O(1)\n * dispatch — only the handler(s) registered for the current page ID are invoked,\n * regardless of how many pages are declared in the application.\n *\n * The signal is intentionally separate from `@alwatr/action`'s action bus:\n * page identity is a routing/lifecycle concern, not a user-interaction action.\n *\n * ## Attribute convention\n *\n * Place `page-id` anywhere in the document — `dispatchPageReady` finds it\n * automatically via `querySelector('[page-id]')`:\n *\n * ```html\n * <body page-id=\"home\">…</body>\n * ```\n *\n * In SSG/SSR setups each generated page has a different `page-id` value baked\n * into the HTML, so `dispatchPageReady()` always reads the correct page without\n * any runtime routing logic.\n */\n\nimport type {Awaitable} from '@alwatr/type-helper';\nimport {createLogger} from '@alwatr/logger';\nimport {createChannelSignal} from '@alwatr/signal';\nimport type {SubscribeResult} from '@alwatr/signal';\n\nconst logger = createLogger('page-ready');\n\n/**\n * Internal channel keyed by page identifier.\n *\n * O(1) dispatch: routes directly to the handler set for the dispatched key,\n * never invoking handlers for other page IDs.\n *\n * @internal\n */\nconst pageReadyChannel_ = createChannelSignal<Record<string, void>>({name: 'page-ready'});\n\n/**\n * Subscribes to a specific page becoming ready.\n *\n * The handler is invoked when `dispatchPageReady()` is called and the\n * `page-id` attribute in the document matches `pageId`.\n *\n * Pass a string literal union as the generic parameter to constrain which\n * page IDs are valid across your application:\n *\n * ```ts\n * type PageId = 'home' | 'about' | 'product-detail';\n *\n * onPageReady<PageId>('home', () => initHomePage());\n * ```\n *\n * @param pageId - The page identifier to listen for (must match the `page-id` attribute value).\n * @param handler - Called with no arguments when the page is ready.\n * @returns A `SubscribeResult` with an `unsubscribe()` method for cleanup.\n *\n * @example\n * ```ts\n * import {onPageReady} from '@alwatr/page-ready';\n *\n * const sub = onPageReady('home', () => initHomePage());\n * sub.unsubscribe(); // stop listening when no longer needed\n * ```\n */\nexport function onPageReady<T extends string>(pageId: T, handler: () => Awaitable<void>): SubscribeResult {\n logger.logMethodArgs?.('onPageReady', {pageId});\n return pageReadyChannel_.on(pageId, handler);\n}\n\n/**\n * Reads the `page-id` attribute from the first matching element in the document\n * and notifies all `onPageReady` subscribers registered for that page identifier.\n *\n * Finds the element via `document.querySelector('[page-id]')` — no argument\n * needed. Call once at application bootstrap after the DOM is ready.\n *\n * If no element with `page-id` is found
|
|
5
|
+
"/**\n * @file page-ready.ts\n *\n * Lightweight page identity signal for MPA routing.\n *\n * ## Design\n *\n * Uses a dedicated `ChannelSignal` keyed by page identifier. This gives O(1)\n * dispatch — only the handler(s) registered for the current page ID are invoked,\n * regardless of how many pages are declared in the application.\n *\n * The signal is intentionally separate from `@alwatr/action`'s action bus:\n * page identity is a routing/lifecycle concern, not a user-interaction action.\n *\n * ## Attribute convention\n *\n * Place `page-id` anywhere in the document — `dispatchPageReady` finds it\n * automatically via `querySelector('[page-id]')`:\n *\n * ```html\n * <body page-id=\"home\">…</body>\n * ```\n *\n * In SSG/SSR setups each generated page has a different `page-id` value baked\n * into the HTML, so `dispatchPageReady()` always reads the correct page without\n * any runtime routing logic.\n */\n\nimport type {Awaitable} from '@alwatr/type-helper';\nimport {createLogger} from '@alwatr/logger';\nimport {createChannelSignal} from '@alwatr/signal';\nimport type {SubscribeResult} from '@alwatr/signal';\n\nconst logger = createLogger('page-ready');\n\n/**\n * Internal channel keyed by page identifier.\n *\n * O(1) dispatch: routes directly to the handler set for the dispatched key,\n * never invoking handlers for other page IDs.\n *\n * @internal\n */\nconst pageReadyChannel_ = createChannelSignal<Record<string, void>>({name: 'page-ready'});\n\n/**\n * Subscribes to a specific page becoming ready.\n *\n * The handler is invoked when `dispatchPageReady()` is called and the\n * `page-id` attribute in the document matches `pageId`.\n *\n * Pass a string literal union as the generic parameter to constrain which\n * page IDs are valid across your application:\n *\n * ```ts\n * type PageId = 'home' | 'about' | 'product-detail';\n *\n * onPageReady<PageId>('home', () => initHomePage());\n * ```\n *\n * @param pageId - The page identifier to listen for (must match the `page-id` attribute value).\n * @param handler - Called with no arguments when the page is ready.\n * @returns A `SubscribeResult` with an `unsubscribe()` method for cleanup.\n *\n * @example\n * ```ts\n * import {onPageReady} from '@alwatr/page-ready';\n *\n * const sub = onPageReady('home', () => initHomePage());\n * sub.unsubscribe(); // stop listening when no longer needed\n * ```\n */\nexport function onPageReady<T extends string>(pageId: T, handler: () => Awaitable<void>): SubscribeResult {\n logger.logMethodArgs?.('onPageReady', {pageId});\n return pageReadyChannel_.on(pageId, handler);\n}\n\n/**\n * Subscribes to **all** page-ready events, regardless of which page ID is dispatched.\n *\n * Unlike `onPageReady` — which targets a single page ID — this handler is invoked\n * every time `dispatchPageReady()` fires, and receives the dispatched page ID as\n * its first argument.\n *\n * Useful for cross-cutting concerns that must react to every page change:\n * analytics tracking, active nav-link updates, layout transitions, etc.\n *\n * Pass a string literal union as the generic parameter to get type-safe page IDs:\n *\n * ```ts\n * type PageId = 'home' | 'about' | 'product-detail';\n *\n * subscribePageReady<PageId>((pageId) => {\n * analytics.trackPageView(pageId);\n * updateActiveNavLink(pageId);\n * });\n * ```\n *\n * @param handler - Called with the dispatched page ID on every `dispatchPageReady()` call.\n * @returns A `SubscribeResult` with an `unsubscribe()` method for cleanup.\n *\n * @example\n * ```ts\n * import {subscribePageReady} from '@alwatr/page-ready';\n *\n * const sub = subscribePageReady((pageId) => console.log('Page ready:', pageId));\n * sub.unsubscribe(); // stop listening when no longer needed\n * ```\n */\nexport function subscribePageReady<T extends string>(handler: (pageId: T) => Awaitable<void>): SubscribeResult {\n logger.logMethod?.('subscribePageReady');\n return pageReadyChannel_.subscribe((message) => {\n handler(message.name as T);\n });\n}\n\n/**\n * Reads the `page-id` attribute from the first matching element in the document\n * and notifies all `onPageReady` subscribers registered for that page identifier.\n *\n * Finds the element via `document.querySelector('[page-id]')` — no argument\n * needed. Call once at application bootstrap after the DOM is ready.\n *\n * If no element with `page-id` is found in the document, an accident is logged\n * and nothing is dispatched. An empty attribute value (`page-id=\"\"`) is treated\n * as a valid identifier and will be dispatched normally.\n *\n * @example\n * ```html\n * <body page-id=\"home\">…</body>\n * ```\n * ```ts\n * import {onPageReady, dispatchPageReady} from '@alwatr/page-ready';\n *\n * onPageReady('home', () => initHomePage());\n * dispatchPageReady();\n * ```\n */\nexport function dispatchPageReady(): void {\n logger.logMethod?.('dispatchPageReady');\n\n const pageId = document.querySelector('[page-id]')?.getAttribute('page-id')?.trim();\n\n if (pageId == null) {\n logger.accident('dispatchPageReady', 'page_id_not_found');\n return;\n }\n\n // An empty string is a valid page identifier — dispatch as-is.\n pageReadyChannel_.dispatch(pageId);\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";AA6BA,uBAAQ,uBACR,8BAAQ,uBAGR,IAAM,EAAS,EAAa,YAAY,EAUlC,EAAoB,EAA0C,CAAC,KAAM,YAAY,CAAC,EA6BjF,SAAS,CAA6B,CAAC,EAAW,EAAiD,CAExG,OADA,EAAO,gBAAgB,cAAe,CAAC,QAAM,CAAC,EACvC,EAAkB,GAAG,EAAQ,CAAO,
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";AA6BA,uBAAQ,uBACR,8BAAQ,uBAGR,IAAM,EAAS,EAAa,YAAY,EAUlC,EAAoB,EAA0C,CAAC,KAAM,YAAY,CAAC,EA6BjF,SAAS,CAA6B,CAAC,EAAW,EAAiD,CAExG,OADA,EAAO,gBAAgB,cAAe,CAAC,QAAM,CAAC,EACvC,EAAkB,GAAG,EAAQ,CAAO,EAmCtC,SAAS,CAAoC,CAAC,EAA0D,CAE7G,OADA,EAAO,YAAY,oBAAoB,EAChC,EAAkB,UAAU,CAAC,IAAY,CAC9C,EAAQ,EAAQ,IAAS,EAC1B,EAyBI,SAAS,CAAiB,EAAS,CACxC,EAAO,YAAY,mBAAmB,EAEtC,IAAM,EAAS,SAAS,cAAc,WAAW,GAAG,aAAa,SAAS,GAAG,KAAK,EAElF,GAAI,GAAU,KAAM,CAClB,EAAO,SAAS,oBAAqB,mBAAmB,EACxD,OAIF,EAAkB,SAAS,CAAM",
|
|
8
|
+
"debugId": "8902EDA064F1D27364756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/page-ready.d.ts
CHANGED
|
@@ -55,6 +55,39 @@ import type { SubscribeResult } from '@alwatr/signal';
|
|
|
55
55
|
* ```
|
|
56
56
|
*/
|
|
57
57
|
export declare function onPageReady<T extends string>(pageId: T, handler: () => Awaitable<void>): SubscribeResult;
|
|
58
|
+
/**
|
|
59
|
+
* Subscribes to **all** page-ready events, regardless of which page ID is dispatched.
|
|
60
|
+
*
|
|
61
|
+
* Unlike `onPageReady` — which targets a single page ID — this handler is invoked
|
|
62
|
+
* every time `dispatchPageReady()` fires, and receives the dispatched page ID as
|
|
63
|
+
* its first argument.
|
|
64
|
+
*
|
|
65
|
+
* Useful for cross-cutting concerns that must react to every page change:
|
|
66
|
+
* analytics tracking, active nav-link updates, layout transitions, etc.
|
|
67
|
+
*
|
|
68
|
+
* Pass a string literal union as the generic parameter to get type-safe page IDs:
|
|
69
|
+
*
|
|
70
|
+
* ```ts
|
|
71
|
+
* type PageId = 'home' | 'about' | 'product-detail';
|
|
72
|
+
*
|
|
73
|
+
* subscribePageReady<PageId>((pageId) => {
|
|
74
|
+
* analytics.trackPageView(pageId);
|
|
75
|
+
* updateActiveNavLink(pageId);
|
|
76
|
+
* });
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* @param handler - Called with the dispatched page ID on every `dispatchPageReady()` call.
|
|
80
|
+
* @returns A `SubscribeResult` with an `unsubscribe()` method for cleanup.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```ts
|
|
84
|
+
* import {subscribePageReady} from '@alwatr/page-ready';
|
|
85
|
+
*
|
|
86
|
+
* const sub = subscribePageReady((pageId) => console.log('Page ready:', pageId));
|
|
87
|
+
* sub.unsubscribe(); // stop listening when no longer needed
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export declare function subscribePageReady<T extends string>(handler: (pageId: T) => Awaitable<void>): SubscribeResult;
|
|
58
91
|
/**
|
|
59
92
|
* Reads the `page-id` attribute from the first matching element in the document
|
|
60
93
|
* and notifies all `onPageReady` subscribers registered for that page identifier.
|
|
@@ -62,8 +95,9 @@ export declare function onPageReady<T extends string>(pageId: T, handler: () =>
|
|
|
62
95
|
* Finds the element via `document.querySelector('[page-id]')` — no argument
|
|
63
96
|
* needed. Call once at application bootstrap after the DOM is ready.
|
|
64
97
|
*
|
|
65
|
-
* If no element with `page-id` is found
|
|
66
|
-
*
|
|
98
|
+
* If no element with `page-id` is found in the document, an accident is logged
|
|
99
|
+
* and nothing is dispatched. An empty attribute value (`page-id=""`) is treated
|
|
100
|
+
* as a valid identifier and will be dispatched normally.
|
|
67
101
|
*
|
|
68
102
|
* @example
|
|
69
103
|
* ```html
|
package/dist/page-ready.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page-ready.d.ts","sourceRoot":"","sources":["../src/page-ready.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAGnD,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAcpD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,eAAe,CAGxG;AAED
|
|
1
|
+
{"version":3,"file":"page-ready.d.ts","sourceRoot":"","sources":["../src/page-ready.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAGnD,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAcpD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,eAAe,CAGxG;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,SAAS,CAAC,IAAI,CAAC,GAAG,eAAe,CAK7G;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAYxC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alwatr/page-ready",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.16.0",
|
|
4
4
|
"description": "Lightweight page identity signal for MPA routing — reads page-id attribute and notifies subscribers.",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"author": "S. Ali Mihandoost <ali.mihandoost@gmail.com> (https://ali.mihandoost.com)",
|
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
},
|
|
22
22
|
"sideEffects": false,
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@alwatr/logger": "9.
|
|
25
|
-
"@alwatr/signal": "9.
|
|
24
|
+
"@alwatr/logger": "9.16.0",
|
|
25
|
+
"@alwatr/signal": "9.16.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@alwatr/nano-build": "9.14.0",
|
|
29
|
-
"@alwatr/standard": "9.
|
|
29
|
+
"@alwatr/standard": "9.16.0",
|
|
30
30
|
"@alwatr/type-helper": "9.14.0",
|
|
31
31
|
"typescript": "^6.0.3"
|
|
32
32
|
},
|
|
@@ -71,5 +71,5 @@
|
|
|
71
71
|
"mpa",
|
|
72
72
|
"typescript"
|
|
73
73
|
],
|
|
74
|
-
"gitHead": "
|
|
74
|
+
"gitHead": "c210044f6e8ab444ec2f9e600f095761cbd279bd"
|
|
75
75
|
}
|
package/src/main.ts
CHANGED
|
@@ -1,29 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @alwatr/page-ready — Lightweight page identity signal for MPA routing.
|
|
3
3
|
*
|
|
4
|
-
* Reads the `page-id` attribute from
|
|
5
|
-
* signal so any part of the application can react to the
|
|
6
|
-
* coupling to a full router.
|
|
4
|
+
* Reads the `page-id` attribute from the first matching element in the document
|
|
5
|
+
* and dispatches a named signal so any part of the application can react to the
|
|
6
|
+
* current page without coupling to a full router.
|
|
7
7
|
*
|
|
8
8
|
* ## Usage
|
|
9
9
|
*
|
|
10
10
|
* ```html
|
|
11
|
+
* <!-- page-id can be placed on any element, not just <body> -->
|
|
11
12
|
* <body page-id="home">…</body>
|
|
12
13
|
* ```
|
|
13
14
|
*
|
|
14
15
|
* ```ts
|
|
15
|
-
* import {onPageReady, dispatchPageReady} from '@alwatr/page-ready';
|
|
16
|
+
* import {onPageReady, subscribePageReady, dispatchPageReady} from '@alwatr/page-ready';
|
|
16
17
|
*
|
|
17
|
-
* // Subscribe
|
|
18
|
+
* // Subscribe to a specific page before dispatching.
|
|
18
19
|
* onPageReady('home', () => initHomePage());
|
|
19
20
|
*
|
|
20
|
-
* //
|
|
21
|
+
* // Or subscribe to ALL pages — handler receives the page ID.
|
|
22
|
+
* subscribePageReady((pageId) => analytics.trackPageView(pageId));
|
|
23
|
+
*
|
|
24
|
+
* // Call once at bootstrap — finds [page-id] anywhere in the document and notifies subscribers.
|
|
21
25
|
* dispatchPageReady();
|
|
22
26
|
* ```
|
|
23
27
|
*
|
|
24
28
|
* ## Public API
|
|
25
29
|
*
|
|
26
|
-
* - `onPageReady(pageId, handler)` — subscribe to a specific page becoming ready
|
|
27
|
-
* - `
|
|
30
|
+
* - `onPageReady(pageId, handler)` — subscribe to a **specific** page becoming ready
|
|
31
|
+
* - `subscribePageReady(handler)` — subscribe to **all** page-ready events; handler receives the page ID
|
|
32
|
+
* - `dispatchPageReady()` — find `[page-id]` via `querySelector` and notify subscribers
|
|
28
33
|
*/
|
|
29
34
|
export * from './page-ready.js';
|
package/src/page-ready.ts
CHANGED
|
@@ -75,6 +75,45 @@ export function onPageReady<T extends string>(pageId: T, handler: () => Awaitabl
|
|
|
75
75
|
return pageReadyChannel_.on(pageId, handler);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Subscribes to **all** page-ready events, regardless of which page ID is dispatched.
|
|
80
|
+
*
|
|
81
|
+
* Unlike `onPageReady` — which targets a single page ID — this handler is invoked
|
|
82
|
+
* every time `dispatchPageReady()` fires, and receives the dispatched page ID as
|
|
83
|
+
* its first argument.
|
|
84
|
+
*
|
|
85
|
+
* Useful for cross-cutting concerns that must react to every page change:
|
|
86
|
+
* analytics tracking, active nav-link updates, layout transitions, etc.
|
|
87
|
+
*
|
|
88
|
+
* Pass a string literal union as the generic parameter to get type-safe page IDs:
|
|
89
|
+
*
|
|
90
|
+
* ```ts
|
|
91
|
+
* type PageId = 'home' | 'about' | 'product-detail';
|
|
92
|
+
*
|
|
93
|
+
* subscribePageReady<PageId>((pageId) => {
|
|
94
|
+
* analytics.trackPageView(pageId);
|
|
95
|
+
* updateActiveNavLink(pageId);
|
|
96
|
+
* });
|
|
97
|
+
* ```
|
|
98
|
+
*
|
|
99
|
+
* @param handler - Called with the dispatched page ID on every `dispatchPageReady()` call.
|
|
100
|
+
* @returns A `SubscribeResult` with an `unsubscribe()` method for cleanup.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* import {subscribePageReady} from '@alwatr/page-ready';
|
|
105
|
+
*
|
|
106
|
+
* const sub = subscribePageReady((pageId) => console.log('Page ready:', pageId));
|
|
107
|
+
* sub.unsubscribe(); // stop listening when no longer needed
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
export function subscribePageReady<T extends string>(handler: (pageId: T) => Awaitable<void>): SubscribeResult {
|
|
111
|
+
logger.logMethod?.('subscribePageReady');
|
|
112
|
+
return pageReadyChannel_.subscribe((message) => {
|
|
113
|
+
handler(message.name as T);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
78
117
|
/**
|
|
79
118
|
* Reads the `page-id` attribute from the first matching element in the document
|
|
80
119
|
* and notifies all `onPageReady` subscribers registered for that page identifier.
|
|
@@ -82,8 +121,9 @@ export function onPageReady<T extends string>(pageId: T, handler: () => Awaitabl
|
|
|
82
121
|
* Finds the element via `document.querySelector('[page-id]')` — no argument
|
|
83
122
|
* needed. Call once at application bootstrap after the DOM is ready.
|
|
84
123
|
*
|
|
85
|
-
* If no element with `page-id` is found
|
|
86
|
-
*
|
|
124
|
+
* If no element with `page-id` is found in the document, an accident is logged
|
|
125
|
+
* and nothing is dispatched. An empty attribute value (`page-id=""`) is treated
|
|
126
|
+
* as a valid identifier and will be dispatched normally.
|
|
87
127
|
*
|
|
88
128
|
* @example
|
|
89
129
|
* ```html
|
|
@@ -101,10 +141,11 @@ export function dispatchPageReady(): void {
|
|
|
101
141
|
|
|
102
142
|
const pageId = document.querySelector('[page-id]')?.getAttribute('page-id')?.trim();
|
|
103
143
|
|
|
104
|
-
if (
|
|
144
|
+
if (pageId == null) {
|
|
105
145
|
logger.accident('dispatchPageReady', 'page_id_not_found');
|
|
106
146
|
return;
|
|
107
147
|
}
|
|
108
148
|
|
|
149
|
+
// An empty string is a valid page identifier — dispatch as-is.
|
|
109
150
|
pageReadyChannel_.dispatch(pageId);
|
|
110
151
|
}
|