@mmstack/primitives 21.0.23 → 21.0.24
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/package.json
CHANGED
|
@@ -564,7 +564,30 @@ declare const mapArray: typeof indexArray;
|
|
|
564
564
|
* @param mapFn The mapping function. Receives the item and its index as a Signal.
|
|
565
565
|
* @param options Optional configuration:
|
|
566
566
|
* - `onDestroy`: A callback invoked when a mapped item is removed from the array.
|
|
567
|
+
* - `key`: A custom key extractor for identity matching (e.g. `(item) => item.id`)
|
|
568
|
+
* when item references change but conceptual identity is preserved.
|
|
567
569
|
* @returns A `Signal<U[]>` containing the mapped array.
|
|
570
|
+
*
|
|
571
|
+
* @example
|
|
572
|
+
* ```ts
|
|
573
|
+
* const users = signal([
|
|
574
|
+
* { id: 1, name: 'Alice' },
|
|
575
|
+
* { id: 2, name: 'Bob' },
|
|
576
|
+
* ]);
|
|
577
|
+
*
|
|
578
|
+
* const rows = keyArray(
|
|
579
|
+
* users,
|
|
580
|
+
* (user, index) => ({
|
|
581
|
+
* label: computed(() => `#${index()} ${user.name}`),
|
|
582
|
+
* id: user.id,
|
|
583
|
+
* }),
|
|
584
|
+
* { key: (u) => u.id },
|
|
585
|
+
* );
|
|
586
|
+
*
|
|
587
|
+
* // Reordering users() rebuilds index signals only — `rows` entries
|
|
588
|
+
* // are matched by id and reused, not re-created.
|
|
589
|
+
* users.set([users()[1], users()[0]]);
|
|
590
|
+
* ```
|
|
568
591
|
*/
|
|
569
592
|
declare function keyArray<T, U, K>(source: Signal<T[]> | (() => T[]), mapFn: (v: T, i: Signal<number>) => U, options?: {
|
|
570
593
|
onDestroy?: (value: U) => void;
|
|
@@ -579,12 +602,71 @@ declare function keyArray<T, U, K>(source: Signal<T[]> | (() => T[]), mapFn: (v:
|
|
|
579
602
|
type MappedObject<T extends object, U> = {
|
|
580
603
|
[K in keyof T]: U;
|
|
581
604
|
};
|
|
605
|
+
/**
|
|
606
|
+
* Reactively maps each property of an object signal into a new object,
|
|
607
|
+
* preserving the same set of keys. For each key, `mapFn` receives a stable
|
|
608
|
+
* per-key signal — outputs for keys that haven't been added or removed are
|
|
609
|
+
* reused on subsequent reads. Sibling to {@link indexArray} / {@link keyArray}
|
|
610
|
+
* but for object records.
|
|
611
|
+
*
|
|
612
|
+
* The type of per-key signal passed into `mapFn` depends on the source:
|
|
613
|
+
* - `MutableSignal<T>` source → `MutableSignal<T[K]>` (in-place mutation)
|
|
614
|
+
* - `WritableSignal<T>` source → `WritableSignal<T[K]>` (two-way binding)
|
|
615
|
+
* - read-only `Signal<T>` or `() => T` source → read-only `Signal<T[K]>`
|
|
616
|
+
*
|
|
617
|
+
* @typeParam T The object type held by the source signal.
|
|
618
|
+
* @typeParam U The type produced for each key by `mapFn`.
|
|
619
|
+
*
|
|
620
|
+
* @param source A `MutableSignal<T>` whose properties are mapped with full
|
|
621
|
+
* in-place mutation capability via the per-key `MutableSignal`.
|
|
622
|
+
* @param mapFn Receives each key and its per-key `MutableSignal<T[K]>`.
|
|
623
|
+
* @param options Optional `onDestroy(value)` callback fired when a key is
|
|
624
|
+
* removed from the source.
|
|
625
|
+
* @returns A read-only signal of the mapped object.
|
|
626
|
+
*
|
|
627
|
+
* @example
|
|
628
|
+
* ```ts
|
|
629
|
+
* const state = mutable({ name: 'Alice', age: 30 });
|
|
630
|
+
* const view = mapObject(state, (key, prop) => ({
|
|
631
|
+
* label: key,
|
|
632
|
+
* current: computed(() => prop()),
|
|
633
|
+
* onInput: (next: any) => prop.set(next),
|
|
634
|
+
* }));
|
|
635
|
+
* view().age.onInput(31);
|
|
636
|
+
* state(); // { name: 'Alice', age: 31 }
|
|
637
|
+
* ```
|
|
638
|
+
*/
|
|
582
639
|
declare function mapObject<T extends object, U>(source: MutableSignal<T>, mapFn: <K extends keyof T>(key: K, value: MutableSignal<T[K]>) => U, options?: {
|
|
583
640
|
onDestroy?: (value: U) => void;
|
|
584
641
|
}): Signal<MappedObject<T, U>>;
|
|
642
|
+
/**
|
|
643
|
+
* Reactively maps each property of a `WritableSignal<T>` into a new object.
|
|
644
|
+
* Each key's per-property signal supports `.set` / `.update` for two-way
|
|
645
|
+
* binding back into the parent object.
|
|
646
|
+
*
|
|
647
|
+
* @example
|
|
648
|
+
* ```ts
|
|
649
|
+
* const user = signal({ name: 'Alice', age: 30 });
|
|
650
|
+
* const inputs = mapObject(user, (key, prop) => ({
|
|
651
|
+
* value: prop,
|
|
652
|
+
* setValue: (v: any) => prop.set(v),
|
|
653
|
+
* }));
|
|
654
|
+
* ```
|
|
655
|
+
*/
|
|
585
656
|
declare function mapObject<T extends object, U>(source: WritableSignal<T>, mapFn: <K extends keyof T>(key: K, value: WritableSignal<T[K]>) => U, options?: {
|
|
586
657
|
onDestroy?: (value: U) => void;
|
|
587
658
|
}): Signal<MappedObject<T, U>>;
|
|
659
|
+
/**
|
|
660
|
+
* Reactively maps each property of a read-only `Signal<T>` (or plain `() => T`
|
|
661
|
+
* accessor) into a new object. Per-key signals are read-only.
|
|
662
|
+
*
|
|
663
|
+
* @example
|
|
664
|
+
* ```ts
|
|
665
|
+
* const config = computed(() => ({ theme: 'dark', density: 'compact' }));
|
|
666
|
+
* const view = mapObject(config, (key, prop) => `${key}: ${prop()}`);
|
|
667
|
+
* view(); // { theme: 'theme: dark', density: 'density: compact' }
|
|
668
|
+
* ```
|
|
669
|
+
*/
|
|
588
670
|
declare function mapObject<T extends object, U>(source: (() => T) | Signal<T>, mapFn: <K extends keyof T>(key: K, value: Signal<T[K]>) => U, options?: {
|
|
589
671
|
onDestroy?: (value: U) => void;
|
|
590
672
|
}): Signal<MappedObject<T, U>>;
|
|
@@ -650,28 +732,136 @@ type PipeableSignal<T, TSig extends Signal<T> = Signal<T>> = TSig & {
|
|
|
650
732
|
*/
|
|
651
733
|
type SignalValue<TSig extends Signal<any>> = TSig extends Signal<infer V> ? V : never;
|
|
652
734
|
|
|
653
|
-
/**
|
|
735
|
+
/**
|
|
736
|
+
* Synchronous projection of a signal value with optional `CreateSignalOptions`
|
|
737
|
+
* (custom `equal`, `debugName`, etc.). Equivalent to `map` plus the ability to
|
|
738
|
+
* pass signal options through to the underlying `computed()`.
|
|
739
|
+
*
|
|
740
|
+
* @example
|
|
741
|
+
* ```ts
|
|
742
|
+
* const user = piped({ id: 1, name: 'Alice' });
|
|
743
|
+
* const name = user.pipe(select((u) => u.name));
|
|
744
|
+
* name(); // 'Alice'
|
|
745
|
+
* ```
|
|
746
|
+
*/
|
|
654
747
|
declare const select: <I, O>(projector: (v: I) => O, opt?: CreateSignalOptions<O>) => Operator<I, O>;
|
|
655
|
-
/**
|
|
748
|
+
/**
|
|
749
|
+
* Combine the piped signal with another `Signal` using a projector. The result
|
|
750
|
+
* recomputes whenever either source changes.
|
|
751
|
+
*
|
|
752
|
+
* @example
|
|
753
|
+
* ```ts
|
|
754
|
+
* const price = piped(10);
|
|
755
|
+
* const quantity = signal(3);
|
|
756
|
+
* const total = price.pipe(combineWith(quantity, (p, q) => p * q));
|
|
757
|
+
* total(); // 30
|
|
758
|
+
* ```
|
|
759
|
+
*/
|
|
656
760
|
declare const combineWith: <A, B, R>(other: Signal<B>, project: (a: A, b: B) => R, opt?: CreateSignalOptions<R>) => Operator<A, R>;
|
|
657
|
-
/**
|
|
761
|
+
/**
|
|
762
|
+
* Suppress emissions while consecutive values are considered equal. The
|
|
763
|
+
* comparator defaults to `Object.is`; pass a custom one for structural or
|
|
764
|
+
* key-based equality (e.g. compare by `id` only).
|
|
765
|
+
*
|
|
766
|
+
* @example
|
|
767
|
+
* ```ts
|
|
768
|
+
* const user = piped({ id: 1, lastSeen: Date.now() });
|
|
769
|
+
* const byId = user.pipe(distinct((a, b) => a.id === b.id));
|
|
770
|
+
* // byId only re-emits when `id` changes, not on every `lastSeen` update
|
|
771
|
+
* ```
|
|
772
|
+
*/
|
|
658
773
|
declare const distinct: <T>(equal?: (a: T, b: T) => boolean) => Operator<T, T>;
|
|
659
|
-
/**
|
|
774
|
+
/**
|
|
775
|
+
* Pure synchronous transform from input to output. Equivalent to a `computed()`
|
|
776
|
+
* that reads the source and returns `fn(value)`.
|
|
777
|
+
*
|
|
778
|
+
* @example
|
|
779
|
+
* ```ts
|
|
780
|
+
* const count = piped(2);
|
|
781
|
+
* const doubled = count.pipe(map((n) => n * 2));
|
|
782
|
+
* doubled(); // 4
|
|
783
|
+
* ```
|
|
784
|
+
*/
|
|
660
785
|
declare const map: <I, O>(fn: (v: I) => O) => Operator<I, O>;
|
|
661
|
-
/**
|
|
786
|
+
/**
|
|
787
|
+
* Keep only values that pass the predicate. The result holds the last passing
|
|
788
|
+
* value across emissions; before any value passes, the result is `undefined` —
|
|
789
|
+
* see {@link filterWith} when you need a non-`undefined` seed.
|
|
790
|
+
*
|
|
791
|
+
* @example
|
|
792
|
+
* ```ts
|
|
793
|
+
* const event = piped<MouseEvent | null>(null);
|
|
794
|
+
* const clicks = event.pipe(filter((e): e is MouseEvent => e?.type === 'click'));
|
|
795
|
+
* clicks(); // undefined until a click happens, then the last MouseEvent
|
|
796
|
+
* ```
|
|
797
|
+
*/
|
|
662
798
|
declare const filter: <T>(predicate: (v: T) => boolean) => Operator<T, T | undefined>;
|
|
663
|
-
/**
|
|
799
|
+
/**
|
|
800
|
+
* Run a side effect on every emission without altering the signal value. Wraps
|
|
801
|
+
* Angular's `effect()`, so it must run in an injection context or receive an
|
|
802
|
+
* explicit `injector`. Use for logging / analytics — not for setting other
|
|
803
|
+
* signals (that's what regular `effect()` is for).
|
|
804
|
+
*
|
|
805
|
+
* @example
|
|
806
|
+
* ```ts
|
|
807
|
+
* const count = piped(0);
|
|
808
|
+
* count.pipe(tap((n) => console.log('count:', n)));
|
|
809
|
+
* count.set(1); // logs 'count: 1'
|
|
810
|
+
* ```
|
|
811
|
+
*/
|
|
664
812
|
declare const tap: <T>(fn: (v: T) => void, injector?: Injector) => Operator<T, T>;
|
|
665
813
|
/**
|
|
666
|
-
* Like {@link filter}, but emits `initial` until a value passes the
|
|
667
|
-
*
|
|
814
|
+
* Like {@link filter}, but emits `initial` until a value first passes the
|
|
815
|
+
* predicate. Eliminates the `T | undefined` return type at the cost of an
|
|
816
|
+
* explicit seed value.
|
|
817
|
+
*
|
|
818
|
+
* @example
|
|
819
|
+
* ```ts
|
|
820
|
+
* const event = piped<MouseEvent | null>(null);
|
|
821
|
+
* const lastClick = event.pipe(filterWith((e) => e?.type === 'click', null));
|
|
822
|
+
* lastClick(); // null until the first click, then the most recent click event
|
|
823
|
+
* ```
|
|
668
824
|
*/
|
|
669
825
|
declare const filterWith: <T>(predicate: (v: T) => boolean, initial: T) => Operator<T, T>;
|
|
670
|
-
/**
|
|
826
|
+
/**
|
|
827
|
+
* Emit `initial` on the first read, then mirror the source on every subsequent
|
|
828
|
+
* read. Useful for giving a pipeline a sensible seed value before the source
|
|
829
|
+
* is ready (e.g. loading state).
|
|
830
|
+
*
|
|
831
|
+
* @example
|
|
832
|
+
* ```ts
|
|
833
|
+
* const data = piped<User | null>(null);
|
|
834
|
+
* const view = data.pipe(startWith<User | null, 'loading'>('loading'));
|
|
835
|
+
* view(); // 'loading' on first read, then User | null afterward
|
|
836
|
+
* ```
|
|
837
|
+
*/
|
|
671
838
|
declare const startWith: <T, U>(initial: U) => Operator<T, T | U>;
|
|
672
|
-
/**
|
|
839
|
+
/**
|
|
840
|
+
* Emit `[prev, curr]` tuples so consumers can react to transitions instead of
|
|
841
|
+
* raw values. On the first emission `prev` is `undefined`.
|
|
842
|
+
*
|
|
843
|
+
* @example
|
|
844
|
+
* ```ts
|
|
845
|
+
* const count = piped(0);
|
|
846
|
+
* const delta = count.pipe(pairwise(), map(([prev, curr]) => curr - (prev ?? 0)));
|
|
847
|
+
* count.set(5);
|
|
848
|
+
* delta(); // 5
|
|
849
|
+
* ```
|
|
850
|
+
*/
|
|
673
851
|
declare const pairwise: <T>() => Operator<T, [T | undefined, T]>;
|
|
674
|
-
/**
|
|
852
|
+
/**
|
|
853
|
+
* Reduce-like accumulator that folds each emission into a running result.
|
|
854
|
+
* Behaves like `Array.prototype.reduce` but applied over time, with the
|
|
855
|
+
* accumulator persisted across emissions.
|
|
856
|
+
*
|
|
857
|
+
* @example
|
|
858
|
+
* ```ts
|
|
859
|
+
* const delta = piped(0);
|
|
860
|
+
* const total = delta.pipe(scan((acc, n) => acc + n, 0));
|
|
861
|
+
* delta.set(5); // total() === 5
|
|
862
|
+
* delta.set(3); // total() === 8
|
|
863
|
+
* ```
|
|
864
|
+
*/
|
|
675
865
|
declare const scan: <T, R>(reducer: (acc: R, curr: T) => R, seed: R) => Operator<T, R>;
|
|
676
866
|
|
|
677
867
|
/**
|
|
@@ -829,6 +1019,15 @@ type BatteryStatus = {
|
|
|
829
1019
|
* Battery Status API. Returns `null` until the underlying `getBattery()`
|
|
830
1020
|
* promise resolves, or permanently when the API is unsupported (Firefox /
|
|
831
1021
|
* Safari at the time of writing). SSR-safe.
|
|
1022
|
+
*
|
|
1023
|
+
* @example
|
|
1024
|
+
* ```ts
|
|
1025
|
+
* const battery = batteryStatus();
|
|
1026
|
+
* effect(() => {
|
|
1027
|
+
* const b = battery();
|
|
1028
|
+
* if (b) console.log(`${Math.round(b.level * 100)}% • charging: ${b.charging}`);
|
|
1029
|
+
* });
|
|
1030
|
+
* ```
|
|
832
1031
|
*/
|
|
833
1032
|
declare function batteryStatus(debugName?: string): Signal<BatteryStatus | null>;
|
|
834
1033
|
|
|
@@ -963,6 +1162,15 @@ type FocusWithinTarget = ElementRef<Element> | Element | Signal<ElementRef<Eleme
|
|
|
963
1162
|
* Defaults `target` to the current `ElementRef` so it can be used inline in a
|
|
964
1163
|
* component's `class` field. SSR-safe — returns a constant `false` signal on
|
|
965
1164
|
* the server.
|
|
1165
|
+
*
|
|
1166
|
+
* @example
|
|
1167
|
+
* ```ts
|
|
1168
|
+
* @Component({ ... })
|
|
1169
|
+
* class MenuComponent {
|
|
1170
|
+
* // Defaults to the host element — flips true when focus is inside.
|
|
1171
|
+
* readonly hasFocus = focusWithin();
|
|
1172
|
+
* }
|
|
1173
|
+
* ```
|
|
966
1174
|
*/
|
|
967
1175
|
declare function focusWithin(target?: FocusWithinTarget): Signal<boolean>;
|
|
968
1176
|
|
|
@@ -1019,6 +1227,14 @@ type IdleSignal = Signal<boolean> & {
|
|
|
1019
1227
|
* activity) resets the timer and flips the signal back to `false`.
|
|
1020
1228
|
*
|
|
1021
1229
|
* SSR-safe — always `false` with a frozen `since` date on the server.
|
|
1230
|
+
*
|
|
1231
|
+
* @example
|
|
1232
|
+
* ```ts
|
|
1233
|
+
* const isAway = idle({ ms: 30_000 });
|
|
1234
|
+
* effect(() => {
|
|
1235
|
+
* if (isAway()) console.log('idle since', isAway.since());
|
|
1236
|
+
* });
|
|
1237
|
+
* ```
|
|
1022
1238
|
*/
|
|
1023
1239
|
declare function idle(opt?: IdleOptions): IdleSignal;
|
|
1024
1240
|
|
|
@@ -1212,6 +1428,14 @@ type NetworkStatusSignal = Signal<boolean> & {
|
|
|
1212
1428
|
*
|
|
1213
1429
|
* @param debugName Optional debug name for the signal.
|
|
1214
1430
|
* @returns A `NetworkStatusSignal` instance.
|
|
1431
|
+
*
|
|
1432
|
+
* @example
|
|
1433
|
+
* ```ts
|
|
1434
|
+
* const online = networkStatus();
|
|
1435
|
+
* effect(() => {
|
|
1436
|
+
* if (!online()) console.log('offline since', online.since());
|
|
1437
|
+
* });
|
|
1438
|
+
* ```
|
|
1215
1439
|
*/
|
|
1216
1440
|
declare function networkStatus(debugName?: string): NetworkStatusSignal;
|
|
1217
1441
|
|
|
@@ -1226,6 +1450,15 @@ type ScreenOrientation = {
|
|
|
1226
1450
|
*
|
|
1227
1451
|
* SSR-safe — returns a constant `portrait-primary / 0°` signal on the server
|
|
1228
1452
|
* and in environments without `screen.orientation` support.
|
|
1453
|
+
*
|
|
1454
|
+
* @example
|
|
1455
|
+
* ```ts
|
|
1456
|
+
* const screenOrientation = orientation();
|
|
1457
|
+
* effect(() => {
|
|
1458
|
+
* const { type, angle } = screenOrientation();
|
|
1459
|
+
* console.log(`${type} at ${angle}°`);
|
|
1460
|
+
* });
|
|
1461
|
+
* ```
|
|
1229
1462
|
*/
|
|
1230
1463
|
declare function orientation(debugName?: string): Signal<ScreenOrientation>;
|
|
1231
1464
|
|
|
@@ -1641,6 +1874,27 @@ type SensorsOptions<TKey extends keyof SensorTypedOptions> = {
|
|
|
1641
1874
|
type Sensors<TKey extends keyof SensorTypedOptions> = {
|
|
1642
1875
|
[K in TKey]: SensorTypedOptions[K]['returnType'];
|
|
1643
1876
|
};
|
|
1877
|
+
/**
|
|
1878
|
+
* Bulk sensor factory — creates several sensor signals at once and returns
|
|
1879
|
+
* them keyed by sensor type. Convenient when a single consumer needs to react
|
|
1880
|
+
* to multiple browser signals; for a single sensor prefer {@link sensor}
|
|
1881
|
+
* directly.
|
|
1882
|
+
*
|
|
1883
|
+
* @typeParam TType The union of sensor keys being requested.
|
|
1884
|
+
* @param track Array of sensor type keys to create.
|
|
1885
|
+
* @param opt Optional per-sensor options keyed by sensor type.
|
|
1886
|
+
* @returns A record `{ [key]: <SensorReturnType> }` for each requested key.
|
|
1887
|
+
*
|
|
1888
|
+
* @example
|
|
1889
|
+
* ```ts
|
|
1890
|
+
* const { windowSize, networkStatus } = sensors(
|
|
1891
|
+
* ['windowSize', 'networkStatus'],
|
|
1892
|
+
* { windowSize: { throttle: 200 } },
|
|
1893
|
+
* );
|
|
1894
|
+
*
|
|
1895
|
+
* effect(() => console.log(windowSize(), networkStatus()));
|
|
1896
|
+
* ```
|
|
1897
|
+
*/
|
|
1644
1898
|
declare function sensors<const TType extends keyof SensorTypedOptions>(track: TType[], opt?: SensorsOptions<TType>): Sensors<TType>;
|
|
1645
1899
|
|
|
1646
1900
|
/**
|