@mmstack/primitives 20.4.5 → 20.4.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.
package/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { CreateSignalOptions, DestroyRef, WritableSignal, Signal, ElementRef, Injector, ValueEqualityFn } from '@angular/core';
1
+ import { CreateSignalOptions, DestroyRef, WritableSignal, Signal, ElementRef, EffectCleanupRegisterFn, CreateEffectOptions, Injector, ValueEqualityFn } from '@angular/core';
2
2
 
3
3
  /**
4
4
  * Options for creating a debounced writable signal.
@@ -468,6 +468,82 @@ declare function mapArray<T, U>(source: Signal<T[]> | (() => T[]), map: (value:
468
468
  onDestroy?: (value: U) => void;
469
469
  }): Signal<U[]>;
470
470
 
471
+ /**
472
+ * Creates an effect that can be nested, similar to SolidJS's `createEffect`.
473
+ *
474
+ * This primitive enables true hierarchical reactivity. A `nestedEffect` created
475
+ * within another `nestedEffect` is automatically destroyed and recreated when
476
+ * the parent re-runs.
477
+ *
478
+ * It automatically handles injector propagation and lifetime management, allowing
479
+ * you to create fine-grained, conditional side-effects that only track
480
+ * dependencies when they are "live".
481
+ *
482
+ * @param effectFn The side-effect function, which receives a cleanup register function.
483
+ * @param options (Optional) Angular's `CreateEffectOptions`.
484
+ * @returns An `EffectRef` for the created effect.
485
+ *
486
+ * @example
487
+ * ```ts
488
+ * // Assume `coldGuard` changes rarely, but `hotSignal` changes often.
489
+ * const coldGuard = signal(false);
490
+ * const hotSignal = signal(0);
491
+ *
492
+ * nestedEffect(() => {
493
+ * // This outer effect only tracks `coldGuard`.
494
+ * if (coldGuard()) {
495
+ *
496
+ * // This inner effect is CREATED when coldGuard is true
497
+ * // and DESTROYED when it becomes false.
498
+ * nestedEffect(() => {
499
+ * // It only tracks `hotSignal` while it exists.
500
+ * console.log('Hot signal is:', hotSignal());
501
+ * });
502
+ * }
503
+ * // If `coldGuard` is false, this outer effect does not track `hotSignal`.
504
+ * });
505
+ * ```
506
+ * @example
507
+ * ```ts
508
+ * const users = signal([
509
+ { id: 1, name: 'Alice' },
510
+ { id: 2, name: 'Bob' }
511
+ ]);
512
+
513
+ // The fine-grained mapped list
514
+ const mappedUsers = mapArray(
515
+ users,
516
+ (userSignal, index) => {
517
+ // 1. Create a fine-grained SIDE EFFECT for *this item*
518
+ // This effect's lifetime is now tied to this specific item. created once on init of this index.
519
+ const effectRef = nestedEffect(() => {
520
+ // This only runs if *this* userSignal changes,
521
+ // not if the whole list changes.
522
+ console.log(`User ${index} updated:`, userSignal().name);
523
+ });
524
+
525
+ // 2. Return the data AND the cleanup logic
526
+ return {
527
+ // The mapped data
528
+ label: computed(() => `User: ${userSignal().name}`),
529
+
530
+ // The cleanup function
531
+ destroyEffect: () => effectRef.destroy()
532
+ };
533
+ },
534
+ {
535
+ // 3. Tell mapArray HOW to clean up when an item is removed, this needs to be manual as it's not a nestedEffect itself
536
+ onDestroy: (mappedItem) => {
537
+ mappedItem.destroyEffect();
538
+ }
539
+ }
540
+ );
541
+ * ```
542
+ */
543
+ declare function nestedEffect(effectFn: (registerCleanup: EffectCleanupRegisterFn) => void, options?: CreateEffectOptions): {
544
+ destroy: () => void;
545
+ };
546
+
471
547
  /**
472
548
  * A pure, synchronous transform from I -> O.
473
549
  * Prefer transforms without side effects to keep derivations predictable.
@@ -1456,5 +1532,5 @@ type CreateHistoryOptions<T> = Omit<CreateSignalOptions<T[]>, 'equal'> & {
1456
1532
  */
1457
1533
  declare function withHistory<T>(source: WritableSignal<T>, opt?: CreateHistoryOptions<T>): SignalWithHistory<T>;
1458
1534
 
1459
- export { combineWith, debounce, debounced, derived, distinct, elementVisibility, filter, isDerivation, isMutable, map, mapArray, mediaQuery, mousePosition, mutable, networkStatus, pageVisibility, pipeable, piped, prefersDarkMode, prefersReducedMotion, scrollPosition, select, sensor, stored, tabSync, tap, throttle, throttled, toFakeDerivation, toFakeSignalDerivation, toWritable, until, windowSize, withHistory };
1535
+ export { combineWith, debounce, debounced, derived, distinct, elementVisibility, filter, isDerivation, isMutable, map, mapArray, mediaQuery, mousePosition, mutable, nestedEffect, networkStatus, pageVisibility, pipeable, piped, prefersDarkMode, prefersReducedMotion, scrollPosition, select, sensor, stored, tabSync, tap, throttle, throttled, toFakeDerivation, toFakeSignalDerivation, toWritable, until, windowSize, withHistory };
1460
1536
  export type { CreateDebouncedOptions, CreateHistoryOptions, CreateStoredOptions, CreateThrottledOptions, DebouncedSignal, DerivedSignal, ElementVisibilityOptions, ElementVisibilitySignal, MousePositionOptions, MousePositionSignal, MutableSignal, NetworkStatusSignal, PipeableSignal, ScrollPosition, ScrollPositionOptions, ScrollPositionSignal, SignalWithHistory, StoredSignal, ThrottledSignal, UntilOptions, WindowSize, WindowSizeOptions, WindowSizeSignal };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmstack/primitives",
3
- "version": "20.4.5",
3
+ "version": "20.4.6",
4
4
  "keywords": [
5
5
  "angular",
6
6
  "signals",