@vielzeug/sentinel 2.0.1
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 +112 -0
- package/dist/_platform.cjs +2 -0
- package/dist/_platform.cjs.map +1 -0
- package/dist/_platform.d.ts +12 -0
- package/dist/_platform.d.ts.map +1 -0
- package/dist/_platform.js +2 -0
- package/dist/_platform.js.map +1 -0
- package/dist/core.cjs +2 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.d.ts +6 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +2 -0
- package/dist/core.js.map +1 -0
- package/dist/element-size.cjs +2 -0
- package/dist/element-size.cjs.map +1 -0
- package/dist/element-size.d.ts +3 -0
- package/dist/element-size.d.ts.map +1 -0
- package/dist/element-size.js +2 -0
- package/dist/element-size.js.map +1 -0
- package/dist/errors.cjs +2 -0
- package/dist/errors.cjs.map +1 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +2 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/intersection.cjs +2 -0
- package/dist/intersection.cjs.map +1 -0
- package/dist/intersection.d.ts +9 -0
- package/dist/intersection.d.ts.map +1 -0
- package/dist/intersection.js +2 -0
- package/dist/intersection.js.map +1 -0
- package/dist/media-query.cjs +2 -0
- package/dist/media-query.cjs.map +1 -0
- package/dist/media-query.d.ts +3 -0
- package/dist/media-query.d.ts.map +1 -0
- package/dist/media-query.js +2 -0
- package/dist/media-query.js.map +1 -0
- package/dist/network.cjs +2 -0
- package/dist/network.cjs.map +1 -0
- package/dist/network.d.ts +3 -0
- package/dist/network.d.ts.map +1 -0
- package/dist/network.js +2 -0
- package/dist/network.js.map +1 -0
- package/dist/sentinel.cjs +2 -0
- package/dist/sentinel.cjs.map +1 -0
- package/dist/sentinel.iife.js +2 -0
- package/dist/sentinel.iife.js.map +1 -0
- package/dist/sentinel.js +2 -0
- package/dist/sentinel.js.map +1 -0
- package/dist/types.d.ts +41 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/viewport.cjs +2 -0
- package/dist/viewport.cjs.map +1 -0
- package/dist/viewport.d.ts +3 -0
- package/dist/viewport.d.ts.map +1 -0
- package/dist/viewport.js +2 -0
- package/dist/viewport.js.map +1 -0
- package/package.json +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# @vielzeug/sentinel
|
|
2
|
+
|
|
3
|
+
Reactive browser and DOM observations with explicit ownership.
|
|
4
|
+
|
|
5
|
+
Sentinel exposes viewport, network, media query, element size, and intersection state as disposable Ripple readables.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
pnpm add @vielzeug/sentinel @vielzeug/ripple
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createViewport } from '@vielzeug/sentinel';
|
|
17
|
+
|
|
18
|
+
function observeViewport(): () => void {
|
|
19
|
+
const viewport = createViewport();
|
|
20
|
+
const render = () => {
|
|
21
|
+
console.log(`${viewport.value.width}×${viewport.value.height} at ${viewport.value.dpr}dpr`);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
render();
|
|
25
|
+
const unsubscribe = viewport.subscribe(render);
|
|
26
|
+
|
|
27
|
+
return () => {
|
|
28
|
+
unsubscribe();
|
|
29
|
+
viewport.dispose();
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const stopObserving = observeViewport();
|
|
34
|
+
// Call stopObserving() when the owning view unmounts.
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
A Sentinel implements Ripple's `Readable<T>` contract:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { computed, watch } from '@vielzeug/ripple';
|
|
41
|
+
import { createMediaQuery, createViewport } from '@vielzeug/sentinel';
|
|
42
|
+
|
|
43
|
+
const viewport = createViewport();
|
|
44
|
+
const mobileQuery = createMediaQuery('(max-width: 768px)');
|
|
45
|
+
const compact = computed(() => mobileQuery.value.matches || viewport.value.width < 400);
|
|
46
|
+
const watcher = watch(compact, console.log, { immediate: true });
|
|
47
|
+
|
|
48
|
+
watcher.dispose();
|
|
49
|
+
mobileQuery.dispose();
|
|
50
|
+
viewport.dispose();
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Element observations start at `null` until the browser reports the first entry:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { createElementSize } from '@vielzeug/sentinel';
|
|
57
|
+
|
|
58
|
+
const element = document.getElementById('panel');
|
|
59
|
+
if (!element) throw new Error('Panel not found');
|
|
60
|
+
|
|
61
|
+
const size = createElementSize(element);
|
|
62
|
+
const unsubscribe = size.subscribe(() => {
|
|
63
|
+
console.log(size.value?.width);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
unsubscribe();
|
|
67
|
+
size.dispose();
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Lifecycle
|
|
71
|
+
|
|
72
|
+
Every Sentinel provides `dispose()`, `disposed`, `disposalSignal`, and `[Symbol.dispose]()`. Pass an external signal when several observers share one lifetime:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
const controller = new AbortController();
|
|
76
|
+
const viewport = createViewport({ signal: controller.signal });
|
|
77
|
+
const network = createNetwork({ signal: controller.signal });
|
|
78
|
+
|
|
79
|
+
controller.abort();
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Isolated Ripple Runtime
|
|
83
|
+
|
|
84
|
+
Pass an isolated Ripple runtime when Sentinel state must participate in that graph:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { createRipple } from '@vielzeug/ripple';
|
|
88
|
+
import { createViewport } from '@vielzeug/sentinel';
|
|
89
|
+
|
|
90
|
+
const ripple = createRipple();
|
|
91
|
+
const viewport = createViewport({ runtime: ripple });
|
|
92
|
+
|
|
93
|
+
viewport.dispose();
|
|
94
|
+
ripple.dispose();
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Dispose Sentinels before disposing their injected Ripple runtime.
|
|
98
|
+
|
|
99
|
+
## Unavailable APIs
|
|
100
|
+
|
|
101
|
+
`createMediaQuery()`, `createElementSize()`, and `createIntersection()` throw `SentinelUnavailableError` when the required browser API is absent. Window-based factories throw the same error when no browser window is available.
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { createMediaQuery, SentinelUnavailableError } from '@vielzeug/sentinel';
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const media = createMediaQuery('(prefers-reduced-motion: reduce)');
|
|
108
|
+
media.dispose();
|
|
109
|
+
} catch (error) {
|
|
110
|
+
if (!(error instanceof SentinelUnavailableError)) throw error;
|
|
111
|
+
}
|
|
112
|
+
```
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./errors.cjs");function t(t){if(t)return t;if(typeof window>`u`)throw new e.SentinelUnavailableError(`Window is not available in this environment.`);return window}function n(t){let n=t.ownerDocument.defaultView;if(!n)throw new e.SentinelUnavailableError(`The element is not connected to a Window.`);return n}function r(e,t){let n=()=>t();if(typeof e.addEventListener==`function`)return e.addEventListener(`change`,n),()=>e.removeEventListener(`change`,n);let r=e;return typeof r.addListener==`function`?(r.addListener(n),()=>r.removeListener?.(n)):()=>{}}exports.listenMediaQuery=r,exports.resolveElementWindow=n,exports.resolveWindow=t;
|
|
2
|
+
//# sourceMappingURL=_platform.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_platform.cjs","names":[],"sources":["../src/_platform.ts"],"sourcesContent":["import { SentinelUnavailableError } from './errors.ts';\n\ntype LegacyMediaQueryList = MediaQueryList & {\n addListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n removeListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n};\n\nexport type ObserverWindow = Window & {\n readonly IntersectionObserver?: {\n new (callback: IntersectionObserverCallback, options?: IntersectionObserverInit): IntersectionObserver;\n };\n readonly ResizeObserver?: {\n new (callback: ResizeObserverCallback): ResizeObserver;\n };\n};\n\nexport function resolveWindow(target?: Window): Window {\n if (target) return target;\n if (typeof window === 'undefined') {\n throw new SentinelUnavailableError('Window is not available in this environment.');\n }\n\n return window;\n}\n\nexport function resolveElementWindow(element: Element): ObserverWindow {\n const ownerWindow = element.ownerDocument.defaultView;\n if (!ownerWindow) {\n throw new SentinelUnavailableError('The element is not connected to a Window.');\n }\n\n return ownerWindow;\n}\n\nexport function listenMediaQuery(mediaQuery: MediaQueryList, listener: () => void): () => void {\n const onChange = () => listener();\n\n if (typeof mediaQuery.addEventListener === 'function') {\n mediaQuery.addEventListener('change', onChange);\n return () => mediaQuery.removeEventListener('change', onChange);\n }\n\n const legacyMediaQuery = mediaQuery as LegacyMediaQueryList;\n if (typeof legacyMediaQuery.addListener === 'function') {\n legacyMediaQuery.addListener(onChange);\n return () => legacyMediaQuery.removeListener?.(onChange);\n }\n\n return () => {};\n}\n"],"mappings":"gCAgBA,SAAgB,EAAc,EAAyB,CACrD,GAAI,EAAQ,OAAO,EACnB,GAAI,OAAO,OAAW,IACpB,MAAM,IAAI,EAAA,yBAAyB,8CAA8C,EAGnF,OAAO,MACT,CAEA,SAAgB,EAAqB,EAAkC,CACrE,IAAM,EAAc,EAAQ,cAAc,YAC1C,GAAI,CAAC,EACH,MAAM,IAAI,EAAA,yBAAyB,2CAA2C,EAGhF,OAAO,CACT,CAEA,SAAgB,EAAiB,EAA4B,EAAkC,CAC7F,IAAM,MAAiB,EAAS,EAEhC,GAAI,OAAO,EAAW,kBAAqB,WAEzC,OADA,EAAW,iBAAiB,SAAU,CAAQ,MACjC,EAAW,oBAAoB,SAAU,CAAQ,EAGhE,IAAM,EAAmB,EAMzB,OALI,OAAO,EAAiB,aAAgB,YAC1C,EAAiB,YAAY,CAAQ,MACxB,EAAiB,iBAAiB,CAAQ,OAG5C,CAAC,CAChB"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type ObserverWindow = Window & {
|
|
2
|
+
readonly IntersectionObserver?: {
|
|
3
|
+
new (callback: IntersectionObserverCallback, options?: IntersectionObserverInit): IntersectionObserver;
|
|
4
|
+
};
|
|
5
|
+
readonly ResizeObserver?: {
|
|
6
|
+
new (callback: ResizeObserverCallback): ResizeObserver;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
export declare function resolveWindow(target?: Window): Window;
|
|
10
|
+
export declare function resolveElementWindow(element: Element): ObserverWindow;
|
|
11
|
+
export declare function listenMediaQuery(mediaQuery: MediaQueryList, listener: () => void): () => void;
|
|
12
|
+
//# sourceMappingURL=_platform.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_platform.d.ts","sourceRoot":"","sources":["../src/_platform.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG;IACpC,QAAQ,CAAC,oBAAoB,CAAC,EAAE;QAC9B,KAAK,QAAQ,EAAE,4BAA4B,EAAE,OAAO,CAAC,EAAE,wBAAwB,GAAG,oBAAoB,CAAC;KACxG,CAAC;IACF,QAAQ,CAAC,cAAc,CAAC,EAAE;QACxB,KAAK,QAAQ,EAAE,sBAAsB,GAAG,cAAc,CAAC;KACxD,CAAC;CACH,CAAC;AAEF,wBAAgB,aAAa,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAOrD;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CAOrE;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAe7F"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{SentinelUnavailableError as e}from"./errors.js";function t(t){if(t)return t;if(typeof window>`u`)throw new e(`Window is not available in this environment.`);return window}function n(t){let n=t.ownerDocument.defaultView;if(!n)throw new e(`The element is not connected to a Window.`);return n}function r(e,t){let n=()=>t();if(typeof e.addEventListener==`function`)return e.addEventListener(`change`,n),()=>e.removeEventListener(`change`,n);let r=e;return typeof r.addListener==`function`?(r.addListener(n),()=>r.removeListener?.(n)):()=>{}}export{r as listenMediaQuery,n as resolveElementWindow,t as resolveWindow};
|
|
2
|
+
//# sourceMappingURL=_platform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_platform.js","names":[],"sources":["../src/_platform.ts"],"sourcesContent":["import { SentinelUnavailableError } from './errors.ts';\n\ntype LegacyMediaQueryList = MediaQueryList & {\n addListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n removeListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n};\n\nexport type ObserverWindow = Window & {\n readonly IntersectionObserver?: {\n new (callback: IntersectionObserverCallback, options?: IntersectionObserverInit): IntersectionObserver;\n };\n readonly ResizeObserver?: {\n new (callback: ResizeObserverCallback): ResizeObserver;\n };\n};\n\nexport function resolveWindow(target?: Window): Window {\n if (target) return target;\n if (typeof window === 'undefined') {\n throw new SentinelUnavailableError('Window is not available in this environment.');\n }\n\n return window;\n}\n\nexport function resolveElementWindow(element: Element): ObserverWindow {\n const ownerWindow = element.ownerDocument.defaultView;\n if (!ownerWindow) {\n throw new SentinelUnavailableError('The element is not connected to a Window.');\n }\n\n return ownerWindow;\n}\n\nexport function listenMediaQuery(mediaQuery: MediaQueryList, listener: () => void): () => void {\n const onChange = () => listener();\n\n if (typeof mediaQuery.addEventListener === 'function') {\n mediaQuery.addEventListener('change', onChange);\n return () => mediaQuery.removeEventListener('change', onChange);\n }\n\n const legacyMediaQuery = mediaQuery as LegacyMediaQueryList;\n if (typeof legacyMediaQuery.addListener === 'function') {\n legacyMediaQuery.addListener(onChange);\n return () => legacyMediaQuery.removeListener?.(onChange);\n }\n\n return () => {};\n}\n"],"mappings":"uDAgBA,SAAgB,EAAc,EAAyB,CACrD,GAAI,EAAQ,OAAO,EACnB,GAAI,OAAO,OAAW,IACpB,MAAM,IAAI,EAAyB,8CAA8C,EAGnF,OAAO,MACT,CAEA,SAAgB,EAAqB,EAAkC,CACrE,IAAM,EAAc,EAAQ,cAAc,YAC1C,GAAI,CAAC,EACH,MAAM,IAAI,EAAyB,2CAA2C,EAGhF,OAAO,CACT,CAEA,SAAgB,EAAiB,EAA4B,EAAkC,CAC7F,IAAM,MAAiB,EAAS,EAEhC,GAAI,OAAO,EAAW,kBAAqB,WAEzC,OADA,EAAW,iBAAiB,SAAU,CAAQ,MACjC,EAAW,oBAAoB,SAAU,CAAQ,EAGhE,IAAM,EAAmB,EAMzB,OALI,OAAO,EAAiB,aAAgB,YAC1C,EAAiB,YAAY,CAAQ,MACxB,EAAiB,iBAAiB,CAAQ,OAG5C,CAAC,CAChB"}
|
package/dist/core.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
let e=require("@vielzeug/ripple");function t(e,t){return new n(e,t)}var n=class{disposalSignal;abortController=new AbortController;state;cleanup;disposedValue=!1;externalSignal;externalAbortListener;constructor(t,n){let r=t.runtime?.signal??e.signal;if(this.state=r(t.initialValue),this.disposalSignal=this.abortController.signal,t.signal?.aborted){this.disposedValue=!0,this.abortController.abort();return}try{this.cleanup=n(e=>{this.disposedValue||(this.state.value=e)})}catch(e){throw this.disposedValue=!0,this.abortController.abort(),e}if(t.signal){if(t.signal.aborted){this.dispose();return}this.externalSignal=t.signal,this.externalAbortListener=()=>this.dispose(),this.externalSignal.addEventListener(`abort`,this.externalAbortListener,{once:!0})}}get disposed(){return this.disposedValue}get value(){return this.state.value}peek(){return this.state.peek()}subscribe(e){return this.state.subscribe(e)}dispose(){if(this.disposedValue)return;this.disposedValue=!0,this.externalSignal&&this.externalAbortListener&&this.externalSignal.removeEventListener(`abort`,this.externalAbortListener);let e=this.cleanup;this.cleanup=void 0,this.externalSignal=void 0,this.externalAbortListener=void 0;try{e?.()}finally{this.abortController.abort()}}[Symbol.dispose](){this.dispose()}};exports.createSentinel=t;
|
|
2
|
+
//# sourceMappingURL=core.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.cjs","names":[],"sources":["../src/core.ts"],"sourcesContent":["import type { Signal, Unsubscribe } from '@vielzeug/ripple';\nimport { signal as createSignal } from '@vielzeug/ripple';\nimport type { Sentinel, SentinelOptions } from './types.ts';\n\nexport interface CreateSentinelOptions<T> extends SentinelOptions {\n readonly initialValue: T;\n}\n\nexport function createSentinel<T>(\n options: CreateSentinelOptions<T>,\n setup: (update: (value: T) => void) => () => void,\n): Sentinel<T> {\n return new SentinelHandle(options, setup);\n}\n\nclass SentinelHandle<T> implements Sentinel<T> {\n readonly disposalSignal: AbortSignal;\n private readonly abortController = new AbortController();\n private readonly state: Signal<T>;\n private cleanup: (() => void) | undefined;\n private disposedValue = false;\n private externalSignal: AbortSignal | undefined;\n private externalAbortListener: (() => void) | undefined;\n\n constructor(options: CreateSentinelOptions<T>, setup: (update: (value: T) => void) => () => void) {\n const signalFactory = options.runtime?.signal ?? createSignal;\n this.state = signalFactory(options.initialValue);\n this.disposalSignal = this.abortController.signal;\n\n if (options.signal?.aborted) {\n this.disposedValue = true;\n this.abortController.abort();\n return;\n }\n\n try {\n this.cleanup = setup((value) => {\n if (!this.disposedValue) this.state.value = value;\n });\n } catch (error) {\n this.disposedValue = true;\n this.abortController.abort();\n throw error;\n }\n\n if (!options.signal) return;\n\n if (options.signal.aborted) {\n this.dispose();\n return;\n }\n\n this.externalSignal = options.signal;\n this.externalAbortListener = () => this.dispose();\n this.externalSignal.addEventListener('abort', this.externalAbortListener, { once: true });\n }\n\n get disposed(): boolean {\n return this.disposedValue;\n }\n\n get value(): T {\n return this.state.value;\n }\n\n peek(): T {\n return this.state.peek();\n }\n\n subscribe(listener: () => void): Unsubscribe {\n return this.state.subscribe(listener);\n }\n\n dispose(): void {\n if (this.disposedValue) return;\n this.disposedValue = true;\n\n if (this.externalSignal && this.externalAbortListener) {\n this.externalSignal.removeEventListener('abort', this.externalAbortListener);\n }\n\n const cleanup = this.cleanup;\n this.cleanup = undefined;\n this.externalSignal = undefined;\n this.externalAbortListener = undefined;\n\n try {\n cleanup?.();\n } finally {\n this.abortController.abort();\n }\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n"],"mappings":"kCAQA,SAAgB,EACd,EACA,EACa,CACb,OAAO,IAAI,EAAe,EAAS,CAAK,CAC1C,CAEA,IAAM,EAAN,KAA+C,CAC7C,eACA,gBAAmC,IAAI,gBACvC,MACA,QACA,cAAwB,GACxB,eACA,sBAEA,YAAY,EAAmC,EAAmD,CAChG,IAAM,EAAgB,EAAQ,SAAS,QAAU,EAAA,OAIjD,GAHA,KAAK,MAAQ,EAAc,EAAQ,YAAY,EAC/C,KAAK,eAAiB,KAAK,gBAAgB,OAEvC,EAAQ,QAAQ,QAAS,CAC3B,KAAK,cAAgB,GACrB,KAAK,gBAAgB,MAAM,EAC3B,MACF,CAEA,GAAI,CACF,KAAK,QAAU,EAAO,GAAU,CACzB,KAAK,gBAAe,KAAK,MAAM,MAAQ,EAC9C,CAAC,CACH,OAAS,EAAO,CAGd,KAFA,MAAK,cAAgB,GACrB,KAAK,gBAAgB,MAAM,EACrB,CACR,CAEK,KAAQ,OAEb,IAAI,EAAQ,OAAO,QAAS,CAC1B,KAAK,QAAQ,EACb,MACF,CAEA,KAAK,eAAiB,EAAQ,OAC9B,KAAK,0BAA8B,KAAK,QAAQ,EAChD,KAAK,eAAe,iBAAiB,QAAS,KAAK,sBAAuB,CAAE,KAAM,EAAK,CAAC,CAJxF,CAKF,CAEA,IAAI,UAAoB,CACtB,OAAO,KAAK,aACd,CAEA,IAAI,OAAW,CACb,OAAO,KAAK,MAAM,KACpB,CAEA,MAAU,CACR,OAAO,KAAK,MAAM,KAAK,CACzB,CAEA,UAAU,EAAmC,CAC3C,OAAO,KAAK,MAAM,UAAU,CAAQ,CACtC,CAEA,SAAgB,CACd,GAAI,KAAK,cAAe,OACxB,KAAK,cAAgB,GAEjB,KAAK,gBAAkB,KAAK,uBAC9B,KAAK,eAAe,oBAAoB,QAAS,KAAK,qBAAqB,EAG7E,IAAM,EAAU,KAAK,QACrB,KAAK,QAAU,IAAA,GACf,KAAK,eAAiB,IAAA,GACtB,KAAK,sBAAwB,IAAA,GAE7B,GAAI,CACF,IAAU,CACZ,QAAU,CACR,KAAK,gBAAgB,MAAM,CAC7B,CACF,CAEA,CAAC,OAAO,UAAiB,CACvB,KAAK,QAAQ,CACf,CACF"}
|
package/dist/core.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Sentinel, SentinelOptions } from './types.ts';
|
|
2
|
+
export interface CreateSentinelOptions<T> extends SentinelOptions {
|
|
3
|
+
readonly initialValue: T;
|
|
4
|
+
}
|
|
5
|
+
export declare function createSentinel<T>(options: CreateSentinelOptions<T>, setup: (update: (value: T) => void) => () => void): Sentinel<T>;
|
|
6
|
+
//# sourceMappingURL=core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE5D,MAAM,WAAW,qBAAqB,CAAC,CAAC,CAAE,SAAQ,eAAe;IAC/D,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC;CAC1B;AAED,wBAAgB,cAAc,CAAC,CAAC,EAC9B,OAAO,EAAE,qBAAqB,CAAC,CAAC,CAAC,EACjC,KAAK,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,KAAK,MAAM,IAAI,GAChD,QAAQ,CAAC,CAAC,CAAC,CAEb"}
|
package/dist/core.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{signal as e}from"@vielzeug/ripple";function t(e,t){return new n(e,t)}var n=class{disposalSignal;abortController=new AbortController;state;cleanup;disposedValue=!1;externalSignal;externalAbortListener;constructor(t,n){let r=t.runtime?.signal??e;if(this.state=r(t.initialValue),this.disposalSignal=this.abortController.signal,t.signal?.aborted){this.disposedValue=!0,this.abortController.abort();return}try{this.cleanup=n(e=>{this.disposedValue||(this.state.value=e)})}catch(e){throw this.disposedValue=!0,this.abortController.abort(),e}if(t.signal){if(t.signal.aborted){this.dispose();return}this.externalSignal=t.signal,this.externalAbortListener=()=>this.dispose(),this.externalSignal.addEventListener(`abort`,this.externalAbortListener,{once:!0})}}get disposed(){return this.disposedValue}get value(){return this.state.value}peek(){return this.state.peek()}subscribe(e){return this.state.subscribe(e)}dispose(){if(this.disposedValue)return;this.disposedValue=!0,this.externalSignal&&this.externalAbortListener&&this.externalSignal.removeEventListener(`abort`,this.externalAbortListener);let e=this.cleanup;this.cleanup=void 0,this.externalSignal=void 0,this.externalAbortListener=void 0;try{e?.()}finally{this.abortController.abort()}}[Symbol.dispose](){this.dispose()}};export{t as createSentinel};
|
|
2
|
+
//# sourceMappingURL=core.js.map
|
package/dist/core.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.js","names":[],"sources":["../src/core.ts"],"sourcesContent":["import type { Signal, Unsubscribe } from '@vielzeug/ripple';\nimport { signal as createSignal } from '@vielzeug/ripple';\nimport type { Sentinel, SentinelOptions } from './types.ts';\n\nexport interface CreateSentinelOptions<T> extends SentinelOptions {\n readonly initialValue: T;\n}\n\nexport function createSentinel<T>(\n options: CreateSentinelOptions<T>,\n setup: (update: (value: T) => void) => () => void,\n): Sentinel<T> {\n return new SentinelHandle(options, setup);\n}\n\nclass SentinelHandle<T> implements Sentinel<T> {\n readonly disposalSignal: AbortSignal;\n private readonly abortController = new AbortController();\n private readonly state: Signal<T>;\n private cleanup: (() => void) | undefined;\n private disposedValue = false;\n private externalSignal: AbortSignal | undefined;\n private externalAbortListener: (() => void) | undefined;\n\n constructor(options: CreateSentinelOptions<T>, setup: (update: (value: T) => void) => () => void) {\n const signalFactory = options.runtime?.signal ?? createSignal;\n this.state = signalFactory(options.initialValue);\n this.disposalSignal = this.abortController.signal;\n\n if (options.signal?.aborted) {\n this.disposedValue = true;\n this.abortController.abort();\n return;\n }\n\n try {\n this.cleanup = setup((value) => {\n if (!this.disposedValue) this.state.value = value;\n });\n } catch (error) {\n this.disposedValue = true;\n this.abortController.abort();\n throw error;\n }\n\n if (!options.signal) return;\n\n if (options.signal.aborted) {\n this.dispose();\n return;\n }\n\n this.externalSignal = options.signal;\n this.externalAbortListener = () => this.dispose();\n this.externalSignal.addEventListener('abort', this.externalAbortListener, { once: true });\n }\n\n get disposed(): boolean {\n return this.disposedValue;\n }\n\n get value(): T {\n return this.state.value;\n }\n\n peek(): T {\n return this.state.peek();\n }\n\n subscribe(listener: () => void): Unsubscribe {\n return this.state.subscribe(listener);\n }\n\n dispose(): void {\n if (this.disposedValue) return;\n this.disposedValue = true;\n\n if (this.externalSignal && this.externalAbortListener) {\n this.externalSignal.removeEventListener('abort', this.externalAbortListener);\n }\n\n const cleanup = this.cleanup;\n this.cleanup = undefined;\n this.externalSignal = undefined;\n this.externalAbortListener = undefined;\n\n try {\n cleanup?.();\n } finally {\n this.abortController.abort();\n }\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n"],"mappings":"0CAQA,SAAgB,EACd,EACA,EACa,CACb,OAAO,IAAI,EAAe,EAAS,CAAK,CAC1C,CAEA,IAAM,EAAN,KAA+C,CAC7C,eACA,gBAAmC,IAAI,gBACvC,MACA,QACA,cAAwB,GACxB,eACA,sBAEA,YAAY,EAAmC,EAAmD,CAChG,IAAM,EAAgB,EAAQ,SAAS,QAAU,EAIjD,GAHA,KAAK,MAAQ,EAAc,EAAQ,YAAY,EAC/C,KAAK,eAAiB,KAAK,gBAAgB,OAEvC,EAAQ,QAAQ,QAAS,CAC3B,KAAK,cAAgB,GACrB,KAAK,gBAAgB,MAAM,EAC3B,MACF,CAEA,GAAI,CACF,KAAK,QAAU,EAAO,GAAU,CACzB,KAAK,gBAAe,KAAK,MAAM,MAAQ,EAC9C,CAAC,CACH,OAAS,EAAO,CAGd,KAFA,MAAK,cAAgB,GACrB,KAAK,gBAAgB,MAAM,EACrB,CACR,CAEK,KAAQ,OAEb,IAAI,EAAQ,OAAO,QAAS,CAC1B,KAAK,QAAQ,EACb,MACF,CAEA,KAAK,eAAiB,EAAQ,OAC9B,KAAK,0BAA8B,KAAK,QAAQ,EAChD,KAAK,eAAe,iBAAiB,QAAS,KAAK,sBAAuB,CAAE,KAAM,EAAK,CAAC,CAJxF,CAKF,CAEA,IAAI,UAAoB,CACtB,OAAO,KAAK,aACd,CAEA,IAAI,OAAW,CACb,OAAO,KAAK,MAAM,KACpB,CAEA,MAAU,CACR,OAAO,KAAK,MAAM,KAAK,CACzB,CAEA,UAAU,EAAmC,CAC3C,OAAO,KAAK,MAAM,UAAU,CAAQ,CACtC,CAEA,SAAgB,CACd,GAAI,KAAK,cAAe,OACxB,KAAK,cAAgB,GAEjB,KAAK,gBAAkB,KAAK,uBAC9B,KAAK,eAAe,oBAAoB,QAAS,KAAK,qBAAqB,EAG7E,IAAM,EAAU,KAAK,QACrB,KAAK,QAAU,IAAA,GACf,KAAK,eAAiB,IAAA,GACtB,KAAK,sBAAwB,IAAA,GAE7B,GAAI,CACF,IAAU,CACZ,QAAU,CACR,KAAK,gBAAgB,MAAM,CAC7B,CACF,CAEA,CAAC,OAAO,UAAiB,CACvB,KAAK,QAAQ,CACf,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./errors.cjs"),t=require("./_platform.cjs"),n=require("./core.cjs");function r(r,i){let a=t.resolveElementWindow(r).ResizeObserver;if(typeof a!=`function`)throw new e.SentinelUnavailableError(`ResizeObserver is not available in this environment.`);return n.createSentinel({initialValue:null,...i},e=>{let t=new a(t=>{let n=t.at(-1);n&&e({height:n.contentRect.height,width:n.contentRect.width})});try{t.observe(r,{box:`content-box`})}catch(e){throw t.disconnect(),e}return()=>t.disconnect()})}exports.createElementSize=r;
|
|
2
|
+
//# sourceMappingURL=element-size.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element-size.cjs","names":[],"sources":["../src/element-size.ts"],"sourcesContent":["import { resolveElementWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { ElementSizeState, Sentinel, SentinelOptions } from './types.ts';\n\nexport function createElementSize(element: Element, options?: SentinelOptions): Sentinel<ElementSizeState | null> {\n const target = resolveElementWindow(element);\n const ResizeObserverConstructor = target.ResizeObserver;\n if (typeof ResizeObserverConstructor !== 'function') {\n throw new SentinelUnavailableError('ResizeObserver is not available in this environment.');\n }\n\n return createSentinel<ElementSizeState | null>(\n {\n initialValue: null,\n ...options,\n },\n (update) => {\n const observer = new ResizeObserverConstructor((entries) => {\n const entry = entries.at(-1);\n if (!entry) return;\n\n update({\n height: entry.contentRect.height,\n width: entry.contentRect.width,\n });\n });\n\n try {\n observer.observe(element, { box: 'content-box' });\n } catch (error) {\n observer.disconnect();\n throw error;\n }\n\n return () => observer.disconnect();\n },\n );\n}\n"],"mappings":"qFAKA,SAAgB,EAAkB,EAAkB,EAA8D,CAEhH,IAAM,EADS,EAAA,qBAAqB,CACF,CAAA,CAAO,eACzC,GAAI,OAAO,GAA8B,WACvC,MAAM,IAAI,EAAA,yBAAyB,sDAAsD,EAG3F,OAAO,EAAA,eACL,CACE,aAAc,KACd,GAAG,CACL,EACC,GAAW,CACV,IAAM,EAAW,IAAI,EAA2B,GAAY,CAC1D,IAAM,EAAQ,EAAQ,GAAG,EAAE,EACtB,GAEL,EAAO,CACL,OAAQ,EAAM,YAAY,OAC1B,MAAO,EAAM,YAAY,KAC3B,CAAC,CACH,CAAC,EAED,GAAI,CACF,EAAS,QAAQ,EAAS,CAAE,IAAK,aAAc,CAAC,CAClD,OAAS,EAAO,CAEd,MADA,EAAS,WAAW,EACd,CACR,CAEA,UAAa,EAAS,WAAW,CACnC,CACF,CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element-size.d.ts","sourceRoot":"","sources":["../src/element-size.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE9E,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,QAAQ,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAiChH"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{SentinelUnavailableError as e}from"./errors.js";import{resolveElementWindow as t}from"./_platform.js";import{createSentinel as n}from"./core.js";function r(r,i){let a=t(r).ResizeObserver;if(typeof a!=`function`)throw new e(`ResizeObserver is not available in this environment.`);return n({initialValue:null,...i},e=>{let t=new a(t=>{let n=t.at(-1);n&&e({height:n.contentRect.height,width:n.contentRect.width})});try{t.observe(r,{box:`content-box`})}catch(e){throw t.disconnect(),e}return()=>t.disconnect()})}export{r as createElementSize};
|
|
2
|
+
//# sourceMappingURL=element-size.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element-size.js","names":[],"sources":["../src/element-size.ts"],"sourcesContent":["import { resolveElementWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { ElementSizeState, Sentinel, SentinelOptions } from './types.ts';\n\nexport function createElementSize(element: Element, options?: SentinelOptions): Sentinel<ElementSizeState | null> {\n const target = resolveElementWindow(element);\n const ResizeObserverConstructor = target.ResizeObserver;\n if (typeof ResizeObserverConstructor !== 'function') {\n throw new SentinelUnavailableError('ResizeObserver is not available in this environment.');\n }\n\n return createSentinel<ElementSizeState | null>(\n {\n initialValue: null,\n ...options,\n },\n (update) => {\n const observer = new ResizeObserverConstructor((entries) => {\n const entry = entries.at(-1);\n if (!entry) return;\n\n update({\n height: entry.contentRect.height,\n width: entry.contentRect.width,\n });\n });\n\n try {\n observer.observe(element, { box: 'content-box' });\n } catch (error) {\n observer.disconnect();\n throw error;\n }\n\n return () => observer.disconnect();\n },\n );\n}\n"],"mappings":"wJAKA,SAAgB,EAAkB,EAAkB,EAA8D,CAEhH,IAAM,EADS,EAAqB,CACF,CAAA,CAAO,eACzC,GAAI,OAAO,GAA8B,WACvC,MAAM,IAAI,EAAyB,sDAAsD,EAG3F,OAAO,EACL,CACE,aAAc,KACd,GAAG,CACL,EACC,GAAW,CACV,IAAM,EAAW,IAAI,EAA2B,GAAY,CAC1D,IAAM,EAAQ,EAAQ,GAAG,EAAE,EACtB,GAEL,EAAO,CACL,OAAQ,EAAM,YAAY,OAC1B,MAAO,EAAM,YAAY,KAC3B,CAAC,CACH,CAAC,EAED,GAAI,CACF,EAAS,QAAQ,EAAS,CAAE,IAAK,aAAc,CAAC,CAClD,OAAS,EAAO,CAEd,MADA,EAAS,WAAW,EACd,CACR,CAEA,UAAa,EAAS,WAAW,CACnC,CACF,CACF"}
|
package/dist/errors.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["export class SentinelError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class SentinelUnavailableError extends SentinelError {}\n"],"mappings":"AAAA,IAAa,EAAb,cAAmC,KAAM,CACvC,YAAY,EAAiB,EAAwB,CACnD,MAAM,EAAS,CAAO,EACtB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAEa,EAAb,cAA8C,CAAc,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,aAAc,SAAQ,KAAK;gBAC1B,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAKpD;AAED,qBAAa,wBAAyB,SAAQ,aAAa;CAAG"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["export class SentinelError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class SentinelUnavailableError extends SentinelError {}\n"],"mappings":"AAAA,IAAa,EAAb,cAAmC,KAAM,CACvC,YAAY,EAAiB,EAAwB,CACnD,MAAM,EAAS,CAAO,EACtB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAEa,EAAb,cAA8C,CAAc,CAAC"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./element-size.cjs"),n=require("./intersection.cjs"),r=require("./media-query.cjs"),i=require("./network.cjs"),a=require("./viewport.cjs");exports.SentinelError=e.SentinelError,exports.SentinelUnavailableError=e.SentinelUnavailableError,exports.createElementSize=t.createElementSize,exports.createIntersection=n.createIntersection,exports.createMediaQuery=r.createMediaQuery,exports.createNetwork=i.createNetwork,exports.createViewport=a.createViewport;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { createElementSize } from './element-size.ts';
|
|
2
|
+
export { SentinelError, SentinelUnavailableError } from './errors.ts';
|
|
3
|
+
export type { CreateIntersectionOptions } from './intersection.ts';
|
|
4
|
+
export { createIntersection } from './intersection.ts';
|
|
5
|
+
export { createMediaQuery } from './media-query.ts';
|
|
6
|
+
export { createNetwork } from './network.ts';
|
|
7
|
+
export type { ElementSizeState, IntersectionState, MediaQueryState, NetworkConnectionSnapshot, NetworkState, Sentinel, SentinelOptions, ViewportState, WindowSentinelOptions, } from './types.ts';
|
|
8
|
+
export { createViewport } from './viewport.ts';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACtE,YAAY,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,yBAAyB,EACzB,YAAY,EACZ,QAAQ,EACR,eAAe,EACf,aAAa,EACb,qBAAqB,GACtB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{SentinelError as e,SentinelUnavailableError as t}from"./errors.js";import{createElementSize as n}from"./element-size.js";import{createIntersection as r}from"./intersection.js";import{createMediaQuery as i}from"./media-query.js";import{createNetwork as a}from"./network.js";import{createViewport as o}from"./viewport.js";export{e as SentinelError,t as SentinelUnavailableError,n as createElementSize,r as createIntersection,i as createMediaQuery,a as createNetwork,o as createViewport};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./errors.cjs"),t=require("./_platform.cjs"),n=require("./core.cjs");function r(r,i){let a=t.resolveElementWindow(r).IntersectionObserver;if(typeof a!=`function`)throw new e.SentinelUnavailableError(`IntersectionObserver is not available in this environment.`);return n.createSentinel({initialValue:null,...i},e=>{let t=new a(t=>{let n=t.at(-1);n&&e({intersectionRatio:n.intersectionRatio,isIntersecting:n.isIntersecting})},{root:i?.root,rootMargin:i?.rootMargin,scrollMargin:i?.scrollMargin,threshold:i?.threshold});try{t.observe(r)}catch(e){throw t.disconnect(),e}return()=>t.disconnect()})}exports.createIntersection=r;
|
|
2
|
+
//# sourceMappingURL=intersection.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intersection.cjs","names":[],"sources":["../src/intersection.ts"],"sourcesContent":["import { resolveElementWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { IntersectionState, Sentinel, SentinelOptions } from './types.ts';\n\nexport interface CreateIntersectionOptions extends SentinelOptions {\n readonly root?: Element | Document | null;\n readonly rootMargin?: string;\n readonly scrollMargin?: string;\n readonly threshold?: number | number[];\n}\n\nexport function createIntersection(\n element: Element,\n options?: CreateIntersectionOptions,\n): Sentinel<IntersectionState | null> {\n const target = resolveElementWindow(element);\n const IntersectionObserverConstructor = target.IntersectionObserver;\n if (typeof IntersectionObserverConstructor !== 'function') {\n throw new SentinelUnavailableError('IntersectionObserver is not available in this environment.');\n }\n\n return createSentinel<IntersectionState | null>(\n {\n initialValue: null,\n ...options,\n },\n (update) => {\n const observer = new IntersectionObserverConstructor(\n (entries) => {\n const entry = entries.at(-1);\n if (!entry) return;\n\n update({\n intersectionRatio: entry.intersectionRatio,\n isIntersecting: entry.isIntersecting,\n });\n },\n {\n root: options?.root,\n rootMargin: options?.rootMargin,\n scrollMargin: options?.scrollMargin,\n threshold: options?.threshold,\n },\n );\n\n try {\n observer.observe(element);\n } catch (error) {\n observer.disconnect();\n throw error;\n }\n\n return () => observer.disconnect();\n },\n );\n}\n"],"mappings":"qFAYA,SAAgB,EACd,EACA,EACoC,CAEpC,IAAM,EADS,EAAA,qBAAqB,CACI,CAAA,CAAO,qBAC/C,GAAI,OAAO,GAAoC,WAC7C,MAAM,IAAI,EAAA,yBAAyB,4DAA4D,EAGjG,OAAO,EAAA,eACL,CACE,aAAc,KACd,GAAG,CACL,EACC,GAAW,CACV,IAAM,EAAW,IAAI,EAClB,GAAY,CACX,IAAM,EAAQ,EAAQ,GAAG,EAAE,EACtB,GAEL,EAAO,CACL,kBAAmB,EAAM,kBACzB,eAAgB,EAAM,cACxB,CAAC,CACH,EACA,CACE,KAAM,GAAS,KACf,WAAY,GAAS,WACrB,aAAc,GAAS,aACvB,UAAW,GAAS,SACtB,CACF,EAEA,GAAI,CACF,EAAS,QAAQ,CAAO,CAC1B,OAAS,EAAO,CAEd,MADA,EAAS,WAAW,EACd,CACR,CAEA,UAAa,EAAS,WAAW,CACnC,CACF,CACF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { IntersectionState, Sentinel, SentinelOptions } from './types.ts';
|
|
2
|
+
export interface CreateIntersectionOptions extends SentinelOptions {
|
|
3
|
+
readonly root?: Element | Document | null;
|
|
4
|
+
readonly rootMargin?: string;
|
|
5
|
+
readonly scrollMargin?: string;
|
|
6
|
+
readonly threshold?: number | number[];
|
|
7
|
+
}
|
|
8
|
+
export declare function createIntersection(element: Element, options?: CreateIntersectionOptions): Sentinel<IntersectionState | null>;
|
|
9
|
+
//# sourceMappingURL=intersection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intersection.d.ts","sourceRoot":"","sources":["../src/intersection.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE/E,MAAM,WAAW,yBAA0B,SAAQ,eAAe;IAChE,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,CAAC;IAC1C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CACxC;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,yBAAyB,GAClC,QAAQ,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAyCpC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{SentinelUnavailableError as e}from"./errors.js";import{resolveElementWindow as t}from"./_platform.js";import{createSentinel as n}from"./core.js";function r(r,i){let a=t(r).IntersectionObserver;if(typeof a!=`function`)throw new e(`IntersectionObserver is not available in this environment.`);return n({initialValue:null,...i},e=>{let t=new a(t=>{let n=t.at(-1);n&&e({intersectionRatio:n.intersectionRatio,isIntersecting:n.isIntersecting})},{root:i?.root,rootMargin:i?.rootMargin,scrollMargin:i?.scrollMargin,threshold:i?.threshold});try{t.observe(r)}catch(e){throw t.disconnect(),e}return()=>t.disconnect()})}export{r as createIntersection};
|
|
2
|
+
//# sourceMappingURL=intersection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intersection.js","names":[],"sources":["../src/intersection.ts"],"sourcesContent":["import { resolveElementWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { IntersectionState, Sentinel, SentinelOptions } from './types.ts';\n\nexport interface CreateIntersectionOptions extends SentinelOptions {\n readonly root?: Element | Document | null;\n readonly rootMargin?: string;\n readonly scrollMargin?: string;\n readonly threshold?: number | number[];\n}\n\nexport function createIntersection(\n element: Element,\n options?: CreateIntersectionOptions,\n): Sentinel<IntersectionState | null> {\n const target = resolveElementWindow(element);\n const IntersectionObserverConstructor = target.IntersectionObserver;\n if (typeof IntersectionObserverConstructor !== 'function') {\n throw new SentinelUnavailableError('IntersectionObserver is not available in this environment.');\n }\n\n return createSentinel<IntersectionState | null>(\n {\n initialValue: null,\n ...options,\n },\n (update) => {\n const observer = new IntersectionObserverConstructor(\n (entries) => {\n const entry = entries.at(-1);\n if (!entry) return;\n\n update({\n intersectionRatio: entry.intersectionRatio,\n isIntersecting: entry.isIntersecting,\n });\n },\n {\n root: options?.root,\n rootMargin: options?.rootMargin,\n scrollMargin: options?.scrollMargin,\n threshold: options?.threshold,\n },\n );\n\n try {\n observer.observe(element);\n } catch (error) {\n observer.disconnect();\n throw error;\n }\n\n return () => observer.disconnect();\n },\n );\n}\n"],"mappings":"wJAYA,SAAgB,EACd,EACA,EACoC,CAEpC,IAAM,EADS,EAAqB,CACI,CAAA,CAAO,qBAC/C,GAAI,OAAO,GAAoC,WAC7C,MAAM,IAAI,EAAyB,4DAA4D,EAGjG,OAAO,EACL,CACE,aAAc,KACd,GAAG,CACL,EACC,GAAW,CACV,IAAM,EAAW,IAAI,EAClB,GAAY,CACX,IAAM,EAAQ,EAAQ,GAAG,EAAE,EACtB,GAEL,EAAO,CACL,kBAAmB,EAAM,kBACzB,eAAgB,EAAM,cACxB,CAAC,CACH,EACA,CACE,KAAM,GAAS,KACf,WAAY,GAAS,WACrB,aAAc,GAAS,aACvB,UAAW,GAAS,SACtB,CACF,EAEA,GAAI,CACF,EAAS,QAAQ,CAAO,CAC1B,OAAS,EAAO,CAEd,MADA,EAAS,WAAW,EACd,CACR,CAEA,UAAa,EAAS,WAAW,CACnC,CACF,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./errors.cjs"),t=require("./_platform.cjs"),n=require("./core.cjs");function r(r,i){let a=t.resolveWindow(i?.target);if(typeof a.matchMedia!=`function`)throw new e.SentinelUnavailableError(`matchMedia is not available for query "${r}".`);let o=a.matchMedia(r),s=()=>({matches:o.matches});return n.createSentinel({initialValue:s(),...i},e=>t.listenMediaQuery(o,()=>e(s())))}exports.createMediaQuery=r;
|
|
2
|
+
//# sourceMappingURL=media-query.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-query.cjs","names":[],"sources":["../src/media-query.ts"],"sourcesContent":["import { listenMediaQuery, resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { MediaQueryState, Sentinel, WindowSentinelOptions } from './types.ts';\n\nexport function createMediaQuery(query: string, options?: WindowSentinelOptions): Sentinel<MediaQueryState> {\n const target = resolveWindow(options?.target);\n if (typeof target.matchMedia !== 'function') {\n throw new SentinelUnavailableError(`matchMedia is not available for query \"${query}\".`);\n }\n\n const mediaQuery = target.matchMedia(query);\n const read = (): MediaQueryState => ({ matches: mediaQuery.matches });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => listenMediaQuery(mediaQuery, () => update(read())),\n );\n}\n"],"mappings":"qFAKA,SAAgB,EAAiB,EAAe,EAA4D,CAC1G,IAAM,EAAS,EAAA,cAAc,GAAS,MAAM,EAC5C,GAAI,OAAO,EAAO,YAAe,WAC/B,MAAM,IAAI,EAAA,yBAAyB,0CAA0C,EAAM,GAAG,EAGxF,IAAM,EAAa,EAAO,WAAW,CAAK,EACpC,OAA+B,CAAE,QAAS,EAAW,OAAQ,GAEnE,OAAO,EAAA,eACL,CACE,aAAc,EAAK,EACnB,GAAG,CACL,EACC,GAAW,EAAA,iBAAiB,MAAkB,EAAO,EAAK,CAAC,CAAC,CAC/D,CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-query.d.ts","sourceRoot":"","sources":["../src/media-query.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEnF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,QAAQ,CAAC,eAAe,CAAC,CAgB1G"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{SentinelUnavailableError as e}from"./errors.js";import{listenMediaQuery as t,resolveWindow as n}from"./_platform.js";import{createSentinel as r}from"./core.js";function i(i,a){let o=n(a?.target);if(typeof o.matchMedia!=`function`)throw new e(`matchMedia is not available for query "${i}".`);let s=o.matchMedia(i),c=()=>({matches:s.matches});return r({initialValue:c(),...a},e=>t(s,()=>e(c())))}export{i as createMediaQuery};
|
|
2
|
+
//# sourceMappingURL=media-query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-query.js","names":[],"sources":["../src/media-query.ts"],"sourcesContent":["import { listenMediaQuery, resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { MediaQueryState, Sentinel, WindowSentinelOptions } from './types.ts';\n\nexport function createMediaQuery(query: string, options?: WindowSentinelOptions): Sentinel<MediaQueryState> {\n const target = resolveWindow(options?.target);\n if (typeof target.matchMedia !== 'function') {\n throw new SentinelUnavailableError(`matchMedia is not available for query \"${query}\".`);\n }\n\n const mediaQuery = target.matchMedia(query);\n const read = (): MediaQueryState => ({ matches: mediaQuery.matches });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => listenMediaQuery(mediaQuery, () => update(read())),\n );\n}\n"],"mappings":"uKAKA,SAAgB,EAAiB,EAAe,EAA4D,CAC1G,IAAM,EAAS,EAAc,GAAS,MAAM,EAC5C,GAAI,OAAO,EAAO,YAAe,WAC/B,MAAM,IAAI,EAAyB,0CAA0C,EAAM,GAAG,EAGxF,IAAM,EAAa,EAAO,WAAW,CAAK,EACpC,OAA+B,CAAE,QAAS,EAAW,OAAQ,GAEnE,OAAO,EACL,CACE,aAAc,EAAK,EACnB,GAAG,CACL,EACC,GAAW,EAAiB,MAAkB,EAAO,EAAK,CAAC,CAAC,CAC/D,CACF"}
|
package/dist/network.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./_platform.cjs"),t=require("./core.cjs");function n(e){return e?{downlink:e.downlink,effectiveType:e.effectiveType,rtt:e.rtt,saveData:e.saveData}:null}function r(r){let i=e.resolveWindow(r?.target),a=i.navigator,o=()=>({connection:n(a.connection),online:a.onLine});return t.createSentinel({initialValue:o(),...r},e=>{let t=()=>e(o()),n=a.connection;return i.addEventListener(`online`,t),i.addEventListener(`offline`,t),n?.addEventListener(`change`,t),()=>{i.removeEventListener(`online`,t),i.removeEventListener(`offline`,t),n?.removeEventListener(`change`,t)}})}exports.createNetwork=r;
|
|
2
|
+
//# sourceMappingURL=network.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.cjs","names":[],"sources":["../src/network.ts"],"sourcesContent":["import { resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { NetworkConnectionSnapshot, NetworkState, Sentinel, WindowSentinelOptions } from './types.ts';\n\ninterface NetworkInformationLike extends EventTarget {\n readonly downlink?: number;\n readonly effectiveType?: NetworkConnectionSnapshot['effectiveType'];\n readonly rtt?: number;\n readonly saveData?: boolean;\n}\n\ntype NavigatorWithConnection = Navigator & {\n readonly connection?: NetworkInformationLike;\n};\n\nfunction normalizeConnection(connection?: NetworkInformationLike): NetworkConnectionSnapshot | null {\n if (!connection) return null;\n\n return {\n downlink: connection.downlink,\n effectiveType: connection.effectiveType,\n rtt: connection.rtt,\n saveData: connection.saveData,\n };\n}\n\nexport function createNetwork(options?: WindowSentinelOptions): Sentinel<NetworkState> {\n const target = resolveWindow(options?.target);\n const navigator = target.navigator as NavigatorWithConnection;\n const read = (): NetworkState => ({\n connection: normalizeConnection(navigator.connection),\n online: navigator.onLine,\n });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => {\n const onChange = () => update(read());\n const connection = navigator.connection;\n\n target.addEventListener('online', onChange);\n target.addEventListener('offline', onChange);\n connection?.addEventListener('change', onChange);\n\n return () => {\n target.removeEventListener('online', onChange);\n target.removeEventListener('offline', onChange);\n connection?.removeEventListener('change', onChange);\n };\n },\n );\n}\n"],"mappings":"2DAeA,SAAS,EAAoB,EAAuE,CAGlG,OAFK,EAEE,CACL,SAAU,EAAW,SACrB,cAAe,EAAW,cAC1B,IAAK,EAAW,IAChB,SAAU,EAAW,QACvB,EAPwB,IAQ1B,CAEA,SAAgB,EAAc,EAAyD,CACrF,IAAM,EAAS,EAAA,cAAc,GAAS,MAAM,EACtC,EAAY,EAAO,UACnB,OAA4B,CAChC,WAAY,EAAoB,EAAU,UAAU,EACpD,OAAQ,EAAU,MACpB,GAEA,OAAO,EAAA,eACL,CACE,aAAc,EAAK,EACnB,GAAG,CACL,EACC,GAAW,CACV,IAAM,MAAiB,EAAO,EAAK,CAAC,EAC9B,EAAa,EAAU,WAM7B,OAJA,EAAO,iBAAiB,SAAU,CAAQ,EAC1C,EAAO,iBAAiB,UAAW,CAAQ,EAC3C,GAAY,iBAAiB,SAAU,CAAQ,MAElC,CACX,EAAO,oBAAoB,SAAU,CAAQ,EAC7C,EAAO,oBAAoB,UAAW,CAAQ,EAC9C,GAAY,oBAAoB,SAAU,CAAQ,CACpD,CACF,CACF,CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../src/network.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAA6B,YAAY,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAwB3G,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,QAAQ,CAAC,YAAY,CAAC,CA4BrF"}
|
package/dist/network.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{resolveWindow as e}from"./_platform.js";import{createSentinel as t}from"./core.js";function n(e){return e?{downlink:e.downlink,effectiveType:e.effectiveType,rtt:e.rtt,saveData:e.saveData}:null}function r(r){let i=e(r?.target),a=i.navigator,o=()=>({connection:n(a.connection),online:a.onLine});return t({initialValue:o(),...r},e=>{let t=()=>e(o()),n=a.connection;return i.addEventListener(`online`,t),i.addEventListener(`offline`,t),n?.addEventListener(`change`,t),()=>{i.removeEventListener(`online`,t),i.removeEventListener(`offline`,t),n?.removeEventListener(`change`,t)}})}export{r as createNetwork};
|
|
2
|
+
//# sourceMappingURL=network.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.js","names":[],"sources":["../src/network.ts"],"sourcesContent":["import { resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { NetworkConnectionSnapshot, NetworkState, Sentinel, WindowSentinelOptions } from './types.ts';\n\ninterface NetworkInformationLike extends EventTarget {\n readonly downlink?: number;\n readonly effectiveType?: NetworkConnectionSnapshot['effectiveType'];\n readonly rtt?: number;\n readonly saveData?: boolean;\n}\n\ntype NavigatorWithConnection = Navigator & {\n readonly connection?: NetworkInformationLike;\n};\n\nfunction normalizeConnection(connection?: NetworkInformationLike): NetworkConnectionSnapshot | null {\n if (!connection) return null;\n\n return {\n downlink: connection.downlink,\n effectiveType: connection.effectiveType,\n rtt: connection.rtt,\n saveData: connection.saveData,\n };\n}\n\nexport function createNetwork(options?: WindowSentinelOptions): Sentinel<NetworkState> {\n const target = resolveWindow(options?.target);\n const navigator = target.navigator as NavigatorWithConnection;\n const read = (): NetworkState => ({\n connection: normalizeConnection(navigator.connection),\n online: navigator.onLine,\n });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => {\n const onChange = () => update(read());\n const connection = navigator.connection;\n\n target.addEventListener('online', onChange);\n target.addEventListener('offline', onChange);\n connection?.addEventListener('change', onChange);\n\n return () => {\n target.removeEventListener('online', onChange);\n target.removeEventListener('offline', onChange);\n connection?.removeEventListener('change', onChange);\n };\n },\n );\n}\n"],"mappings":"0FAeA,SAAS,EAAoB,EAAuE,CAGlG,OAFK,EAEE,CACL,SAAU,EAAW,SACrB,cAAe,EAAW,cAC1B,IAAK,EAAW,IAChB,SAAU,EAAW,QACvB,EAPwB,IAQ1B,CAEA,SAAgB,EAAc,EAAyD,CACrF,IAAM,EAAS,EAAc,GAAS,MAAM,EACtC,EAAY,EAAO,UACnB,OAA4B,CAChC,WAAY,EAAoB,EAAU,UAAU,EACpD,OAAQ,EAAU,MACpB,GAEA,OAAO,EACL,CACE,aAAc,EAAK,EACnB,GAAG,CACL,EACC,GAAW,CACV,IAAM,MAAiB,EAAO,EAAK,CAAC,EAC9B,EAAa,EAAU,WAM7B,OAJA,EAAO,iBAAiB,SAAU,CAAQ,EAC1C,EAAO,iBAAiB,UAAW,CAAQ,EAC3C,GAAY,iBAAiB,SAAU,CAAQ,MAElC,CACX,EAAO,oBAAoB,SAAU,CAAQ,EAC7C,EAAO,oBAAoB,UAAW,CAAQ,EAC9C,GAAY,oBAAoB,SAAU,CAAQ,CACpD,CACF,CACF,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@vielzeug/ripple");var t=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},n=class extends t{};function r(e){if(e)return e;if(typeof window>`u`)throw new n(`Window is not available in this environment.`);return window}function i(e){let t=e.ownerDocument.defaultView;if(!t)throw new n(`The element is not connected to a Window.`);return t}function a(e,t){let n=()=>t();if(typeof e.addEventListener==`function`)return e.addEventListener(`change`,n),()=>e.removeEventListener(`change`,n);let r=e;return typeof r.addListener==`function`?(r.addListener(n),()=>r.removeListener?.(n)):()=>{}}function o(e,t){return new s(e,t)}var s=class{disposalSignal;abortController=new AbortController;state;cleanup;disposedValue=!1;externalSignal;externalAbortListener;constructor(t,n){let r=t.runtime?.signal??e.signal;if(this.state=r(t.initialValue),this.disposalSignal=this.abortController.signal,t.signal?.aborted){this.disposedValue=!0,this.abortController.abort();return}try{this.cleanup=n(e=>{this.disposedValue||(this.state.value=e)})}catch(e){throw this.disposedValue=!0,this.abortController.abort(),e}if(t.signal){if(t.signal.aborted){this.dispose();return}this.externalSignal=t.signal,this.externalAbortListener=()=>this.dispose(),this.externalSignal.addEventListener(`abort`,this.externalAbortListener,{once:!0})}}get disposed(){return this.disposedValue}get value(){return this.state.value}peek(){return this.state.peek()}subscribe(e){return this.state.subscribe(e)}dispose(){if(this.disposedValue)return;this.disposedValue=!0,this.externalSignal&&this.externalAbortListener&&this.externalSignal.removeEventListener(`abort`,this.externalAbortListener);let e=this.cleanup;this.cleanup=void 0,this.externalSignal=void 0,this.externalAbortListener=void 0;try{e?.()}finally{this.abortController.abort()}}[Symbol.dispose](){this.dispose()}};function c(e,t){let r=i(e).ResizeObserver;if(typeof r!=`function`)throw new n(`ResizeObserver is not available in this environment.`);return o({initialValue:null,...t},t=>{let n=new r(e=>{let n=e.at(-1);n&&t({height:n.contentRect.height,width:n.contentRect.width})});try{n.observe(e,{box:`content-box`})}catch(e){throw n.disconnect(),e}return()=>n.disconnect()})}function l(e,t){let r=i(e).IntersectionObserver;if(typeof r!=`function`)throw new n(`IntersectionObserver is not available in this environment.`);return o({initialValue:null,...t},n=>{let i=new r(e=>{let t=e.at(-1);t&&n({intersectionRatio:t.intersectionRatio,isIntersecting:t.isIntersecting})},{root:t?.root,rootMargin:t?.rootMargin,scrollMargin:t?.scrollMargin,threshold:t?.threshold});try{i.observe(e)}catch(e){throw i.disconnect(),e}return()=>i.disconnect()})}function u(e,t){let i=r(t?.target);if(typeof i.matchMedia!=`function`)throw new n(`matchMedia is not available for query "${e}".`);let s=i.matchMedia(e),c=()=>({matches:s.matches});return o({initialValue:c(),...t},e=>a(s,()=>e(c())))}function d(e){return e?{downlink:e.downlink,effectiveType:e.effectiveType,rtt:e.rtt,saveData:e.saveData}:null}function f(e){let t=r(e?.target),n=t.navigator,i=()=>({connection:d(n.connection),online:n.onLine});return o({initialValue:i(),...e},e=>{let r=()=>e(i()),a=n.connection;return t.addEventListener(`online`,r),t.addEventListener(`offline`,r),a?.addEventListener(`change`,r),()=>{t.removeEventListener(`online`,r),t.removeEventListener(`offline`,r),a?.removeEventListener(`change`,r)}})}function p(e){let t=r(e?.target),n=()=>({dpr:t.devicePixelRatio,height:t.innerHeight,width:t.innerWidth});return o({initialValue:n(),...e},e=>{let r=()=>e(n()),i,o=()=>{i?.(),i=void 0,typeof t.matchMedia==`function`&&(i=a(t.matchMedia(`(resolution: ${t.devicePixelRatio}dppx)`),()=>{e(n()),o()}))};return t.addEventListener(`resize`,r),o(),()=>{t.removeEventListener(`resize`,r),i?.()}})}exports.SentinelError=t,exports.SentinelUnavailableError=n,exports.createElementSize=c,exports.createIntersection=l,exports.createMediaQuery=u,exports.createNetwork=f,exports.createViewport=p;
|
|
2
|
+
//# sourceMappingURL=sentinel.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sentinel.cjs","names":[],"sources":["../src/errors.ts","../src/_platform.ts","../src/core.ts","../src/element-size.ts","../src/intersection.ts","../src/media-query.ts","../src/network.ts","../src/viewport.ts"],"sourcesContent":["export class SentinelError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class SentinelUnavailableError extends SentinelError {}\n","import { SentinelUnavailableError } from './errors.ts';\n\ntype LegacyMediaQueryList = MediaQueryList & {\n addListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n removeListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n};\n\nexport type ObserverWindow = Window & {\n readonly IntersectionObserver?: {\n new (callback: IntersectionObserverCallback, options?: IntersectionObserverInit): IntersectionObserver;\n };\n readonly ResizeObserver?: {\n new (callback: ResizeObserverCallback): ResizeObserver;\n };\n};\n\nexport function resolveWindow(target?: Window): Window {\n if (target) return target;\n if (typeof window === 'undefined') {\n throw new SentinelUnavailableError('Window is not available in this environment.');\n }\n\n return window;\n}\n\nexport function resolveElementWindow(element: Element): ObserverWindow {\n const ownerWindow = element.ownerDocument.defaultView;\n if (!ownerWindow) {\n throw new SentinelUnavailableError('The element is not connected to a Window.');\n }\n\n return ownerWindow;\n}\n\nexport function listenMediaQuery(mediaQuery: MediaQueryList, listener: () => void): () => void {\n const onChange = () => listener();\n\n if (typeof mediaQuery.addEventListener === 'function') {\n mediaQuery.addEventListener('change', onChange);\n return () => mediaQuery.removeEventListener('change', onChange);\n }\n\n const legacyMediaQuery = mediaQuery as LegacyMediaQueryList;\n if (typeof legacyMediaQuery.addListener === 'function') {\n legacyMediaQuery.addListener(onChange);\n return () => legacyMediaQuery.removeListener?.(onChange);\n }\n\n return () => {};\n}\n","import type { Signal, Unsubscribe } from '@vielzeug/ripple';\nimport { signal as createSignal } from '@vielzeug/ripple';\nimport type { Sentinel, SentinelOptions } from './types.ts';\n\nexport interface CreateSentinelOptions<T> extends SentinelOptions {\n readonly initialValue: T;\n}\n\nexport function createSentinel<T>(\n options: CreateSentinelOptions<T>,\n setup: (update: (value: T) => void) => () => void,\n): Sentinel<T> {\n return new SentinelHandle(options, setup);\n}\n\nclass SentinelHandle<T> implements Sentinel<T> {\n readonly disposalSignal: AbortSignal;\n private readonly abortController = new AbortController();\n private readonly state: Signal<T>;\n private cleanup: (() => void) | undefined;\n private disposedValue = false;\n private externalSignal: AbortSignal | undefined;\n private externalAbortListener: (() => void) | undefined;\n\n constructor(options: CreateSentinelOptions<T>, setup: (update: (value: T) => void) => () => void) {\n const signalFactory = options.runtime?.signal ?? createSignal;\n this.state = signalFactory(options.initialValue);\n this.disposalSignal = this.abortController.signal;\n\n if (options.signal?.aborted) {\n this.disposedValue = true;\n this.abortController.abort();\n return;\n }\n\n try {\n this.cleanup = setup((value) => {\n if (!this.disposedValue) this.state.value = value;\n });\n } catch (error) {\n this.disposedValue = true;\n this.abortController.abort();\n throw error;\n }\n\n if (!options.signal) return;\n\n if (options.signal.aborted) {\n this.dispose();\n return;\n }\n\n this.externalSignal = options.signal;\n this.externalAbortListener = () => this.dispose();\n this.externalSignal.addEventListener('abort', this.externalAbortListener, { once: true });\n }\n\n get disposed(): boolean {\n return this.disposedValue;\n }\n\n get value(): T {\n return this.state.value;\n }\n\n peek(): T {\n return this.state.peek();\n }\n\n subscribe(listener: () => void): Unsubscribe {\n return this.state.subscribe(listener);\n }\n\n dispose(): void {\n if (this.disposedValue) return;\n this.disposedValue = true;\n\n if (this.externalSignal && this.externalAbortListener) {\n this.externalSignal.removeEventListener('abort', this.externalAbortListener);\n }\n\n const cleanup = this.cleanup;\n this.cleanup = undefined;\n this.externalSignal = undefined;\n this.externalAbortListener = undefined;\n\n try {\n cleanup?.();\n } finally {\n this.abortController.abort();\n }\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n","import { resolveElementWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { ElementSizeState, Sentinel, SentinelOptions } from './types.ts';\n\nexport function createElementSize(element: Element, options?: SentinelOptions): Sentinel<ElementSizeState | null> {\n const target = resolveElementWindow(element);\n const ResizeObserverConstructor = target.ResizeObserver;\n if (typeof ResizeObserverConstructor !== 'function') {\n throw new SentinelUnavailableError('ResizeObserver is not available in this environment.');\n }\n\n return createSentinel<ElementSizeState | null>(\n {\n initialValue: null,\n ...options,\n },\n (update) => {\n const observer = new ResizeObserverConstructor((entries) => {\n const entry = entries.at(-1);\n if (!entry) return;\n\n update({\n height: entry.contentRect.height,\n width: entry.contentRect.width,\n });\n });\n\n try {\n observer.observe(element, { box: 'content-box' });\n } catch (error) {\n observer.disconnect();\n throw error;\n }\n\n return () => observer.disconnect();\n },\n );\n}\n","import { resolveElementWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { IntersectionState, Sentinel, SentinelOptions } from './types.ts';\n\nexport interface CreateIntersectionOptions extends SentinelOptions {\n readonly root?: Element | Document | null;\n readonly rootMargin?: string;\n readonly scrollMargin?: string;\n readonly threshold?: number | number[];\n}\n\nexport function createIntersection(\n element: Element,\n options?: CreateIntersectionOptions,\n): Sentinel<IntersectionState | null> {\n const target = resolveElementWindow(element);\n const IntersectionObserverConstructor = target.IntersectionObserver;\n if (typeof IntersectionObserverConstructor !== 'function') {\n throw new SentinelUnavailableError('IntersectionObserver is not available in this environment.');\n }\n\n return createSentinel<IntersectionState | null>(\n {\n initialValue: null,\n ...options,\n },\n (update) => {\n const observer = new IntersectionObserverConstructor(\n (entries) => {\n const entry = entries.at(-1);\n if (!entry) return;\n\n update({\n intersectionRatio: entry.intersectionRatio,\n isIntersecting: entry.isIntersecting,\n });\n },\n {\n root: options?.root,\n rootMargin: options?.rootMargin,\n scrollMargin: options?.scrollMargin,\n threshold: options?.threshold,\n },\n );\n\n try {\n observer.observe(element);\n } catch (error) {\n observer.disconnect();\n throw error;\n }\n\n return () => observer.disconnect();\n },\n );\n}\n","import { listenMediaQuery, resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { MediaQueryState, Sentinel, WindowSentinelOptions } from './types.ts';\n\nexport function createMediaQuery(query: string, options?: WindowSentinelOptions): Sentinel<MediaQueryState> {\n const target = resolveWindow(options?.target);\n if (typeof target.matchMedia !== 'function') {\n throw new SentinelUnavailableError(`matchMedia is not available for query \"${query}\".`);\n }\n\n const mediaQuery = target.matchMedia(query);\n const read = (): MediaQueryState => ({ matches: mediaQuery.matches });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => listenMediaQuery(mediaQuery, () => update(read())),\n );\n}\n","import { resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { NetworkConnectionSnapshot, NetworkState, Sentinel, WindowSentinelOptions } from './types.ts';\n\ninterface NetworkInformationLike extends EventTarget {\n readonly downlink?: number;\n readonly effectiveType?: NetworkConnectionSnapshot['effectiveType'];\n readonly rtt?: number;\n readonly saveData?: boolean;\n}\n\ntype NavigatorWithConnection = Navigator & {\n readonly connection?: NetworkInformationLike;\n};\n\nfunction normalizeConnection(connection?: NetworkInformationLike): NetworkConnectionSnapshot | null {\n if (!connection) return null;\n\n return {\n downlink: connection.downlink,\n effectiveType: connection.effectiveType,\n rtt: connection.rtt,\n saveData: connection.saveData,\n };\n}\n\nexport function createNetwork(options?: WindowSentinelOptions): Sentinel<NetworkState> {\n const target = resolveWindow(options?.target);\n const navigator = target.navigator as NavigatorWithConnection;\n const read = (): NetworkState => ({\n connection: normalizeConnection(navigator.connection),\n online: navigator.onLine,\n });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => {\n const onChange = () => update(read());\n const connection = navigator.connection;\n\n target.addEventListener('online', onChange);\n target.addEventListener('offline', onChange);\n connection?.addEventListener('change', onChange);\n\n return () => {\n target.removeEventListener('online', onChange);\n target.removeEventListener('offline', onChange);\n connection?.removeEventListener('change', onChange);\n };\n },\n );\n}\n","import { listenMediaQuery, resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { Sentinel, ViewportState, WindowSentinelOptions } from './types.ts';\n\nexport function createViewport(options?: WindowSentinelOptions): Sentinel<ViewportState> {\n const target = resolveWindow(options?.target);\n const read = (): ViewportState => ({\n dpr: target.devicePixelRatio,\n height: target.innerHeight,\n width: target.innerWidth,\n });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => {\n const onResize = () => update(read());\n let stopDprObservation: (() => void) | undefined;\n\n const observeDpr = () => {\n stopDprObservation?.();\n stopDprObservation = undefined;\n\n if (typeof target.matchMedia !== 'function') return;\n\n const mediaQuery = target.matchMedia(`(resolution: ${target.devicePixelRatio}dppx)`);\n stopDprObservation = listenMediaQuery(mediaQuery, () => {\n update(read());\n observeDpr();\n });\n };\n\n target.addEventListener('resize', onResize);\n observeDpr();\n\n return () => {\n target.removeEventListener('resize', onResize);\n stopDprObservation?.();\n };\n },\n );\n}\n"],"mappings":"qGAAA,IAAa,EAAb,cAAmC,KAAM,CACvC,YAAY,EAAiB,EAAwB,CACnD,MAAM,EAAS,CAAO,EACtB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAEa,EAAb,cAA8C,CAAc,CAAC,ECQ7D,SAAgB,EAAc,EAAyB,CACrD,GAAI,EAAQ,OAAO,EACnB,GAAI,OAAO,OAAW,IACpB,MAAM,IAAI,EAAyB,8CAA8C,EAGnF,OAAO,MACT,CAEA,SAAgB,EAAqB,EAAkC,CACrE,IAAM,EAAc,EAAQ,cAAc,YAC1C,GAAI,CAAC,EACH,MAAM,IAAI,EAAyB,2CAA2C,EAGhF,OAAO,CACT,CAEA,SAAgB,EAAiB,EAA4B,EAAkC,CAC7F,IAAM,MAAiB,EAAS,EAEhC,GAAI,OAAO,EAAW,kBAAqB,WAEzC,OADA,EAAW,iBAAiB,SAAU,CAAQ,MACjC,EAAW,oBAAoB,SAAU,CAAQ,EAGhE,IAAM,EAAmB,EAMzB,OALI,OAAO,EAAiB,aAAgB,YAC1C,EAAiB,YAAY,CAAQ,MACxB,EAAiB,iBAAiB,CAAQ,OAG5C,CAAC,CAChB,CCzCA,SAAgB,EACd,EACA,EACa,CACb,OAAO,IAAI,EAAe,EAAS,CAAK,CAC1C,CAEA,IAAM,EAAN,KAA+C,CAC7C,eACA,gBAAmC,IAAI,gBACvC,MACA,QACA,cAAwB,GACxB,eACA,sBAEA,YAAY,EAAmC,EAAmD,CAChG,IAAM,EAAgB,EAAQ,SAAS,QAAU,EAAA,OAIjD,GAHA,KAAK,MAAQ,EAAc,EAAQ,YAAY,EAC/C,KAAK,eAAiB,KAAK,gBAAgB,OAEvC,EAAQ,QAAQ,QAAS,CAC3B,KAAK,cAAgB,GACrB,KAAK,gBAAgB,MAAM,EAC3B,MACF,CAEA,GAAI,CACF,KAAK,QAAU,EAAO,GAAU,CACzB,KAAK,gBAAe,KAAK,MAAM,MAAQ,EAC9C,CAAC,CACH,OAAS,EAAO,CAGd,KAFA,MAAK,cAAgB,GACrB,KAAK,gBAAgB,MAAM,EACrB,CACR,CAEK,KAAQ,OAEb,IAAI,EAAQ,OAAO,QAAS,CAC1B,KAAK,QAAQ,EACb,MACF,CAEA,KAAK,eAAiB,EAAQ,OAC9B,KAAK,0BAA8B,KAAK,QAAQ,EAChD,KAAK,eAAe,iBAAiB,QAAS,KAAK,sBAAuB,CAAE,KAAM,EAAK,CAAC,CAJxF,CAKF,CAEA,IAAI,UAAoB,CACtB,OAAO,KAAK,aACd,CAEA,IAAI,OAAW,CACb,OAAO,KAAK,MAAM,KACpB,CAEA,MAAU,CACR,OAAO,KAAK,MAAM,KAAK,CACzB,CAEA,UAAU,EAAmC,CAC3C,OAAO,KAAK,MAAM,UAAU,CAAQ,CACtC,CAEA,SAAgB,CACd,GAAI,KAAK,cAAe,OACxB,KAAK,cAAgB,GAEjB,KAAK,gBAAkB,KAAK,uBAC9B,KAAK,eAAe,oBAAoB,QAAS,KAAK,qBAAqB,EAG7E,IAAM,EAAU,KAAK,QACrB,KAAK,QAAU,IAAA,GACf,KAAK,eAAiB,IAAA,GACtB,KAAK,sBAAwB,IAAA,GAE7B,GAAI,CACF,IAAU,CACZ,QAAU,CACR,KAAK,gBAAgB,MAAM,CAC7B,CACF,CAEA,CAAC,OAAO,UAAiB,CACvB,KAAK,QAAQ,CACf,CACF,EC3FA,SAAgB,EAAkB,EAAkB,EAA8D,CAEhH,IAAM,EADS,EAAqB,CACF,CAAA,CAAO,eACzC,GAAI,OAAO,GAA8B,WACvC,MAAM,IAAI,EAAyB,sDAAsD,EAG3F,OAAO,EACL,CACE,aAAc,KACd,GAAG,CACL,EACC,GAAW,CACV,IAAM,EAAW,IAAI,EAA2B,GAAY,CAC1D,IAAM,EAAQ,EAAQ,GAAG,EAAE,EACtB,GAEL,EAAO,CACL,OAAQ,EAAM,YAAY,OAC1B,MAAO,EAAM,YAAY,KAC3B,CAAC,CACH,CAAC,EAED,GAAI,CACF,EAAS,QAAQ,EAAS,CAAE,IAAK,aAAc,CAAC,CAClD,OAAS,EAAO,CAEd,MADA,EAAS,WAAW,EACd,CACR,CAEA,UAAa,EAAS,WAAW,CACnC,CACF,CACF,CC1BA,SAAgB,EACd,EACA,EACoC,CAEpC,IAAM,EADS,EAAqB,CACI,CAAA,CAAO,qBAC/C,GAAI,OAAO,GAAoC,WAC7C,MAAM,IAAI,EAAyB,4DAA4D,EAGjG,OAAO,EACL,CACE,aAAc,KACd,GAAG,CACL,EACC,GAAW,CACV,IAAM,EAAW,IAAI,EAClB,GAAY,CACX,IAAM,EAAQ,EAAQ,GAAG,EAAE,EACtB,GAEL,EAAO,CACL,kBAAmB,EAAM,kBACzB,eAAgB,EAAM,cACxB,CAAC,CACH,EACA,CACE,KAAM,GAAS,KACf,WAAY,GAAS,WACrB,aAAc,GAAS,aACvB,UAAW,GAAS,SACtB,CACF,EAEA,GAAI,CACF,EAAS,QAAQ,CAAO,CAC1B,OAAS,EAAO,CAEd,MADA,EAAS,WAAW,EACd,CACR,CAEA,UAAa,EAAS,WAAW,CACnC,CACF,CACF,CCnDA,SAAgB,EAAiB,EAAe,EAA4D,CAC1G,IAAM,EAAS,EAAc,GAAS,MAAM,EAC5C,GAAI,OAAO,EAAO,YAAe,WAC/B,MAAM,IAAI,EAAyB,0CAA0C,EAAM,GAAG,EAGxF,IAAM,EAAa,EAAO,WAAW,CAAK,EACpC,OAA+B,CAAE,QAAS,EAAW,OAAQ,GAEnE,OAAO,EACL,CACE,aAAc,EAAK,EACnB,GAAG,CACL,EACC,GAAW,EAAiB,MAAkB,EAAO,EAAK,CAAC,CAAC,CAC/D,CACF,CCNA,SAAS,EAAoB,EAAuE,CAGlG,OAFK,EAEE,CACL,SAAU,EAAW,SACrB,cAAe,EAAW,cAC1B,IAAK,EAAW,IAChB,SAAU,EAAW,QACvB,EAPwB,IAQ1B,CAEA,SAAgB,EAAc,EAAyD,CACrF,IAAM,EAAS,EAAc,GAAS,MAAM,EACtC,EAAY,EAAO,UACnB,OAA4B,CAChC,WAAY,EAAoB,EAAU,UAAU,EACpD,OAAQ,EAAU,MACpB,GAEA,OAAO,EACL,CACE,aAAc,EAAK,EACnB,GAAG,CACL,EACC,GAAW,CACV,IAAM,MAAiB,EAAO,EAAK,CAAC,EAC9B,EAAa,EAAU,WAM7B,OAJA,EAAO,iBAAiB,SAAU,CAAQ,EAC1C,EAAO,iBAAiB,UAAW,CAAQ,EAC3C,GAAY,iBAAiB,SAAU,CAAQ,MAElC,CACX,EAAO,oBAAoB,SAAU,CAAQ,EAC7C,EAAO,oBAAoB,UAAW,CAAQ,EAC9C,GAAY,oBAAoB,SAAU,CAAQ,CACpD,CACF,CACF,CACF,CClDA,SAAgB,EAAe,EAA0D,CACvF,IAAM,EAAS,EAAc,GAAS,MAAM,EACtC,OAA6B,CACjC,IAAK,EAAO,iBACZ,OAAQ,EAAO,YACf,MAAO,EAAO,UAChB,GAEA,OAAO,EACL,CACE,aAAc,EAAK,EACnB,GAAG,CACL,EACC,GAAW,CACV,IAAM,MAAiB,EAAO,EAAK,CAAC,EAChC,EAEE,MAAmB,CACvB,IAAqB,EACrB,EAAqB,IAAA,GAEjB,OAAO,EAAO,YAAe,aAGjC,EAAqB,EADF,EAAO,WAAW,gBAAgB,EAAO,iBAAiB,MACvC,MAAkB,CACtD,EAAO,EAAK,CAAC,EACb,EAAW,CACb,CAAC,EACH,EAKA,OAHA,EAAO,iBAAiB,SAAU,CAAQ,EAC1C,EAAW,MAEE,CACX,EAAO,oBAAoB,SAAU,CAAQ,EAC7C,IAAqB,CACvB,CACF,CACF,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var Sentinel=(function(e,t){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var n=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},r=class extends n{};function i(e){if(e)return e;if(typeof window>`u`)throw new r(`Window is not available in this environment.`);return window}function a(e){let t=e.ownerDocument.defaultView;if(!t)throw new r(`The element is not connected to a Window.`);return t}function o(e,t){let n=()=>t();if(typeof e.addEventListener==`function`)return e.addEventListener(`change`,n),()=>e.removeEventListener(`change`,n);let r=e;return typeof r.addListener==`function`?(r.addListener(n),()=>r.removeListener?.(n)):()=>{}}function s(e,t){return new c(e,t)}var c=class{disposalSignal;abortController=new AbortController;state;cleanup;disposedValue=!1;externalSignal;externalAbortListener;constructor(e,n){let r=e.runtime?.signal??t.signal;if(this.state=r(e.initialValue),this.disposalSignal=this.abortController.signal,e.signal?.aborted){this.disposedValue=!0,this.abortController.abort();return}try{this.cleanup=n(e=>{this.disposedValue||(this.state.value=e)})}catch(e){throw this.disposedValue=!0,this.abortController.abort(),e}if(e.signal){if(e.signal.aborted){this.dispose();return}this.externalSignal=e.signal,this.externalAbortListener=()=>this.dispose(),this.externalSignal.addEventListener(`abort`,this.externalAbortListener,{once:!0})}}get disposed(){return this.disposedValue}get value(){return this.state.value}peek(){return this.state.peek()}subscribe(e){return this.state.subscribe(e)}dispose(){if(this.disposedValue)return;this.disposedValue=!0,this.externalSignal&&this.externalAbortListener&&this.externalSignal.removeEventListener(`abort`,this.externalAbortListener);let e=this.cleanup;this.cleanup=void 0,this.externalSignal=void 0,this.externalAbortListener=void 0;try{e?.()}finally{this.abortController.abort()}}[Symbol.dispose](){this.dispose()}};function l(e,t){let n=a(e).ResizeObserver;if(typeof n!=`function`)throw new r(`ResizeObserver is not available in this environment.`);return s({initialValue:null,...t},t=>{let r=new n(e=>{let n=e.at(-1);n&&t({height:n.contentRect.height,width:n.contentRect.width})});try{r.observe(e,{box:`content-box`})}catch(e){throw r.disconnect(),e}return()=>r.disconnect()})}function u(e,t){let n=a(e).IntersectionObserver;if(typeof n!=`function`)throw new r(`IntersectionObserver is not available in this environment.`);return s({initialValue:null,...t},r=>{let i=new n(e=>{let t=e.at(-1);t&&r({intersectionRatio:t.intersectionRatio,isIntersecting:t.isIntersecting})},{root:t?.root,rootMargin:t?.rootMargin,scrollMargin:t?.scrollMargin,threshold:t?.threshold});try{i.observe(e)}catch(e){throw i.disconnect(),e}return()=>i.disconnect()})}function d(e,t){let n=i(t?.target);if(typeof n.matchMedia!=`function`)throw new r(`matchMedia is not available for query "${e}".`);let a=n.matchMedia(e),c=()=>({matches:a.matches});return s({initialValue:c(),...t},e=>o(a,()=>e(c())))}function f(e){return e?{downlink:e.downlink,effectiveType:e.effectiveType,rtt:e.rtt,saveData:e.saveData}:null}function p(e){let t=i(e?.target),n=t.navigator,r=()=>({connection:f(n.connection),online:n.onLine});return s({initialValue:r(),...e},e=>{let i=()=>e(r()),a=n.connection;return t.addEventListener(`online`,i),t.addEventListener(`offline`,i),a?.addEventListener(`change`,i),()=>{t.removeEventListener(`online`,i),t.removeEventListener(`offline`,i),a?.removeEventListener(`change`,i)}})}function m(e){let t=i(e?.target),n=()=>({dpr:t.devicePixelRatio,height:t.innerHeight,width:t.innerWidth});return s({initialValue:n(),...e},e=>{let r=()=>e(n()),i,a=()=>{i?.(),i=void 0,typeof t.matchMedia==`function`&&(i=o(t.matchMedia(`(resolution: ${t.devicePixelRatio}dppx)`),()=>{e(n()),a()}))};return t.addEventListener(`resize`,r),a(),()=>{t.removeEventListener(`resize`,r),i?.()}})}return e.SentinelError=n,e.SentinelUnavailableError=r,e.createElementSize=l,e.createIntersection=u,e.createMediaQuery=d,e.createNetwork=p,e.createViewport=m,e})({},Ripple);
|
|
2
|
+
//# sourceMappingURL=sentinel.iife.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sentinel.iife.js","names":[],"sources":["../src/errors.ts","../src/_platform.ts","../src/core.ts","../src/element-size.ts","../src/intersection.ts","../src/media-query.ts","../src/network.ts","../src/viewport.ts"],"sourcesContent":["export class SentinelError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class SentinelUnavailableError extends SentinelError {}\n","import { SentinelUnavailableError } from './errors.ts';\n\ntype LegacyMediaQueryList = MediaQueryList & {\n addListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n removeListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n};\n\nexport type ObserverWindow = Window & {\n readonly IntersectionObserver?: {\n new (callback: IntersectionObserverCallback, options?: IntersectionObserverInit): IntersectionObserver;\n };\n readonly ResizeObserver?: {\n new (callback: ResizeObserverCallback): ResizeObserver;\n };\n};\n\nexport function resolveWindow(target?: Window): Window {\n if (target) return target;\n if (typeof window === 'undefined') {\n throw new SentinelUnavailableError('Window is not available in this environment.');\n }\n\n return window;\n}\n\nexport function resolveElementWindow(element: Element): ObserverWindow {\n const ownerWindow = element.ownerDocument.defaultView;\n if (!ownerWindow) {\n throw new SentinelUnavailableError('The element is not connected to a Window.');\n }\n\n return ownerWindow;\n}\n\nexport function listenMediaQuery(mediaQuery: MediaQueryList, listener: () => void): () => void {\n const onChange = () => listener();\n\n if (typeof mediaQuery.addEventListener === 'function') {\n mediaQuery.addEventListener('change', onChange);\n return () => mediaQuery.removeEventListener('change', onChange);\n }\n\n const legacyMediaQuery = mediaQuery as LegacyMediaQueryList;\n if (typeof legacyMediaQuery.addListener === 'function') {\n legacyMediaQuery.addListener(onChange);\n return () => legacyMediaQuery.removeListener?.(onChange);\n }\n\n return () => {};\n}\n","import type { Signal, Unsubscribe } from '@vielzeug/ripple';\nimport { signal as createSignal } from '@vielzeug/ripple';\nimport type { Sentinel, SentinelOptions } from './types.ts';\n\nexport interface CreateSentinelOptions<T> extends SentinelOptions {\n readonly initialValue: T;\n}\n\nexport function createSentinel<T>(\n options: CreateSentinelOptions<T>,\n setup: (update: (value: T) => void) => () => void,\n): Sentinel<T> {\n return new SentinelHandle(options, setup);\n}\n\nclass SentinelHandle<T> implements Sentinel<T> {\n readonly disposalSignal: AbortSignal;\n private readonly abortController = new AbortController();\n private readonly state: Signal<T>;\n private cleanup: (() => void) | undefined;\n private disposedValue = false;\n private externalSignal: AbortSignal | undefined;\n private externalAbortListener: (() => void) | undefined;\n\n constructor(options: CreateSentinelOptions<T>, setup: (update: (value: T) => void) => () => void) {\n const signalFactory = options.runtime?.signal ?? createSignal;\n this.state = signalFactory(options.initialValue);\n this.disposalSignal = this.abortController.signal;\n\n if (options.signal?.aborted) {\n this.disposedValue = true;\n this.abortController.abort();\n return;\n }\n\n try {\n this.cleanup = setup((value) => {\n if (!this.disposedValue) this.state.value = value;\n });\n } catch (error) {\n this.disposedValue = true;\n this.abortController.abort();\n throw error;\n }\n\n if (!options.signal) return;\n\n if (options.signal.aborted) {\n this.dispose();\n return;\n }\n\n this.externalSignal = options.signal;\n this.externalAbortListener = () => this.dispose();\n this.externalSignal.addEventListener('abort', this.externalAbortListener, { once: true });\n }\n\n get disposed(): boolean {\n return this.disposedValue;\n }\n\n get value(): T {\n return this.state.value;\n }\n\n peek(): T {\n return this.state.peek();\n }\n\n subscribe(listener: () => void): Unsubscribe {\n return this.state.subscribe(listener);\n }\n\n dispose(): void {\n if (this.disposedValue) return;\n this.disposedValue = true;\n\n if (this.externalSignal && this.externalAbortListener) {\n this.externalSignal.removeEventListener('abort', this.externalAbortListener);\n }\n\n const cleanup = this.cleanup;\n this.cleanup = undefined;\n this.externalSignal = undefined;\n this.externalAbortListener = undefined;\n\n try {\n cleanup?.();\n } finally {\n this.abortController.abort();\n }\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n","import { resolveElementWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { ElementSizeState, Sentinel, SentinelOptions } from './types.ts';\n\nexport function createElementSize(element: Element, options?: SentinelOptions): Sentinel<ElementSizeState | null> {\n const target = resolveElementWindow(element);\n const ResizeObserverConstructor = target.ResizeObserver;\n if (typeof ResizeObserverConstructor !== 'function') {\n throw new SentinelUnavailableError('ResizeObserver is not available in this environment.');\n }\n\n return createSentinel<ElementSizeState | null>(\n {\n initialValue: null,\n ...options,\n },\n (update) => {\n const observer = new ResizeObserverConstructor((entries) => {\n const entry = entries.at(-1);\n if (!entry) return;\n\n update({\n height: entry.contentRect.height,\n width: entry.contentRect.width,\n });\n });\n\n try {\n observer.observe(element, { box: 'content-box' });\n } catch (error) {\n observer.disconnect();\n throw error;\n }\n\n return () => observer.disconnect();\n },\n );\n}\n","import { resolveElementWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { IntersectionState, Sentinel, SentinelOptions } from './types.ts';\n\nexport interface CreateIntersectionOptions extends SentinelOptions {\n readonly root?: Element | Document | null;\n readonly rootMargin?: string;\n readonly scrollMargin?: string;\n readonly threshold?: number | number[];\n}\n\nexport function createIntersection(\n element: Element,\n options?: CreateIntersectionOptions,\n): Sentinel<IntersectionState | null> {\n const target = resolveElementWindow(element);\n const IntersectionObserverConstructor = target.IntersectionObserver;\n if (typeof IntersectionObserverConstructor !== 'function') {\n throw new SentinelUnavailableError('IntersectionObserver is not available in this environment.');\n }\n\n return createSentinel<IntersectionState | null>(\n {\n initialValue: null,\n ...options,\n },\n (update) => {\n const observer = new IntersectionObserverConstructor(\n (entries) => {\n const entry = entries.at(-1);\n if (!entry) return;\n\n update({\n intersectionRatio: entry.intersectionRatio,\n isIntersecting: entry.isIntersecting,\n });\n },\n {\n root: options?.root,\n rootMargin: options?.rootMargin,\n scrollMargin: options?.scrollMargin,\n threshold: options?.threshold,\n },\n );\n\n try {\n observer.observe(element);\n } catch (error) {\n observer.disconnect();\n throw error;\n }\n\n return () => observer.disconnect();\n },\n );\n}\n","import { listenMediaQuery, resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { MediaQueryState, Sentinel, WindowSentinelOptions } from './types.ts';\n\nexport function createMediaQuery(query: string, options?: WindowSentinelOptions): Sentinel<MediaQueryState> {\n const target = resolveWindow(options?.target);\n if (typeof target.matchMedia !== 'function') {\n throw new SentinelUnavailableError(`matchMedia is not available for query \"${query}\".`);\n }\n\n const mediaQuery = target.matchMedia(query);\n const read = (): MediaQueryState => ({ matches: mediaQuery.matches });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => listenMediaQuery(mediaQuery, () => update(read())),\n );\n}\n","import { resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { NetworkConnectionSnapshot, NetworkState, Sentinel, WindowSentinelOptions } from './types.ts';\n\ninterface NetworkInformationLike extends EventTarget {\n readonly downlink?: number;\n readonly effectiveType?: NetworkConnectionSnapshot['effectiveType'];\n readonly rtt?: number;\n readonly saveData?: boolean;\n}\n\ntype NavigatorWithConnection = Navigator & {\n readonly connection?: NetworkInformationLike;\n};\n\nfunction normalizeConnection(connection?: NetworkInformationLike): NetworkConnectionSnapshot | null {\n if (!connection) return null;\n\n return {\n downlink: connection.downlink,\n effectiveType: connection.effectiveType,\n rtt: connection.rtt,\n saveData: connection.saveData,\n };\n}\n\nexport function createNetwork(options?: WindowSentinelOptions): Sentinel<NetworkState> {\n const target = resolveWindow(options?.target);\n const navigator = target.navigator as NavigatorWithConnection;\n const read = (): NetworkState => ({\n connection: normalizeConnection(navigator.connection),\n online: navigator.onLine,\n });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => {\n const onChange = () => update(read());\n const connection = navigator.connection;\n\n target.addEventListener('online', onChange);\n target.addEventListener('offline', onChange);\n connection?.addEventListener('change', onChange);\n\n return () => {\n target.removeEventListener('online', onChange);\n target.removeEventListener('offline', onChange);\n connection?.removeEventListener('change', onChange);\n };\n },\n );\n}\n","import { listenMediaQuery, resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { Sentinel, ViewportState, WindowSentinelOptions } from './types.ts';\n\nexport function createViewport(options?: WindowSentinelOptions): Sentinel<ViewportState> {\n const target = resolveWindow(options?.target);\n const read = (): ViewportState => ({\n dpr: target.devicePixelRatio,\n height: target.innerHeight,\n width: target.innerWidth,\n });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => {\n const onResize = () => update(read());\n let stopDprObservation: (() => void) | undefined;\n\n const observeDpr = () => {\n stopDprObservation?.();\n stopDprObservation = undefined;\n\n if (typeof target.matchMedia !== 'function') return;\n\n const mediaQuery = target.matchMedia(`(resolution: ${target.devicePixelRatio}dppx)`);\n stopDprObservation = listenMediaQuery(mediaQuery, () => {\n update(read());\n observeDpr();\n });\n };\n\n target.addEventListener('resize', onResize);\n observeDpr();\n\n return () => {\n target.removeEventListener('resize', onResize);\n stopDprObservation?.();\n };\n },\n );\n}\n"],"mappings":"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"}
|
package/dist/sentinel.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{signal as e}from"@vielzeug/ripple";var t=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},n=class extends t{};function r(e){if(e)return e;if(typeof window>`u`)throw new n(`Window is not available in this environment.`);return window}function i(e){let t=e.ownerDocument.defaultView;if(!t)throw new n(`The element is not connected to a Window.`);return t}function a(e,t){let n=()=>t();if(typeof e.addEventListener==`function`)return e.addEventListener(`change`,n),()=>e.removeEventListener(`change`,n);let r=e;return typeof r.addListener==`function`?(r.addListener(n),()=>r.removeListener?.(n)):()=>{}}function o(e,t){return new s(e,t)}var s=class{disposalSignal;abortController=new AbortController;state;cleanup;disposedValue=!1;externalSignal;externalAbortListener;constructor(t,n){let r=t.runtime?.signal??e;if(this.state=r(t.initialValue),this.disposalSignal=this.abortController.signal,t.signal?.aborted){this.disposedValue=!0,this.abortController.abort();return}try{this.cleanup=n(e=>{this.disposedValue||(this.state.value=e)})}catch(e){throw this.disposedValue=!0,this.abortController.abort(),e}if(t.signal){if(t.signal.aborted){this.dispose();return}this.externalSignal=t.signal,this.externalAbortListener=()=>this.dispose(),this.externalSignal.addEventListener(`abort`,this.externalAbortListener,{once:!0})}}get disposed(){return this.disposedValue}get value(){return this.state.value}peek(){return this.state.peek()}subscribe(e){return this.state.subscribe(e)}dispose(){if(this.disposedValue)return;this.disposedValue=!0,this.externalSignal&&this.externalAbortListener&&this.externalSignal.removeEventListener(`abort`,this.externalAbortListener);let e=this.cleanup;this.cleanup=void 0,this.externalSignal=void 0,this.externalAbortListener=void 0;try{e?.()}finally{this.abortController.abort()}}[Symbol.dispose](){this.dispose()}};function c(e,t){let r=i(e).ResizeObserver;if(typeof r!=`function`)throw new n(`ResizeObserver is not available in this environment.`);return o({initialValue:null,...t},t=>{let n=new r(e=>{let n=e.at(-1);n&&t({height:n.contentRect.height,width:n.contentRect.width})});try{n.observe(e,{box:`content-box`})}catch(e){throw n.disconnect(),e}return()=>n.disconnect()})}function l(e,t){let r=i(e).IntersectionObserver;if(typeof r!=`function`)throw new n(`IntersectionObserver is not available in this environment.`);return o({initialValue:null,...t},n=>{let i=new r(e=>{let t=e.at(-1);t&&n({intersectionRatio:t.intersectionRatio,isIntersecting:t.isIntersecting})},{root:t?.root,rootMargin:t?.rootMargin,scrollMargin:t?.scrollMargin,threshold:t?.threshold});try{i.observe(e)}catch(e){throw i.disconnect(),e}return()=>i.disconnect()})}function u(e,t){let i=r(t?.target);if(typeof i.matchMedia!=`function`)throw new n(`matchMedia is not available for query "${e}".`);let s=i.matchMedia(e),c=()=>({matches:s.matches});return o({initialValue:c(),...t},e=>a(s,()=>e(c())))}function d(e){return e?{downlink:e.downlink,effectiveType:e.effectiveType,rtt:e.rtt,saveData:e.saveData}:null}function f(e){let t=r(e?.target),n=t.navigator,i=()=>({connection:d(n.connection),online:n.onLine});return o({initialValue:i(),...e},e=>{let r=()=>e(i()),a=n.connection;return t.addEventListener(`online`,r),t.addEventListener(`offline`,r),a?.addEventListener(`change`,r),()=>{t.removeEventListener(`online`,r),t.removeEventListener(`offline`,r),a?.removeEventListener(`change`,r)}})}function p(e){let t=r(e?.target),n=()=>({dpr:t.devicePixelRatio,height:t.innerHeight,width:t.innerWidth});return o({initialValue:n(),...e},e=>{let r=()=>e(n()),i,o=()=>{i?.(),i=void 0,typeof t.matchMedia==`function`&&(i=a(t.matchMedia(`(resolution: ${t.devicePixelRatio}dppx)`),()=>{e(n()),o()}))};return t.addEventListener(`resize`,r),o(),()=>{t.removeEventListener(`resize`,r),i?.()}})}export{t as SentinelError,n as SentinelUnavailableError,c as createElementSize,l as createIntersection,u as createMediaQuery,f as createNetwork,p as createViewport};
|
|
2
|
+
//# sourceMappingURL=sentinel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sentinel.js","names":[],"sources":["../src/errors.ts","../src/_platform.ts","../src/core.ts","../src/element-size.ts","../src/intersection.ts","../src/media-query.ts","../src/network.ts","../src/viewport.ts"],"sourcesContent":["export class SentinelError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class SentinelUnavailableError extends SentinelError {}\n","import { SentinelUnavailableError } from './errors.ts';\n\ntype LegacyMediaQueryList = MediaQueryList & {\n addListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n removeListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n};\n\nexport type ObserverWindow = Window & {\n readonly IntersectionObserver?: {\n new (callback: IntersectionObserverCallback, options?: IntersectionObserverInit): IntersectionObserver;\n };\n readonly ResizeObserver?: {\n new (callback: ResizeObserverCallback): ResizeObserver;\n };\n};\n\nexport function resolveWindow(target?: Window): Window {\n if (target) return target;\n if (typeof window === 'undefined') {\n throw new SentinelUnavailableError('Window is not available in this environment.');\n }\n\n return window;\n}\n\nexport function resolveElementWindow(element: Element): ObserverWindow {\n const ownerWindow = element.ownerDocument.defaultView;\n if (!ownerWindow) {\n throw new SentinelUnavailableError('The element is not connected to a Window.');\n }\n\n return ownerWindow;\n}\n\nexport function listenMediaQuery(mediaQuery: MediaQueryList, listener: () => void): () => void {\n const onChange = () => listener();\n\n if (typeof mediaQuery.addEventListener === 'function') {\n mediaQuery.addEventListener('change', onChange);\n return () => mediaQuery.removeEventListener('change', onChange);\n }\n\n const legacyMediaQuery = mediaQuery as LegacyMediaQueryList;\n if (typeof legacyMediaQuery.addListener === 'function') {\n legacyMediaQuery.addListener(onChange);\n return () => legacyMediaQuery.removeListener?.(onChange);\n }\n\n return () => {};\n}\n","import type { Signal, Unsubscribe } from '@vielzeug/ripple';\nimport { signal as createSignal } from '@vielzeug/ripple';\nimport type { Sentinel, SentinelOptions } from './types.ts';\n\nexport interface CreateSentinelOptions<T> extends SentinelOptions {\n readonly initialValue: T;\n}\n\nexport function createSentinel<T>(\n options: CreateSentinelOptions<T>,\n setup: (update: (value: T) => void) => () => void,\n): Sentinel<T> {\n return new SentinelHandle(options, setup);\n}\n\nclass SentinelHandle<T> implements Sentinel<T> {\n readonly disposalSignal: AbortSignal;\n private readonly abortController = new AbortController();\n private readonly state: Signal<T>;\n private cleanup: (() => void) | undefined;\n private disposedValue = false;\n private externalSignal: AbortSignal | undefined;\n private externalAbortListener: (() => void) | undefined;\n\n constructor(options: CreateSentinelOptions<T>, setup: (update: (value: T) => void) => () => void) {\n const signalFactory = options.runtime?.signal ?? createSignal;\n this.state = signalFactory(options.initialValue);\n this.disposalSignal = this.abortController.signal;\n\n if (options.signal?.aborted) {\n this.disposedValue = true;\n this.abortController.abort();\n return;\n }\n\n try {\n this.cleanup = setup((value) => {\n if (!this.disposedValue) this.state.value = value;\n });\n } catch (error) {\n this.disposedValue = true;\n this.abortController.abort();\n throw error;\n }\n\n if (!options.signal) return;\n\n if (options.signal.aborted) {\n this.dispose();\n return;\n }\n\n this.externalSignal = options.signal;\n this.externalAbortListener = () => this.dispose();\n this.externalSignal.addEventListener('abort', this.externalAbortListener, { once: true });\n }\n\n get disposed(): boolean {\n return this.disposedValue;\n }\n\n get value(): T {\n return this.state.value;\n }\n\n peek(): T {\n return this.state.peek();\n }\n\n subscribe(listener: () => void): Unsubscribe {\n return this.state.subscribe(listener);\n }\n\n dispose(): void {\n if (this.disposedValue) return;\n this.disposedValue = true;\n\n if (this.externalSignal && this.externalAbortListener) {\n this.externalSignal.removeEventListener('abort', this.externalAbortListener);\n }\n\n const cleanup = this.cleanup;\n this.cleanup = undefined;\n this.externalSignal = undefined;\n this.externalAbortListener = undefined;\n\n try {\n cleanup?.();\n } finally {\n this.abortController.abort();\n }\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n","import { resolveElementWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { ElementSizeState, Sentinel, SentinelOptions } from './types.ts';\n\nexport function createElementSize(element: Element, options?: SentinelOptions): Sentinel<ElementSizeState | null> {\n const target = resolveElementWindow(element);\n const ResizeObserverConstructor = target.ResizeObserver;\n if (typeof ResizeObserverConstructor !== 'function') {\n throw new SentinelUnavailableError('ResizeObserver is not available in this environment.');\n }\n\n return createSentinel<ElementSizeState | null>(\n {\n initialValue: null,\n ...options,\n },\n (update) => {\n const observer = new ResizeObserverConstructor((entries) => {\n const entry = entries.at(-1);\n if (!entry) return;\n\n update({\n height: entry.contentRect.height,\n width: entry.contentRect.width,\n });\n });\n\n try {\n observer.observe(element, { box: 'content-box' });\n } catch (error) {\n observer.disconnect();\n throw error;\n }\n\n return () => observer.disconnect();\n },\n );\n}\n","import { resolveElementWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { IntersectionState, Sentinel, SentinelOptions } from './types.ts';\n\nexport interface CreateIntersectionOptions extends SentinelOptions {\n readonly root?: Element | Document | null;\n readonly rootMargin?: string;\n readonly scrollMargin?: string;\n readonly threshold?: number | number[];\n}\n\nexport function createIntersection(\n element: Element,\n options?: CreateIntersectionOptions,\n): Sentinel<IntersectionState | null> {\n const target = resolveElementWindow(element);\n const IntersectionObserverConstructor = target.IntersectionObserver;\n if (typeof IntersectionObserverConstructor !== 'function') {\n throw new SentinelUnavailableError('IntersectionObserver is not available in this environment.');\n }\n\n return createSentinel<IntersectionState | null>(\n {\n initialValue: null,\n ...options,\n },\n (update) => {\n const observer = new IntersectionObserverConstructor(\n (entries) => {\n const entry = entries.at(-1);\n if (!entry) return;\n\n update({\n intersectionRatio: entry.intersectionRatio,\n isIntersecting: entry.isIntersecting,\n });\n },\n {\n root: options?.root,\n rootMargin: options?.rootMargin,\n scrollMargin: options?.scrollMargin,\n threshold: options?.threshold,\n },\n );\n\n try {\n observer.observe(element);\n } catch (error) {\n observer.disconnect();\n throw error;\n }\n\n return () => observer.disconnect();\n },\n );\n}\n","import { listenMediaQuery, resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport { SentinelUnavailableError } from './errors.ts';\nimport type { MediaQueryState, Sentinel, WindowSentinelOptions } from './types.ts';\n\nexport function createMediaQuery(query: string, options?: WindowSentinelOptions): Sentinel<MediaQueryState> {\n const target = resolveWindow(options?.target);\n if (typeof target.matchMedia !== 'function') {\n throw new SentinelUnavailableError(`matchMedia is not available for query \"${query}\".`);\n }\n\n const mediaQuery = target.matchMedia(query);\n const read = (): MediaQueryState => ({ matches: mediaQuery.matches });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => listenMediaQuery(mediaQuery, () => update(read())),\n );\n}\n","import { resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { NetworkConnectionSnapshot, NetworkState, Sentinel, WindowSentinelOptions } from './types.ts';\n\ninterface NetworkInformationLike extends EventTarget {\n readonly downlink?: number;\n readonly effectiveType?: NetworkConnectionSnapshot['effectiveType'];\n readonly rtt?: number;\n readonly saveData?: boolean;\n}\n\ntype NavigatorWithConnection = Navigator & {\n readonly connection?: NetworkInformationLike;\n};\n\nfunction normalizeConnection(connection?: NetworkInformationLike): NetworkConnectionSnapshot | null {\n if (!connection) return null;\n\n return {\n downlink: connection.downlink,\n effectiveType: connection.effectiveType,\n rtt: connection.rtt,\n saveData: connection.saveData,\n };\n}\n\nexport function createNetwork(options?: WindowSentinelOptions): Sentinel<NetworkState> {\n const target = resolveWindow(options?.target);\n const navigator = target.navigator as NavigatorWithConnection;\n const read = (): NetworkState => ({\n connection: normalizeConnection(navigator.connection),\n online: navigator.onLine,\n });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => {\n const onChange = () => update(read());\n const connection = navigator.connection;\n\n target.addEventListener('online', onChange);\n target.addEventListener('offline', onChange);\n connection?.addEventListener('change', onChange);\n\n return () => {\n target.removeEventListener('online', onChange);\n target.removeEventListener('offline', onChange);\n connection?.removeEventListener('change', onChange);\n };\n },\n );\n}\n","import { listenMediaQuery, resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { Sentinel, ViewportState, WindowSentinelOptions } from './types.ts';\n\nexport function createViewport(options?: WindowSentinelOptions): Sentinel<ViewportState> {\n const target = resolveWindow(options?.target);\n const read = (): ViewportState => ({\n dpr: target.devicePixelRatio,\n height: target.innerHeight,\n width: target.innerWidth,\n });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => {\n const onResize = () => update(read());\n let stopDprObservation: (() => void) | undefined;\n\n const observeDpr = () => {\n stopDprObservation?.();\n stopDprObservation = undefined;\n\n if (typeof target.matchMedia !== 'function') return;\n\n const mediaQuery = target.matchMedia(`(resolution: ${target.devicePixelRatio}dppx)`);\n stopDprObservation = listenMediaQuery(mediaQuery, () => {\n update(read());\n observeDpr();\n });\n };\n\n target.addEventListener('resize', onResize);\n observeDpr();\n\n return () => {\n target.removeEventListener('resize', onResize);\n stopDprObservation?.();\n };\n },\n );\n}\n"],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Readable, Ripple } from '@vielzeug/ripple';
|
|
2
|
+
export interface Sentinel<T> extends Readable<T> {
|
|
3
|
+
readonly disposalSignal: AbortSignal;
|
|
4
|
+
dispose(): void;
|
|
5
|
+
readonly disposed: boolean;
|
|
6
|
+
[Symbol.dispose](): void;
|
|
7
|
+
}
|
|
8
|
+
export interface SentinelOptions {
|
|
9
|
+
readonly runtime?: Pick<Ripple, 'signal'>;
|
|
10
|
+
readonly signal?: AbortSignal;
|
|
11
|
+
}
|
|
12
|
+
export interface WindowSentinelOptions extends SentinelOptions {
|
|
13
|
+
readonly target?: Window;
|
|
14
|
+
}
|
|
15
|
+
export interface ViewportState {
|
|
16
|
+
readonly dpr: number;
|
|
17
|
+
readonly height: number;
|
|
18
|
+
readonly width: number;
|
|
19
|
+
}
|
|
20
|
+
export interface NetworkConnectionSnapshot {
|
|
21
|
+
readonly downlink?: number;
|
|
22
|
+
readonly effectiveType?: 'slow-2g' | '2g' | '3g' | '4g';
|
|
23
|
+
readonly rtt?: number;
|
|
24
|
+
readonly saveData?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface NetworkState {
|
|
27
|
+
readonly connection: NetworkConnectionSnapshot | null;
|
|
28
|
+
readonly online: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface MediaQueryState {
|
|
31
|
+
readonly matches: boolean;
|
|
32
|
+
}
|
|
33
|
+
export interface ElementSizeState {
|
|
34
|
+
readonly height: number;
|
|
35
|
+
readonly width: number;
|
|
36
|
+
}
|
|
37
|
+
export interface IntersectionState {
|
|
38
|
+
readonly intersectionRatio: number;
|
|
39
|
+
readonly isIntersecting: boolean;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEzD,MAAM,WAAW,QAAQ,CAAC,CAAC,CAAE,SAAQ,QAAQ,CAAC,CAAC,CAAC;IAC9C,QAAQ,CAAC,cAAc,EAAE,WAAW,CAAC;IACrC,OAAO,IAAI,IAAI,CAAC;IAChB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC1C,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAsB,SAAQ,eAAe;IAC5D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IACxD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,UAAU,EAAE,yBAAyB,GAAG,IAAI,CAAC;IACtD,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;CAClC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./_platform.cjs"),t=require("./core.cjs");function n(n){let r=e.resolveWindow(n?.target),i=()=>({dpr:r.devicePixelRatio,height:r.innerHeight,width:r.innerWidth});return t.createSentinel({initialValue:i(),...n},t=>{let n=()=>t(i()),a,o=()=>{if(a?.(),a=void 0,typeof r.matchMedia!=`function`)return;let n=r.matchMedia(`(resolution: ${r.devicePixelRatio}dppx)`);a=e.listenMediaQuery(n,()=>{t(i()),o()})};return r.addEventListener(`resize`,n),o(),()=>{r.removeEventListener(`resize`,n),a?.()}})}exports.createViewport=n;
|
|
2
|
+
//# sourceMappingURL=viewport.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewport.cjs","names":[],"sources":["../src/viewport.ts"],"sourcesContent":["import { listenMediaQuery, resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { Sentinel, ViewportState, WindowSentinelOptions } from './types.ts';\n\nexport function createViewport(options?: WindowSentinelOptions): Sentinel<ViewportState> {\n const target = resolveWindow(options?.target);\n const read = (): ViewportState => ({\n dpr: target.devicePixelRatio,\n height: target.innerHeight,\n width: target.innerWidth,\n });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => {\n const onResize = () => update(read());\n let stopDprObservation: (() => void) | undefined;\n\n const observeDpr = () => {\n stopDprObservation?.();\n stopDprObservation = undefined;\n\n if (typeof target.matchMedia !== 'function') return;\n\n const mediaQuery = target.matchMedia(`(resolution: ${target.devicePixelRatio}dppx)`);\n stopDprObservation = listenMediaQuery(mediaQuery, () => {\n update(read());\n observeDpr();\n });\n };\n\n target.addEventListener('resize', onResize);\n observeDpr();\n\n return () => {\n target.removeEventListener('resize', onResize);\n stopDprObservation?.();\n };\n },\n );\n}\n"],"mappings":"2DAIA,SAAgB,EAAe,EAA0D,CACvF,IAAM,EAAS,EAAA,cAAc,GAAS,MAAM,EACtC,OAA6B,CACjC,IAAK,EAAO,iBACZ,OAAQ,EAAO,YACf,MAAO,EAAO,UAChB,GAEA,OAAO,EAAA,eACL,CACE,aAAc,EAAK,EACnB,GAAG,CACL,EACC,GAAW,CACV,IAAM,MAAiB,EAAO,EAAK,CAAC,EAChC,EAEE,MAAmB,CAIvB,GAHA,IAAqB,EACrB,EAAqB,IAAA,GAEjB,OAAO,EAAO,YAAe,WAAY,OAE7C,IAAM,EAAa,EAAO,WAAW,gBAAgB,EAAO,iBAAiB,MAAM,EACnF,EAAqB,EAAA,iBAAiB,MAAkB,CACtD,EAAO,EAAK,CAAC,EACb,EAAW,CACb,CAAC,CACH,EAKA,OAHA,EAAO,iBAAiB,SAAU,CAAQ,EAC1C,EAAW,MAEE,CACX,EAAO,oBAAoB,SAAU,CAAQ,EAC7C,IAAqB,CACvB,CACF,CACF,CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewport.d.ts","sourceRoot":"","sources":["../src/viewport.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEjF,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,QAAQ,CAAC,aAAa,CAAC,CAuCvF"}
|
package/dist/viewport.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{listenMediaQuery as e,resolveWindow as t}from"./_platform.js";import{createSentinel as n}from"./core.js";function r(r){let i=t(r?.target),a=()=>({dpr:i.devicePixelRatio,height:i.innerHeight,width:i.innerWidth});return n({initialValue:a(),...r},t=>{let n=()=>t(a()),r,o=()=>{if(r?.(),r=void 0,typeof i.matchMedia!=`function`)return;let n=i.matchMedia(`(resolution: ${i.devicePixelRatio}dppx)`);r=e(n,()=>{t(a()),o()})};return i.addEventListener(`resize`,n),o(),()=>{i.removeEventListener(`resize`,n),r?.()}})}export{r as createViewport};
|
|
2
|
+
//# sourceMappingURL=viewport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewport.js","names":[],"sources":["../src/viewport.ts"],"sourcesContent":["import { listenMediaQuery, resolveWindow } from './_platform.ts';\nimport { createSentinel } from './core.ts';\nimport type { Sentinel, ViewportState, WindowSentinelOptions } from './types.ts';\n\nexport function createViewport(options?: WindowSentinelOptions): Sentinel<ViewportState> {\n const target = resolveWindow(options?.target);\n const read = (): ViewportState => ({\n dpr: target.devicePixelRatio,\n height: target.innerHeight,\n width: target.innerWidth,\n });\n\n return createSentinel(\n {\n initialValue: read(),\n ...options,\n },\n (update) => {\n const onResize = () => update(read());\n let stopDprObservation: (() => void) | undefined;\n\n const observeDpr = () => {\n stopDprObservation?.();\n stopDprObservation = undefined;\n\n if (typeof target.matchMedia !== 'function') return;\n\n const mediaQuery = target.matchMedia(`(resolution: ${target.devicePixelRatio}dppx)`);\n stopDprObservation = listenMediaQuery(mediaQuery, () => {\n update(read());\n observeDpr();\n });\n };\n\n target.addEventListener('resize', onResize);\n observeDpr();\n\n return () => {\n target.removeEventListener('resize', onResize);\n stopDprObservation?.();\n };\n },\n );\n}\n"],"mappings":"gHAIA,SAAgB,EAAe,EAA0D,CACvF,IAAM,EAAS,EAAc,GAAS,MAAM,EACtC,OAA6B,CACjC,IAAK,EAAO,iBACZ,OAAQ,EAAO,YACf,MAAO,EAAO,UAChB,GAEA,OAAO,EACL,CACE,aAAc,EAAK,EACnB,GAAG,CACL,EACC,GAAW,CACV,IAAM,MAAiB,EAAO,EAAK,CAAC,EAChC,EAEE,MAAmB,CAIvB,GAHA,IAAqB,EACrB,EAAqB,IAAA,GAEjB,OAAO,EAAO,YAAe,WAAY,OAE7C,IAAM,EAAa,EAAO,WAAW,gBAAgB,EAAO,iBAAiB,MAAM,EACnF,EAAqB,EAAiB,MAAkB,CACtD,EAAO,EAAK,CAAC,EACb,EAAW,CACb,CAAC,CACH,EAKA,OAHA,EAAO,iBAAiB,SAAU,CAAQ,EAC1C,EAAW,MAEE,CACX,EAAO,oBAAoB,SAAU,CAAQ,EAC7C,IAAqB,CACvB,CACF,CACF,CACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vielzeug/sentinel",
|
|
3
|
+
"version": "2.0.1",
|
|
4
|
+
"description": "Reactive observables for external browser environment state",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/helmuthdu/vielzeug",
|
|
8
|
+
"directory": "packages/sentinel"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=22"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"main": "./dist/index.cjs",
|
|
18
|
+
"module": "./dist/index.js",
|
|
19
|
+
"types": "dist/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"import": "./dist/index.js",
|
|
23
|
+
"require": "./dist/index.cjs",
|
|
24
|
+
"types": "./dist/index.d.ts"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public",
|
|
29
|
+
"registry": "https://registry.npmjs.org/"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "vite build && pnpm run build:bundle && pnpm run build:types",
|
|
33
|
+
"build:bundle": "vite build --config vite.bundle.config.ts",
|
|
34
|
+
"build:types": "tsc -p tsconfig.declarations.json",
|
|
35
|
+
"fix": "biome check --write src",
|
|
36
|
+
"lint": "biome ci src",
|
|
37
|
+
"prepublishOnly": "pnpm run build",
|
|
38
|
+
"test": "vitest",
|
|
39
|
+
"test:e2e": "pnpm --filter @vielzeug/ripple build && pnpm run build && playwright test"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"@vielzeug/ripple": "^2.2.3"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@playwright/test": "^1.62.1",
|
|
46
|
+
"@types/node": "^26.2.0",
|
|
47
|
+
"@vielzeug/ripple": "2.2.3",
|
|
48
|
+
"typescript": "^6.0.3",
|
|
49
|
+
"vite": "^8.2.1",
|
|
50
|
+
"vitest": "^4.1.10"
|
|
51
|
+
}
|
|
52
|
+
}
|