@mmstack/primitives 19.3.5 → 19.3.7
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 +276 -924
- package/fesm2022/mmstack-primitives.mjs +632 -73
- package/fesm2022/mmstack-primitives.mjs.map +1 -1
- package/lib/mappers/key-array.d.ts +23 -0
- package/lib/mappers/map-object.d.ts +59 -0
- package/lib/pipeable/operators.d.ts +127 -8
- package/lib/sensors/battery-status.d.ts +23 -0
- package/lib/sensors/clipboard.d.ts +22 -0
- package/lib/sensors/focus-within.d.ts +21 -0
- package/lib/sensors/geolocation.d.ts +29 -0
- package/lib/sensors/idle.d.ts +35 -0
- package/lib/sensors/index.d.ts +7 -0
- package/lib/sensors/network-status.d.ts +8 -0
- package/lib/sensors/orientation.d.ts +23 -0
- package/lib/sensors/sensor.d.ts +89 -0
- package/lib/sensors/signal-from-event.d.ts +42 -0
- package/lib/tabSync.d.ts +10 -36
- package/lib/throttled.d.ts +12 -0
- package/lib/with-history.d.ts +1 -1
- package/package.json +1 -1
|
@@ -15,7 +15,30 @@ import { type Signal } from '@angular/core';
|
|
|
15
15
|
* @param mapFn The mapping function. Receives the item and its index as a Signal.
|
|
16
16
|
* @param options Optional configuration:
|
|
17
17
|
* - `onDestroy`: A callback invoked when a mapped item is removed from the array.
|
|
18
|
+
* - `key`: A custom key extractor for identity matching (e.g. `(item) => item.id`)
|
|
19
|
+
* when item references change but conceptual identity is preserved.
|
|
18
20
|
* @returns A `Signal<U[]>` containing the mapped array.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* const users = signal([
|
|
25
|
+
* { id: 1, name: 'Alice' },
|
|
26
|
+
* { id: 2, name: 'Bob' },
|
|
27
|
+
* ]);
|
|
28
|
+
*
|
|
29
|
+
* const rows = keyArray(
|
|
30
|
+
* users,
|
|
31
|
+
* (user, index) => ({
|
|
32
|
+
* label: computed(() => `#${index()} ${user.name}`),
|
|
33
|
+
* id: user.id,
|
|
34
|
+
* }),
|
|
35
|
+
* { key: (u) => u.id },
|
|
36
|
+
* );
|
|
37
|
+
*
|
|
38
|
+
* // Reordering users() rebuilds index signals only — `rows` entries
|
|
39
|
+
* // are matched by id and reused, not re-created.
|
|
40
|
+
* users.set([users()[1], users()[0]]);
|
|
41
|
+
* ```
|
|
19
42
|
*/
|
|
20
43
|
export declare function keyArray<T, U, K>(source: Signal<T[]> | (() => T[]), mapFn: (v: T, i: Signal<number>) => U, options?: {
|
|
21
44
|
onDestroy?: (value: U) => void;
|
|
@@ -3,12 +3,71 @@ import { type MutableSignal } from '../mutable';
|
|
|
3
3
|
type MappedObject<T extends object, U> = {
|
|
4
4
|
[K in keyof T]: U;
|
|
5
5
|
};
|
|
6
|
+
/**
|
|
7
|
+
* Reactively maps each property of an object signal into a new object,
|
|
8
|
+
* preserving the same set of keys. For each key, `mapFn` receives a stable
|
|
9
|
+
* per-key signal — outputs for keys that haven't been added or removed are
|
|
10
|
+
* reused on subsequent reads. Sibling to {@link indexArray} / {@link keyArray}
|
|
11
|
+
* but for object records.
|
|
12
|
+
*
|
|
13
|
+
* The type of per-key signal passed into `mapFn` depends on the source:
|
|
14
|
+
* - `MutableSignal<T>` source → `MutableSignal<T[K]>` (in-place mutation)
|
|
15
|
+
* - `WritableSignal<T>` source → `WritableSignal<T[K]>` (two-way binding)
|
|
16
|
+
* - read-only `Signal<T>` or `() => T` source → read-only `Signal<T[K]>`
|
|
17
|
+
*
|
|
18
|
+
* @typeParam T The object type held by the source signal.
|
|
19
|
+
* @typeParam U The type produced for each key by `mapFn`.
|
|
20
|
+
*
|
|
21
|
+
* @param source A `MutableSignal<T>` whose properties are mapped with full
|
|
22
|
+
* in-place mutation capability via the per-key `MutableSignal`.
|
|
23
|
+
* @param mapFn Receives each key and its per-key `MutableSignal<T[K]>`.
|
|
24
|
+
* @param options Optional `onDestroy(value)` callback fired when a key is
|
|
25
|
+
* removed from the source.
|
|
26
|
+
* @returns A read-only signal of the mapped object.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* const state = mutable({ name: 'Alice', age: 30 });
|
|
31
|
+
* const view = mapObject(state, (key, prop) => ({
|
|
32
|
+
* label: key,
|
|
33
|
+
* current: computed(() => prop()),
|
|
34
|
+
* onInput: (next: any) => prop.set(next),
|
|
35
|
+
* }));
|
|
36
|
+
* view().age.onInput(31);
|
|
37
|
+
* state(); // { name: 'Alice', age: 31 }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
6
40
|
export declare function mapObject<T extends object, U>(source: MutableSignal<T>, mapFn: <K extends keyof T>(key: K, value: MutableSignal<T[K]>) => U, options?: {
|
|
7
41
|
onDestroy?: (value: U) => void;
|
|
8
42
|
}): Signal<MappedObject<T, U>>;
|
|
43
|
+
/**
|
|
44
|
+
* Reactively maps each property of a `WritableSignal<T>` into a new object.
|
|
45
|
+
* Each key's per-property signal supports `.set` / `.update` for two-way
|
|
46
|
+
* binding back into the parent object.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* const user = signal({ name: 'Alice', age: 30 });
|
|
51
|
+
* const inputs = mapObject(user, (key, prop) => ({
|
|
52
|
+
* value: prop,
|
|
53
|
+
* setValue: (v: any) => prop.set(v),
|
|
54
|
+
* }));
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
9
57
|
export declare function mapObject<T extends object, U>(source: WritableSignal<T>, mapFn: <K extends keyof T>(key: K, value: WritableSignal<T[K]>) => U, options?: {
|
|
10
58
|
onDestroy?: (value: U) => void;
|
|
11
59
|
}): Signal<MappedObject<T, U>>;
|
|
60
|
+
/**
|
|
61
|
+
* Reactively maps each property of a read-only `Signal<T>` (or plain `() => T`
|
|
62
|
+
* accessor) into a new object. Per-key signals are read-only.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* const config = computed(() => ({ theme: 'dark', density: 'compact' }));
|
|
67
|
+
* const view = mapObject(config, (key, prop) => `${key}: ${prop()}`);
|
|
68
|
+
* view(); // { theme: 'theme: dark', density: 'density: compact' }
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
12
71
|
export declare function mapObject<T extends object, U>(source: (() => T) | Signal<T>, mapFn: <K extends keyof T>(key: K, value: Signal<T[K]>) => U, options?: {
|
|
13
72
|
onDestroy?: (value: U) => void;
|
|
14
73
|
}): Signal<MappedObject<T, U>>;
|
|
@@ -1,14 +1,133 @@
|
|
|
1
|
-
import { type CreateSignalOptions, type Signal } from '@angular/core';
|
|
1
|
+
import { type CreateSignalOptions, type Injector, type Signal } from '@angular/core';
|
|
2
2
|
import { type Operator } from './types';
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Synchronous projection of a signal value with optional `CreateSignalOptions`
|
|
5
|
+
* (custom `equal`, `debugName`, etc.). Equivalent to `map` plus the ability to
|
|
6
|
+
* pass signal options through to the underlying `computed()`.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* const user = piped({ id: 1, name: 'Alice' });
|
|
11
|
+
* const name = user.pipe(select((u) => u.name));
|
|
12
|
+
* name(); // 'Alice'
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
4
15
|
export declare const select: <I, O>(projector: (v: I) => O, opt?: CreateSignalOptions<O>) => Operator<I, O>;
|
|
5
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Combine the piped signal with another `Signal` using a projector. The result
|
|
18
|
+
* recomputes whenever either source changes.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* const price = piped(10);
|
|
23
|
+
* const quantity = signal(3);
|
|
24
|
+
* const total = price.pipe(combineWith(quantity, (p, q) => p * q));
|
|
25
|
+
* total(); // 30
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
6
28
|
export declare const combineWith: <A, B, R>(other: Signal<B>, project: (a: A, b: B) => R, opt?: CreateSignalOptions<R>) => Operator<A, R>;
|
|
7
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* Suppress emissions while consecutive values are considered equal. The
|
|
31
|
+
* comparator defaults to `Object.is`; pass a custom one for structural or
|
|
32
|
+
* key-based equality (e.g. compare by `id` only).
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* const user = piped({ id: 1, lastSeen: Date.now() });
|
|
37
|
+
* const byId = user.pipe(distinct((a, b) => a.id === b.id));
|
|
38
|
+
* // byId only re-emits when `id` changes, not on every `lastSeen` update
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
8
41
|
export declare const distinct: <T>(equal?: (a: T, b: T) => boolean) => Operator<T, T>;
|
|
9
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* Pure synchronous transform from input to output. Equivalent to a `computed()`
|
|
44
|
+
* that reads the source and returns `fn(value)`.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* const count = piped(2);
|
|
49
|
+
* const doubled = count.pipe(map((n) => n * 2));
|
|
50
|
+
* doubled(); // 4
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
10
53
|
export declare const map: <I, O>(fn: (v: I) => O) => Operator<I, O>;
|
|
11
|
-
/**
|
|
54
|
+
/**
|
|
55
|
+
* Keep only values that pass the predicate. The result holds the last passing
|
|
56
|
+
* value across emissions; before any value passes, the result is `undefined` —
|
|
57
|
+
* see {@link filterWith} when you need a non-`undefined` seed.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts
|
|
61
|
+
* const event = piped<MouseEvent | null>(null);
|
|
62
|
+
* const clicks = event.pipe(filter((e): e is MouseEvent => e?.type === 'click'));
|
|
63
|
+
* clicks(); // undefined until a click happens, then the last MouseEvent
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
12
66
|
export declare const filter: <T>(predicate: (v: T) => boolean) => Operator<T, T | undefined>;
|
|
13
|
-
/**
|
|
14
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Run a side effect on every emission without altering the signal value. Wraps
|
|
69
|
+
* Angular's `effect()`, so it must run in an injection context or receive an
|
|
70
|
+
* explicit `injector`. Use for logging / analytics — not for setting other
|
|
71
|
+
* signals (that's what regular `effect()` is for).
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* const count = piped(0);
|
|
76
|
+
* count.pipe(tap((n) => console.log('count:', n)));
|
|
77
|
+
* count.set(1); // logs 'count: 1'
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export declare const tap: <T>(fn: (v: T) => void, injector?: Injector) => Operator<T, T>;
|
|
81
|
+
/**
|
|
82
|
+
* Like {@link filter}, but emits `initial` until a value first passes the
|
|
83
|
+
* predicate. Eliminates the `T | undefined` return type at the cost of an
|
|
84
|
+
* explicit seed value.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```ts
|
|
88
|
+
* const event = piped<MouseEvent | null>(null);
|
|
89
|
+
* const lastClick = event.pipe(filterWith((e) => e?.type === 'click', null));
|
|
90
|
+
* lastClick(); // null until the first click, then the most recent click event
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export declare const filterWith: <T>(predicate: (v: T) => boolean, initial: T) => Operator<T, T>;
|
|
94
|
+
/**
|
|
95
|
+
* Emit `initial` on the first read, then mirror the source on every subsequent
|
|
96
|
+
* read. Useful for giving a pipeline a sensible seed value before the source
|
|
97
|
+
* is ready (e.g. loading state).
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* const data = piped<User | null>(null);
|
|
102
|
+
* const view = data.pipe(startWith<User | null, 'loading'>('loading'));
|
|
103
|
+
* view(); // 'loading' on first read, then User | null afterward
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export declare const startWith: <T, U>(initial: U) => Operator<T, T | U>;
|
|
107
|
+
/**
|
|
108
|
+
* Emit `[prev, curr]` tuples so consumers can react to transitions instead of
|
|
109
|
+
* raw values. On the first emission `prev` is `undefined`.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```ts
|
|
113
|
+
* const count = piped(0);
|
|
114
|
+
* const delta = count.pipe(pairwise(), map(([prev, curr]) => curr - (prev ?? 0)));
|
|
115
|
+
* count.set(5);
|
|
116
|
+
* delta(); // 5
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
export declare const pairwise: <T>() => Operator<T, [T | undefined, T]>;
|
|
120
|
+
/**
|
|
121
|
+
* Reduce-like accumulator that folds each emission into a running result.
|
|
122
|
+
* Behaves like `Array.prototype.reduce` but applied over time, with the
|
|
123
|
+
* accumulator persisted across emissions.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```ts
|
|
127
|
+
* const delta = piped(0);
|
|
128
|
+
* const total = delta.pipe(scan((acc, n) => acc + n, 0));
|
|
129
|
+
* delta.set(5); // total() === 5
|
|
130
|
+
* delta.set(3); // total() === 8
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
export declare const scan: <T, R>(reducer: (acc: R, curr: T) => R, seed: R) => Operator<T, R>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Signal } from '@angular/core';
|
|
2
|
+
export type BatteryStatus = {
|
|
3
|
+
readonly level: number;
|
|
4
|
+
readonly charging: boolean;
|
|
5
|
+
readonly chargingTime: number;
|
|
6
|
+
readonly dischargingTime: number;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Creates a read-only signal that tracks the system battery status using the
|
|
10
|
+
* Battery Status API. Returns `null` until the underlying `getBattery()`
|
|
11
|
+
* promise resolves, or permanently when the API is unsupported (Firefox /
|
|
12
|
+
* Safari at the time of writing). SSR-safe.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* const battery = batteryStatus();
|
|
17
|
+
* effect(() => {
|
|
18
|
+
* const b = battery();
|
|
19
|
+
* if (b) console.log(`${Math.round(b.level * 100)}% • charging: ${b.charging}`);
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function batteryStatus(debugName?: string): Signal<BatteryStatus | null>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type Signal } from '@angular/core';
|
|
2
|
+
export type ClipboardSignal = Signal<string> & {
|
|
3
|
+
/**
|
|
4
|
+
* Writes `value` to the system clipboard. Resolves once the write completes;
|
|
5
|
+
* rejects if the Clipboard API rejects (denied permission, insecure context).
|
|
6
|
+
*/
|
|
7
|
+
readonly copy: (value: string) => Promise<void>;
|
|
8
|
+
/** `true` iff the Clipboard API is available in this environment. */
|
|
9
|
+
readonly isSupported: Signal<boolean>;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Creates a read-only signal mirroring the system clipboard contents.
|
|
13
|
+
*
|
|
14
|
+
* The signal value starts empty and updates whenever a `copy` event fires on
|
|
15
|
+
* the document (or {@link ClipboardSignal.copy} is invoked from this app).
|
|
16
|
+
* SSR-safe — returns `''` and `isSupported: false` on the server.
|
|
17
|
+
*
|
|
18
|
+
* Note: read access requires the Clipboard API and an active permission grant
|
|
19
|
+
* in browsers that gate it. Errors from `navigator.clipboard.readText` are
|
|
20
|
+
* swallowed silently to keep the signal value stable.
|
|
21
|
+
*/
|
|
22
|
+
export declare function clipboard(debugName?: string): ClipboardSignal;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ElementRef, type Signal } from '@angular/core';
|
|
2
|
+
type FocusWithinTarget = ElementRef<Element> | Element | Signal<ElementRef<Element> | Element | null>;
|
|
3
|
+
/**
|
|
4
|
+
* Creates a read-only signal that tracks whether the focused element is the
|
|
5
|
+
* target or a descendant of it. Mirrors the CSS `:focus-within` pseudo-class.
|
|
6
|
+
*
|
|
7
|
+
* Defaults `target` to the current `ElementRef` so it can be used inline in a
|
|
8
|
+
* component's `class` field. SSR-safe — returns a constant `false` signal on
|
|
9
|
+
* the server.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* @Component({ ... })
|
|
14
|
+
* class MenuComponent {
|
|
15
|
+
* // Defaults to the host element — flips true when focus is inside.
|
|
16
|
+
* readonly hasFocus = focusWithin();
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare function focusWithin(target?: FocusWithinTarget): Signal<boolean>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type Signal } from '@angular/core';
|
|
2
|
+
export type GeolocationOptions = PositionOptions & {
|
|
3
|
+
/**
|
|
4
|
+
* If `true`, uses `navigator.geolocation.watchPosition` and updates the
|
|
5
|
+
* signal continuously. Otherwise a single `getCurrentPosition` call is made.
|
|
6
|
+
* @default false
|
|
7
|
+
*/
|
|
8
|
+
watch?: boolean;
|
|
9
|
+
/** Optional debug name for the produced signal. */
|
|
10
|
+
debugName?: string;
|
|
11
|
+
};
|
|
12
|
+
export type GeolocationSignal = Signal<GeolocationPosition | null> & {
|
|
13
|
+
readonly error: Signal<GeolocationPositionError | null>;
|
|
14
|
+
readonly loading: Signal<boolean>;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Creates a read-only signal that exposes the current geolocation position.
|
|
18
|
+
*
|
|
19
|
+
* The returned signal carries `error` and `loading` sub-signals for permission
|
|
20
|
+
* failures and the in-flight initial fetch respectively. SSR-safe — on the
|
|
21
|
+
* server the position is `null`, loading is `false`, and no API calls are made.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* const where = geolocation({ watch: true, enableHighAccuracy: true });
|
|
26
|
+
* effect(() => console.log(where()?.coords, where.error()));
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function geolocation(opt?: GeolocationOptions): GeolocationSignal;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type Signal } from '@angular/core';
|
|
2
|
+
export type IdleOptions = {
|
|
3
|
+
/**
|
|
4
|
+
* Milliseconds of user inactivity before the signal flips to `true`.
|
|
5
|
+
* @default 60_000
|
|
6
|
+
*/
|
|
7
|
+
ms?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Activity events that reset the idle timer.
|
|
10
|
+
* @default ['mousemove','keydown','touchstart','scroll','visibilitychange']
|
|
11
|
+
*/
|
|
12
|
+
events?: string[];
|
|
13
|
+
/** Optional debug name for the produced signal. */
|
|
14
|
+
debugName?: string;
|
|
15
|
+
};
|
|
16
|
+
export type IdleSignal = Signal<boolean> & {
|
|
17
|
+
/** Timestamp of the last idle/active transition. */
|
|
18
|
+
readonly since: Signal<Date>;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Creates a read-only signal that flips to `true` after a window of user
|
|
22
|
+
* inactivity. Any of the configured `events` (default: pointer/keyboard/scroll
|
|
23
|
+
* activity) resets the timer and flips the signal back to `false`.
|
|
24
|
+
*
|
|
25
|
+
* SSR-safe — always `false` with a frozen `since` date on the server.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* const isAway = idle({ ms: 30_000 });
|
|
30
|
+
* effect(() => {
|
|
31
|
+
* if (isAway()) console.log('idle since', isAway.since());
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare function idle(opt?: IdleOptions): IdleSignal;
|
package/lib/sensors/index.d.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
export * from './battery-status';
|
|
2
|
+
export * from './clipboard';
|
|
1
3
|
export * from './element-size';
|
|
2
4
|
export * from './element-visibility';
|
|
5
|
+
export * from './focus-within';
|
|
6
|
+
export * from './geolocation';
|
|
7
|
+
export * from './idle';
|
|
3
8
|
export * from './media-query';
|
|
4
9
|
export * from './mouse-position';
|
|
5
10
|
export * from './network-status';
|
|
11
|
+
export * from './orientation';
|
|
6
12
|
export * from './page-visibility';
|
|
7
13
|
export * from './scroll-position';
|
|
8
14
|
export * from './sensor';
|
|
15
|
+
export * from './signal-from-event';
|
|
9
16
|
export * from './window-size';
|
|
@@ -16,5 +16,13 @@ export type NetworkStatusSignal = Signal<boolean> & {
|
|
|
16
16
|
*
|
|
17
17
|
* @param debugName Optional debug name for the signal.
|
|
18
18
|
* @returns A `NetworkStatusSignal` instance.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* const online = networkStatus();
|
|
23
|
+
* effect(() => {
|
|
24
|
+
* if (!online()) console.log('offline since', online.since());
|
|
25
|
+
* });
|
|
26
|
+
* ```
|
|
19
27
|
*/
|
|
20
28
|
export declare function networkStatus(debugName?: string): NetworkStatusSignal;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Signal } from '@angular/core';
|
|
2
|
+
export type ScreenOrientation = {
|
|
3
|
+
/** Angle in degrees relative to the natural orientation. */
|
|
4
|
+
readonly angle: number;
|
|
5
|
+
/** One of the four `OrientationType` strings. */
|
|
6
|
+
readonly type: OrientationType;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Creates a read-only signal that tracks `screen.orientation`.
|
|
10
|
+
*
|
|
11
|
+
* SSR-safe — returns a constant `portrait-primary / 0°` signal on the server
|
|
12
|
+
* and in environments without `screen.orientation` support.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* const screenOrientation = orientation();
|
|
17
|
+
* effect(() => {
|
|
18
|
+
* const { type, angle } = screenOrientation();
|
|
19
|
+
* console.log(`${type} at ${angle}°`);
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function orientation(debugName?: string): Signal<ScreenOrientation>;
|
package/lib/sensors/sensor.d.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { type ElementRef, type Signal } from '@angular/core';
|
|
2
|
+
import { type BatteryStatus } from './battery-status';
|
|
3
|
+
import { type ClipboardSignal } from './clipboard';
|
|
2
4
|
import { type ElementSizeOptions as BaseElementSizeOptions, type ElementSizeSignal } from './element-size';
|
|
3
5
|
import { type ElementVisibilityOptions as BaseElementVisibilityOptions, type ElementVisibilitySignal } from './element-visibility';
|
|
6
|
+
import { type GeolocationOptions, type GeolocationSignal } from './geolocation';
|
|
7
|
+
import { type IdleOptions, type IdleSignal } from './idle';
|
|
4
8
|
import { type MousePositionOptions, type MousePositionSignal } from './mouse-position';
|
|
5
9
|
import { type NetworkStatusSignal } from './network-status';
|
|
10
|
+
import { type ScreenOrientation } from './orientation';
|
|
6
11
|
import { type ScrollPositionOptions, type ScrollPositionSignal } from './scroll-position';
|
|
7
12
|
import { type WindowSizeOptions, type WindowSizeSignal } from './window-size';
|
|
8
13
|
type SensorTypedOptions = {
|
|
@@ -61,6 +66,39 @@ type SensorTypedOptions = {
|
|
|
61
66
|
};
|
|
62
67
|
returnType: Signal<boolean>;
|
|
63
68
|
};
|
|
69
|
+
geolocation: {
|
|
70
|
+
opt: GeolocationOptions;
|
|
71
|
+
returnType: GeolocationSignal;
|
|
72
|
+
};
|
|
73
|
+
clipboard: {
|
|
74
|
+
opt: {
|
|
75
|
+
debugName?: string;
|
|
76
|
+
};
|
|
77
|
+
returnType: ClipboardSignal;
|
|
78
|
+
};
|
|
79
|
+
orientation: {
|
|
80
|
+
opt: {
|
|
81
|
+
debugName?: string;
|
|
82
|
+
};
|
|
83
|
+
returnType: Signal<ScreenOrientation>;
|
|
84
|
+
};
|
|
85
|
+
batteryStatus: {
|
|
86
|
+
opt: {
|
|
87
|
+
debugName?: string;
|
|
88
|
+
};
|
|
89
|
+
returnType: Signal<BatteryStatus | null>;
|
|
90
|
+
};
|
|
91
|
+
idle: {
|
|
92
|
+
opt: IdleOptions;
|
|
93
|
+
returnType: IdleSignal;
|
|
94
|
+
};
|
|
95
|
+
focusWithin: {
|
|
96
|
+
opt: {
|
|
97
|
+
debugName?: string;
|
|
98
|
+
target?: ElementRef<Element> | Element | Signal<ElementRef<Element> | Element | null>;
|
|
99
|
+
};
|
|
100
|
+
returnType: Signal<boolean>;
|
|
101
|
+
};
|
|
64
102
|
};
|
|
65
103
|
/**
|
|
66
104
|
* Creates a sensor signal that the elements visiblity within the viewport
|
|
@@ -152,11 +190,62 @@ export declare function sensor(type: 'windowSize', options?: SensorTypedOptions[
|
|
|
152
190
|
* @example const pageScroll = sensor('scrollPosition', { throttle: 150 });
|
|
153
191
|
*/
|
|
154
192
|
export declare function sensor(type: 'scrollPosition', options?: SensorTypedOptions['scrollPosition']['opt']): ScrollPositionSignal;
|
|
193
|
+
/**
|
|
194
|
+
* Creates a sensor signal exposing the device's current geolocation position.
|
|
195
|
+
* @see {geolocation}
|
|
196
|
+
*/
|
|
197
|
+
export declare function sensor(type: 'geolocation', options?: SensorTypedOptions['geolocation']['opt']): GeolocationSignal;
|
|
198
|
+
/**
|
|
199
|
+
* Creates a sensor signal mirroring the system clipboard contents.
|
|
200
|
+
* @see {clipboard}
|
|
201
|
+
*/
|
|
202
|
+
export declare function sensor(type: 'clipboard', options?: SensorTypedOptions['clipboard']['opt']): ClipboardSignal;
|
|
203
|
+
/**
|
|
204
|
+
* Creates a sensor signal tracking the screen orientation.
|
|
205
|
+
* @see {orientation}
|
|
206
|
+
*/
|
|
207
|
+
export declare function sensor(type: 'orientation', options?: SensorTypedOptions['orientation']['opt']): Signal<ScreenOrientation>;
|
|
208
|
+
/**
|
|
209
|
+
* Creates a sensor signal tracking the system battery status.
|
|
210
|
+
* @see {batteryStatus}
|
|
211
|
+
*/
|
|
212
|
+
export declare function sensor(type: 'batteryStatus', options?: SensorTypedOptions['batteryStatus']['opt']): Signal<BatteryStatus | null>;
|
|
213
|
+
/**
|
|
214
|
+
* Creates a sensor signal that flips to `true` after a window of user inactivity.
|
|
215
|
+
* @see {idle}
|
|
216
|
+
*/
|
|
217
|
+
export declare function sensor(type: 'idle', options?: SensorTypedOptions['idle']['opt']): IdleSignal;
|
|
218
|
+
/**
|
|
219
|
+
* Creates a sensor signal tracking whether focus is within a target subtree.
|
|
220
|
+
* @see {focusWithin}
|
|
221
|
+
*/
|
|
222
|
+
export declare function sensor(type: 'focusWithin', options?: SensorTypedOptions['focusWithin']['opt']): Signal<boolean>;
|
|
155
223
|
type SensorsOptions<TKey extends keyof SensorTypedOptions> = {
|
|
156
224
|
[K in TKey]: SensorTypedOptions[K]['opt'];
|
|
157
225
|
};
|
|
158
226
|
type Sensors<TKey extends keyof SensorTypedOptions> = {
|
|
159
227
|
[K in TKey]: SensorTypedOptions[K]['returnType'];
|
|
160
228
|
};
|
|
229
|
+
/**
|
|
230
|
+
* Bulk sensor factory — creates several sensor signals at once and returns
|
|
231
|
+
* them keyed by sensor type. Convenient when a single consumer needs to react
|
|
232
|
+
* to multiple browser signals; for a single sensor prefer {@link sensor}
|
|
233
|
+
* directly.
|
|
234
|
+
*
|
|
235
|
+
* @typeParam TType The union of sensor keys being requested.
|
|
236
|
+
* @param track Array of sensor type keys to create.
|
|
237
|
+
* @param opt Optional per-sensor options keyed by sensor type.
|
|
238
|
+
* @returns A record `{ [key]: <SensorReturnType> }` for each requested key.
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* ```ts
|
|
242
|
+
* const { windowSize, networkStatus } = sensors(
|
|
243
|
+
* ['windowSize', 'networkStatus'],
|
|
244
|
+
* { windowSize: { throttle: 200 } },
|
|
245
|
+
* );
|
|
246
|
+
*
|
|
247
|
+
* effect(() => console.log(windowSize(), networkStatus()));
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
161
250
|
export declare function sensors<const TType extends keyof SensorTypedOptions>(track: TType[], opt?: SensorsOptions<TType>): Sensors<TType>;
|
|
162
251
|
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { DestroyRef, ElementRef, Injector, type Signal } from '@angular/core';
|
|
2
|
+
/**
|
|
3
|
+
* Options for {@link signalFromEvent}. Extends the native
|
|
4
|
+
* `AddEventListenerOptions` so callers can opt into `capture`, `passive`, etc.
|
|
5
|
+
*/
|
|
6
|
+
export type SignalFromEventOptions = AddEventListenerOptions & {
|
|
7
|
+
/** Optional debug name for the produced signal. */
|
|
8
|
+
debugName?: string;
|
|
9
|
+
/** Override the DestroyRef used to remove the listener on teardown. */
|
|
10
|
+
destroyRef?: DestroyRef;
|
|
11
|
+
/** Override the Injector used to inject dependencies. */
|
|
12
|
+
injector?: Injector;
|
|
13
|
+
};
|
|
14
|
+
type EventTargetLike = EventTarget | ElementRef<EventTarget>;
|
|
15
|
+
type ResolvableTarget = EventTargetLike | Signal<EventTargetLike | null>;
|
|
16
|
+
/**
|
|
17
|
+
* Creates a read-only signal that emits the latest event dispatched on a
|
|
18
|
+
* target. The target can be a static `EventTarget`, an `ElementRef`, or a
|
|
19
|
+
* `Signal` that resolves to one (or `null` to detach).
|
|
20
|
+
*
|
|
21
|
+
* SSR-safe: on the server the signal returns the provided `initial` value and
|
|
22
|
+
* no listener is registered.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const click = signalFromEvent(document, 'click', null);
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* // With a projection — store just the coordinates.
|
|
32
|
+
* const point = signalFromEvent<MouseEvent, { x: number; y: number }>(
|
|
33
|
+
* document,
|
|
34
|
+
* 'mousemove',
|
|
35
|
+
* { x: 0, y: 0 },
|
|
36
|
+
* (e) => ({ x: e.clientX, y: e.clientY }),
|
|
37
|
+
* );
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function signalFromEvent<TEvent extends Event>(target: ResolvableTarget, eventName: string, initial: TEvent | null, opt?: SignalFromEventOptions): Signal<TEvent | null>;
|
|
41
|
+
export declare function signalFromEvent<TEvent extends Event, U>(target: ResolvableTarget, eventName: string, initial: U, project: (event: TEvent) => U, opt?: SignalFromEventOptions): Signal<U>;
|
|
42
|
+
export {};
|
package/lib/tabSync.d.ts
CHANGED
|
@@ -12,45 +12,19 @@ export declare class MessageBus {
|
|
|
12
12
|
static ɵprov: i0.ɵɵInjectableDeclaration<MessageBus>;
|
|
13
13
|
}
|
|
14
14
|
export declare function generateDeterministicID(): string;
|
|
15
|
-
type
|
|
15
|
+
type LegacySyncSignalOptions = {
|
|
16
16
|
id?: string;
|
|
17
17
|
};
|
|
18
|
+
export type SyncSignalOptions = {
|
|
19
|
+
id: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* @example tabSync(signal('dark), {id: 'theme})
|
|
23
|
+
*/
|
|
24
|
+
export declare function tabSync<T extends WritableSignal<any>>(sig: T, opt: SyncSignalOptions | string): T;
|
|
18
25
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* Creates a shared signal that automatically syncs its value between all tabs
|
|
22
|
-
* of the same application. When the signal is updated in one tab, all other
|
|
23
|
-
* tabs will receive the new value automatically.
|
|
24
|
-
*
|
|
25
|
-
* @template T - The type of the WritableSignal
|
|
26
|
-
* @param sig - The WritableSignal to synchronize across tabs
|
|
27
|
-
* @param opt - Optional configuration object
|
|
28
|
-
* @param opt.id - Explicit channel ID for synchronization. If not provided,
|
|
29
|
-
* a deterministic ID is generated based on the call site.
|
|
30
|
-
* Use explicit IDs in production for reliability.
|
|
31
|
-
*
|
|
32
|
-
* @returns The same WritableSignal instance, now synchronized across tabs
|
|
33
|
-
*
|
|
26
|
+
* @deprecated Use `tabSync` with `SyncSignalOptions` instead and pass the options as the second argument
|
|
34
27
|
* @throws {Error} When deterministic ID generation fails and no explicit ID is provided
|
|
35
|
-
*
|
|
36
|
-
* @example
|
|
37
|
-
* ```typescript
|
|
38
|
-
* // Basic usage - auto-generates channel ID from call site
|
|
39
|
-
* const theme = tabSync(signal('dark'));
|
|
40
|
-
*
|
|
41
|
-
* // With explicit ID (recommended for production)
|
|
42
|
-
* const userPrefs = tabSync(signal({ lang: 'en' }), { id: 'user-preferences' });
|
|
43
|
-
*
|
|
44
|
-
* // Changes in one tab will sync to all other tabs
|
|
45
|
-
* theme.set('light'); // All tabs will update to 'light'
|
|
46
|
-
* ```
|
|
47
|
-
*
|
|
48
|
-
* @remarks
|
|
49
|
-
* - Only works in browser environments (returns original signal on server)
|
|
50
|
-
* - Uses a single BroadcastChannel for all synchronized signals
|
|
51
|
-
* - Automatically cleans up listeners when the injection context is destroyed
|
|
52
|
-
* - Initial signal value after sync setup is not broadcasted to prevent loops
|
|
53
|
-
*
|
|
54
28
|
*/
|
|
55
|
-
export declare function tabSync<T extends WritableSignal<any>>(sig: T, opt?:
|
|
29
|
+
export declare function tabSync<T extends WritableSignal<any>>(sig: T, opt?: LegacySyncSignalOptions): T;
|
|
56
30
|
export {};
|
package/lib/throttled.d.ts
CHANGED
|
@@ -19,6 +19,18 @@ export type CreateThrottledOptions<T> = CreateSignalOptions<T> & {
|
|
|
19
19
|
* If it is not provided or injected, the timer will not be cleared automatically...which is usually fine :)
|
|
20
20
|
*/
|
|
21
21
|
destroyRef?: DestroyRef;
|
|
22
|
+
/**
|
|
23
|
+
* If `true`, the throttled signal emits the first value immediately when a
|
|
24
|
+
* burst starts, then enforces the cooldown window before the next emission.
|
|
25
|
+
* @default false
|
|
26
|
+
*/
|
|
27
|
+
leading?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* If `true`, the throttled signal emits the latest pending value at the end
|
|
30
|
+
* of each cooldown window (only when at least one write occurred during it).
|
|
31
|
+
* @default true
|
|
32
|
+
*/
|
|
33
|
+
trailing?: boolean;
|
|
22
34
|
};
|
|
23
35
|
/**
|
|
24
36
|
* A specialized `WritableSignal` whose publicly readable value updates are throttled.
|