@excom/network-status 0.1.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/.rush/temp/chunked-rush-logs/network-status.apply-exports.chunks.jsonl +1 -0
- package/.rush/temp/chunked-rush-logs/network-status.build_docs.chunks.jsonl +1 -0
- package/.rush/temp/chunked-rush-logs/network-status.build_package-metas.chunks.jsonl +1 -0
- package/.rush/temp/operation/apply-exports/all.log +1 -0
- package/.rush/temp/operation/apply-exports/log-chunks.jsonl +1 -0
- package/.rush/temp/operation/apply-exports/state.json +3 -0
- package/.rush/temp/operation/build_docs/all.log +1 -0
- package/.rush/temp/operation/build_docs/log-chunks.jsonl +1 -0
- package/.rush/temp/operation/build_docs/state.json +3 -0
- package/.rush/temp/operation/build_package-metas/all.log +1 -0
- package/.rush/temp/operation/build_package-metas/log-chunks.jsonl +1 -0
- package/.rush/temp/operation/build_package-metas/state.json +3 -0
- package/.rush/temp/shrinkwrap-deps.json +3 -0
- package/config/rig.json +5 -0
- package/index.ts +17 -0
- package/network-status.ts +215 -0
- package/package.json +42 -0
- package/rush-logs/network-status.apply-exports.cache.log +1 -0
- package/rush-logs/network-status.apply-exports.log +1 -0
- package/rush-logs/network-status.build_docs.cache.log +1 -0
- package/rush-logs/network-status.build_docs.log +1 -0
- package/rush-logs/network-status.build_package-metas.cache.log +1 -0
- package/rush-logs/network-status.build_package-metas.log +1 -0
- package/support/custom-elements.json +156 -0
- package/support/demos/offline-banner.html +6 -0
- package/support/demos/simple.html +5 -0
- package/support/dist-docs/network-status.md +125 -0
- package/support/docs/INTERNAL.md +12 -0
- package/support/docs/README.md +65 -0
- package/support/package-meta.json +103 -0
- package/support/tests/network-status.test.ts +401 -0
- package/support/tests/offline-banner.view.test.ts +20 -0
- package/support/tests/simple.view.test.ts +22 -0
- package/tsconfig.json +5 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"kind":"O","text":"Invoking: cd \"$RUSH_PROJECT_FOLDER\" && node ../heft-rig/scripts/apply-exports.mjs \n"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs \n"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs \n"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Invoking: cd "$RUSH_PROJECT_FOLDER" && node ../heft-rig/scripts/apply-exports.mjs
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"kind":"O","text":"Invoking: cd \"$RUSH_PROJECT_FOLDER\" && node ../heft-rig/scripts/apply-exports.mjs \n"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs \n"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs \n"}
|
package/config/rig.json
ADDED
package/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { NetworkStatus } from "./network-status";
|
|
2
|
+
|
|
3
|
+
NetworkStatus.define();
|
|
4
|
+
|
|
5
|
+
export { NetworkStatus };
|
|
6
|
+
|
|
7
|
+
type T_HTMLNetworkStatusElement = typeof NetworkStatus.CustomElement;
|
|
8
|
+
declare global {
|
|
9
|
+
interface HTMLNetworkStatusElement extends T_HTMLNetworkStatusElement {}
|
|
10
|
+
interface Window {
|
|
11
|
+
HTMLNetworkStatusElement: HTMLNetworkStatusElement;
|
|
12
|
+
}
|
|
13
|
+
interface HTMLElementTagNameMap {
|
|
14
|
+
"network-status": HTMLNetworkStatusElement;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export type { HTMLNetworkStatusElement };
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { ConstructorType, Neutron, TEvent } from "@excom/neutron";
|
|
2
|
+
|
|
3
|
+
interface NetworkConnection {
|
|
4
|
+
readonly type?: string;
|
|
5
|
+
readonly effectiveType?: string;
|
|
6
|
+
readonly downlink?: number;
|
|
7
|
+
readonly rtt?: number;
|
|
8
|
+
addEventListener(type: string, listener: EventListener): void;
|
|
9
|
+
removeEventListener(type: string, listener: EventListener): void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
declare global {
|
|
13
|
+
interface Navigator {
|
|
14
|
+
readonly connection?: NetworkConnection;
|
|
15
|
+
readonly mozConnection?: NetworkConnection;
|
|
16
|
+
readonly webkitConnection?: NetworkConnection;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface NetworkStatusProvision {
|
|
21
|
+
isOnline: boolean;
|
|
22
|
+
connectionType?: string | null;
|
|
23
|
+
effectiveType?: string | null;
|
|
24
|
+
downlink?: number | null;
|
|
25
|
+
rtt?: number | null;
|
|
26
|
+
lastOnline?: number | null;
|
|
27
|
+
lastOffline?: number | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type NetworkStatusOnlineEvent = TEvent & {
|
|
31
|
+
type: "network-status-online";
|
|
32
|
+
detail: NetworkStatusProvision;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type NetworkStatusOfflineEvent = TEvent & {
|
|
36
|
+
type: "network-status-offline";
|
|
37
|
+
detail: NetworkStatusProvision;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type NetworkStatusChangeEvent = TEvent & {
|
|
41
|
+
type: "network-status-change";
|
|
42
|
+
detail: NetworkStatusProvision;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Reports live network connectivity — `navigator.onLine` plus, where the
|
|
47
|
+
* Network Information API is available, connection type and effective
|
|
48
|
+
* speed. Reflects `is-mounted` so CSS can distinguish "not yet upgraded"
|
|
49
|
+
* from a genuine offline state.
|
|
50
|
+
*
|
|
51
|
+
* The Network Information API (`connection-type` / `effective-type` /
|
|
52
|
+
* `downlink` / `rtt`) is Chromium-only — unsupported in Safari and
|
|
53
|
+
* Firefox, where those fields stay `null` and only `is-online` /
|
|
54
|
+
* `network-status-online` / `network-status-offline` are meaningful. See
|
|
55
|
+
* `INTERNAL.md` for known `navigator.onLine` reliability caveats.
|
|
56
|
+
*
|
|
57
|
+
* Nothing fires at mount — the first read only sets `is-online` and
|
|
58
|
+
* `.provision`. Select on the attribute / read the provision for the
|
|
59
|
+
* initial state; the events report transitions.
|
|
60
|
+
*
|
|
61
|
+
* @fires network-status-online - Dispatched on a transition from offline
|
|
62
|
+
* to online, never at mount. `event.detail` is the full `.provision`
|
|
63
|
+
* payload.
|
|
64
|
+
* @type NetworkStatusOnlineEvent
|
|
65
|
+
* @fires network-status-offline - Dispatched on a transition from online
|
|
66
|
+
* to offline, never at mount. `event.detail` is the full `.provision`
|
|
67
|
+
* payload.
|
|
68
|
+
* @type NetworkStatusOfflineEvent
|
|
69
|
+
* @fires network-status-change - Dispatched when connection details
|
|
70
|
+
* change without an online/offline transition (Network Information API
|
|
71
|
+
* only), never at mount. `event.detail` is the full `.provision`
|
|
72
|
+
* payload.
|
|
73
|
+
* @type NetworkStatusChangeEvent
|
|
74
|
+
*/
|
|
75
|
+
export const NetworkStatus = Neutron({
|
|
76
|
+
tag: "network-status",
|
|
77
|
+
reflectDefaultProps: ["isMounted"],
|
|
78
|
+
props: {
|
|
79
|
+
// state
|
|
80
|
+
/**
|
|
81
|
+
* @provision
|
|
82
|
+
* Full connectivity snapshot: `{ isOnline, connectionType,
|
|
83
|
+
* effectiveType, downlink, rtt, lastOnline, lastOffline }`. Set at
|
|
84
|
+
* mount and on every change; the same object dispatched as
|
|
85
|
+
* `event.detail` on every fired event. `lastOnline` / `lastOffline`
|
|
86
|
+
* are stamped on transitions only. Not reflected as an attribute.
|
|
87
|
+
* @type NetworkStatusProvision
|
|
88
|
+
*/
|
|
89
|
+
provision: Object as unknown as ConstructorType<NetworkStatusProvision>,
|
|
90
|
+
/**
|
|
91
|
+
* @state
|
|
92
|
+
* Current `navigator.onLine` value.
|
|
93
|
+
*/
|
|
94
|
+
isOnline: Boolean,
|
|
95
|
+
/**
|
|
96
|
+
* @state
|
|
97
|
+
* Connection type (`wifi`, `cellular`, `ethernet`, …) from the
|
|
98
|
+
* Network Information API. `null` where unsupported (Safari,
|
|
99
|
+
* Firefox).
|
|
100
|
+
*/
|
|
101
|
+
connectionType: String,
|
|
102
|
+
/**
|
|
103
|
+
* @state
|
|
104
|
+
* Effective connection type (`4g`, `3g`, `2g`, `slow-2g`) from the
|
|
105
|
+
* Network Information API. `null` where unsupported (Safari,
|
|
106
|
+
* Firefox).
|
|
107
|
+
*/
|
|
108
|
+
effectiveType: String,
|
|
109
|
+
// private
|
|
110
|
+
networkConnection: Object as unknown as ConstructorType<NetworkConnection>,
|
|
111
|
+
},
|
|
112
|
+
})
|
|
113
|
+
.defineMethods({
|
|
114
|
+
setNetworkData: (
|
|
115
|
+
{ isOnline, networkConnection, provision },
|
|
116
|
+
argData: { isOnline?: boolean } | undefined = {}
|
|
117
|
+
) => {
|
|
118
|
+
const _isOnline = argData.isOnline ?? isOnline;
|
|
119
|
+
/* `provision` is only set here, so its absence is the first read.
|
|
120
|
+
A Boolean prop reads `false` before it's set, so `isOnline` alone
|
|
121
|
+
can't tell "unset" from "offline". */
|
|
122
|
+
const isFirstRead = !provision;
|
|
123
|
+
const isOnlineChanged = !isFirstRead && isOnline !== _isOnline;
|
|
124
|
+
const _connectionType = networkConnection?.type;
|
|
125
|
+
const _effectiveType = networkConnection?.effectiveType;
|
|
126
|
+
const newData = {
|
|
127
|
+
isOnline: _isOnline,
|
|
128
|
+
connectionType: _connectionType,
|
|
129
|
+
effectiveType: _effectiveType,
|
|
130
|
+
downlink: networkConnection?.downlink,
|
|
131
|
+
rtt: networkConnection?.rtt,
|
|
132
|
+
lastOnline:
|
|
133
|
+
isOnlineChanged && _isOnline ? Date.now() : provision?.lastOnline,
|
|
134
|
+
lastOffline:
|
|
135
|
+
isOnlineChanged && !_isOnline ? Date.now() : provision?.lastOffline,
|
|
136
|
+
};
|
|
137
|
+
return {
|
|
138
|
+
isOnline: _isOnline,
|
|
139
|
+
connectionType: _connectionType,
|
|
140
|
+
effectiveType: _effectiveType,
|
|
141
|
+
provision: newData as NetworkStatusProvision,
|
|
142
|
+
/* First read is state, not a transition: nothing fires at mount.
|
|
143
|
+
After that: `online` / `offline` on a flip, `change` otherwise. */
|
|
144
|
+
...(isFirstRead
|
|
145
|
+
? {}
|
|
146
|
+
: {
|
|
147
|
+
emit: [
|
|
148
|
+
`network-status-${
|
|
149
|
+
isOnlineChanged
|
|
150
|
+
? _isOnline
|
|
151
|
+
? "online"
|
|
152
|
+
: "offline"
|
|
153
|
+
: "change"
|
|
154
|
+
}`,
|
|
155
|
+
{ detail: newData },
|
|
156
|
+
],
|
|
157
|
+
}),
|
|
158
|
+
};
|
|
159
|
+
},
|
|
160
|
+
handleOnline: () => ({
|
|
161
|
+
setNetworkData: [{ isOnline: true }],
|
|
162
|
+
}),
|
|
163
|
+
handleOffline: () => ({
|
|
164
|
+
setNetworkData: [{ isOnline: false }],
|
|
165
|
+
}),
|
|
166
|
+
handleChange: () => ({
|
|
167
|
+
setNetworkData: [],
|
|
168
|
+
}),
|
|
169
|
+
})
|
|
170
|
+
.onConnected(({ isMoving, handleOnline, handleOffline, handleChange }) => {
|
|
171
|
+
// A move: keep the existing listeners
|
|
172
|
+
if (isMoving) return;
|
|
173
|
+
|
|
174
|
+
// Window + connection listeners
|
|
175
|
+
window.addEventListener("online", handleOnline);
|
|
176
|
+
window.addEventListener("offline", handleOffline);
|
|
177
|
+
|
|
178
|
+
// Initial `networkConnection`
|
|
179
|
+
const networkConnection =
|
|
180
|
+
navigator.connection ||
|
|
181
|
+
navigator.mozConnection ||
|
|
182
|
+
navigator.webkitConnection;
|
|
183
|
+
|
|
184
|
+
networkConnection?.addEventListener("change", handleChange);
|
|
185
|
+
|
|
186
|
+
return [
|
|
187
|
+
{ networkConnection },
|
|
188
|
+
{
|
|
189
|
+
setNetworkData: [
|
|
190
|
+
{
|
|
191
|
+
isOnline: navigator.onLine,
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
},
|
|
195
|
+
];
|
|
196
|
+
})
|
|
197
|
+
.onDisconnected(
|
|
198
|
+
({
|
|
199
|
+
isMoving,
|
|
200
|
+
handleOnline,
|
|
201
|
+
handleOffline,
|
|
202
|
+
handleChange,
|
|
203
|
+
networkConnection,
|
|
204
|
+
}) => {
|
|
205
|
+
// A move: keep the existing listeners
|
|
206
|
+
if (isMoving) return;
|
|
207
|
+
// Drop window + connection listeners
|
|
208
|
+
window.removeEventListener("online", handleOnline);
|
|
209
|
+
window.removeEventListener("offline", handleOffline);
|
|
210
|
+
networkConnection?.removeEventListener("change", handleChange);
|
|
211
|
+
return {
|
|
212
|
+
networkConnection: null,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
);
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@excom/network-status",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "<network-status> custom element",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=24.13.0"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@excom/neutron": "^0.1.0"
|
|
12
|
+
},
|
|
13
|
+
"peerDependencies": {},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@excom/heft-rig": "^0.1.0"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"url": "excom-dev/nucleus",
|
|
19
|
+
"directory": "packages/network-status"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://github.com/excom-dev/nucleus/tree/main/packages/network-status/support/docs/README.md",
|
|
22
|
+
"bugs": "https://github.com/excom-dev/nucleus/issues",
|
|
23
|
+
"keywords": [
|
|
24
|
+
"network-status",
|
|
25
|
+
"neutron",
|
|
26
|
+
"custom-elements"
|
|
27
|
+
],
|
|
28
|
+
"excom": {
|
|
29
|
+
"packageType": "kit-element"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "node node_modules/@excom/heft-rig/scripts/vite-build.mjs",
|
|
33
|
+
"build:watch": "node node_modules/@excom/heft-rig/scripts/vite-build-watch.mjs",
|
|
34
|
+
"format": "node node_modules/@excom/heft-rig/scripts/format.mjs",
|
|
35
|
+
"test": "node node_modules/@excom/heft-rig/scripts/vitest.mjs",
|
|
36
|
+
"coverage": "node node_modules/@excom/heft-rig/scripts/coverage.mjs",
|
|
37
|
+
"dev": "node node_modules/@excom/heft-rig/scripts/vite-dev.mjs",
|
|
38
|
+
"preview": "node node_modules/@excom/heft-rig/scripts/vite-preview.mjs",
|
|
39
|
+
"build:package-metas": "node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs",
|
|
40
|
+
"build:docs": "node node_modules/@excom/heft-rig/scripts/build-docs.mjs"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Caching has been disabled for this project's "apply-exports" command.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Invoking: cd "$RUSH_PROJECT_FOLDER" && node ../heft-rig/scripts/apply-exports.mjs
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
This project does not define the caching behavior of the "build:docs" command, so caching has been disabled.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
This project does not define the caching behavior of the "build:package-metas" command, so caching has been disabled.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "1.0.0",
|
|
3
|
+
"modules": [
|
|
4
|
+
{
|
|
5
|
+
"kind": "javascript-module",
|
|
6
|
+
"path": "network-status.ts",
|
|
7
|
+
"declarations": [
|
|
8
|
+
{
|
|
9
|
+
"kind": "class",
|
|
10
|
+
"name": "NetworkStatus",
|
|
11
|
+
"customElement": true,
|
|
12
|
+
"tagName": "network-status",
|
|
13
|
+
"description": "Reports live network connectivity — `navigator.onLine` plus, where the Network Information API is available, connection type and effective speed. Reflects `is-mounted` so CSS can distinguish \"not yet upgraded\" from a genuine offline state. The Network Information API (`connection-type` / `effective-type` / `downlink` / `rtt`) is Chromium-only — unsupported in Safari and Firefox, where those fields stay `null` and only `is-online` / `network-status-online` / `network-status-offline` are meaningful. See `INTERNAL.md` for known `navigator.onLine` reliability caveats. Nothing fires at mount — the first read only sets `is-online` and `.provision`. Select on the attribute / read the provision for the initial state; the events report transitions.",
|
|
14
|
+
"attributes": [
|
|
15
|
+
{
|
|
16
|
+
"name": "is-online",
|
|
17
|
+
"type": {
|
|
18
|
+
"text": "boolean"
|
|
19
|
+
},
|
|
20
|
+
"description": "Current `navigator.onLine` value.",
|
|
21
|
+
"fieldName": "isOnline"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"name": "connection-type",
|
|
25
|
+
"type": {
|
|
26
|
+
"text": "string"
|
|
27
|
+
},
|
|
28
|
+
"description": "Connection type (`wifi`, `cellular`, `ethernet`, …) from the Network Information API. `null` where unsupported (Safari, Firefox).",
|
|
29
|
+
"fieldName": "connectionType"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"name": "effective-type",
|
|
33
|
+
"type": {
|
|
34
|
+
"text": "string"
|
|
35
|
+
},
|
|
36
|
+
"description": "Effective connection type (`4g`, `3g`, `2g`, `slow-2g`) from the Network Information API. `null` where unsupported (Safari, Firefox).",
|
|
37
|
+
"fieldName": "effectiveType"
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"members": [
|
|
41
|
+
{
|
|
42
|
+
"kind": "field",
|
|
43
|
+
"name": "provision",
|
|
44
|
+
"type": {
|
|
45
|
+
"text": "NetworkStatusProvision",
|
|
46
|
+
"expanded": "{ isOnline: boolean; connectionType?: string | null; effectiveType?: string | null; downlink?: number | null; rtt?: number | null; lastOnline?: number | null; lastOffline?: number | null; }"
|
|
47
|
+
},
|
|
48
|
+
"privacy": "public",
|
|
49
|
+
"readonly": false,
|
|
50
|
+
"description": "Full connectivity snapshot: `{ isOnline, connectionType, effectiveType, downlink, rtt, lastOnline, lastOffline }`. Set at mount and on every change; the same object dispatched as `event.detail` on every fired event. `lastOnline` / `lastOffline` are stamped on transitions only. Not reflected as an attribute.",
|
|
51
|
+
"_neutron": {
|
|
52
|
+
"surface": "option"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"kind": "field",
|
|
57
|
+
"name": "isOnline",
|
|
58
|
+
"type": {
|
|
59
|
+
"text": "boolean"
|
|
60
|
+
},
|
|
61
|
+
"privacy": "public",
|
|
62
|
+
"readonly": true,
|
|
63
|
+
"description": "Current `navigator.onLine` value.",
|
|
64
|
+
"_neutron": {
|
|
65
|
+
"surface": "state"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"kind": "field",
|
|
70
|
+
"name": "connectionType",
|
|
71
|
+
"type": {
|
|
72
|
+
"text": "string"
|
|
73
|
+
},
|
|
74
|
+
"privacy": "public",
|
|
75
|
+
"readonly": true,
|
|
76
|
+
"description": "Connection type (`wifi`, `cellular`, `ethernet`, …) from the Network Information API. `null` where unsupported (Safari, Firefox).",
|
|
77
|
+
"_neutron": {
|
|
78
|
+
"surface": "state"
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"kind": "field",
|
|
83
|
+
"name": "effectiveType",
|
|
84
|
+
"type": {
|
|
85
|
+
"text": "string"
|
|
86
|
+
},
|
|
87
|
+
"privacy": "public",
|
|
88
|
+
"readonly": true,
|
|
89
|
+
"description": "Effective connection type (`4g`, `3g`, `2g`, `slow-2g`) from the Network Information API. `null` where unsupported (Safari, Firefox).",
|
|
90
|
+
"_neutron": {
|
|
91
|
+
"surface": "state"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
],
|
|
95
|
+
"events": [
|
|
96
|
+
{
|
|
97
|
+
"name": "network-status-online",
|
|
98
|
+
"description": "Dispatched on a transition from offline to online, never at mount. `event.detail` is the full `.provision` payload.",
|
|
99
|
+
"type": {
|
|
100
|
+
"text": "NetworkStatusOnlineEvent",
|
|
101
|
+
"expanded": "CustomEvent & { type: \"network-status-online\"; detail: { isOnline: boolean; connectionType?: string | null; effectiveType?: string | null; downlink?: number | null; rtt?: number | null; lastOnline?: number | null; lastOffline?: number | null; }; bubbles: true; cancelable: true; composed: true }"
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"name": "network-status-offline",
|
|
106
|
+
"description": "Dispatched on a transition from online to offline, never at mount. `event.detail` is the full `.provision` payload.",
|
|
107
|
+
"type": {
|
|
108
|
+
"text": "NetworkStatusOfflineEvent",
|
|
109
|
+
"expanded": "CustomEvent & { type: \"network-status-offline\"; detail: { isOnline: boolean; connectionType?: string | null; effectiveType?: string | null; downlink?: number | null; rtt?: number | null; lastOnline?: number | null; lastOffline?: number | null; }; bubbles: true; cancelable: true; composed: true }"
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"name": "network-status-change",
|
|
114
|
+
"description": "Dispatched when connection details change without an online/offline transition (Network Information API only), never at mount. `event.detail` is the full `.provision` payload.",
|
|
115
|
+
"type": {
|
|
116
|
+
"text": "NetworkStatusChangeEvent",
|
|
117
|
+
"expanded": "CustomEvent & { type: \"network-status-change\"; detail: { isOnline: boolean; connectionType?: string | null; effectiveType?: string | null; downlink?: number | null; rtt?: number | null; lastOnline?: number | null; lastOffline?: number | null; }; bubbles: true; cancelable: true; composed: true }"
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
],
|
|
121
|
+
"_neutron": {
|
|
122
|
+
"provisions": [
|
|
123
|
+
{
|
|
124
|
+
"name": "provision",
|
|
125
|
+
"type": {
|
|
126
|
+
"text": "NetworkStatusProvision",
|
|
127
|
+
"expanded": "{ isOnline: boolean; connectionType?: string | null; effectiveType?: string | null; downlink?: number | null; rtt?: number | null; lastOnline?: number | null; lastOffline?: number | null; }"
|
|
128
|
+
},
|
|
129
|
+
"description": "Full connectivity snapshot: `{ isOnline, connectionType, effectiveType, downlink, rtt, lastOnline, lastOffline }`. Set at mount and on every change; the same object dispatched as `event.detail` on every fired event. `lastOnline` / `lastOffline` are stamped on transitions only. Not reflected as an attribute.",
|
|
130
|
+
"fieldName": "provision"
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
],
|
|
136
|
+
"exports": [
|
|
137
|
+
{
|
|
138
|
+
"kind": "js",
|
|
139
|
+
"name": "NetworkStatus",
|
|
140
|
+
"declaration": {
|
|
141
|
+
"name": "NetworkStatus",
|
|
142
|
+
"module": "network-status.ts"
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"kind": "custom-element-definition",
|
|
147
|
+
"name": "network-status",
|
|
148
|
+
"declaration": {
|
|
149
|
+
"name": "NetworkStatus",
|
|
150
|
+
"module": "network-status.ts"
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
}
|
|
155
|
+
]
|
|
156
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# network-status
|
|
2
|
+
|
|
3
|
+
Live connectivity readout — `navigator.onLine` plus connection type and
|
|
4
|
+
speed where the browser exposes them.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
```html
|
|
8
|
+
<div>
|
|
9
|
+
<network-status></network-status>
|
|
10
|
+
<small role="note">Live — reflects real connectivity. Toggle your
|
|
11
|
+
devtools network throttling (or your device's wifi/data) to see it change.</small>
|
|
12
|
+
</div>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- **CSS-driven UI** Style from `[is-online]` / `[connection-type]`
|
|
19
|
+
- **Live events** `online` / `offline` / `change` fire as connectivity
|
|
20
|
+
shifts — on transitions, never at mount
|
|
21
|
+
- **Bindable state** `.provision` is the full snapshot; read it from Quark
|
|
22
|
+
with `prop("provision")`
|
|
23
|
+
- **Network Information API** Connection type + effective speed where
|
|
24
|
+
supported
|
|
25
|
+
- **Graceful degradation** Unsupported fields stay `null`; core
|
|
26
|
+
online/offline still works everywhere
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
`@excom/network-status` v0.1.0
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pnpm add @excom/network-status
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install @excom/network-status
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
yarn add @excom/network-status
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Import
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import "@excom/network-status";
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
## Usage
|
|
54
|
+
|
|
55
|
+
Drop it anywhere and hook `[is-online]` / `[is-mounted]` with CSS, or
|
|
56
|
+
listen for its events from a parent.
|
|
57
|
+
|
|
58
|
+
```html
|
|
59
|
+
<network-status></network-status>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```css
|
|
63
|
+
network-status:not([is-mounted])::before {
|
|
64
|
+
content: "Loading…";
|
|
65
|
+
}
|
|
66
|
+
network-status[is-online]::before {
|
|
67
|
+
content: "📶 Online";
|
|
68
|
+
}
|
|
69
|
+
network-status[is-mounted]:not([is-online])::before {
|
|
70
|
+
content: "📵 Offline";
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Nothing fires at mount: the first read sets `is-online` and `.provision`
|
|
75
|
+
only. React to the attribute (or `prop("provision")`) for the initial
|
|
76
|
+
state; `network-status-online` / `-offline` report transitions.
|
|
77
|
+
|
|
78
|
+
The Network Information API (`connection-type`, `effective-type`,
|
|
79
|
+
`downlink`, `rtt`) is Chromium-only — Safari and Firefox leave those
|
|
80
|
+
fields `null`. Build critical UX on `is-online` alone; treat the rest as
|
|
81
|
+
a progressive enhancement. Safari/iOS also have known `navigator.onLine`
|
|
82
|
+
reliability quirks — see `INTERNAL.md`.
|
|
83
|
+
|
|
84
|
+
### API Reference
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
#### Attributes
|
|
88
|
+
|
|
89
|
+
| Name | Surface | Type | Default | Values | Description |
|
|
90
|
+
| --- | --- | --- | --- | --- | --- |
|
|
91
|
+
| `is-online` | state | `boolean` | | | Current `navigator.onLine` value. |
|
|
92
|
+
| `connection-type` | state | `string` | | | Connection type (`wifi`, `cellular`, `ethernet`, …) from the Network Information API. `null` where unsupported (Safari, Firefox). |
|
|
93
|
+
| `effective-type` | state | `string` | | | Effective connection type (`4g`, `3g`, `2g`, `slow-2g`) from the Network Information API. `null` where unsupported (Safari, Firefox). |
|
|
94
|
+
|
|
95
|
+
#### Provision
|
|
96
|
+
|
|
97
|
+
| Name | Type | Description |
|
|
98
|
+
| --- | --- | --- |
|
|
99
|
+
| `provision` | `NetworkStatusProvision` (`{ isOnline: boolean; connectionType?: string \| null; effectiveType?: string \| null; downlink?: number \| null; rtt?: number \| null; lastOnline?: number \| null; lastOffline?: number \| null; }`) | Full connectivity snapshot: `{ isOnline, connectionType, effectiveType, downlink, rtt, lastOnline, lastOffline }`. Set at mount and on every change; the same object dispatched as `event.detail` on every fired event. `lastOnline` / `lastOffline` are stamped on transitions only. Not reflected as an attribute. |
|
|
100
|
+
|
|
101
|
+
#### Fires
|
|
102
|
+
|
|
103
|
+
| Name | Type | Description |
|
|
104
|
+
| --- | --- | --- |
|
|
105
|
+
| `network-status-online` | `NetworkStatusOnlineEvent` (`CustomEvent & { type: "network-status-online"; detail: { isOnline: boolean; connectionType?: string \| null; effectiveType?: string \| null; downlink?: number \| null; rtt?: number \| null; lastOnline?: number \| null; lastOffline?: number \| null; }; bubbles: true; cancelable: true; composed: true }`) | Dispatched on a transition from offline to online, never at mount. `event.detail` is the full `.provision` payload. |
|
|
106
|
+
| `network-status-offline` | `NetworkStatusOfflineEvent` (`CustomEvent & { type: "network-status-offline"; detail: { isOnline: boolean; connectionType?: string \| null; effectiveType?: string \| null; downlink?: number \| null; rtt?: number \| null; lastOnline?: number \| null; lastOffline?: number \| null; }; bubbles: true; cancelable: true; composed: true }`) | Dispatched on a transition from online to offline, never at mount. `event.detail` is the full `.provision` payload. |
|
|
107
|
+
| `network-status-change` | `NetworkStatusChangeEvent` (`CustomEvent & { type: "network-status-change"; detail: { isOnline: boolean; connectionType?: string \| null; effectiveType?: string \| null; downlink?: number \| null; rtt?: number \| null; lastOnline?: number \| null; lastOffline?: number \| null; }; bubbles: true; cancelable: true; composed: true }`) | Dispatched when connection details change without an online/offline transition (Network Information API only), never at mount. `event.detail` is the full `.provision` payload. |
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
### Examples
|
|
112
|
+
|
|
113
|
+
#### Offline banner
|
|
114
|
+
|
|
115
|
+
A banner shown purely by CSS attribute selector — no listeners needed.
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
```html
|
|
119
|
+
<div>
|
|
120
|
+
<network-status></network-status>
|
|
121
|
+
<p role="alert">You're offline — some features may be unavailable.</p>
|
|
122
|
+
<small role="note">Banner is CSS-only, driven by
|
|
123
|
+
<code>[is-online]</code>. Go offline to see it appear.</small>
|
|
124
|
+
</div>
|
|
125
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
- `navigator.onLine` is not a reliable "can reach the internet" signal.
|
|
2
|
+
Findings from the ws investigation: iOS Safari (and other WebKit
|
|
3
|
+
embeds) can report `true` while on a captive portal or a cellular
|
|
4
|
+
connection with no actual data path, and some Android WebViews lag
|
|
5
|
+
behind the real link state by several seconds after a transition.
|
|
6
|
+
Treat `is-online` as "the OS thinks it has a link", not as an
|
|
7
|
+
end-to-end connectivity check — pair with an actual request/heartbeat
|
|
8
|
+
if you need the stronger guarantee.
|
|
9
|
+
- Network Information API (`connection-type` / `effective-type` /
|
|
10
|
+
`downlink` / `rtt`) is Chromium-only. Safari and Firefox never expose
|
|
11
|
+
`navigator.connection`, so those fields stay `null` indefinitely on
|
|
12
|
+
those browsers — this is expected, not a bug.
|