@oimdb/browser 0.0.0-stage → 1.0.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 +73 -2
- package/dist/index.cjs +184 -0
- package/dist/index.d.cts +67 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.js +149 -0
- package/llms.txt +4076 -0
- package/package.json +69 -4
package/README.md
CHANGED
|
@@ -1,3 +1,74 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @oimdb/browser
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Browser state as OIMDB single slots: the network, window focus, document visibility and the location.
|
|
4
|
+
An effect, a computed or a React hook reads them like any other slot, and the browser listener exists
|
|
5
|
+
only while something is subscribed.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @oimdb/browser @oimdb/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## A factory returns a single slot service
|
|
12
|
+
|
|
13
|
+
Each factory returns an `OIMSingleSlotService`: build a store slot on your `db` and read the slot through
|
|
14
|
+
the service, as with any single.
|
|
15
|
+
|
|
16
|
+
```ts setup
|
|
17
|
+
import { OIMDB, OIMEffectService } from '@oimdb/core';
|
|
18
|
+
import {
|
|
19
|
+
createOIMBrowserOnlineSingleSlotService,
|
|
20
|
+
createOIMBrowserFocusSingleSlotService,
|
|
21
|
+
createOIMBrowserVisibilitySingleSlotService,
|
|
22
|
+
createOIMBrowserLocationSingleSlotService,
|
|
23
|
+
} from '@oimdb/browser';
|
|
24
|
+
|
|
25
|
+
const db = new OIMDB();
|
|
26
|
+
const refetchStale = (): void => undefined;
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
const onlineService = createOIMBrowserOnlineSingleSlotService();
|
|
31
|
+
const online = onlineService.createStoreSlot(db);
|
|
32
|
+
const onlineSlot = onlineService.getSingleSlot(online);
|
|
33
|
+
|
|
34
|
+
OIMEffectService.INSTANCE.createEffect(db, () => {
|
|
35
|
+
if (onlineSlot.entity?.isOnline === true) refetchStale();
|
|
36
|
+
}, [onlineSlot]);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
In React, `useOIMSingleEntity(onlineService, online)` from `@oimdb/react` reads the same entity.
|
|
40
|
+
|
|
41
|
+
| Factory | Entity | Listens to |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| `createOIMBrowserOnlineSingleSlotService` | `TOIMBrowserOnlineEntity` `{ isOnline }` | `online`, `offline` on `window` |
|
|
44
|
+
| `createOIMBrowserFocusSingleSlotService` | `TOIMBrowserFocusEntity` `{ isFocused }` | `focus`, `blur` on `window` |
|
|
45
|
+
| `createOIMBrowserVisibilitySingleSlotService` | `TOIMBrowserVisibilityEntity` `{ isVisible }` | `visibilitychange` on `document` |
|
|
46
|
+
| `createOIMBrowserLocationSingleSlotService` | `TOIMBrowserLocationEntity` `{ href, pathname, search, hash }` | `currententrychange` on `navigation`; without the Navigation API, `popstate` and `hashchange` on `window` |
|
|
47
|
+
|
|
48
|
+
Every factory takes an optional `defaults` (an `OIMDefaults` of `@oimdb/core`), passed on to the
|
|
49
|
+
single slot service and its entity content service.
|
|
50
|
+
|
|
51
|
+
## When the browser is listened to
|
|
52
|
+
|
|
53
|
+
The service is built with a slot subscription listener. When the slot gets its first subscriber, the
|
|
54
|
+
listener adds its browser listener and writes the current value; when the last subscriber goes, it
|
|
55
|
+
takes the browser listener off. A subscriber reached through a computed counts too: an effect on a
|
|
56
|
+
computed that reads the slot is enough.
|
|
57
|
+
|
|
58
|
+
A write that would land during a flush is made right after that flush ends, because a channel refuses
|
|
59
|
+
writes for the whole of a flush. An event that leaves the value as it was wakes nobody.
|
|
60
|
+
|
|
61
|
+
## What each value means
|
|
62
|
+
|
|
63
|
+
- **`isOnline`** is `navigator.onLine`: `true` means a network interface exists, not that the internet
|
|
64
|
+
answers. Without `navigator.onLine` it is `true`.
|
|
65
|
+
- **`isFocused`** is `document.hasFocus()`, read on every `focus` and `blur`, not taken from the event:
|
|
66
|
+
focus moving into an iframe fires `blur` on the window while the page still has focus. Without
|
|
67
|
+
`document.hasFocus` it is `true`.
|
|
68
|
+
- **`isVisible`** is `document.visibilityState !== 'hidden'`; without `document` it is `true`.
|
|
69
|
+
- **The location**: `history.pushState` fires no event, so only the Navigation API sees a router push.
|
|
70
|
+
Without it the slot follows `popstate` and `hashchange`, and a push made by a router is seen at the next
|
|
71
|
+
of those events.
|
|
72
|
+
|
|
73
|
+
Each check reads the API it needs, so the package loads under SSR and in workers and leaves the slot at
|
|
74
|
+
the value above.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
OIMBrowserFocusSlotSubscriptionListener: () => OIMBrowserFocusSlotSubscriptionListener,
|
|
24
|
+
OIMBrowserLocationSlotSubscriptionListener: () => OIMBrowserLocationSlotSubscriptionListener,
|
|
25
|
+
OIMBrowserOnlineSlotSubscriptionListener: () => OIMBrowserOnlineSlotSubscriptionListener,
|
|
26
|
+
OIMBrowserSlotSubscriptionListener: () => OIMBrowserSlotSubscriptionListener,
|
|
27
|
+
OIMBrowserVisibilitySlotSubscriptionListener: () => OIMBrowserVisibilitySlotSubscriptionListener,
|
|
28
|
+
createOIMBrowserFocusSingleSlotService: () => createOIMBrowserFocusSingleSlotService,
|
|
29
|
+
createOIMBrowserLocationSingleSlotService: () => createOIMBrowserLocationSingleSlotService,
|
|
30
|
+
createOIMBrowserOnlineSingleSlotService: () => createOIMBrowserOnlineSingleSlotService,
|
|
31
|
+
createOIMBrowserVisibilitySingleSlotService: () => createOIMBrowserVisibilitySingleSlotService
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(index_exports);
|
|
34
|
+
|
|
35
|
+
// src/OIMBrowserSlotSubscriptionListener.ts
|
|
36
|
+
var import_core = require("@oimdb/core");
|
|
37
|
+
var OIMBrowserSlotSubscriptionListener = class {
|
|
38
|
+
constructor(slotContentEntityService) {
|
|
39
|
+
this.browserListenerDisposersBySlot = /* @__PURE__ */ new Map();
|
|
40
|
+
this.slotContentEntityService = slotContentEntityService;
|
|
41
|
+
}
|
|
42
|
+
onSlotFirstSubscribed(slot) {
|
|
43
|
+
const entitySlot = slot;
|
|
44
|
+
this.browserListenerDisposersBySlot.set(slot, this.listenToBrowser(() => {
|
|
45
|
+
this.writeCurrentEntity(entitySlot);
|
|
46
|
+
}));
|
|
47
|
+
this.writeCurrentEntity(entitySlot);
|
|
48
|
+
}
|
|
49
|
+
onSlotLastUnsubscribed(slot) {
|
|
50
|
+
const browserListenerDisposer = this.browserListenerDisposersBySlot.get(slot);
|
|
51
|
+
if (browserListenerDisposer === void 0) return;
|
|
52
|
+
this.browserListenerDisposersBySlot.delete(slot);
|
|
53
|
+
browserListenerDisposer();
|
|
54
|
+
}
|
|
55
|
+
writeCurrentEntity(entitySlot) {
|
|
56
|
+
const channelSlot = entitySlot.storeSlot.channelSlot;
|
|
57
|
+
if (!import_core.OIMChannelService.INSTANCE.isRefusingWrites(channelSlot)) {
|
|
58
|
+
this.slotContentEntityService.writeSlotContent(entitySlot, this.readCurrentEntity());
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const afterFlushDisposer = import_core.OIMChannelService.INSTANCE.subscribeAfterEachFlush(channelSlot, () => {
|
|
62
|
+
afterFlushDisposer();
|
|
63
|
+
if ((0, import_core.oimIsSlotReleased)(entitySlot)) return;
|
|
64
|
+
this.slotContentEntityService.writeSlotContent(entitySlot, this.readCurrentEntity());
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// src/constants/OIM_NO_BROWSER_LISTENER_DISPOSER.ts
|
|
70
|
+
var OIM_NO_BROWSER_LISTENER_DISPOSER = () => void 0;
|
|
71
|
+
|
|
72
|
+
// src/utils/oimListenToBrowserWindow.ts
|
|
73
|
+
function oimListenToBrowserWindow(eventNames, onBrowserChanged) {
|
|
74
|
+
const browserWindow = globalThis;
|
|
75
|
+
if (typeof browserWindow.addEventListener !== "function" || typeof browserWindow.removeEventListener !== "function") {
|
|
76
|
+
return OIM_NO_BROWSER_LISTENER_DISPOSER;
|
|
77
|
+
}
|
|
78
|
+
const addEventListener = browserWindow.addEventListener.bind(globalThis);
|
|
79
|
+
const removeEventListener = browserWindow.removeEventListener.bind(globalThis);
|
|
80
|
+
for (const eventName of eventNames) addEventListener(eventName, onBrowserChanged);
|
|
81
|
+
return () => {
|
|
82
|
+
for (const eventName of eventNames) removeEventListener(eventName, onBrowserChanged);
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/OIMBrowserOnlineSlotSubscriptionListener.ts
|
|
87
|
+
var OIMBrowserOnlineSlotSubscriptionListener = class extends OIMBrowserSlotSubscriptionListener {
|
|
88
|
+
readCurrentEntity() {
|
|
89
|
+
const navigatorOfBrowser = globalThis.navigator;
|
|
90
|
+
return { isOnline: typeof navigatorOfBrowser?.onLine === "boolean" ? navigatorOfBrowser.onLine : true };
|
|
91
|
+
}
|
|
92
|
+
listenToBrowser(onBrowserChanged) {
|
|
93
|
+
return oimListenToBrowserWindow(["online", "offline"], onBrowserChanged);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// src/createOIMBrowserSingleSlotService.ts
|
|
98
|
+
var import_core2 = require("@oimdb/core");
|
|
99
|
+
function createOIMBrowserSingleSlotService(createSlotSubscriptionListener, options) {
|
|
100
|
+
const slotContentService = (0, import_core2.createOIMSlotContentEntityService)({ defaults: options.defaults });
|
|
101
|
+
return (0, import_core2.createOIMSingleSlotService)({
|
|
102
|
+
defaults: options.defaults,
|
|
103
|
+
slotContentService,
|
|
104
|
+
slotSubscriptionListener: createSlotSubscriptionListener(slotContentService)
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/createOIMBrowserOnlineSingleSlotService.ts
|
|
109
|
+
function createOIMBrowserOnlineSingleSlotService(options = {}) {
|
|
110
|
+
return createOIMBrowserSingleSlotService((slotContentEntityService) => new OIMBrowserOnlineSlotSubscriptionListener(slotContentEntityService), options);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/OIMBrowserFocusSlotSubscriptionListener.ts
|
|
114
|
+
var OIMBrowserFocusSlotSubscriptionListener = class extends OIMBrowserSlotSubscriptionListener {
|
|
115
|
+
readCurrentEntity() {
|
|
116
|
+
const documentOfBrowser = globalThis.document;
|
|
117
|
+
return { isFocused: typeof documentOfBrowser?.hasFocus === "function" ? documentOfBrowser.hasFocus.call(documentOfBrowser) : true };
|
|
118
|
+
}
|
|
119
|
+
listenToBrowser(onBrowserChanged) {
|
|
120
|
+
return oimListenToBrowserWindow(["focus", "blur"], onBrowserChanged);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// src/createOIMBrowserFocusSingleSlotService.ts
|
|
125
|
+
function createOIMBrowserFocusSingleSlotService(options = {}) {
|
|
126
|
+
return createOIMBrowserSingleSlotService((slotContentEntityService) => new OIMBrowserFocusSlotSubscriptionListener(slotContentEntityService), options);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// src/OIMBrowserVisibilitySlotSubscriptionListener.ts
|
|
130
|
+
var OIMBrowserVisibilitySlotSubscriptionListener = class extends OIMBrowserSlotSubscriptionListener {
|
|
131
|
+
readCurrentEntity() {
|
|
132
|
+
const documentOfBrowser = globalThis.document;
|
|
133
|
+
return { isVisible: documentOfBrowser?.visibilityState !== "hidden" };
|
|
134
|
+
}
|
|
135
|
+
listenToBrowser(onBrowserChanged) {
|
|
136
|
+
const documentOfBrowser = globalThis.document;
|
|
137
|
+
if (documentOfBrowser === void 0 || typeof documentOfBrowser.addEventListener !== "function") return OIM_NO_BROWSER_LISTENER_DISPOSER;
|
|
138
|
+
documentOfBrowser.addEventListener("visibilitychange", onBrowserChanged);
|
|
139
|
+
return () => {
|
|
140
|
+
documentOfBrowser.removeEventListener("visibilitychange", onBrowserChanged);
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// src/createOIMBrowserVisibilitySingleSlotService.ts
|
|
146
|
+
function createOIMBrowserVisibilitySingleSlotService(options = {}) {
|
|
147
|
+
return createOIMBrowserSingleSlotService((slotContentEntityService) => new OIMBrowserVisibilitySlotSubscriptionListener(slotContentEntityService), options);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// src/OIMBrowserLocationSlotSubscriptionListener.ts
|
|
151
|
+
var OIMBrowserLocationSlotSubscriptionListener = class extends OIMBrowserSlotSubscriptionListener {
|
|
152
|
+
readCurrentEntity() {
|
|
153
|
+
const locationOfBrowser = globalThis.location;
|
|
154
|
+
if (locationOfBrowser === void 0) return { href: "", pathname: "", search: "", hash: "" };
|
|
155
|
+
return { href: locationOfBrowser.href, pathname: locationOfBrowser.pathname, search: locationOfBrowser.search, hash: locationOfBrowser.hash };
|
|
156
|
+
}
|
|
157
|
+
listenToBrowser(onBrowserChanged) {
|
|
158
|
+
const navigationOfBrowser = globalThis.navigation;
|
|
159
|
+
if (navigationOfBrowser !== void 0 && typeof navigationOfBrowser.addEventListener === "function") {
|
|
160
|
+
navigationOfBrowser.addEventListener("currententrychange", onBrowserChanged);
|
|
161
|
+
return () => {
|
|
162
|
+
navigationOfBrowser.removeEventListener("currententrychange", onBrowserChanged);
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return oimListenToBrowserWindow(["popstate", "hashchange"], onBrowserChanged);
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// src/createOIMBrowserLocationSingleSlotService.ts
|
|
170
|
+
function createOIMBrowserLocationSingleSlotService(options = {}) {
|
|
171
|
+
return createOIMBrowserSingleSlotService((slotContentEntityService) => new OIMBrowserLocationSlotSubscriptionListener(slotContentEntityService), options);
|
|
172
|
+
}
|
|
173
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
174
|
+
0 && (module.exports = {
|
|
175
|
+
OIMBrowserFocusSlotSubscriptionListener,
|
|
176
|
+
OIMBrowserLocationSlotSubscriptionListener,
|
|
177
|
+
OIMBrowserOnlineSlotSubscriptionListener,
|
|
178
|
+
OIMBrowserSlotSubscriptionListener,
|
|
179
|
+
OIMBrowserVisibilitySlotSubscriptionListener,
|
|
180
|
+
createOIMBrowserFocusSingleSlotService,
|
|
181
|
+
createOIMBrowserLocationSingleSlotService,
|
|
182
|
+
createOIMBrowserOnlineSingleSlotService,
|
|
183
|
+
createOIMBrowserVisibilitySingleSlotService
|
|
184
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { IOIMSlotSubscriptionListener, OIMSlotContentEntityService, TOIMSlot, TOIMDisposer, OIMSingleSlotService, TOIMEntityContentSlot, OIMDefaults } from '@oimdb/core';
|
|
2
|
+
|
|
3
|
+
declare abstract class OIMBrowserSlotSubscriptionListener<TEntity extends object> implements IOIMSlotSubscriptionListener {
|
|
4
|
+
private readonly slotContentEntityService;
|
|
5
|
+
private readonly browserListenerDisposersBySlot;
|
|
6
|
+
constructor(slotContentEntityService: OIMSlotContentEntityService<TEntity>);
|
|
7
|
+
onSlotFirstSubscribed(slot: TOIMSlot): void;
|
|
8
|
+
onSlotLastUnsubscribed(slot: TOIMSlot): void;
|
|
9
|
+
protected abstract readCurrentEntity(): TEntity;
|
|
10
|
+
protected abstract listenToBrowser(onBrowserChanged: () => void): TOIMDisposer;
|
|
11
|
+
private writeCurrentEntity;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type TOIMBrowserSingleSlotService<TEntity extends object> = OIMSingleSlotService<TOIMEntityContentSlot<TEntity>, OIMSlotContentEntityService<TEntity>>;
|
|
15
|
+
|
|
16
|
+
type TOIMBrowserSingleSlotServiceOptions = {
|
|
17
|
+
defaults?: OIMDefaults | undefined;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type TOIMBrowserOnlineEntity = {
|
|
21
|
+
readonly isOnline: boolean;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
declare function createOIMBrowserOnlineSingleSlotService(options?: TOIMBrowserSingleSlotServiceOptions): TOIMBrowserSingleSlotService<TOIMBrowserOnlineEntity>;
|
|
25
|
+
|
|
26
|
+
declare class OIMBrowserOnlineSlotSubscriptionListener extends OIMBrowserSlotSubscriptionListener<TOIMBrowserOnlineEntity> {
|
|
27
|
+
protected readCurrentEntity(): TOIMBrowserOnlineEntity;
|
|
28
|
+
protected listenToBrowser(onBrowserChanged: () => void): TOIMDisposer;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type TOIMBrowserFocusEntity = {
|
|
32
|
+
readonly isFocused: boolean;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
declare function createOIMBrowserFocusSingleSlotService(options?: TOIMBrowserSingleSlotServiceOptions): TOIMBrowserSingleSlotService<TOIMBrowserFocusEntity>;
|
|
36
|
+
|
|
37
|
+
declare class OIMBrowserFocusSlotSubscriptionListener extends OIMBrowserSlotSubscriptionListener<TOIMBrowserFocusEntity> {
|
|
38
|
+
protected readCurrentEntity(): TOIMBrowserFocusEntity;
|
|
39
|
+
protected listenToBrowser(onBrowserChanged: () => void): TOIMDisposer;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type TOIMBrowserVisibilityEntity = {
|
|
43
|
+
readonly isVisible: boolean;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
declare function createOIMBrowserVisibilitySingleSlotService(options?: TOIMBrowserSingleSlotServiceOptions): TOIMBrowserSingleSlotService<TOIMBrowserVisibilityEntity>;
|
|
47
|
+
|
|
48
|
+
declare class OIMBrowserVisibilitySlotSubscriptionListener extends OIMBrowserSlotSubscriptionListener<TOIMBrowserVisibilityEntity> {
|
|
49
|
+
protected readCurrentEntity(): TOIMBrowserVisibilityEntity;
|
|
50
|
+
protected listenToBrowser(onBrowserChanged: () => void): TOIMDisposer;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type TOIMBrowserLocationEntity = {
|
|
54
|
+
readonly href: string;
|
|
55
|
+
readonly pathname: string;
|
|
56
|
+
readonly search: string;
|
|
57
|
+
readonly hash: string;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
declare function createOIMBrowserLocationSingleSlotService(options?: TOIMBrowserSingleSlotServiceOptions): TOIMBrowserSingleSlotService<TOIMBrowserLocationEntity>;
|
|
61
|
+
|
|
62
|
+
declare class OIMBrowserLocationSlotSubscriptionListener extends OIMBrowserSlotSubscriptionListener<TOIMBrowserLocationEntity> {
|
|
63
|
+
protected readCurrentEntity(): TOIMBrowserLocationEntity;
|
|
64
|
+
protected listenToBrowser(onBrowserChanged: () => void): TOIMDisposer;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { OIMBrowserFocusSlotSubscriptionListener, OIMBrowserLocationSlotSubscriptionListener, OIMBrowserOnlineSlotSubscriptionListener, OIMBrowserSlotSubscriptionListener, OIMBrowserVisibilitySlotSubscriptionListener, type TOIMBrowserFocusEntity, type TOIMBrowserLocationEntity, type TOIMBrowserOnlineEntity, type TOIMBrowserSingleSlotService, type TOIMBrowserSingleSlotServiceOptions, type TOIMBrowserVisibilityEntity, createOIMBrowserFocusSingleSlotService, createOIMBrowserLocationSingleSlotService, createOIMBrowserOnlineSingleSlotService, createOIMBrowserVisibilitySingleSlotService };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { IOIMSlotSubscriptionListener, OIMSlotContentEntityService, TOIMSlot, TOIMDisposer, OIMSingleSlotService, TOIMEntityContentSlot, OIMDefaults } from '@oimdb/core';
|
|
2
|
+
|
|
3
|
+
declare abstract class OIMBrowserSlotSubscriptionListener<TEntity extends object> implements IOIMSlotSubscriptionListener {
|
|
4
|
+
private readonly slotContentEntityService;
|
|
5
|
+
private readonly browserListenerDisposersBySlot;
|
|
6
|
+
constructor(slotContentEntityService: OIMSlotContentEntityService<TEntity>);
|
|
7
|
+
onSlotFirstSubscribed(slot: TOIMSlot): void;
|
|
8
|
+
onSlotLastUnsubscribed(slot: TOIMSlot): void;
|
|
9
|
+
protected abstract readCurrentEntity(): TEntity;
|
|
10
|
+
protected abstract listenToBrowser(onBrowserChanged: () => void): TOIMDisposer;
|
|
11
|
+
private writeCurrentEntity;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type TOIMBrowserSingleSlotService<TEntity extends object> = OIMSingleSlotService<TOIMEntityContentSlot<TEntity>, OIMSlotContentEntityService<TEntity>>;
|
|
15
|
+
|
|
16
|
+
type TOIMBrowserSingleSlotServiceOptions = {
|
|
17
|
+
defaults?: OIMDefaults | undefined;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type TOIMBrowserOnlineEntity = {
|
|
21
|
+
readonly isOnline: boolean;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
declare function createOIMBrowserOnlineSingleSlotService(options?: TOIMBrowserSingleSlotServiceOptions): TOIMBrowserSingleSlotService<TOIMBrowserOnlineEntity>;
|
|
25
|
+
|
|
26
|
+
declare class OIMBrowserOnlineSlotSubscriptionListener extends OIMBrowserSlotSubscriptionListener<TOIMBrowserOnlineEntity> {
|
|
27
|
+
protected readCurrentEntity(): TOIMBrowserOnlineEntity;
|
|
28
|
+
protected listenToBrowser(onBrowserChanged: () => void): TOIMDisposer;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type TOIMBrowserFocusEntity = {
|
|
32
|
+
readonly isFocused: boolean;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
declare function createOIMBrowserFocusSingleSlotService(options?: TOIMBrowserSingleSlotServiceOptions): TOIMBrowserSingleSlotService<TOIMBrowserFocusEntity>;
|
|
36
|
+
|
|
37
|
+
declare class OIMBrowserFocusSlotSubscriptionListener extends OIMBrowserSlotSubscriptionListener<TOIMBrowserFocusEntity> {
|
|
38
|
+
protected readCurrentEntity(): TOIMBrowserFocusEntity;
|
|
39
|
+
protected listenToBrowser(onBrowserChanged: () => void): TOIMDisposer;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type TOIMBrowserVisibilityEntity = {
|
|
43
|
+
readonly isVisible: boolean;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
declare function createOIMBrowserVisibilitySingleSlotService(options?: TOIMBrowserSingleSlotServiceOptions): TOIMBrowserSingleSlotService<TOIMBrowserVisibilityEntity>;
|
|
47
|
+
|
|
48
|
+
declare class OIMBrowserVisibilitySlotSubscriptionListener extends OIMBrowserSlotSubscriptionListener<TOIMBrowserVisibilityEntity> {
|
|
49
|
+
protected readCurrentEntity(): TOIMBrowserVisibilityEntity;
|
|
50
|
+
protected listenToBrowser(onBrowserChanged: () => void): TOIMDisposer;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type TOIMBrowserLocationEntity = {
|
|
54
|
+
readonly href: string;
|
|
55
|
+
readonly pathname: string;
|
|
56
|
+
readonly search: string;
|
|
57
|
+
readonly hash: string;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
declare function createOIMBrowserLocationSingleSlotService(options?: TOIMBrowserSingleSlotServiceOptions): TOIMBrowserSingleSlotService<TOIMBrowserLocationEntity>;
|
|
61
|
+
|
|
62
|
+
declare class OIMBrowserLocationSlotSubscriptionListener extends OIMBrowserSlotSubscriptionListener<TOIMBrowserLocationEntity> {
|
|
63
|
+
protected readCurrentEntity(): TOIMBrowserLocationEntity;
|
|
64
|
+
protected listenToBrowser(onBrowserChanged: () => void): TOIMDisposer;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { OIMBrowserFocusSlotSubscriptionListener, OIMBrowserLocationSlotSubscriptionListener, OIMBrowserOnlineSlotSubscriptionListener, OIMBrowserSlotSubscriptionListener, OIMBrowserVisibilitySlotSubscriptionListener, type TOIMBrowserFocusEntity, type TOIMBrowserLocationEntity, type TOIMBrowserOnlineEntity, type TOIMBrowserSingleSlotService, type TOIMBrowserSingleSlotServiceOptions, type TOIMBrowserVisibilityEntity, createOIMBrowserFocusSingleSlotService, createOIMBrowserLocationSingleSlotService, createOIMBrowserOnlineSingleSlotService, createOIMBrowserVisibilitySingleSlotService };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// src/OIMBrowserSlotSubscriptionListener.ts
|
|
2
|
+
import { OIMChannelService, oimIsSlotReleased } from "@oimdb/core";
|
|
3
|
+
var OIMBrowserSlotSubscriptionListener = class {
|
|
4
|
+
constructor(slotContentEntityService) {
|
|
5
|
+
this.browserListenerDisposersBySlot = /* @__PURE__ */ new Map();
|
|
6
|
+
this.slotContentEntityService = slotContentEntityService;
|
|
7
|
+
}
|
|
8
|
+
onSlotFirstSubscribed(slot) {
|
|
9
|
+
const entitySlot = slot;
|
|
10
|
+
this.browserListenerDisposersBySlot.set(slot, this.listenToBrowser(() => {
|
|
11
|
+
this.writeCurrentEntity(entitySlot);
|
|
12
|
+
}));
|
|
13
|
+
this.writeCurrentEntity(entitySlot);
|
|
14
|
+
}
|
|
15
|
+
onSlotLastUnsubscribed(slot) {
|
|
16
|
+
const browserListenerDisposer = this.browserListenerDisposersBySlot.get(slot);
|
|
17
|
+
if (browserListenerDisposer === void 0) return;
|
|
18
|
+
this.browserListenerDisposersBySlot.delete(slot);
|
|
19
|
+
browserListenerDisposer();
|
|
20
|
+
}
|
|
21
|
+
writeCurrentEntity(entitySlot) {
|
|
22
|
+
const channelSlot = entitySlot.storeSlot.channelSlot;
|
|
23
|
+
if (!OIMChannelService.INSTANCE.isRefusingWrites(channelSlot)) {
|
|
24
|
+
this.slotContentEntityService.writeSlotContent(entitySlot, this.readCurrentEntity());
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const afterFlushDisposer = OIMChannelService.INSTANCE.subscribeAfterEachFlush(channelSlot, () => {
|
|
28
|
+
afterFlushDisposer();
|
|
29
|
+
if (oimIsSlotReleased(entitySlot)) return;
|
|
30
|
+
this.slotContentEntityService.writeSlotContent(entitySlot, this.readCurrentEntity());
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// src/constants/OIM_NO_BROWSER_LISTENER_DISPOSER.ts
|
|
36
|
+
var OIM_NO_BROWSER_LISTENER_DISPOSER = () => void 0;
|
|
37
|
+
|
|
38
|
+
// src/utils/oimListenToBrowserWindow.ts
|
|
39
|
+
function oimListenToBrowserWindow(eventNames, onBrowserChanged) {
|
|
40
|
+
const browserWindow = globalThis;
|
|
41
|
+
if (typeof browserWindow.addEventListener !== "function" || typeof browserWindow.removeEventListener !== "function") {
|
|
42
|
+
return OIM_NO_BROWSER_LISTENER_DISPOSER;
|
|
43
|
+
}
|
|
44
|
+
const addEventListener = browserWindow.addEventListener.bind(globalThis);
|
|
45
|
+
const removeEventListener = browserWindow.removeEventListener.bind(globalThis);
|
|
46
|
+
for (const eventName of eventNames) addEventListener(eventName, onBrowserChanged);
|
|
47
|
+
return () => {
|
|
48
|
+
for (const eventName of eventNames) removeEventListener(eventName, onBrowserChanged);
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/OIMBrowserOnlineSlotSubscriptionListener.ts
|
|
53
|
+
var OIMBrowserOnlineSlotSubscriptionListener = class extends OIMBrowserSlotSubscriptionListener {
|
|
54
|
+
readCurrentEntity() {
|
|
55
|
+
const navigatorOfBrowser = globalThis.navigator;
|
|
56
|
+
return { isOnline: typeof navigatorOfBrowser?.onLine === "boolean" ? navigatorOfBrowser.onLine : true };
|
|
57
|
+
}
|
|
58
|
+
listenToBrowser(onBrowserChanged) {
|
|
59
|
+
return oimListenToBrowserWindow(["online", "offline"], onBrowserChanged);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// src/createOIMBrowserSingleSlotService.ts
|
|
64
|
+
import { createOIMSingleSlotService, createOIMSlotContentEntityService } from "@oimdb/core";
|
|
65
|
+
function createOIMBrowserSingleSlotService(createSlotSubscriptionListener, options) {
|
|
66
|
+
const slotContentService = createOIMSlotContentEntityService({ defaults: options.defaults });
|
|
67
|
+
return createOIMSingleSlotService({
|
|
68
|
+
defaults: options.defaults,
|
|
69
|
+
slotContentService,
|
|
70
|
+
slotSubscriptionListener: createSlotSubscriptionListener(slotContentService)
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/createOIMBrowserOnlineSingleSlotService.ts
|
|
75
|
+
function createOIMBrowserOnlineSingleSlotService(options = {}) {
|
|
76
|
+
return createOIMBrowserSingleSlotService((slotContentEntityService) => new OIMBrowserOnlineSlotSubscriptionListener(slotContentEntityService), options);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/OIMBrowserFocusSlotSubscriptionListener.ts
|
|
80
|
+
var OIMBrowserFocusSlotSubscriptionListener = class extends OIMBrowserSlotSubscriptionListener {
|
|
81
|
+
readCurrentEntity() {
|
|
82
|
+
const documentOfBrowser = globalThis.document;
|
|
83
|
+
return { isFocused: typeof documentOfBrowser?.hasFocus === "function" ? documentOfBrowser.hasFocus.call(documentOfBrowser) : true };
|
|
84
|
+
}
|
|
85
|
+
listenToBrowser(onBrowserChanged) {
|
|
86
|
+
return oimListenToBrowserWindow(["focus", "blur"], onBrowserChanged);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// src/createOIMBrowserFocusSingleSlotService.ts
|
|
91
|
+
function createOIMBrowserFocusSingleSlotService(options = {}) {
|
|
92
|
+
return createOIMBrowserSingleSlotService((slotContentEntityService) => new OIMBrowserFocusSlotSubscriptionListener(slotContentEntityService), options);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/OIMBrowserVisibilitySlotSubscriptionListener.ts
|
|
96
|
+
var OIMBrowserVisibilitySlotSubscriptionListener = class extends OIMBrowserSlotSubscriptionListener {
|
|
97
|
+
readCurrentEntity() {
|
|
98
|
+
const documentOfBrowser = globalThis.document;
|
|
99
|
+
return { isVisible: documentOfBrowser?.visibilityState !== "hidden" };
|
|
100
|
+
}
|
|
101
|
+
listenToBrowser(onBrowserChanged) {
|
|
102
|
+
const documentOfBrowser = globalThis.document;
|
|
103
|
+
if (documentOfBrowser === void 0 || typeof documentOfBrowser.addEventListener !== "function") return OIM_NO_BROWSER_LISTENER_DISPOSER;
|
|
104
|
+
documentOfBrowser.addEventListener("visibilitychange", onBrowserChanged);
|
|
105
|
+
return () => {
|
|
106
|
+
documentOfBrowser.removeEventListener("visibilitychange", onBrowserChanged);
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// src/createOIMBrowserVisibilitySingleSlotService.ts
|
|
112
|
+
function createOIMBrowserVisibilitySingleSlotService(options = {}) {
|
|
113
|
+
return createOIMBrowserSingleSlotService((slotContentEntityService) => new OIMBrowserVisibilitySlotSubscriptionListener(slotContentEntityService), options);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/OIMBrowserLocationSlotSubscriptionListener.ts
|
|
117
|
+
var OIMBrowserLocationSlotSubscriptionListener = class extends OIMBrowserSlotSubscriptionListener {
|
|
118
|
+
readCurrentEntity() {
|
|
119
|
+
const locationOfBrowser = globalThis.location;
|
|
120
|
+
if (locationOfBrowser === void 0) return { href: "", pathname: "", search: "", hash: "" };
|
|
121
|
+
return { href: locationOfBrowser.href, pathname: locationOfBrowser.pathname, search: locationOfBrowser.search, hash: locationOfBrowser.hash };
|
|
122
|
+
}
|
|
123
|
+
listenToBrowser(onBrowserChanged) {
|
|
124
|
+
const navigationOfBrowser = globalThis.navigation;
|
|
125
|
+
if (navigationOfBrowser !== void 0 && typeof navigationOfBrowser.addEventListener === "function") {
|
|
126
|
+
navigationOfBrowser.addEventListener("currententrychange", onBrowserChanged);
|
|
127
|
+
return () => {
|
|
128
|
+
navigationOfBrowser.removeEventListener("currententrychange", onBrowserChanged);
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return oimListenToBrowserWindow(["popstate", "hashchange"], onBrowserChanged);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// src/createOIMBrowserLocationSingleSlotService.ts
|
|
136
|
+
function createOIMBrowserLocationSingleSlotService(options = {}) {
|
|
137
|
+
return createOIMBrowserSingleSlotService((slotContentEntityService) => new OIMBrowserLocationSlotSubscriptionListener(slotContentEntityService), options);
|
|
138
|
+
}
|
|
139
|
+
export {
|
|
140
|
+
OIMBrowserFocusSlotSubscriptionListener,
|
|
141
|
+
OIMBrowserLocationSlotSubscriptionListener,
|
|
142
|
+
OIMBrowserOnlineSlotSubscriptionListener,
|
|
143
|
+
OIMBrowserSlotSubscriptionListener,
|
|
144
|
+
OIMBrowserVisibilitySlotSubscriptionListener,
|
|
145
|
+
createOIMBrowserFocusSingleSlotService,
|
|
146
|
+
createOIMBrowserLocationSingleSlotService,
|
|
147
|
+
createOIMBrowserOnlineSingleSlotService,
|
|
148
|
+
createOIMBrowserVisibilitySingleSlotService
|
|
149
|
+
};
|