@mmstack/primitives 19.3.4 → 19.3.6

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.
@@ -1,4 +1,4 @@
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
  /** Project with optional equality. Pure & sync. */
4
4
  export declare const select: <I, O>(projector: (v: I) => O, opt?: CreateSignalOptions<O>) => Operator<I, O>;
@@ -11,4 +11,15 @@ export declare const map: <I, O>(fn: (v: I) => O) => Operator<I, O>;
11
11
  /** filter values, keeping the last value if it was ever available, if first value is filtered will return undefined */
12
12
  export declare const filter: <T>(predicate: (v: T) => boolean) => Operator<T, T | undefined>;
13
13
  /** tap into the value */
14
- export declare const tap: <T>(fn: (v: T) => void) => Operator<T, T>;
14
+ export declare const tap: <T>(fn: (v: T) => void, injector?: Injector) => Operator<T, T>;
15
+ /**
16
+ * Like {@link filter}, but emits `initial` until a value passes the predicate
17
+ * for the first time. Avoids the `T | undefined` return type.
18
+ */
19
+ export declare const filterWith: <T>(predicate: (v: T) => boolean, initial: T) => Operator<T, T>;
20
+ /** Emits `initial` first, then mirrors source. */
21
+ export declare const startWith: <T, U>(initial: U) => Operator<T, T | U>;
22
+ /** Emits `[prev, curr]` pairs. The first emission has prev = undefined. */
23
+ export declare const pairwise: <T>() => Operator<T, [T | undefined, T]>;
24
+ /** Reduce-like accumulator across emissions. */
25
+ export declare const scan: <T, R>(reducer: (acc: R, curr: T) => R, seed: R) => Operator<T, R>;
@@ -0,0 +1,14 @@
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
+ 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,12 @@
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
+ export declare function focusWithin(target?: FocusWithinTarget): Signal<boolean>;
12
+ 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,27 @@
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
+ export declare function idle(opt?: IdleOptions): IdleSignal;
@@ -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';
@@ -0,0 +1,14 @@
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
+ export declare function orientation(debugName?: string): Signal<ScreenOrientation>;
@@ -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,6 +190,36 @@ 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
  };
@@ -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 SyncSignalOptions = {
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
- * Synchronizes a WritableSignal across browser tabs using BroadcastChannel API.
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?: SyncSignalOptions): T;
29
+ export declare function tabSync<T extends WritableSignal<any>>(sig: T, opt?: LegacySyncSignalOptions): T;
56
30
  export {};
@@ -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.
@@ -91,4 +91,4 @@ export type CreateHistoryOptions<T> = Omit<CreateSignalOptions<T[]>, 'equal'> &
91
91
  * console.log('Can undo:', name.canUndo()); // false
92
92
  * ```
93
93
  */
94
- export declare function withHistory<T>(source: WritableSignal<T>, opt?: CreateHistoryOptions<T>): SignalWithHistory<T>;
94
+ export declare function withHistory<T>(sourceOrValue: WritableSignal<T> | T, opt?: CreateHistoryOptions<T>): SignalWithHistory<T>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmstack/primitives",
3
- "version": "19.3.4",
3
+ "version": "19.3.6",
4
4
  "keywords": [
5
5
  "angular",
6
6
  "signals",