@mmstack/primitives 21.0.4 → 21.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmstack/primitives",
3
- "version": "21.0.4",
3
+ "version": "21.0.6",
4
4
  "keywords": [
5
5
  "angular",
6
6
  "signals",
@@ -1,4 +1,4 @@
1
- import { CreateSignalOptions, DestroyRef, WritableSignal, Signal, EffectCleanupRegisterFn, CreateEffectOptions, ElementRef, Injector, ValueEqualityFn } from '@angular/core';
1
+ import { CreateSignalOptions, DestroyRef, WritableSignal, Signal, EffectCleanupRegisterFn, CreateEffectOptions, Injector, EffectRef, ElementRef, ValueEqualityFn } from '@angular/core';
2
2
 
3
3
  /**
4
4
  * Options for creating a debounced writable signal.
@@ -271,31 +271,32 @@ declare function derived<T, U>(source: WritableSignal<T>, opt: CreateDerivedOpti
271
271
  * console.log(user().name); // Outputs: Jane
272
272
  * ```
273
273
  */
274
- declare function derived<T extends UnknownObject, TKey extends keyof T>(source: WritableSignal<T>, key: TKey, opt?: CreateSignalOptions<T[TKey]>): DerivedSignal<T, T[TKey]>;
274
+ declare function derived<T extends UnknownObject, TKey extends keyof T>(source: MutableSignal<T>, key: TKey, opt?: CreateSignalOptions<T[TKey]>): DerivedSignal<T, T[TKey]> & MutableSignal<T[TKey]>;
275
275
  /**
276
- * Creates a `DerivedSignal` from an array, deriving an element by its index.
277
- * This overload is a convenient shorthand for accessing array elements.
276
+ * Creates a `DerivedSignal` that derives a property from an object held by the source signal.
277
+ * This overload is a convenient shorthand for accessing object properties.
278
278
  *
279
- * @typeParam T The type of the source signal's value (must be an array).
280
- * @param source The source `WritableSignal` (holding an array).
281
- * @param index The index of the element to derive.
279
+ * @typeParam T The type of the source signal's value (must be an object).
280
+ * @typeParam TKey The key of the property to derive.
281
+ * @param source The source `WritableSignal` (holding an object).
282
+ * @param key The key of the property to derive.
282
283
  * @param options Optional signal options for the derived signal.
283
284
  * @returns A `DerivedSignal` instance.
284
285
  *
285
286
  * @example
286
287
  * ```ts
287
- * const numbers = signal([1, 2, 3]);
288
- * const secondNumber = derived(numbers, 1);
288
+ * const user = signal({ name: 'John', age: 30 });
289
+ * const name = derived(user, 'name');
289
290
  *
290
- * console.log(secondNumber()); // Outputs: 2
291
+ * console.log(name()); // Outputs: John
291
292
  *
292
293
  * // Update the derived signal, which also updates the source
293
- * secondNumber.set(5);
294
+ * name.set('Jane');
294
295
  *
295
- * console.log(numbers()); // Outputs: [1, 5, 3]
296
+ * console.log(user().name); // Outputs: Jane
296
297
  * ```
297
298
  */
298
- declare function derived<T extends any[]>(source: WritableSignal<T>, index: number, opt?: CreateSignalOptions<T[number]>): DerivedSignal<T, T[number]>;
299
+ declare function derived<T extends UnknownObject, TKey extends keyof T>(source: WritableSignal<T>, key: TKey, opt?: CreateSignalOptions<T[TKey]>): DerivedSignal<T, T[TKey]>;
299
300
  /**
300
301
  * Creates a `DerivedSignal` that derives its value from another `MutableSignal`.
301
302
  * Use mutuable signals with caution, but very useful for deeply nested structures.
@@ -319,6 +320,30 @@ declare function derived<T extends any[]>(source: WritableSignal<T>, index: numb
319
320
  * ```
320
321
  */
321
322
  declare function derived<T, U>(source: MutableSignal<T>, optOrKey: CreateDerivedOptions<T, U> | keyof T, opt?: CreateSignalOptions<U>): DerivedSignal<T, U> & MutableSignal<U>;
323
+ /**
324
+ * Creates a `DerivedSignal` from an array, deriving an element by its index.
325
+ * This overload is a convenient shorthand for accessing array elements.
326
+ *
327
+ * @typeParam T The type of the source signal's value (must be an array).
328
+ * @param source The source `WritableSignal` (holding an array).
329
+ * @param index The index of the element to derive.
330
+ * @param options Optional signal options for the derived signal.
331
+ * @returns A `DerivedSignal` instance.
332
+ *
333
+ * @example
334
+ * ```ts
335
+ * const numbers = signal([1, 2, 3]);
336
+ * const secondNumber = derived(numbers, 1);
337
+ *
338
+ * console.log(secondNumber()); // Outputs: 2
339
+ *
340
+ * // Update the derived signal, which also updates the source
341
+ * secondNumber.set(5);
342
+ *
343
+ * console.log(numbers()); // Outputs: [1, 5, 3]
344
+ * ```
345
+ */
346
+ declare function derived<T extends any[]>(source: WritableSignal<T>, index: number, opt?: CreateSignalOptions<T[number]>): DerivedSignal<T, T[number]>;
322
347
  /**
323
348
  * Creates a "fake" `DerivedSignal` from a simple value. This is useful for creating
324
349
  * `FormControlSignal` instances that are not directly derived from another signal.
@@ -407,31 +432,51 @@ declare function indexArray<T, U>(source: Signal<T[]> | (() => T[]), map: (value
407
432
  declare const mapArray: typeof indexArray;
408
433
 
409
434
  /**
410
- * Reactively maps items from a source array to a new array using a key function to maintain stability.
435
+ * Reactively maps items from a source array to a new array by value (identity).
411
436
  *
412
- * This function preserves the `mapped` signals for items even if they move within the array,
413
- * as long as their key remains the same. This is equivalent to SolidJS's `mapArray` or Angular's `@for (item of items; track item.id)`.
437
+ * similar to `Array.prototype.map`, but:
438
+ * 1. The `mapFn` receives the `index` as a Signal.
439
+ * 2. If an item in the `source` array moves to a new position, the *result* of the map function is reused and moved.
440
+ * The `index` signal is updated to the new index.
441
+ * 3. The `mapFn` is only run for *new* items.
414
442
  *
415
- * @template T The type of items in the source array.
416
- * @template U The type of items in the resulting mapped array.
417
- * @template K The type of the key.
443
+ * This is useful for building efficient lists where DOM nodes or heavy instances should be reused
444
+ * when the list is reordered.
418
445
  *
419
446
  * @param source A `Signal<T[]>` or a function returning `T[]`.
420
- * @param keyFn A function to extract a unique key for each item.
421
- * @param map The mapping function. It receives a stable signal for the item and a signal for its index.
422
- * @param options Optional configuration, including `CreateSignalOptions` and an `onDestroy` callback.
447
+ * @param mapFn The mapping function. Receives the item and its index as a Signal.
448
+ * @param options Optional configuration:
449
+ * - `onDestroy`: A callback invoked when a mapped item is removed from the array.
423
450
  * @returns A `Signal<U[]>` containing the mapped array.
424
451
  */
425
- declare function keyArray<T, U>(source: MutableSignal<T[]>, keyFn: (item: T) => string | number, map: (value: MutableSignal<T>, index: Signal<number>) => U, options?: CreateSignalOptions<T> & {
452
+ declare function keyArray<T, U, K>(source: Signal<T[]> | (() => T[]), mapFn: (v: T, i: Signal<number>) => U, options?: {
426
453
  onDestroy?: (value: U) => void;
454
+ /**
455
+ * Optional function to use a custom key for item comparison.
456
+ * Use this if you want to reuse mapped items based on a property (like an ID)
457
+ * even if the item reference changes.
458
+ */
459
+ key?: (item: T) => K;
427
460
  }): Signal<U[]>;
428
- declare function keyArray<T, U>(source: WritableSignal<T[]>, keyFn: (item: T) => string | number, map: (value: WritableSignal<T>, index: Signal<number>) => U, options?: CreateSignalOptions<T> & {
461
+
462
+ type MappedObject<T extends Record<string, any>, U> = {
463
+ [K in keyof T]: U;
464
+ };
465
+ declare function mapObject<T extends Record<string, any>, U>(source: MutableSignal<T>, mapFn: <K extends keyof T>(key: K, value: MutableSignal<T[K]>) => U, options?: {
429
466
  onDestroy?: (value: U) => void;
430
- }): Signal<U[]>;
431
- declare function keyArray<T, U>(source: Signal<T[]> | (() => T[]), keyFn: (item: T) => string | number, map: (value: Signal<T>, index: Signal<number>) => U, options?: CreateSignalOptions<T> & {
467
+ }): Signal<MappedObject<T, U>>;
468
+ declare function mapObject<T extends Record<string, any>, U>(source: WritableSignal<T>, mapFn: <K extends keyof T>(key: K, value: WritableSignal<T[K]>) => U, options?: {
432
469
  onDestroy?: (value: U) => void;
433
- }): Signal<U[]>;
470
+ }): Signal<MappedObject<T, U>>;
471
+ declare function mapObject<T extends Record<string, any>, U>(source: (() => T) | Signal<T>, mapFn: <K extends keyof T>(key: K, value: Signal<T[K]>) => U, options?: {
472
+ onDestroy?: (value: U) => void;
473
+ }): Signal<MappedObject<T, U>>;
434
474
 
475
+ type Frame = {
476
+ injector: Injector;
477
+ parent: Frame | null;
478
+ children: Set<EffectRef>;
479
+ };
435
480
  /**
436
481
  * Creates an effect that can be nested, similar to SolidJS's `createEffect`.
437
482
  *
@@ -470,42 +515,42 @@ declare function keyArray<T, U>(source: Signal<T[]> | (() => T[]), keyFn: (item:
470
515
  * @example
471
516
  * ```ts
472
517
  * const users = signal([
473
- { id: 1, name: 'Alice' },
474
- { id: 2, name: 'Bob' }
475
- ]);
476
-
477
- // The fine-grained mapped list
478
- const mappedUsers = mapArray(
479
- users,
480
- (userSignal, index) => {
481
- // 1. Create a fine-grained SIDE EFFECT for *this item*
482
- // This effect's lifetime is now tied to this specific item. created once on init of this index.
483
- const effectRef = nestedEffect(() => {
484
- // This only runs if *this* userSignal changes,
485
- // not if the whole list changes.
486
- console.log(`User ${index} updated:`, userSignal().name);
487
- });
488
-
489
- // 2. Return the data AND the cleanup logic
490
- return {
491
- // The mapped data
492
- label: computed(() => `User: ${userSignal().name}`),
493
-
494
- // The cleanup function
495
- destroyEffect: () => effectRef.destroy()
496
- };
497
- },
498
- {
499
- // 3. Tell mapArray HOW to clean up when an item is removed, this needs to be manual as it's not a nestedEffect itself
500
- onDestroy: (mappedItem) => {
501
- mappedItem.destroyEffect();
502
- }
503
- }
504
- );
518
+ * { id: 1, name: 'Alice' },
519
+ * { id: 2, name: 'Bob' }
520
+ * ]);
521
+ *
522
+ * // The fine-grained mapped list
523
+ * const mappedUsers = mapArray(
524
+ * users,
525
+ * (userSignal, index) => {
526
+ * // 1. Create a fine-grained SIDE EFFECT for *this item*
527
+ * // This effect's lifetime is now tied to this specific item. created once on init of this index.
528
+ * const effectRef = nestedEffect(() => {
529
+ * // This only runs if *this* userSignal changes,
530
+ * // not if the whole list changes.
531
+ * console.log(`User ${index} updated:`, userSignal().name);
532
+ * });
533
+ *
534
+ * // 2. Return the data AND the cleanup logic
535
+ * return {
536
+ * // The mapped data
537
+ * label: computed(() => `User: ${userSignal().name}`),
538
+ *
539
+ * // The cleanup function
540
+ * destroyEffect: () => effectRef.destroy()
541
+ * };
542
+ * },
543
+ * {
544
+ * // 3. Tell mapArray HOW to clean up when an item is removed, this needs to be manual as it's not a nestedEffect itself
545
+ * onDestroy: (mappedItem) => {
546
+ * mappedItem.destroyEffect();
547
+ * }
548
+ * }
549
+ * );
505
550
  * ```
506
551
  */
507
552
  declare function nestedEffect(effectFn: (registerCleanup: EffectCleanupRegisterFn) => void, options?: CreateEffectOptions & {
508
- bindToFrame?: number;
553
+ bindToFrame?: (parent: Frame | null) => Frame | null;
509
554
  }): {
510
555
  destroy: () => void;
511
556
  };
@@ -1252,25 +1297,21 @@ type Sensors<TKey extends keyof SensorTypedOptions> = {
1252
1297
  declare function sensors<const TType extends keyof SensorTypedOptions>(track: TType[], opt?: SensorsOptions<TType>): Sensors<TType>;
1253
1298
 
1254
1299
  type AnyRecord = Record<PropertyKey, any>;
1255
- type NonRecord = Iterable<any> | WeakSet<any> | WeakMap<any, any> | Promise<any> | Date | Error | RegExp | ArrayBuffer | DataView | Function | Array<any>;
1256
- type IsRecord<T> = T extends object ? T extends NonRecord ? false : true : false;
1257
- type IsUnknownRecord<T> = keyof T extends never ? true : string extends keyof T ? true : symbol extends keyof T ? true : number extends keyof T ? true : false;
1258
- type IsKnownRecord<T> = IsRecord<T> extends true ? IsUnknownRecord<T> extends true ? false : true : false;
1259
- type SignalStore<T> = Signal<T> & (IsKnownRecord<T> extends true ? Readonly<{
1260
- [K in keyof T]: IsKnownRecord<T[K]> extends true ? SignalStore<T[K]> : Signal<T[K]>;
1261
- }> : unknown);
1262
- type WritableSignalStore<T> = WritableSignal<T> & (IsKnownRecord<T> extends true ? Readonly<{
1263
- [K in keyof T]: IsKnownRecord<T[K]> extends true ? WritableSignalStore<T[K]> : WritableSignal<T[K]>;
1264
- }> : unknown);
1265
- type MutableSignalStore<T> = MutableSignal<T> & (IsKnownRecord<T> extends true ? Readonly<{
1266
- [K in keyof T]: IsKnownRecord<T[K]> extends true ? MutableSignalStore<T[K]> : MutableSignal<T[K]>;
1267
- }> : unknown);
1268
- type inferValid<T extends AnyRecord> = IsKnownRecord<T> extends false ? never : T;
1269
- declare function toStore<T extends AnyRecord>(source: MutableSignal<inferValid<T>>, injector?: Injector): MutableSignalStore<T>;
1270
- declare function toStore<T extends AnyRecord>(source: WritableSignal<inferValid<T>>, injector?: Injector): WritableSignalStore<T>;
1271
- declare function toStore<T extends AnyRecord>(source: Signal<inferValid<T>>, injector?: Injector): SignalStore<T>;
1272
- declare function store<T extends AnyRecord>(value: inferValid<T>, opt?: CreateSignalOptions<T>): WritableSignalStore<T>;
1273
- declare function mutableStore<T extends AnyRecord>(value: inferValid<T>, opt?: CreateSignalOptions<T>): MutableSignalStore<T>;
1300
+ type BaseType = string | number | boolean | symbol | undefined | null | Function | Date | RegExp | any[];
1301
+ type SignalStore<T> = Signal<T> & (T extends BaseType ? unknown : Readonly<{
1302
+ [K in keyof T]: SignalStore<T[K]>;
1303
+ }>);
1304
+ type WritableSignalStore<T> = WritableSignal<T> & (T extends BaseType ? unknown : Readonly<{
1305
+ [K in keyof T]: WritableSignalStore<T[K]>;
1306
+ }>);
1307
+ type MutableSignalStore<T> = MutableSignal<T> & (T extends BaseType ? unknown : Readonly<{
1308
+ [K in keyof T]: MutableSignalStore<T[K]>;
1309
+ }>);
1310
+ declare function toStore<T extends AnyRecord>(source: MutableSignal<T>, injector?: Injector): MutableSignalStore<T>;
1311
+ declare function toStore<T extends AnyRecord>(source: WritableSignal<T>, injector?: Injector): WritableSignalStore<T>;
1312
+ declare function toStore<T extends AnyRecord>(source: Signal<T>, injector?: Injector): SignalStore<T>;
1313
+ declare function store<T extends AnyRecord>(value: T, opt?: CreateSignalOptions<T>): WritableSignalStore<T>;
1314
+ declare function mutableStore<T extends AnyRecord>(value: T, opt?: CreateSignalOptions<T>): MutableSignalStore<T>;
1274
1315
 
1275
1316
  /**
1276
1317
  * Interface for storage mechanisms compatible with the `stored` signal.
@@ -1709,5 +1750,5 @@ type CreateHistoryOptions<T> = Omit<CreateSignalOptions<T[]>, 'equal'> & {
1709
1750
  */
1710
1751
  declare function withHistory<T>(source: WritableSignal<T>, opt?: CreateHistoryOptions<T>): SignalWithHistory<T>;
1711
1752
 
1712
- export { combineWith, debounce, debounced, derived, distinct, elementSize, elementVisibility, filter, indexArray, isDerivation, isMutable, keyArray, map, mapArray, mediaQuery, mousePosition, mutable, mutableStore, nestedEffect, networkStatus, pageVisibility, pipeable, piped, prefersDarkMode, prefersReducedMotion, scrollPosition, select, sensor, sensors, store, stored, tabSync, tap, throttle, throttled, toFakeDerivation, toFakeSignalDerivation, toStore, toWritable, until, windowSize, withHistory };
1713
- export type { CreateDebouncedOptions, CreateHistoryOptions, CreateStoredOptions, CreateThrottledOptions, DebouncedSignal, DerivedSignal, ElementSize, ElementSizeOptions, ElementSizeSignal, ElementVisibilityOptions, ElementVisibilitySignal, IsKnownRecord, IsRecord, IsUnknownRecord, MousePositionOptions, MousePositionSignal, MutableSignal, MutableSignalStore, NetworkStatusSignal, PipeableSignal, ScrollPosition, ScrollPositionOptions, ScrollPositionSignal, SignalStore, SignalWithHistory, StoredSignal, ThrottledSignal, UntilOptions, WindowSize, WindowSizeOptions, WindowSizeSignal, WritableSignalStore };
1753
+ export { combineWith, debounce, debounced, derived, distinct, elementSize, elementVisibility, filter, indexArray, isDerivation, isMutable, keyArray, map, mapArray, mapObject, mediaQuery, mousePosition, mutable, mutableStore, nestedEffect, networkStatus, pageVisibility, pipeable, piped, prefersDarkMode, prefersReducedMotion, scrollPosition, select, sensor, sensors, store, stored, tabSync, tap, throttle, throttled, toFakeDerivation, toFakeSignalDerivation, toStore, toWritable, until, windowSize, withHistory };
1754
+ export type { CreateDebouncedOptions, CreateHistoryOptions, CreateStoredOptions, CreateThrottledOptions, DebouncedSignal, DerivedSignal, ElementSize, ElementSizeOptions, ElementSizeSignal, ElementVisibilityOptions, ElementVisibilitySignal, MousePositionOptions, MousePositionSignal, MutableSignal, MutableSignalStore, NetworkStatusSignal, PipeableSignal, ScrollPosition, ScrollPositionOptions, ScrollPositionSignal, SignalStore, SignalWithHistory, StoredSignal, ThrottledSignal, UntilOptions, WindowSize, WindowSizeOptions, WindowSizeSignal, WritableSignalStore };