@oscarpalmer/mora 0.22.0 → 0.23.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.js +8 -9
- package/dist/constants.js +34 -0
- package/dist/effect.js +7 -8
- package/dist/helpers/is.js +8 -18
- package/dist/helpers/proxy.js +3 -2
- package/dist/helpers/value.js +7 -8
- package/dist/index.js +1 -1
- package/dist/models.js +0 -0
- package/dist/mora.full.js +218 -130
- package/dist/subscription.js +7 -5
- package/dist/value/array.js +24 -24
- package/dist/value/computed.js +18 -21
- package/dist/value/signal.js +2 -2
- package/dist/value/store.js +8 -2
- package/package.json +9 -10
- package/src/batch.ts +11 -13
- package/src/constants.ts +49 -0
- package/src/effect.ts +11 -16
- package/src/helpers/is.ts +41 -19
- package/src/helpers/proxy.ts +34 -38
- package/src/helpers/value.ts +19 -14
- package/src/index.ts +1 -0
- package/src/models.ts +66 -0
- package/src/subscription.ts +14 -16
- package/src/value/array.ts +86 -44
- package/src/value/computed.ts +29 -39
- package/src/value/reactive.ts +9 -24
- package/src/value/signal.ts +9 -3
- package/src/value/store.ts +40 -9
- package/types/batch.d.ts +3 -5
- package/types/constants.d.ts +14 -0
- package/types/effect.d.ts +2 -1
- package/types/helpers/is.d.ts +23 -10
- package/types/helpers/proxy.d.ts +2 -3
- package/types/helpers/value.d.ts +1 -1
- package/types/index.d.ts +1 -0
- package/types/models.d.ts +56 -0
- package/types/subscription.d.ts +3 -6
- package/types/value/array.d.ts +48 -7
- package/types/value/computed.d.ts +2 -11
- package/types/value/reactive.d.ts +8 -17
- package/types/value/signal.d.ts +7 -1
- package/types/value/store.d.ts +28 -4
package/dist/value/array.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { noop, subscribe } from "../subscription.js";
|
|
2
|
-
import {
|
|
2
|
+
import { METHODS_AFFECTING_LENGTH, METHODS_UPDATE, NAME_ARRAY } from "../constants.js";
|
|
3
3
|
import { Reactive } from "./reactive.js";
|
|
4
4
|
import { computed } from "./computed.js";
|
|
5
5
|
import { emitValue, equalArrays, getValue } from "../helpers/value.js";
|
|
@@ -15,28 +15,38 @@ var ReactiveArray = class extends Reactive {
|
|
|
15
15
|
if (typeof value === "number" && value >= 0 && value !== this.state.value.length) this.get().length = value;
|
|
16
16
|
}
|
|
17
17
|
constructor(value, options) {
|
|
18
|
-
super(
|
|
19
|
-
get: (target, property) =>
|
|
20
|
-
set: (target, property, value$1) => setValueInProxy(
|
|
18
|
+
super(NAME_ARRAY, new Proxy(value, {
|
|
19
|
+
get: (target, property) => METHODS_UPDATE.has(property) ? updateArray(property, target, this.state, this.#size) : Reflect.get(target, property),
|
|
20
|
+
set: (target, property, value$1) => setValueInProxy({
|
|
21
|
+
property,
|
|
22
|
+
target,
|
|
23
|
+
value: value$1,
|
|
24
|
+
isArray: true,
|
|
25
|
+
state: this.state,
|
|
26
|
+
length: this.#size
|
|
27
|
+
})
|
|
21
28
|
}), options);
|
|
22
29
|
this.#size.set(value.length);
|
|
23
30
|
}
|
|
24
31
|
clear() {
|
|
25
32
|
this.length = 0;
|
|
26
33
|
}
|
|
34
|
+
filter(callback) {
|
|
35
|
+
return computed(() => this.get().filter(callback));
|
|
36
|
+
}
|
|
27
37
|
get(first) {
|
|
28
38
|
if (typeof first === "number") return getReactiveValueInProxy(this, this.#indiced, first, true).get();
|
|
29
39
|
if (first === "length") return this.length;
|
|
30
40
|
return getValue(this.state);
|
|
31
41
|
}
|
|
32
|
-
filter(callback) {
|
|
33
|
-
return computed(() => this.get().filter(callback));
|
|
34
|
-
}
|
|
35
42
|
map(callback) {
|
|
36
43
|
return computed(() => this.get().map(callback));
|
|
37
44
|
}
|
|
45
|
+
notify() {
|
|
46
|
+
emitValue(this.state);
|
|
47
|
+
}
|
|
38
48
|
peek(value) {
|
|
39
|
-
if (value ===
|
|
49
|
+
if (value === "length") return this.#size.peek();
|
|
40
50
|
if (typeof value === "number") return this.state.value.at(value);
|
|
41
51
|
return [...this.state.value];
|
|
42
52
|
}
|
|
@@ -49,7 +59,7 @@ var ReactiveArray = class extends Reactive {
|
|
|
49
59
|
set(first, second) {
|
|
50
60
|
if (first == null || Array.isArray(first)) this.state.value.splice(0, this.state.value.length, ...first ?? []);
|
|
51
61
|
else if (first === "length") this.length = second;
|
|
52
|
-
else if (typeof first === "number") this.state.value
|
|
62
|
+
else if (typeof first === "number" && !Number.isNaN(first)) setAtIndex(this.state.value, first, second);
|
|
53
63
|
}
|
|
54
64
|
shift() {
|
|
55
65
|
return this.state.value.shift();
|
|
@@ -73,7 +83,7 @@ function array(value, options) {
|
|
|
73
83
|
return new ReactiveArray(Array.isArray(value) ? value : [], options);
|
|
74
84
|
}
|
|
75
85
|
function updateArray(type, array$1, state, length) {
|
|
76
|
-
const affectsLength =
|
|
86
|
+
const affectsLength = METHODS_AFFECTING_LENGTH.has(type);
|
|
77
87
|
const previousArray = affectsLength ? [] : [...array$1];
|
|
78
88
|
const previousLength = array$1.length;
|
|
79
89
|
return (...args) => {
|
|
@@ -85,18 +95,8 @@ function updateArray(type, array$1, state, length) {
|
|
|
85
95
|
return result;
|
|
86
96
|
};
|
|
87
97
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
"unshift"
|
|
93
|
-
]);
|
|
94
|
-
var updateMethods = new Set([
|
|
95
|
-
...lengthAffectingMethods,
|
|
96
|
-
"copyWithin",
|
|
97
|
-
"fill",
|
|
98
|
-
"reverse",
|
|
99
|
-
"sort",
|
|
100
|
-
"splice"
|
|
101
|
-
]);
|
|
98
|
+
function setAtIndex(array$1, index, value) {
|
|
99
|
+
const actual = index < 0 ? array$1.length + index : index;
|
|
100
|
+
if (actual > -1) array$1[actual] = value;
|
|
101
|
+
}
|
|
102
102
|
export { ReactiveArray, array };
|
package/dist/value/computed.js
CHANGED
|
@@ -1,39 +1,36 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { batchDepth, batchedHandlers } from "../batch.js";
|
|
1
|
+
import { ACTIVE, BATCH, NAME_COMPUTED } from "../constants.js";
|
|
2
|
+
import { effect, runEffect } from "../effect.js";
|
|
4
3
|
import { Reactive } from "./reactive.js";
|
|
5
|
-
let activeComputed;
|
|
6
4
|
var Computed = class extends Reactive {
|
|
7
5
|
effect = {
|
|
8
6
|
dirty: true,
|
|
9
7
|
instance: void 0
|
|
10
8
|
};
|
|
11
9
|
constructor(callback, options) {
|
|
12
|
-
super(
|
|
10
|
+
super(NAME_COMPUTED, void 0, options);
|
|
13
11
|
this.effect.instance = effect(() => {
|
|
14
|
-
if (this.effect.dirty)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
this.effect.dirty = false;
|
|
12
|
+
if (!this.effect.dirty) return;
|
|
13
|
+
const previousComputed = ACTIVE.computed;
|
|
14
|
+
ACTIVE.computed = this;
|
|
15
|
+
const value = callback();
|
|
16
|
+
ACTIVE.computed = previousComputed;
|
|
17
|
+
if (!this.state.equal(this.state.value, value)) {
|
|
18
|
+
this.state.value = value;
|
|
19
|
+
for (const computed$1 of this.state.computeds) computed$1.effect.dirty = true;
|
|
20
|
+
for (const effect$1 of this.state.effects) BATCH.handlers.add(effect$1);
|
|
21
|
+
for (const [, subscription] of this.state.subscriptions) subscription.callback(value);
|
|
26
22
|
}
|
|
23
|
+
this.effect.dirty = false;
|
|
27
24
|
});
|
|
28
25
|
}
|
|
29
26
|
get() {
|
|
30
|
-
if (
|
|
31
|
-
if (
|
|
32
|
-
if (this.effect.dirty &&
|
|
27
|
+
if (ACTIVE.computed != null && this !== ACTIVE.computed) this.state.computeds.add(ACTIVE.computed);
|
|
28
|
+
if (ACTIVE.effect != null && ACTIVE.effect !== this.effect.instance) this.state.effects.add(ACTIVE.effect);
|
|
29
|
+
if (this.effect.dirty && BATCH.depth === 0) runEffect(this.effect.instance);
|
|
33
30
|
return this.state.value;
|
|
34
31
|
}
|
|
35
32
|
};
|
|
36
33
|
function computed(callback, options) {
|
|
37
34
|
return new Computed(callback, options);
|
|
38
35
|
}
|
|
39
|
-
export { Computed,
|
|
36
|
+
export { Computed, computed };
|
package/dist/value/signal.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { NAME_SIGNAL } from "../constants.js";
|
|
2
2
|
import { Reactive } from "./reactive.js";
|
|
3
3
|
import { emitValue, getValue } from "../helpers/value.js";
|
|
4
4
|
var Signal = class extends Reactive {
|
|
5
5
|
constructor(value, options) {
|
|
6
|
-
super(
|
|
6
|
+
super(NAME_SIGNAL, value, options);
|
|
7
7
|
}
|
|
8
8
|
get() {
|
|
9
9
|
return getValue(this.state);
|
package/dist/value/store.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { noop, subscribe } from "../subscription.js";
|
|
2
|
-
import {
|
|
2
|
+
import { NAME_STORE } from "../constants.js";
|
|
3
3
|
import { Reactive } from "./reactive.js";
|
|
4
4
|
import { getValue } from "../helpers/value.js";
|
|
5
5
|
import { getReactiveValueInProxy, setProxyValue, setValueInProxy } from "../helpers/proxy.js";
|
|
@@ -7,7 +7,13 @@ import { isKey, isPlainObject } from "@oscarpalmer/atoms/is";
|
|
|
7
7
|
var Store = class extends Reactive {
|
|
8
8
|
#keyed = /* @__PURE__ */ new Map();
|
|
9
9
|
constructor(value, options) {
|
|
10
|
-
super(
|
|
10
|
+
super(NAME_STORE, new Proxy(value, { set: (target, property, value$1) => setValueInProxy({
|
|
11
|
+
target,
|
|
12
|
+
property,
|
|
13
|
+
value: value$1,
|
|
14
|
+
isArray: false,
|
|
15
|
+
state: this.state
|
|
16
|
+
}) }), options);
|
|
11
17
|
}
|
|
12
18
|
get(key) {
|
|
13
19
|
return isKey(key) ? getReactiveValueInProxy(this, this.#keyed, key, false).get() : getValue(this.state);
|
package/package.json
CHANGED
|
@@ -4,23 +4,22 @@
|
|
|
4
4
|
"url": "https://oscarpalmer.se"
|
|
5
5
|
},
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@oscarpalmer/atoms": "^0.
|
|
7
|
+
"@oscarpalmer/atoms": "^0.114"
|
|
8
8
|
},
|
|
9
9
|
"description": "Signals and stuff…",
|
|
10
10
|
"devDependencies": {
|
|
11
|
-
"@biomejs/biome": "^2.
|
|
11
|
+
"@biomejs/biome": "^2.3",
|
|
12
12
|
"@rollup/plugin-node-resolve": "^16",
|
|
13
|
-
"@rollup/plugin-typescript": "^12.
|
|
14
|
-
"@types/node": "^24.
|
|
15
|
-
"@vitest/coverage-istanbul": "^
|
|
16
|
-
"dts-bundle-generator": "^9.5",
|
|
13
|
+
"@rollup/plugin-typescript": "^12.3",
|
|
14
|
+
"@types/node": "^24.9",
|
|
15
|
+
"@vitest/coverage-istanbul": "^4",
|
|
17
16
|
"glob": "^11",
|
|
18
|
-
"jsdom": "^
|
|
19
|
-
"rollup": "^4.
|
|
17
|
+
"jsdom": "^27.1",
|
|
18
|
+
"rollup": "^4.52",
|
|
20
19
|
"tslib": "^2.8",
|
|
21
20
|
"typescript": "^5.9",
|
|
22
21
|
"vite": "npm:rolldown-vite@latest",
|
|
23
|
-
"vitest": "^
|
|
22
|
+
"vitest": "^4"
|
|
24
23
|
},
|
|
25
24
|
"exports": {
|
|
26
25
|
"./package.json": "./package.json",
|
|
@@ -48,5 +47,5 @@
|
|
|
48
47
|
},
|
|
49
48
|
"type": "module",
|
|
50
49
|
"types": "./types/index.d.ts",
|
|
51
|
-
"version": "0.
|
|
50
|
+
"version": "0.23.0"
|
|
52
51
|
}
|
package/src/batch.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {BATCH} from './constants';
|
|
2
|
+
import {runEffect} from './effect';
|
|
2
3
|
import {isEffect} from './helpers/is';
|
|
3
|
-
import type {Subscription} from './subscription';
|
|
4
4
|
|
|
5
5
|
export function flushHandlers(): void {
|
|
6
|
-
while (
|
|
7
|
-
const handlers = [...
|
|
6
|
+
while (BATCH.depth === 0 && BATCH.handlers.size > 0) {
|
|
7
|
+
const handlers = [...BATCH.handlers];
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
BATCH.handlers.clear();
|
|
10
10
|
|
|
11
11
|
for (const handler of handlers) {
|
|
12
12
|
if (isEffect(handler)) {
|
|
@@ -19,23 +19,21 @@ export function flushHandlers(): void {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
* Start batching effects
|
|
22
|
+
* Start batching effects
|
|
23
|
+
*
|
|
24
|
+
* _(Use {@link stopBatch} to flush and run batched effects)_
|
|
23
25
|
*/
|
|
24
26
|
export function startBatch(): void {
|
|
25
|
-
|
|
27
|
+
BATCH.depth += 1;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
/**
|
|
29
31
|
* Stop batching effects and flush _(run)_ them
|
|
30
32
|
*/
|
|
31
33
|
export function stopBatch(): void {
|
|
32
|
-
if (
|
|
33
|
-
|
|
34
|
+
if (BATCH.depth > 0) {
|
|
35
|
+
BATCH.depth -= 1;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
flushHandlers();
|
|
37
39
|
}
|
|
38
|
-
|
|
39
|
-
export const batchedHandlers = new Set<Effect | Subscription>();
|
|
40
|
-
|
|
41
|
-
export let batchDepth = 0;
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type {Effect} from './effect';
|
|
2
|
+
import type {Active, Batch} from './models';
|
|
3
|
+
import type {Subscription} from './subscription';
|
|
4
|
+
|
|
5
|
+
export const ACTIVE: Active = {};
|
|
6
|
+
|
|
7
|
+
export const ARRAY_THRESHOLD = 100;
|
|
8
|
+
|
|
9
|
+
export const ARRAY_OFFSET = 25;
|
|
10
|
+
|
|
11
|
+
export const ARRAY_PEEK = 10;
|
|
12
|
+
|
|
13
|
+
export const BATCH: Batch = {
|
|
14
|
+
depth: 0,
|
|
15
|
+
handlers: new Set<Effect | Subscription>(),
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const METHODS_AFFECTING_LENGTH: Set<string> = new Set<string>([
|
|
19
|
+
'pop',
|
|
20
|
+
'push',
|
|
21
|
+
'shift',
|
|
22
|
+
'unshift',
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
export const METHODS_UPDATE: Set<string> = new Set<string>([
|
|
26
|
+
...METHODS_AFFECTING_LENGTH,
|
|
27
|
+
'copyWithin',
|
|
28
|
+
'fill',
|
|
29
|
+
'reverse',
|
|
30
|
+
'sort',
|
|
31
|
+
'splice',
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
export const NAME_ARRAY = 'array';
|
|
35
|
+
|
|
36
|
+
export const NAME_COMPUTED = 'computed';
|
|
37
|
+
|
|
38
|
+
export const NAME_EFFECT = 'effect';
|
|
39
|
+
|
|
40
|
+
export const NAME_SIGNAL = 'signal';
|
|
41
|
+
|
|
42
|
+
export const NAME_STORE = 'store';
|
|
43
|
+
|
|
44
|
+
export const NAMES: Set<string> = new Set([
|
|
45
|
+
NAME_ARRAY,
|
|
46
|
+
NAME_COMPUTED,
|
|
47
|
+
NAME_SIGNAL,
|
|
48
|
+
NAME_STORE,
|
|
49
|
+
]);
|
package/src/effect.ts
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import type {GenericCallback} from '@oscarpalmer/atoms/models';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
type EffectState = {
|
|
5
|
-
callback: GenericCallback;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
type InternalEffect = {
|
|
9
|
-
state: EffectState;
|
|
10
|
-
};
|
|
2
|
+
import {ACTIVE, NAME_EFFECT} from './constants';
|
|
3
|
+
import type {EffectState, InternalEffect} from './models';
|
|
11
4
|
|
|
12
5
|
export class Effect {
|
|
13
6
|
private declare readonly $mora: string;
|
|
@@ -16,7 +9,7 @@ export class Effect {
|
|
|
16
9
|
|
|
17
10
|
constructor(callback: GenericCallback) {
|
|
18
11
|
Object.defineProperty(this, '$mora', {
|
|
19
|
-
value:
|
|
12
|
+
value: NAME_EFFECT,
|
|
20
13
|
});
|
|
21
14
|
|
|
22
15
|
this.state = {
|
|
@@ -28,22 +21,24 @@ export class Effect {
|
|
|
28
21
|
}
|
|
29
22
|
|
|
30
23
|
export function runEffect(effect: Effect): void {
|
|
31
|
-
const previousEffect =
|
|
24
|
+
const previousEffect = ACTIVE.effect;
|
|
32
25
|
|
|
33
|
-
|
|
26
|
+
ACTIVE.effect = effect;
|
|
34
27
|
|
|
35
28
|
try {
|
|
36
29
|
(effect as unknown as InternalEffect).state.callback();
|
|
37
30
|
} finally {
|
|
38
|
-
|
|
31
|
+
ACTIVE.effect = previousEffect;
|
|
39
32
|
}
|
|
40
33
|
}
|
|
41
34
|
|
|
42
35
|
/**
|
|
43
36
|
* Create an effect
|
|
37
|
+
* @param callback Callback for handling signal effects
|
|
38
|
+
* @returns Effect
|
|
44
39
|
*/
|
|
45
40
|
export function effect(callback: GenericCallback): Effect {
|
|
46
|
-
return
|
|
41
|
+
return typeof callback === 'function'
|
|
42
|
+
? new Effect(callback)
|
|
43
|
+
: (undefined as never);
|
|
47
44
|
}
|
|
48
|
-
|
|
49
|
-
export let activeEffect: Effect | undefined;
|
package/src/helpers/is.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import type {PlainObject} from '@oscarpalmer/atoms/models';
|
|
2
|
+
import {
|
|
3
|
+
NAME_ARRAY,
|
|
4
|
+
NAME_COMPUTED,
|
|
5
|
+
NAME_EFFECT,
|
|
6
|
+
NAME_SIGNAL,
|
|
7
|
+
NAME_STORE,
|
|
8
|
+
NAMES,
|
|
9
|
+
} from '../constants';
|
|
2
10
|
import type {Effect} from '../effect';
|
|
3
11
|
import type {ReactiveArray} from '../value/array';
|
|
4
12
|
import type {Computed} from '../value/computed';
|
|
@@ -8,23 +16,29 @@ import type {Store} from '../value/store';
|
|
|
8
16
|
|
|
9
17
|
/**
|
|
10
18
|
* Is the value a reactive array?
|
|
19
|
+
* @param value Value to check
|
|
20
|
+
* @returns True if value is a {@link ReactiveArray}
|
|
11
21
|
*/
|
|
12
|
-
export function isArray(value: unknown): value is ReactiveArray<
|
|
13
|
-
return isMora<ReactiveArray<
|
|
22
|
+
export function isArray<Item>(value: unknown): value is ReactiveArray<Item> {
|
|
23
|
+
return isMora<ReactiveArray<Item>>(value, NAME_ARRAY);
|
|
14
24
|
}
|
|
15
25
|
|
|
16
26
|
/**
|
|
17
27
|
* Is the value a computed signal?
|
|
28
|
+
* @param value Value to check
|
|
29
|
+
* @returns True if value is a {@link Computed}
|
|
18
30
|
*/
|
|
19
|
-
export function isComputed(value: unknown): value is Computed<
|
|
20
|
-
return isMora<Computed<
|
|
31
|
+
export function isComputed<Value>(value: unknown): value is Computed<Value> {
|
|
32
|
+
return isMora<Computed<Value>>(value, NAME_COMPUTED);
|
|
21
33
|
}
|
|
22
34
|
|
|
23
35
|
/**
|
|
24
36
|
* Is the value an effect?
|
|
37
|
+
* @param value Value to check
|
|
38
|
+
* @returns True if value is an {@link Effect}
|
|
25
39
|
*/
|
|
26
40
|
export function isEffect(value: unknown): value is Effect {
|
|
27
|
-
return isMora<Effect>(value,
|
|
41
|
+
return isMora<Effect>(value, NAME_EFFECT);
|
|
28
42
|
}
|
|
29
43
|
|
|
30
44
|
function isMora<T>(value: unknown, name: string | Set<string>): value is T {
|
|
@@ -38,25 +52,33 @@ function isMora<T>(value: unknown, name: string | Set<string>): value is T {
|
|
|
38
52
|
);
|
|
39
53
|
}
|
|
40
54
|
|
|
41
|
-
|
|
42
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Is the value reactive?
|
|
57
|
+
* @param value Value to check
|
|
58
|
+
* @returns True if value is a {@link Reactive}
|
|
59
|
+
*/
|
|
60
|
+
export function isReactive<Value, Equal = Value>(
|
|
61
|
+
value: unknown,
|
|
62
|
+
): value is Reactive<Value, Equal> {
|
|
63
|
+
return isMora<Reactive<Value, Equal>>(value, NAMES);
|
|
43
64
|
}
|
|
44
65
|
|
|
45
66
|
/**
|
|
46
67
|
* Is the value a signal?
|
|
68
|
+
* @param value Value to check
|
|
69
|
+
* @returns True if value is a {@link Signal}
|
|
47
70
|
*/
|
|
48
|
-
export function isSignal(value: unknown): value is Signal<
|
|
49
|
-
return isMora<Signal<
|
|
71
|
+
export function isSignal<Value>(value: unknown): value is Signal<Value> {
|
|
72
|
+
return isMora<Signal<Value>>(value, NAME_SIGNAL);
|
|
50
73
|
}
|
|
51
74
|
|
|
52
|
-
|
|
53
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Is the value a reactive store?
|
|
77
|
+
* @param value Value to check
|
|
78
|
+
* @returns True if value is a {@link Store}
|
|
79
|
+
*/
|
|
80
|
+
export function isStore<Value extends PlainObject>(
|
|
81
|
+
value: unknown,
|
|
82
|
+
): value is Store<Value> {
|
|
83
|
+
return isMora<Store<Value>>(value, NAME_STORE);
|
|
54
84
|
}
|
|
55
|
-
|
|
56
|
-
export const arrayName = 'array';
|
|
57
|
-
export const computedName = 'computed';
|
|
58
|
-
export const effectName = 'effect';
|
|
59
|
-
export const signalName = 'signal';
|
|
60
|
-
export const storeName = 'store';
|
|
61
|
-
|
|
62
|
-
const reactiveNames = new Set([arrayName, computedName, signalName, storeName]);
|
package/src/helpers/proxy.ts
CHANGED
|
@@ -4,50 +4,49 @@ import type {
|
|
|
4
4
|
PlainObject,
|
|
5
5
|
} from '@oscarpalmer/atoms/models';
|
|
6
6
|
import {startBatch, stopBatch} from '../batch';
|
|
7
|
+
import type {SetValueInProxyParameters} from '../models';
|
|
7
8
|
import type {ReactiveArray} from '../value/array';
|
|
8
9
|
import {type Computed, computed} from '../value/computed';
|
|
9
|
-
import type {ReactiveState} from '../value/reactive';
|
|
10
|
-
import type {Signal} from '../value/signal';
|
|
11
10
|
import type {Store} from '../value/store';
|
|
12
11
|
import {emitValue} from './value';
|
|
13
12
|
|
|
14
13
|
export function getReactiveValueInProxy<Value>(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
array: ReactiveArray<Value>,
|
|
15
|
+
mapped: Map<Key, Computed<unknown>>,
|
|
16
|
+
index: number,
|
|
17
|
+
isArray: true,
|
|
18
|
+
): Computed<unknown>;
|
|
20
19
|
|
|
21
20
|
export function getReactiveValueInProxy<Value extends PlainObject>(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
store: Store<Value>,
|
|
22
|
+
mapped: Map<Key, Computed<unknown>>,
|
|
23
|
+
key: Key,
|
|
24
|
+
isArray: false,
|
|
25
|
+
): Computed<unknown>;
|
|
27
26
|
|
|
28
27
|
export function getReactiveValueInProxy(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
mapped.set(key, item);
|
|
46
|
-
}
|
|
28
|
+
reactive: ReactiveArray<unknown> | Store<PlainObject>,
|
|
29
|
+
mapped: Map<Key, Computed<unknown>>,
|
|
30
|
+
key: Key,
|
|
31
|
+
isArray: boolean,
|
|
32
|
+
): Computed<unknown> {
|
|
33
|
+
let item = mapped.get(key);
|
|
34
|
+
|
|
35
|
+
if (item == null) {
|
|
36
|
+
item = computed(() => {
|
|
37
|
+
const value = reactive.get();
|
|
38
|
+
|
|
39
|
+
return isArray
|
|
40
|
+
? (value as unknown[]).at(key as number)
|
|
41
|
+
: (value as PlainObject)[key];
|
|
42
|
+
}) as Computed<unknown>;
|
|
47
43
|
|
|
48
|
-
|
|
44
|
+
mapped.set(key, item);
|
|
49
45
|
}
|
|
50
46
|
|
|
47
|
+
return item;
|
|
48
|
+
}
|
|
49
|
+
|
|
51
50
|
export function setProxyValue(
|
|
52
51
|
proxy: ArrayOrPlainObject,
|
|
53
52
|
value: ArrayOrPlainObject,
|
|
@@ -83,18 +82,15 @@ export function setProxyValue(
|
|
|
83
82
|
}
|
|
84
83
|
|
|
85
84
|
export function setValueInProxy<Value extends ArrayOrPlainObject, Equal>(
|
|
86
|
-
|
|
87
|
-
property: PropertyKey,
|
|
88
|
-
value: unknown,
|
|
89
|
-
state: ReactiveState<Value, Equal>,
|
|
90
|
-
isArray: boolean,
|
|
91
|
-
length?: Signal<number>,
|
|
85
|
+
parameters: SetValueInProxyParameters<Value, Equal>,
|
|
92
86
|
): boolean {
|
|
87
|
+
const {isArray, length, property, state, target, value} = parameters;
|
|
88
|
+
|
|
93
89
|
if (isArray) {
|
|
94
90
|
const isIndex = !Number.isNaN(Number(property));
|
|
95
91
|
const isLength = property === 'length';
|
|
96
92
|
|
|
97
|
-
if (!isIndex
|
|
93
|
+
if (!(isIndex || isLength)) {
|
|
98
94
|
return Reflect.set(target, property, value);
|
|
99
95
|
}
|
|
100
96
|
}
|
package/src/helpers/value.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {flushHandlers} from '../batch';
|
|
2
|
+
import {
|
|
3
|
+
ACTIVE,
|
|
4
|
+
ARRAY_OFFSET,
|
|
5
|
+
ARRAY_PEEK,
|
|
6
|
+
ARRAY_THRESHOLD,
|
|
7
|
+
BATCH,
|
|
8
|
+
} from '../constants';
|
|
9
|
+
import type {InternalComputed, ReactiveState} from '../models';
|
|
5
10
|
|
|
6
11
|
export function emitValue<Value>(state: ReactiveState<Value, never>): void {
|
|
7
12
|
for (const computed of state.computeds) {
|
|
@@ -9,14 +14,14 @@ export function emitValue<Value>(state: ReactiveState<Value, never>): void {
|
|
|
9
14
|
}
|
|
10
15
|
|
|
11
16
|
for (const effect of state.effects) {
|
|
12
|
-
|
|
17
|
+
BATCH.handlers.add(effect);
|
|
13
18
|
}
|
|
14
19
|
|
|
15
20
|
for (const [, subscription] of state.subscriptions) {
|
|
16
|
-
|
|
21
|
+
BATCH.handlers.add(subscription);
|
|
17
22
|
}
|
|
18
23
|
|
|
19
|
-
if (
|
|
24
|
+
if (BATCH.depth === 0) {
|
|
20
25
|
flushHandlers();
|
|
21
26
|
}
|
|
22
27
|
}
|
|
@@ -34,9 +39,9 @@ export function equalArrays<Value>(
|
|
|
34
39
|
|
|
35
40
|
let offset = 0;
|
|
36
41
|
|
|
37
|
-
if (length >=
|
|
38
|
-
offset = Math.round(length /
|
|
39
|
-
offset = offset >
|
|
42
|
+
if (length >= ARRAY_THRESHOLD) {
|
|
43
|
+
offset = Math.round(length / ARRAY_PEEK);
|
|
44
|
+
offset = offset > ARRAY_OFFSET ? ARRAY_OFFSET : offset;
|
|
40
45
|
|
|
41
46
|
for (let index = 0; index < offset; index += 1) {
|
|
42
47
|
if (!state.equal(first[index], second[index])) {
|
|
@@ -57,12 +62,12 @@ export function equalArrays<Value>(
|
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
export function getValue<Value>(state: ReactiveState<Value, never>): Value {
|
|
60
|
-
if (
|
|
61
|
-
state.computeds.add(
|
|
65
|
+
if (ACTIVE.computed != null) {
|
|
66
|
+
state.computeds.add(ACTIVE.computed);
|
|
62
67
|
}
|
|
63
68
|
|
|
64
|
-
if (
|
|
65
|
-
state.effects.add(
|
|
69
|
+
if (ACTIVE.effect != null) {
|
|
70
|
+
state.effects.add(ACTIVE.effect);
|
|
66
71
|
}
|
|
67
72
|
|
|
68
73
|
return state.value;
|
package/src/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ export {
|
|
|
7
7
|
isReactive,
|
|
8
8
|
isSignal,
|
|
9
9
|
} from './helpers/is';
|
|
10
|
+
export type {Unsubscribe} from './models';
|
|
10
11
|
export {array, type ReactiveArray} from './value/array';
|
|
11
12
|
export {computed, type Computed} from './value/computed';
|
|
12
13
|
export type {Reactive} from './value/reactive';
|