@mmstack/primitives 19.0.0
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 +3 -0
- package/fesm2022/mmstack-primitives.mjs +95 -0
- package/fesm2022/mmstack-primitives.mjs.map +1 -0
- package/index.d.ts +2 -0
- package/lib/mutable.d.ts +80 -0
- package/lib/to-writable.d.ts +30 -0
- package/package.json +22 -0
package/README.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { signal, untracked } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
const { is } = Object;
|
|
4
|
+
function mutable(initial, opt) {
|
|
5
|
+
const baseEqual = opt?.equal ?? is;
|
|
6
|
+
let trigger = false;
|
|
7
|
+
const equal = (a, b) => {
|
|
8
|
+
if (trigger)
|
|
9
|
+
return false;
|
|
10
|
+
return baseEqual(a, b);
|
|
11
|
+
};
|
|
12
|
+
const sig = signal(initial, {
|
|
13
|
+
...opt,
|
|
14
|
+
equal,
|
|
15
|
+
});
|
|
16
|
+
const internalUpdate = sig.update;
|
|
17
|
+
sig.mutate = (updater) => {
|
|
18
|
+
trigger = true;
|
|
19
|
+
internalUpdate(updater);
|
|
20
|
+
trigger = false;
|
|
21
|
+
};
|
|
22
|
+
sig.inline = (updater) => {
|
|
23
|
+
sig.mutate((prev) => {
|
|
24
|
+
updater(prev);
|
|
25
|
+
return prev;
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
return sig;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Type guard function to check if a given `WritableSignal` is a `MutableSignal`. This is useful
|
|
32
|
+
* for situations where you need to conditionally use the `mutate` or `inline` methods.
|
|
33
|
+
*
|
|
34
|
+
* @typeParam T - The type of the signal's value (optional, defaults to `any`).
|
|
35
|
+
* @param value - The `WritableSignal` to check.
|
|
36
|
+
* @returns `true` if the signal is a `MutableSignal`, `false` otherwise.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* const mySignal = signal(0);
|
|
40
|
+
* const myMutableSignal = mutable(0);
|
|
41
|
+
*
|
|
42
|
+
* if (isMutable(mySignal)) {
|
|
43
|
+
* mySignal.mutate(x => x + 1); // This would cause a type error, as mySignal is not a MutableSignal.
|
|
44
|
+
* }
|
|
45
|
+
*
|
|
46
|
+
* if (isMutable(myMutableSignal)) {
|
|
47
|
+
* myMutableSignal.mutate(x => x + 1); // This is safe.
|
|
48
|
+
* }
|
|
49
|
+
*/
|
|
50
|
+
function isMutable(value) {
|
|
51
|
+
return 'mutate' in value && typeof value.mutate === 'function';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Converts a read-only `Signal` into a `WritableSignal` by providing custom `set` and, optionally, `update` functions.
|
|
56
|
+
* This can be useful for creating controlled write access to a signal that is otherwise read-only.
|
|
57
|
+
*
|
|
58
|
+
* @typeParam T - The type of value held by the signal.
|
|
59
|
+
*
|
|
60
|
+
* @param signal - The read-only `Signal` to be made writable.
|
|
61
|
+
* @param set - A function that will be used to set the signal's value. This function *must* handle
|
|
62
|
+
* the actual update mechanism (e.g., updating a backing store, emitting an event, etc.).
|
|
63
|
+
* @param update - (Optional) A function that will be used to update the signal's value based on its
|
|
64
|
+
* previous value. If not provided, a default `update` implementation is used that
|
|
65
|
+
* calls the provided `set` function with the result of the updater function. The
|
|
66
|
+
* default implementation uses `untracked` to avoid creating unnecessary dependencies
|
|
67
|
+
* within the updater function.
|
|
68
|
+
*
|
|
69
|
+
* @returns A `WritableSignal` that uses the provided `set` and `update` functions. The `asReadonly`
|
|
70
|
+
* method of the returned signal will still return the original read-only signal.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* // Basic usage: Making a read-only signal writable with a custom set function.
|
|
74
|
+
* const originalValue = signal({a: 0});
|
|
75
|
+
* const readOnlySignal = computed(() => originalValue().a);
|
|
76
|
+
* const writableSignal = toWritable(readOnlySignal, (newValue) => {
|
|
77
|
+
* originalValue.update((prev) => { ...prev, a: newValue });
|
|
78
|
+
* });
|
|
79
|
+
*
|
|
80
|
+
* writableSignal.set(5); // sets value of originalValue.a to 5 & triggers all signals
|
|
81
|
+
*/
|
|
82
|
+
function toWritable(signal, set, update) {
|
|
83
|
+
const internal = signal;
|
|
84
|
+
internal.asReadonly = () => signal;
|
|
85
|
+
internal.set = set;
|
|
86
|
+
internal.update = update ?? ((updater) => set(updater(untracked(internal))));
|
|
87
|
+
return internal;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Generated bundle index. Do not edit.
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
export { isMutable, mutable, toWritable };
|
|
95
|
+
//# sourceMappingURL=mmstack-primitives.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mmstack-primitives.mjs","sources":["../../../../packages/primitives/src/lib/mutable.ts","../../../../packages/primitives/src/lib/to-writable.ts","../../../../packages/primitives/src/mmstack-primitives.ts"],"sourcesContent":["import {\r\n signal,\r\n type CreateSignalOptions,\r\n type ValueEqualityFn,\r\n type WritableSignal,\r\n} from '@angular/core';\r\n\r\nconst { is } = Object;\r\n\r\n/**\r\n * A `MutableSignal` is a special type of `WritableSignal` that allows for in-place mutation of its value.\r\n * In addition to the standard `set` and `update` methods, it provides a `mutate` method. This is useful\r\n * for performance optimization when dealing with complex objects or arrays, as it avoids unnecessary\r\n * object copying.\r\n *\r\n * @typeParam T - The type of value held by the signal.\r\n */\r\nexport type MutableSignal<T> = WritableSignal<T> & {\r\n /**\r\n * Mutates the signal's value in-place. This is similar to `update`, but it's optimized for\r\n * scenarios where you want to modify the existing object directly rather than creating a new one.\r\n *\r\n * @param updater - A function that takes the current value as input and modifies it directly.\r\n *\r\n * @example\r\n * const myArray = mutable([1, 2, 3]);\r\n * myArray.mutate((arr) => {\r\n * arr.push(4);\r\n * return arr;\r\n * }); // myArray() now returns [1, 2, 3, 4]\r\n */\r\n mutate: WritableSignal<T>['update'];\r\n\r\n /**\r\n * Mutates the signal's value in-place, similar to `mutate`, but with a void-returning value in updater\r\n * function. This further emphasizes that the mutation is happening inline, improving readability\r\n * in some cases.\r\n * @param updater - Function to change to the current value\r\n * @example\r\n * const myObject = mutable({ a: 1, b: 2 });\r\n * myObject.inline((obj) => (obj.a = 3)); // myObject() now returns { a: 3, b: 2 }\r\n */\r\n inline: (updater: Parameters<WritableSignal<T>['update']>[0]) => void;\r\n};\r\n\r\n/**\r\n * Creates a `MutableSignal`. This function overloads the standard `signal` function to provide\r\n * the additional `mutate` and `inline` methods.\r\n *\r\n * @typeParam T - The type of value held by the signal.\r\n *\r\n * @param initial - The initial value of the signal. If no initial value is provided, it defaults to `undefined`.\r\n * @param options - Optional. An object containing signal options, including a custom equality function (`equal`).\r\n *\r\n * @returns A `MutableSignal` instance.\r\n *\r\n * @example\r\n * // Create a mutable signal with an initial value:\r\n * const mySignal = mutable({ count: 0 }) // MutableSignal<{ count: number }>;\r\n *\r\n * // Create a mutable signal with no initial value (starts as undefined):\r\n * const = mutable<number>(); // MutableSignal<number | undefined>\r\n *\r\n * // Create a mutable signal with a custom equality function:\r\n * const myCustomSignal = mutable({ a: 1 }, { equal: (a, b) => a.a === b.a });\r\n */\r\nexport function mutable<T>(): MutableSignal<T | undefined>;\r\nexport function mutable<T>(initial: T): MutableSignal<T>;\r\nexport function mutable<T>(\r\n initial: T,\r\n opt?: CreateSignalOptions<T>,\r\n): MutableSignal<T>;\r\n\r\nexport function mutable<T>(\r\n initial?: T,\r\n opt?: CreateSignalOptions<T>,\r\n): MutableSignal<T> {\r\n const baseEqual = opt?.equal ?? is;\r\n let trigger = false;\r\n\r\n const equal: ValueEqualityFn<T | undefined> = (a, b) => {\r\n if (trigger) return false;\r\n return baseEqual(a, b);\r\n };\r\n\r\n const sig = signal<T | undefined>(initial, {\r\n ...opt,\r\n equal,\r\n }) as MutableSignal<T>;\r\n\r\n const internalUpdate = sig.update;\r\n\r\n sig.mutate = (updater) => {\r\n trigger = true;\r\n internalUpdate(updater);\r\n trigger = false;\r\n };\r\n\r\n sig.inline = (updater) => {\r\n sig.mutate((prev) => {\r\n updater(prev);\r\n return prev;\r\n });\r\n };\r\n\r\n return sig;\r\n}\r\n\r\n/**\r\n * Type guard function to check if a given `WritableSignal` is a `MutableSignal`. This is useful\r\n * for situations where you need to conditionally use the `mutate` or `inline` methods.\r\n *\r\n * @typeParam T - The type of the signal's value (optional, defaults to `any`).\r\n * @param value - The `WritableSignal` to check.\r\n * @returns `true` if the signal is a `MutableSignal`, `false` otherwise.\r\n *\r\n * @example\r\n * const mySignal = signal(0);\r\n * const myMutableSignal = mutable(0);\r\n *\r\n * if (isMutable(mySignal)) {\r\n * mySignal.mutate(x => x + 1); // This would cause a type error, as mySignal is not a MutableSignal.\r\n * }\r\n *\r\n * if (isMutable(myMutableSignal)) {\r\n * myMutableSignal.mutate(x => x + 1); // This is safe.\r\n * }\r\n */\r\nexport function isMutable<T = any>(\r\n value: WritableSignal<T>,\r\n): value is MutableSignal<T> {\r\n return 'mutate' in value && typeof value.mutate === 'function';\r\n}\r\n","import { Signal, untracked, WritableSignal } from '@angular/core';\n\n/**\n * Converts a read-only `Signal` into a `WritableSignal` by providing custom `set` and, optionally, `update` functions.\n * This can be useful for creating controlled write access to a signal that is otherwise read-only.\n *\n * @typeParam T - The type of value held by the signal.\n *\n * @param signal - The read-only `Signal` to be made writable.\n * @param set - A function that will be used to set the signal's value. This function *must* handle\n * the actual update mechanism (e.g., updating a backing store, emitting an event, etc.).\n * @param update - (Optional) A function that will be used to update the signal's value based on its\n * previous value. If not provided, a default `update` implementation is used that\n * calls the provided `set` function with the result of the updater function. The\n * default implementation uses `untracked` to avoid creating unnecessary dependencies\n * within the updater function.\n *\n * @returns A `WritableSignal` that uses the provided `set` and `update` functions. The `asReadonly`\n * method of the returned signal will still return the original read-only signal.\n *\n * @example\n * // Basic usage: Making a read-only signal writable with a custom set function.\n * const originalValue = signal({a: 0});\n * const readOnlySignal = computed(() => originalValue().a);\n * const writableSignal = toWritable(readOnlySignal, (newValue) => {\n * originalValue.update((prev) => { ...prev, a: newValue });\n * });\n *\n * writableSignal.set(5); // sets value of originalValue.a to 5 & triggers all signals\n */\nexport function toWritable<T>(\n signal: Signal<T>,\n set: (value: T) => void,\n update?: (updater: (value: T) => T) => void,\n): WritableSignal<T> {\n const internal = signal as WritableSignal<T>;\n internal.asReadonly = () => signal;\n internal.set = set;\n internal.update = update ?? ((updater) => set(updater(untracked(internal))));\n\n return internal;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;AAOA,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM;AAkEL,SAAA,OAAO,CACrB,OAAW,EACX,GAA4B,EAAA;AAE5B,IAAA,MAAM,SAAS,GAAG,GAAG,EAAE,KAAK,IAAI,EAAE;IAClC,IAAI,OAAO,GAAG,KAAK;AAEnB,IAAA,MAAM,KAAK,GAAmC,CAAC,CAAC,EAAE,CAAC,KAAI;AACrD,QAAA,IAAI,OAAO;AAAE,YAAA,OAAO,KAAK;AACzB,QAAA,OAAO,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC;AACxB,KAAC;AAED,IAAA,MAAM,GAAG,GAAG,MAAM,CAAgB,OAAO,EAAE;AACzC,QAAA,GAAG,GAAG;QACN,KAAK;AACN,KAAA,CAAqB;AAEtB,IAAA,MAAM,cAAc,GAAG,GAAG,CAAC,MAAM;AAEjC,IAAA,GAAG,CAAC,MAAM,GAAG,CAAC,OAAO,KAAI;QACvB,OAAO,GAAG,IAAI;QACd,cAAc,CAAC,OAAO,CAAC;QACvB,OAAO,GAAG,KAAK;AACjB,KAAC;AAED,IAAA,GAAG,CAAC,MAAM,GAAG,CAAC,OAAO,KAAI;AACvB,QAAA,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,KAAI;YAClB,OAAO,CAAC,IAAI,CAAC;AACb,YAAA,OAAO,IAAI;AACb,SAAC,CAAC;AACJ,KAAC;AAED,IAAA,OAAO,GAAG;AACZ;AAEA;;;;;;;;;;;;;;;;;;;AAmBG;AACG,SAAU,SAAS,CACvB,KAAwB,EAAA;IAExB,OAAO,QAAQ,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,UAAU;AAChE;;AClIA;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BG;SACa,UAAU,CACxB,MAAiB,EACjB,GAAuB,EACvB,MAA2C,EAAA;IAE3C,MAAM,QAAQ,GAAG,MAA2B;AAC5C,IAAA,QAAQ,CAAC,UAAU,GAAG,MAAM,MAAM;AAClC,IAAA,QAAQ,CAAC,GAAG,GAAG,GAAG;IAClB,QAAQ,CAAC,MAAM,GAAG,MAAM,KAAK,CAAC,OAAO,KAAK,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AAE5E,IAAA,OAAO,QAAQ;AACjB;;ACzCA;;AAEG;;;;"}
|
package/index.d.ts
ADDED
package/lib/mutable.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type CreateSignalOptions, type WritableSignal } from '@angular/core';
|
|
2
|
+
/**
|
|
3
|
+
* A `MutableSignal` is a special type of `WritableSignal` that allows for in-place mutation of its value.
|
|
4
|
+
* In addition to the standard `set` and `update` methods, it provides a `mutate` method. This is useful
|
|
5
|
+
* for performance optimization when dealing with complex objects or arrays, as it avoids unnecessary
|
|
6
|
+
* object copying.
|
|
7
|
+
*
|
|
8
|
+
* @typeParam T - The type of value held by the signal.
|
|
9
|
+
*/
|
|
10
|
+
export type MutableSignal<T> = WritableSignal<T> & {
|
|
11
|
+
/**
|
|
12
|
+
* Mutates the signal's value in-place. This is similar to `update`, but it's optimized for
|
|
13
|
+
* scenarios where you want to modify the existing object directly rather than creating a new one.
|
|
14
|
+
*
|
|
15
|
+
* @param updater - A function that takes the current value as input and modifies it directly.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* const myArray = mutable([1, 2, 3]);
|
|
19
|
+
* myArray.mutate((arr) => {
|
|
20
|
+
* arr.push(4);
|
|
21
|
+
* return arr;
|
|
22
|
+
* }); // myArray() now returns [1, 2, 3, 4]
|
|
23
|
+
*/
|
|
24
|
+
mutate: WritableSignal<T>['update'];
|
|
25
|
+
/**
|
|
26
|
+
* Mutates the signal's value in-place, similar to `mutate`, but with a void-returning value in updater
|
|
27
|
+
* function. This further emphasizes that the mutation is happening inline, improving readability
|
|
28
|
+
* in some cases.
|
|
29
|
+
* @param updater - Function to change to the current value
|
|
30
|
+
* @example
|
|
31
|
+
* const myObject = mutable({ a: 1, b: 2 });
|
|
32
|
+
* myObject.inline((obj) => (obj.a = 3)); // myObject() now returns { a: 3, b: 2 }
|
|
33
|
+
*/
|
|
34
|
+
inline: (updater: Parameters<WritableSignal<T>['update']>[0]) => void;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Creates a `MutableSignal`. This function overloads the standard `signal` function to provide
|
|
38
|
+
* the additional `mutate` and `inline` methods.
|
|
39
|
+
*
|
|
40
|
+
* @typeParam T - The type of value held by the signal.
|
|
41
|
+
*
|
|
42
|
+
* @param initial - The initial value of the signal. If no initial value is provided, it defaults to `undefined`.
|
|
43
|
+
* @param options - Optional. An object containing signal options, including a custom equality function (`equal`).
|
|
44
|
+
*
|
|
45
|
+
* @returns A `MutableSignal` instance.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* // Create a mutable signal with an initial value:
|
|
49
|
+
* const mySignal = mutable({ count: 0 }) // MutableSignal<{ count: number }>;
|
|
50
|
+
*
|
|
51
|
+
* // Create a mutable signal with no initial value (starts as undefined):
|
|
52
|
+
* const = mutable<number>(); // MutableSignal<number | undefined>
|
|
53
|
+
*
|
|
54
|
+
* // Create a mutable signal with a custom equality function:
|
|
55
|
+
* const myCustomSignal = mutable({ a: 1 }, { equal: (a, b) => a.a === b.a });
|
|
56
|
+
*/
|
|
57
|
+
export declare function mutable<T>(): MutableSignal<T | undefined>;
|
|
58
|
+
export declare function mutable<T>(initial: T): MutableSignal<T>;
|
|
59
|
+
export declare function mutable<T>(initial: T, opt?: CreateSignalOptions<T>): MutableSignal<T>;
|
|
60
|
+
/**
|
|
61
|
+
* Type guard function to check if a given `WritableSignal` is a `MutableSignal`. This is useful
|
|
62
|
+
* for situations where you need to conditionally use the `mutate` or `inline` methods.
|
|
63
|
+
*
|
|
64
|
+
* @typeParam T - The type of the signal's value (optional, defaults to `any`).
|
|
65
|
+
* @param value - The `WritableSignal` to check.
|
|
66
|
+
* @returns `true` if the signal is a `MutableSignal`, `false` otherwise.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* const mySignal = signal(0);
|
|
70
|
+
* const myMutableSignal = mutable(0);
|
|
71
|
+
*
|
|
72
|
+
* if (isMutable(mySignal)) {
|
|
73
|
+
* mySignal.mutate(x => x + 1); // This would cause a type error, as mySignal is not a MutableSignal.
|
|
74
|
+
* }
|
|
75
|
+
*
|
|
76
|
+
* if (isMutable(myMutableSignal)) {
|
|
77
|
+
* myMutableSignal.mutate(x => x + 1); // This is safe.
|
|
78
|
+
* }
|
|
79
|
+
*/
|
|
80
|
+
export declare function isMutable<T = any>(value: WritableSignal<T>): value is MutableSignal<T>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Signal, WritableSignal } from '@angular/core';
|
|
2
|
+
/**
|
|
3
|
+
* Converts a read-only `Signal` into a `WritableSignal` by providing custom `set` and, optionally, `update` functions.
|
|
4
|
+
* This can be useful for creating controlled write access to a signal that is otherwise read-only.
|
|
5
|
+
*
|
|
6
|
+
* @typeParam T - The type of value held by the signal.
|
|
7
|
+
*
|
|
8
|
+
* @param signal - The read-only `Signal` to be made writable.
|
|
9
|
+
* @param set - A function that will be used to set the signal's value. This function *must* handle
|
|
10
|
+
* the actual update mechanism (e.g., updating a backing store, emitting an event, etc.).
|
|
11
|
+
* @param update - (Optional) A function that will be used to update the signal's value based on its
|
|
12
|
+
* previous value. If not provided, a default `update` implementation is used that
|
|
13
|
+
* calls the provided `set` function with the result of the updater function. The
|
|
14
|
+
* default implementation uses `untracked` to avoid creating unnecessary dependencies
|
|
15
|
+
* within the updater function.
|
|
16
|
+
*
|
|
17
|
+
* @returns A `WritableSignal` that uses the provided `set` and `update` functions. The `asReadonly`
|
|
18
|
+
* method of the returned signal will still return the original read-only signal.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* // Basic usage: Making a read-only signal writable with a custom set function.
|
|
22
|
+
* const originalValue = signal({a: 0});
|
|
23
|
+
* const readOnlySignal = computed(() => originalValue().a);
|
|
24
|
+
* const writableSignal = toWritable(readOnlySignal, (newValue) => {
|
|
25
|
+
* originalValue.update((prev) => { ...prev, a: newValue });
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* writableSignal.set(5); // sets value of originalValue.a to 5 & triggers all signals
|
|
29
|
+
*/
|
|
30
|
+
export declare function toWritable<T>(signal: Signal<T>, set: (value: T) => void, update?: (updater: (value: T) => T) => void): WritableSignal<T>;
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mmstack/primitives",
|
|
3
|
+
"version": "19.0.0",
|
|
4
|
+
"peerDependencies": {
|
|
5
|
+
"@angular/core": "19.2.3"
|
|
6
|
+
},
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"module": "fesm2022/mmstack-primitives.mjs",
|
|
9
|
+
"typings": "index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
"./package.json": {
|
|
12
|
+
"default": "./package.json"
|
|
13
|
+
},
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./index.d.ts",
|
|
16
|
+
"default": "./fesm2022/mmstack-primitives.mjs"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"tslib": "^2.3.0"
|
|
21
|
+
}
|
|
22
|
+
}
|