@oscarpalmer/mora 0.32.0 → 0.33.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/dist/batch.mjs +2 -2
- package/dist/constants.d.mts +2 -0
- package/dist/constants.mjs +3 -1
- package/dist/effect.d.mts +1 -0
- package/dist/effect.mjs +8 -5
- package/dist/helpers/is.d.mts +6 -6
- package/dist/helpers/misc.d.mts +4 -0
- package/dist/helpers/misc.mjs +9 -0
- package/dist/helpers/proxy.d.mts +8 -10
- package/dist/helpers/proxy.mjs +71 -33
- package/dist/helpers/subscription.d.mts +4 -4
- package/dist/helpers/subscription.mjs +16 -13
- package/dist/helpers/value.d.mts +8 -7
- package/dist/helpers/value.mjs +35 -22
- package/dist/models.d.mts +47 -24
- package/dist/models.mjs +1 -0
- package/dist/mora.full.mjs +307 -236
- package/dist/value/array.d.mts +1 -0
- package/dist/value/array.mjs +69 -61
- package/dist/value/computed.d.mts +3 -1
- package/dist/value/computed.mjs +39 -25
- package/dist/value/readonly.d.mts +3 -3
- package/dist/value/readonly.mjs +31 -24
- package/dist/value/signal.d.mts +1 -0
- package/dist/value/signal.mjs +27 -20
- package/dist/value/store.d.mts +1 -0
- package/dist/value/store.mjs +23 -41
- package/package.json +1 -1
- package/src/batch.ts +2 -2
- package/src/constants.ts +4 -0
- package/src/effect.ts +16 -11
- package/src/helpers/is.ts +6 -6
- package/src/helpers/misc.ts +15 -0
- package/src/helpers/proxy.ts +116 -89
- package/src/helpers/subscription.ts +46 -35
- package/src/helpers/value.ts +61 -35
- package/src/models.ts +54 -28
- package/src/value/array.ts +103 -124
- package/src/value/computed.ts +70 -40
- package/src/value/readonly.ts +54 -41
- package/src/value/signal.ts +46 -35
- package/src/value/store.ts +36 -83
- package/dist/value/reactive.d.mts +0 -4
- package/dist/value/reactive.mjs +0 -15
- package/src/value/reactive.ts +0 -27
package/dist/value/array.d.mts
CHANGED
package/dist/value/array.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { METHODS_AFFECTING_LENGTH, METHODS_UPDATE, NAME_ARRAY, NAME_MORA } from "../constants.mjs";
|
|
2
|
-
import { emitValue, equalArrays } from "../helpers/value.mjs";
|
|
1
|
+
import { METHODS_AFFECTING_LENGTH, METHODS_UPDATE, NAME_ARRAY, NAME_MORA, PROPERTY_LENGTH, SYMBOL_STATE } from "../constants.mjs";
|
|
2
|
+
import { emitValue, equalArrays, getStringValue } from "../helpers/value.mjs";
|
|
3
|
+
import { getState } from "../helpers/misc.mjs";
|
|
3
4
|
import { subscribeToProxy } from "../helpers/subscription.mjs";
|
|
4
|
-
import { reactive } from "./reactive.mjs";
|
|
5
5
|
import { computed } from "./computed.mjs";
|
|
6
6
|
import { emitProxyValues, getValueInProxy, peekValueInProxy, setProxyValue, setValueInProxy, updateProxyValue } from "../helpers/proxy.mjs";
|
|
7
7
|
import { getReadonlyInstance } from "./readonly.mjs";
|
|
@@ -9,77 +9,85 @@ import { signal } from "./signal.mjs";
|
|
|
9
9
|
import { select } from "@oscarpalmer/atoms/array";
|
|
10
10
|
import { filter } from "@oscarpalmer/atoms/array/filter";
|
|
11
11
|
//#region src/value/array.ts
|
|
12
|
-
function
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const readonlies = {};
|
|
18
|
-
state.value = new Proxy([], {
|
|
19
|
-
get: (target, property) => METHODS_UPDATE.has(property) ? updateArray(property, target, state, length) : Reflect.get(target, property),
|
|
20
|
-
set: (target, property, value) => setValueInProxy(isArray, state, target, property, value, length)
|
|
21
|
-
});
|
|
22
|
-
function get(value) {
|
|
23
|
-
return value === "length" ? length.get() : getValueInProxy(isArray, instance, state, mapped, value);
|
|
24
|
-
}
|
|
25
|
-
function set(first, second) {
|
|
26
|
-
setProxyValue(true, state, setArrayValue, setAtIndex, first, second);
|
|
27
|
-
}
|
|
28
|
-
const instance = {
|
|
29
|
-
...rx,
|
|
30
|
-
asReadonly: (frozen) => getReadonlyInstance(state, readonlies, frozen === true),
|
|
31
|
-
at: (index) => get(index),
|
|
32
|
-
clear: () => {
|
|
33
|
-
state.value.length = 0;
|
|
34
|
-
},
|
|
35
|
-
filter: (callback) => computed(() => filter(get(), callback)),
|
|
36
|
-
get: (value) => get(value),
|
|
37
|
-
map: (callback) => computed(() => get().map(callback)),
|
|
38
|
-
notify: () => emitProxyValues(state, mapped),
|
|
39
|
-
peek: (first, second) => peekArrayValue(state, length, first, second),
|
|
40
|
-
pop: () => state.value.pop(),
|
|
41
|
-
push: (...items) => state.value.push(...items),
|
|
42
|
-
select: (filter, map) => computed(() => select(get(), filter, map)),
|
|
43
|
-
set: (first, second) => set(first, second),
|
|
44
|
-
shift: () => state.value.shift(),
|
|
45
|
-
splice: (from, to, ...items) => state.value.splice(from, to ?? state.value.length, ...items),
|
|
46
|
-
subscribe: (first, second, third) => subscribeToProxy(isArray, instance, state, mapped, first, second, third),
|
|
47
|
-
unshift: (...items) => state.value.unshift(...items),
|
|
48
|
-
update: (callback) => updateProxyValue(true, state, setArrayValue, setAtIndex, callback)
|
|
12
|
+
function ReactiveArray(value, options) {
|
|
13
|
+
this[SYMBOL_STATE] = {
|
|
14
|
+
...getState(void 0, options),
|
|
15
|
+
isArray: true,
|
|
16
|
+
length: signal(0)
|
|
49
17
|
};
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
enumerable: true,
|
|
54
|
-
get: () => length.get(),
|
|
55
|
-
set: (value) => setArrayLength(state, value)
|
|
56
|
-
}
|
|
18
|
+
this[SYMBOL_STATE].value = new Proxy([], {
|
|
19
|
+
get: (target, property) => METHODS_UPDATE.has(property) ? updateArray(this, property, target) : Reflect.get(target, property),
|
|
20
|
+
set: (target, property, value) => setValueInProxy(this, target, property, value)
|
|
57
21
|
});
|
|
58
|
-
|
|
59
|
-
|
|
22
|
+
Object.defineProperty(this, PROPERTY_LENGTH, {
|
|
23
|
+
enumerable: true,
|
|
24
|
+
get: () => this[SYMBOL_STATE].length.get(),
|
|
25
|
+
set: (value) => setArrayLength(this[SYMBOL_STATE], value)
|
|
26
|
+
});
|
|
27
|
+
setProxyValue.call(this, value);
|
|
60
28
|
}
|
|
61
|
-
|
|
62
|
-
|
|
29
|
+
ReactiveArray.prototype[NAME_MORA] = NAME_ARRAY;
|
|
30
|
+
ReactiveArray.prototype.asReadonly = getReadonlyInstance;
|
|
31
|
+
ReactiveArray.prototype.at = getArrayValues;
|
|
32
|
+
ReactiveArray.prototype.get = getArrayValues;
|
|
33
|
+
ReactiveArray.prototype.notify = emitProxyValues;
|
|
34
|
+
ReactiveArray.prototype.peek = peekArrayValue;
|
|
35
|
+
ReactiveArray.prototype.set = setProxyValue;
|
|
36
|
+
ReactiveArray.prototype.subscribe = subscribeToProxy;
|
|
37
|
+
ReactiveArray.prototype.toString = getStringValue;
|
|
38
|
+
ReactiveArray.prototype.update = updateProxyValue;
|
|
39
|
+
ReactiveArray.prototype.clear = function() {
|
|
40
|
+
this[SYMBOL_STATE].value.length = 0;
|
|
41
|
+
};
|
|
42
|
+
ReactiveArray.prototype.filter = function(callback) {
|
|
43
|
+
return computed(() => filter(getArrayValues.call(this), callback));
|
|
44
|
+
};
|
|
45
|
+
ReactiveArray.prototype.map = function(callback) {
|
|
46
|
+
return computed(() => getArrayValues.call(this).map(callback));
|
|
47
|
+
};
|
|
48
|
+
ReactiveArray.prototype.pop = function() {
|
|
49
|
+
return this[SYMBOL_STATE].value.pop();
|
|
50
|
+
};
|
|
51
|
+
ReactiveArray.prototype.push = function(...items) {
|
|
52
|
+
return this[SYMBOL_STATE].value.push(...items);
|
|
53
|
+
};
|
|
54
|
+
ReactiveArray.prototype.select = function(filter, map) {
|
|
55
|
+
return computed(() => select(getArrayValues.call(this), filter, map));
|
|
56
|
+
};
|
|
57
|
+
ReactiveArray.prototype.shift = function() {
|
|
58
|
+
return this[SYMBOL_STATE].value.shift();
|
|
59
|
+
};
|
|
60
|
+
ReactiveArray.prototype.splice = function(from, to, ...items) {
|
|
61
|
+
return this[SYMBOL_STATE].value.splice(from, to ?? this[SYMBOL_STATE].value.length, ...items);
|
|
62
|
+
};
|
|
63
|
+
ReactiveArray.prototype.toJSON = function() {
|
|
64
|
+
return this[SYMBOL_STATE].value;
|
|
65
|
+
};
|
|
66
|
+
ReactiveArray.prototype.unshift = function(...items) {
|
|
67
|
+
return this[SYMBOL_STATE].value.unshift(...items);
|
|
68
|
+
};
|
|
69
|
+
function array(value, options) {
|
|
70
|
+
return new ReactiveArray(value, options);
|
|
63
71
|
}
|
|
64
|
-
function
|
|
65
|
-
|
|
72
|
+
function getArrayValues(value) {
|
|
73
|
+
return value === "length" ? this[SYMBOL_STATE].length.get() : getValueInProxy.call(this, value);
|
|
66
74
|
}
|
|
67
|
-
function
|
|
68
|
-
|
|
75
|
+
function peekArrayValue(first, second) {
|
|
76
|
+
return first === "length" ? this[SYMBOL_STATE].length.peek() : peekValueInProxy.call(this, first, second);
|
|
69
77
|
}
|
|
70
|
-
function
|
|
71
|
-
|
|
72
|
-
if (actual > -1) state.value[actual] = value;
|
|
78
|
+
function setArrayLength(state, value) {
|
|
79
|
+
if (typeof value === "number" && !Number.isNaN(value) && value >= 0 && value !== state.value.length) state.value.length = value;
|
|
73
80
|
}
|
|
74
|
-
function updateArray(type, array
|
|
81
|
+
function updateArray(instance, type, array) {
|
|
82
|
+
const state = instance[SYMBOL_STATE];
|
|
75
83
|
const affectsLength = METHODS_AFFECTING_LENGTH.has(type);
|
|
76
84
|
const previousArray = affectsLength ? [] : array.slice();
|
|
77
85
|
const previousLength = array.length;
|
|
78
86
|
return (...args) => {
|
|
79
87
|
const result = array[type](...args);
|
|
80
88
|
if (affectsLength ? array.length !== previousLength : !equalArrays(state, previousArray, array)) {
|
|
81
|
-
emitValue(
|
|
82
|
-
length.set(array.length);
|
|
89
|
+
emitValue(instance);
|
|
90
|
+
state.length.set(array.length);
|
|
83
91
|
}
|
|
84
92
|
return result;
|
|
85
93
|
};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Computed, ComputedEffect, ReactiveOptions } from "../models.mjs";
|
|
2
|
+
import { GenericCallback } from "@oscarpalmer/atoms/models";
|
|
2
3
|
//#region src/value/computed.d.ts
|
|
4
|
+
declare function Computed(this: any, callback: GenericCallback, options?: ReactiveOptions<unknown>): void;
|
|
3
5
|
/**
|
|
4
6
|
* Create a computed value
|
|
5
7
|
*
|
|
@@ -8,5 +10,5 @@ import { Computed, ComputedEffect, ReactiveOptions } from "../models.mjs";
|
|
|
8
10
|
* @returns Computed value
|
|
9
11
|
*/
|
|
10
12
|
export declare function computed<Value>(callback: () => Value | Promise<Value>, options?: ReactiveOptions<Value>): Computed<Value>;
|
|
11
|
-
export declare function internalComputed
|
|
13
|
+
export declare function internalComputed(callback: GenericCallback, options?: ReactiveOptions<unknown>): [Computed<unknown>, ComputedEffect];
|
|
12
14
|
//#endregion
|
package/dist/value/computed.mjs
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
|
-
import { ACTIVE, BATCH, NAME_COMPUTED, NAME_MORA, SUBSCRIPTION_TYPES, SUBSCRIPTION_TYPES_COPY } from "../constants.mjs";
|
|
1
|
+
import { ACTIVE, BATCH, NAME_COMPUTED, NAME_MORA, SUBSCRIPTION_TYPES, SUBSCRIPTION_TYPES_COPY, SYMBOL_EFFECT, SYMBOL_STATE } from "../constants.mjs";
|
|
2
2
|
import { internalEffect, runEffect } from "../effect.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { getStringValue, handleSignalValue, peekSignalValue } from "../helpers/value.mjs";
|
|
4
4
|
import { flushHandlers } from "../batch.mjs";
|
|
5
|
+
import { getState } from "../helpers/misc.mjs";
|
|
5
6
|
import { subscribeToSignal } from "../helpers/subscription.mjs";
|
|
6
|
-
import { reactive } from "./reactive.mjs";
|
|
7
7
|
//#region src/value/computed.ts
|
|
8
|
+
function Computed(callback, options) {
|
|
9
|
+
this[SYMBOL_STATE] = getState(void 0, options);
|
|
10
|
+
this[SYMBOL_EFFECT] = getComputedEffect(this, callback);
|
|
11
|
+
}
|
|
12
|
+
Computed.prototype[NAME_MORA] = NAME_COMPUTED;
|
|
13
|
+
Computed.prototype.get = getComputedValue;
|
|
14
|
+
Computed.prototype.peek = peekSignalValue;
|
|
15
|
+
Computed.prototype.subscribe = subscribeToSignal;
|
|
16
|
+
Computed.prototype.toString = getStringValue;
|
|
17
|
+
Computed.prototype.toJSON = function() {
|
|
18
|
+
return this[SYMBOL_STATE].value;
|
|
19
|
+
};
|
|
8
20
|
/**
|
|
9
21
|
* Create a computed value
|
|
10
22
|
*
|
|
@@ -17,19 +29,10 @@ function computed(callback, options) {
|
|
|
17
29
|
}
|
|
18
30
|
function getComputed(callback, options) {
|
|
19
31
|
if (typeof callback !== "function") throw new TypeError("Computed callback must be a function");
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
const instance = {
|
|
23
|
-
...rx,
|
|
24
|
-
get: () => getValue(state, fx),
|
|
25
|
-
peek: (copy) => peekSimpleValue(state.value, copy === true),
|
|
26
|
-
subscribe: (callback, copy) => subscribeToSignal(state, callback, copy === true)
|
|
27
|
-
};
|
|
28
|
-
Object.defineProperty(instance, NAME_MORA, { value: NAME_COMPUTED });
|
|
29
|
-
fx = getComputedEffect(state, callback);
|
|
30
|
-
return [Object.freeze(instance), fx];
|
|
32
|
+
const instance = new Computed(callback, options);
|
|
33
|
+
return [instance, instance[SYMBOL_EFFECT]];
|
|
31
34
|
}
|
|
32
|
-
function getComputedEffect(
|
|
35
|
+
function getComputedEffect(instance, callback) {
|
|
33
36
|
const fx = {
|
|
34
37
|
dirty: true,
|
|
35
38
|
instance: void 0
|
|
@@ -38,7 +41,7 @@ function getComputedEffect(state, callback) {
|
|
|
38
41
|
if (fx.dirty) {
|
|
39
42
|
const previousComputed = ACTIVE.computed;
|
|
40
43
|
ACTIVE.computed = fx;
|
|
41
|
-
|
|
44
|
+
handleSignalValue(instance, callback, setAndEmit, () => {
|
|
42
45
|
ACTIVE.computed = previousComputed;
|
|
43
46
|
});
|
|
44
47
|
fx.dirty = false;
|
|
@@ -46,25 +49,36 @@ function getComputedEffect(state, callback) {
|
|
|
46
49
|
});
|
|
47
50
|
return fx;
|
|
48
51
|
}
|
|
49
|
-
function
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
function getComputedValue() {
|
|
53
|
+
const fx = this[SYMBOL_EFFECT];
|
|
54
|
+
const state = this[SYMBOL_STATE];
|
|
55
|
+
if (ACTIVE.computed != null && fx !== ACTIVE.computed) {
|
|
56
|
+
state.computeds ??= /* @__PURE__ */ new Set();
|
|
57
|
+
state.computeds.add(ACTIVE.computed);
|
|
58
|
+
}
|
|
59
|
+
if (ACTIVE.effect != null && ACTIVE.effect !== fx.instance) {
|
|
60
|
+
state.effects ??= /* @__PURE__ */ new Set();
|
|
61
|
+
state.effects.add(ACTIVE.effect);
|
|
62
|
+
}
|
|
52
63
|
if (fx.dirty && BATCH.depth === 0) runEffect(fx.instance);
|
|
53
64
|
return state.value;
|
|
54
65
|
}
|
|
55
66
|
function internalComputed(callback, options) {
|
|
56
67
|
return getComputed(callback, options);
|
|
57
68
|
}
|
|
58
|
-
function setAndEmit(
|
|
59
|
-
|
|
69
|
+
function setAndEmit(instance, value) {
|
|
70
|
+
const state = instance[SYMBOL_STATE];
|
|
71
|
+
if ((state.equal ?? Object.is)(state.value, value)) return;
|
|
60
72
|
state.value = value;
|
|
61
|
-
for (const computed of state.computeds) computed.dirty = true;
|
|
62
|
-
for (const effect of state.effects) BATCH.handlers.set(effect, effect);
|
|
73
|
+
if (state.computeds != null && state.computeds.size > 0) for (const computed of state.computeds) computed.dirty = true;
|
|
74
|
+
if (state.effects != null && state.effects.size > 0) for (const effect of state.effects) BATCH.handlers.set(effect, effect);
|
|
63
75
|
for (const type of SUBSCRIPTION_TYPES) {
|
|
64
76
|
if (type === "frozen") continue;
|
|
65
|
-
const copy = SUBSCRIPTION_TYPES_COPY.has(type);
|
|
66
77
|
const subscriptions = state.subscriptions?.values.to.keyed?.get(type);
|
|
67
|
-
if (subscriptions != null
|
|
78
|
+
if (subscriptions != null && subscriptions.size > 0) {
|
|
79
|
+
const copy = SUBSCRIPTION_TYPES_COPY.has(type);
|
|
80
|
+
for (const [, callback] of subscriptions) callback(peekSignalValue.call(instance, copy));
|
|
81
|
+
}
|
|
68
82
|
}
|
|
69
83
|
flushHandlers();
|
|
70
84
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { InternalSignal, ReadonlyFrozenSignal, ReadonlySignal, SignalState } from "../models.mjs";
|
|
2
2
|
//#region src/value/readonly.d.ts
|
|
3
|
-
export declare function getReadonlyInstance<Value>(
|
|
4
|
-
export declare function getReadonlySignal
|
|
3
|
+
export declare function getReadonlyInstance<Value>(this: InternalSignal, frozen?: never): ReadonlySignal<Value>;
|
|
4
|
+
export declare function getReadonlySignal(state: SignalState, frozen: boolean): ReadonlySignal<unknown> | ReadonlyFrozenSignal<unknown>;
|
|
5
5
|
//#endregion
|
package/dist/value/readonly.mjs
CHANGED
|
@@ -1,32 +1,39 @@
|
|
|
1
|
-
import { NAME_MORA, NAME_READONLY, SUBSCRIPTION_TYPE_FROZEN,
|
|
2
|
-
import { getFrozenValue,
|
|
3
|
-
import {
|
|
1
|
+
import { NAME_MORA, NAME_READONLY, SUBSCRIPTION_TYPE_FROZEN, SUBSCRIPTION_TYPE_ORIGINAL, SYMBOL_STATE } from "../constants.mjs";
|
|
2
|
+
import { getFrozenValue, getSignalValue, getStringValue, peekSignalValue } from "../helpers/value.mjs";
|
|
3
|
+
import { subscribeToReadonly } from "../helpers/subscription.mjs";
|
|
4
4
|
//#region src/value/readonly.ts
|
|
5
|
-
function
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
function Readonly(state, frozen) {
|
|
6
|
+
this[SYMBOL_STATE] = state;
|
|
7
|
+
Object.defineProperty(this, SUBSCRIPTION_TYPE_FROZEN, {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
value: frozen
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
Readonly.prototype[NAME_MORA] = NAME_READONLY;
|
|
13
|
+
Readonly.prototype.subscribe = subscribeToReadonly;
|
|
14
|
+
Readonly.prototype.toString = getStringValue;
|
|
15
|
+
Readonly.prototype.get = function() {
|
|
16
|
+
return getReadonlyValue(this, false, this[SUBSCRIPTION_TYPE_FROZEN]);
|
|
17
|
+
};
|
|
18
|
+
Readonly.prototype.peek = function(copy) {
|
|
19
|
+
return getReadonlyValue(this, true, this[SUBSCRIPTION_TYPE_FROZEN], copy);
|
|
20
|
+
};
|
|
21
|
+
Readonly.prototype.toJSON = function() {
|
|
22
|
+
return this[SYMBOL_STATE].value;
|
|
23
|
+
};
|
|
24
|
+
function getReadonlyInstance(frozen) {
|
|
25
|
+
const key = frozen === true ? SUBSCRIPTION_TYPE_FROZEN : SUBSCRIPTION_TYPE_ORIGINAL;
|
|
26
|
+
this[SYMBOL_STATE].readonlies ??= {};
|
|
27
|
+
this[SYMBOL_STATE].readonlies[key] ??= getReadonlySignal(this[SYMBOL_STATE], frozen === true);
|
|
28
|
+
return this[SYMBOL_STATE].readonlies[key];
|
|
9
29
|
}
|
|
10
30
|
function getReadonlySignal(state, frozen) {
|
|
11
|
-
|
|
12
|
-
const instance = {
|
|
13
|
-
get: () => getReadonlyValue(state, false, frozen),
|
|
14
|
-
peek: (copy) => getReadonlyValue(state, true, frozen, copy),
|
|
15
|
-
subscribe: (callback) => subscribeToReactive(type, state, callback)
|
|
16
|
-
};
|
|
17
|
-
Object.defineProperties(instance, {
|
|
18
|
-
[NAME_MORA]: { value: NAME_READONLY },
|
|
19
|
-
frozen: {
|
|
20
|
-
enumerable: true,
|
|
21
|
-
value: frozen
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
return Object.freeze(instance);
|
|
31
|
+
return new Readonly(state, frozen);
|
|
25
32
|
}
|
|
26
|
-
function getReadonlyValue(
|
|
33
|
+
function getReadonlyValue(instance, peek, frozen, copy) {
|
|
27
34
|
let value;
|
|
28
|
-
if (peek) value =
|
|
29
|
-
else value =
|
|
35
|
+
if (peek) value = peekSignalValue.call(instance, copy === true);
|
|
36
|
+
else value = getSignalValue.call(instance);
|
|
30
37
|
return frozen ? getFrozenValue(value) : value;
|
|
31
38
|
}
|
|
32
39
|
//#endregion
|
package/dist/value/signal.d.mts
CHANGED
package/dist/value/signal.mjs
CHANGED
|
@@ -1,30 +1,37 @@
|
|
|
1
|
-
import { NAME_MORA, NAME_SIGNAL } from "../constants.mjs";
|
|
2
|
-
import { emitValue,
|
|
1
|
+
import { NAME_MORA, NAME_SIGNAL, SYMBOL_STATE } from "../constants.mjs";
|
|
2
|
+
import { emitValue, getSignalValue, getStringValue, handleSignalValue, peekSignalValue, updateSignalValue } from "../helpers/value.mjs";
|
|
3
|
+
import { getState } from "../helpers/misc.mjs";
|
|
3
4
|
import { subscribeToSignal } from "../helpers/subscription.mjs";
|
|
4
|
-
import { reactive } from "./reactive.mjs";
|
|
5
5
|
import { getReadonlyInstance } from "./readonly.mjs";
|
|
6
6
|
//#region src/value/signal.ts
|
|
7
|
-
function
|
|
8
|
-
|
|
7
|
+
function Signal(value, options) {
|
|
8
|
+
this[SYMBOL_STATE] = getState(void 0, options);
|
|
9
|
+
handleSignalValue(this, value, setAndEmit);
|
|
10
|
+
}
|
|
11
|
+
Signal.prototype[NAME_MORA] = NAME_SIGNAL;
|
|
12
|
+
Signal.prototype.asReadonly = getReadonlyInstance;
|
|
13
|
+
Signal.prototype.get = getSignalValue;
|
|
14
|
+
Signal.prototype.peek = peekSignalValue;
|
|
15
|
+
Signal.prototype.subscribe = subscribeToSignal;
|
|
16
|
+
Signal.prototype.toString = getStringValue;
|
|
17
|
+
Signal.prototype.set = function(value) {
|
|
18
|
+
return handleSignalValue(this, value, setAndEmit);
|
|
19
|
+
};
|
|
20
|
+
Signal.prototype.toJSON = function() {
|
|
21
|
+
return this[SYMBOL_STATE].value;
|
|
22
|
+
};
|
|
23
|
+
Signal.prototype.update = function(callback) {
|
|
24
|
+
return updateSignalValue(this, callback, setAndEmit);
|
|
25
|
+
};
|
|
26
|
+
function setAndEmit(instance, value) {
|
|
27
|
+
const state = instance[SYMBOL_STATE];
|
|
28
|
+
if (!(state.equal ?? Object.is)(state.value, value)) {
|
|
9
29
|
state.value = value;
|
|
10
|
-
emitValue(
|
|
30
|
+
emitValue(instance);
|
|
11
31
|
}
|
|
12
32
|
}
|
|
13
33
|
function signal(value, options) {
|
|
14
|
-
|
|
15
|
-
const readonlies = {};
|
|
16
|
-
const instance = {
|
|
17
|
-
...rx,
|
|
18
|
-
asReadonly: (frozen) => getReadonlyInstance(state, readonlies, frozen === true),
|
|
19
|
-
get: () => getSimpleValue(state),
|
|
20
|
-
peek: (copy) => peekSimpleValue(state.value, copy === true),
|
|
21
|
-
set: (value) => handleSimpleValue(state, value, setAndEmit),
|
|
22
|
-
subscribe: (callback, copy) => subscribeToSignal(state, callback, copy === true),
|
|
23
|
-
update: (callback) => updateSimpleValue(state, callback, setAndEmit)
|
|
24
|
-
};
|
|
25
|
-
Object.defineProperty(instance, NAME_MORA, { value: NAME_SIGNAL });
|
|
26
|
-
handleSimpleValue(state, value, setAndEmit);
|
|
27
|
-
return Object.freeze(instance);
|
|
34
|
+
return new Signal(value, options);
|
|
28
35
|
}
|
|
29
36
|
//#endregion
|
|
30
37
|
export { signal };
|
package/dist/value/store.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ReactiveOptions, ReactiveStore } from "../models.mjs";
|
|
2
2
|
import { PlainObject } from "@oscarpalmer/atoms/models";
|
|
3
3
|
//#region src/value/store.d.ts
|
|
4
|
+
declare function ReactiveStore(this: any, value: never, options?: ReactiveOptions<unknown>): void;
|
|
4
5
|
/**
|
|
5
6
|
* Create a reactive store from a function result
|
|
6
7
|
*
|
package/dist/value/store.mjs
CHANGED
|
@@ -1,50 +1,32 @@
|
|
|
1
|
-
import { NAME_MORA, NAME_STORE } from "../constants.mjs";
|
|
2
|
-
import {
|
|
1
|
+
import { NAME_MORA, NAME_STORE, SYMBOL_STATE } from "../constants.mjs";
|
|
2
|
+
import { getStringValue } from "../helpers/value.mjs";
|
|
3
|
+
import { getState } from "../helpers/misc.mjs";
|
|
3
4
|
import { subscribeToProxy } from "../helpers/subscription.mjs";
|
|
4
|
-
import { reactive } from "./reactive.mjs";
|
|
5
5
|
import { emitProxyValues, getValueInProxy, peekValueInProxy, setProxyValue, setValueInProxy, updateProxyValue } from "../helpers/proxy.mjs";
|
|
6
6
|
import { getReadonlyInstance } from "./readonly.mjs";
|
|
7
7
|
//#region src/value/store.ts
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const proxyKeys = Object.keys(proxy);
|
|
16
|
-
const actualKeys = Object.keys(actual);
|
|
17
|
-
let { length } = proxyKeys;
|
|
18
|
-
for (let index = 0; index < length; index += 1) {
|
|
19
|
-
const key = proxyKeys[index];
|
|
20
|
-
proxy[key] = actualKeys.includes(key) ? actual[key] : void 0;
|
|
21
|
-
}
|
|
22
|
-
length = actualKeys.length;
|
|
23
|
-
for (let index = 0; index < length; index += 1) {
|
|
24
|
-
const key = actualKeys[index];
|
|
25
|
-
if (!proxyKeys.includes(key)) proxy[key] = actual[key];
|
|
26
|
-
}
|
|
27
|
-
stopBatch();
|
|
8
|
+
function ReactiveStore(value, options) {
|
|
9
|
+
this[SYMBOL_STATE] = {
|
|
10
|
+
...getState(void 0, options),
|
|
11
|
+
isArray: false
|
|
12
|
+
};
|
|
13
|
+
this[SYMBOL_STATE].value = new Proxy({}, { set: (target, property, value) => setValueInProxy(this, target, property, value) });
|
|
14
|
+
setProxyValue.call(this, value);
|
|
28
15
|
}
|
|
16
|
+
ReactiveStore.prototype[NAME_MORA] = NAME_STORE;
|
|
17
|
+
ReactiveStore.prototype.asReadonly = getReadonlyInstance;
|
|
18
|
+
ReactiveStore.prototype.get = getValueInProxy;
|
|
19
|
+
ReactiveStore.prototype.notify = emitProxyValues;
|
|
20
|
+
ReactiveStore.prototype.peek = peekValueInProxy;
|
|
21
|
+
ReactiveStore.prototype.set = setProxyValue;
|
|
22
|
+
ReactiveStore.prototype.subscribe = subscribeToProxy;
|
|
23
|
+
ReactiveStore.prototype.toString = getStringValue;
|
|
24
|
+
ReactiveStore.prototype.update = updateProxyValue;
|
|
25
|
+
ReactiveStore.prototype.toJSON = function() {
|
|
26
|
+
return this[SYMBOL_STATE].value;
|
|
27
|
+
};
|
|
29
28
|
function store(value, options) {
|
|
30
|
-
|
|
31
|
-
const isArray = false;
|
|
32
|
-
const keyed = /* @__PURE__ */ new Map();
|
|
33
|
-
const readonlies = {};
|
|
34
|
-
state.value = new Proxy({}, { set: (target, property, value) => setValueInProxy(isArray, state, target, property, value) });
|
|
35
|
-
const instance = {
|
|
36
|
-
...rx,
|
|
37
|
-
asReadonly: (frozen) => getReadonlyInstance(state, readonlies, frozen === true),
|
|
38
|
-
get: (value) => getValueInProxy(isArray, instance, state, keyed, value),
|
|
39
|
-
notify: () => emitProxyValues(state, keyed),
|
|
40
|
-
peek: (first, second) => peekValueInProxy(isArray, state, first, second),
|
|
41
|
-
set: (first, second) => setProxyValue(isArray, state, setStoreValue, setPropertyValue, first, second),
|
|
42
|
-
subscribe: (first, second, third) => subscribeToProxy(isArray, instance, state, keyed, first, second, third),
|
|
43
|
-
update: (callback) => updateProxyValue(isArray, state, setStoreValue, setPropertyValue, callback)
|
|
44
|
-
};
|
|
45
|
-
Object.defineProperty(instance, NAME_MORA, { value: NAME_STORE });
|
|
46
|
-
instance.set(value);
|
|
47
|
-
return Object.freeze(instance);
|
|
29
|
+
return new ReactiveStore(value, options);
|
|
48
30
|
}
|
|
49
31
|
//#endregion
|
|
50
32
|
export { store };
|
package/package.json
CHANGED
package/src/batch.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {BATCH} from './constants';
|
|
2
2
|
import {runEffect} from './effect';
|
|
3
|
-
import {getFrozenValue,
|
|
3
|
+
import {getFrozenValue, peekSignalValue} from './helpers/value';
|
|
4
4
|
import type {EffectState, StoredSubscription} from './models';
|
|
5
5
|
|
|
6
6
|
// #region Functions
|
|
@@ -27,7 +27,7 @@ export function flushHandlers(): void {
|
|
|
27
27
|
subscription.callback(
|
|
28
28
|
subscription.frozen
|
|
29
29
|
? getFrozenValue(subscription.state.value)
|
|
30
|
-
:
|
|
30
|
+
: peekSignalValue.call(subscription.instance, subscription.copy),
|
|
31
31
|
);
|
|
32
32
|
} else {
|
|
33
33
|
runEffect(handler as EffectState);
|
package/src/constants.ts
CHANGED
|
@@ -53,6 +53,10 @@ export const NAME_ALL = new Set([
|
|
|
53
53
|
|
|
54
54
|
export const PROPERTY_LENGTH = 'length';
|
|
55
55
|
|
|
56
|
+
export const SYMBOL_EFFECT = Symbol('effect');
|
|
57
|
+
|
|
58
|
+
export const SYMBOL_STATE = Symbol('state');
|
|
59
|
+
|
|
56
60
|
export const SUBSCRIPTION_PROPERTY = {
|
|
57
61
|
key: NAME_MORA,
|
|
58
62
|
value: NAME_SUBSCRIPTION,
|
package/src/effect.ts
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import type {GenericCallback} from '@oscarpalmer/atoms/models';
|
|
2
|
-
import {ACTIVE, NAME_EFFECT, NAME_MORA} from './constants';
|
|
2
|
+
import {ACTIVE, NAME_EFFECT, NAME_MORA, SYMBOL_STATE} from './constants';
|
|
3
3
|
import type {Effect, EffectState} from './models';
|
|
4
4
|
|
|
5
|
+
// #region Instance
|
|
6
|
+
|
|
7
|
+
function Effect(this: any, callback: GenericCallback) {
|
|
8
|
+
this[SYMBOL_STATE] = {callback};
|
|
9
|
+
|
|
10
|
+
runEffect(this[SYMBOL_STATE]);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
Effect.prototype[NAME_MORA] = NAME_EFFECT;
|
|
14
|
+
|
|
15
|
+
// #endregion
|
|
16
|
+
|
|
5
17
|
// #region Functions
|
|
6
18
|
|
|
7
19
|
/**
|
|
@@ -19,17 +31,10 @@ export function effect(callback: GenericCallback): Effect {
|
|
|
19
31
|
}
|
|
20
32
|
|
|
21
33
|
function getEffect(callback: GenericCallback): [Effect, EffectState] {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const instance = Object.freeze({
|
|
27
|
-
[NAME_MORA]: NAME_EFFECT,
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
runEffect(state);
|
|
34
|
+
// @ts-expect-error All good, no worries :-)
|
|
35
|
+
const instance = new Effect(callback);
|
|
31
36
|
|
|
32
|
-
return [instance,
|
|
37
|
+
return [instance, instance[SYMBOL_STATE]];
|
|
33
38
|
}
|
|
34
39
|
|
|
35
40
|
export function internalEffect(callback: GenericCallback): EffectState {
|
package/src/helpers/is.ts
CHANGED
|
@@ -27,7 +27,7 @@ import type {
|
|
|
27
27
|
* @param value Value to check
|
|
28
28
|
* @returns True if value is a {@link Computed}
|
|
29
29
|
*/
|
|
30
|
-
export function isComputed<Value>(value: unknown): value is Computed<Value> {
|
|
30
|
+
export function isComputed<Value = unknown>(value: unknown): value is Computed<Value> {
|
|
31
31
|
return isMora<Computed<Value>>(value, NAME_COMPUTED);
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -58,7 +58,7 @@ function isMora<Instance>(value: unknown, name: string | Set<string>): value is
|
|
|
58
58
|
* @param value Value to check
|
|
59
59
|
* @returns True if value is a {@link Reactive}
|
|
60
60
|
*/
|
|
61
|
-
export function isReactive<Value>(value: unknown): value is Reactive<Value> {
|
|
61
|
+
export function isReactive<Value = unknown>(value: unknown): value is Reactive<Value> {
|
|
62
62
|
return isMora<Reactive<Value>>(value, NAME_ALL);
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -68,7 +68,7 @@ export function isReactive<Value>(value: unknown): value is Reactive<Value> {
|
|
|
68
68
|
* @param value Value to check
|
|
69
69
|
* @returns True if value is a {@link Signal}
|
|
70
70
|
*/
|
|
71
|
-
export function isSignal<Value>(value: unknown): value is Signal<Value> {
|
|
71
|
+
export function isSignal<Value = unknown>(value: unknown): value is Signal<Value> {
|
|
72
72
|
return isMora<Signal<Value>>(value, NAME_SIGNAL);
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -78,7 +78,7 @@ export function isSignal<Value>(value: unknown): value is Signal<Value> {
|
|
|
78
78
|
* @param value Value to check
|
|
79
79
|
* @returns True if value is a {@link ReactiveArray}
|
|
80
80
|
*/
|
|
81
|
-
export function isReactiveArray<Item>(value: unknown): value is ReactiveArray<Item> {
|
|
81
|
+
export function isReactiveArray<Item = unknown>(value: unknown): value is ReactiveArray<Item> {
|
|
82
82
|
return isMora<ReactiveArray<Item>>(value, NAME_ARRAY);
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -88,7 +88,7 @@ export function isReactiveArray<Item>(value: unknown): value is ReactiveArray<It
|
|
|
88
88
|
* @param value Value to check
|
|
89
89
|
* @returns True if value is a {@link Store}
|
|
90
90
|
*/
|
|
91
|
-
export function isReactiveStore<Value extends PlainObject>(
|
|
91
|
+
export function isReactiveStore<Value extends PlainObject = PlainObject>(
|
|
92
92
|
value: unknown,
|
|
93
93
|
): value is ReactiveStore<Value> {
|
|
94
94
|
return isMora<ReactiveStore<Value>>(value, NAME_STORE);
|
|
@@ -100,7 +100,7 @@ export function isReactiveStore<Value extends PlainObject>(
|
|
|
100
100
|
* @param value Value to check
|
|
101
101
|
* @returns True if value is a {@link ReadonlySignal}
|
|
102
102
|
*/
|
|
103
|
-
export function isReadonlySignal<Value>(value: unknown): value is ReadonlySignal<Value> {
|
|
103
|
+
export function isReadonlySignal<Value = unknown>(value: unknown): value is ReadonlySignal<Value> {
|
|
104
104
|
return isMora<ReadonlySignal<Value>>(value, NAME_READONLY);
|
|
105
105
|
}
|
|
106
106
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type {ReactiveOptions, ReactiveState} from '../models';
|
|
2
|
+
|
|
3
|
+
// #region Functions
|
|
4
|
+
|
|
5
|
+
export function getState(value: unknown, options?: ReactiveOptions<unknown>): ReactiveState {
|
|
6
|
+
return {
|
|
7
|
+
value,
|
|
8
|
+
equal:
|
|
9
|
+
typeof options === 'object' && typeof options?.equal === 'function'
|
|
10
|
+
? options.equal
|
|
11
|
+
: undefined,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// #endregion
|