@oscarpalmer/mora 0.26.0 → 0.28.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.d.mts +14 -0
- package/dist/{batch.js → batch.mjs} +13 -3
- package/dist/constants.d.mts +20 -0
- package/dist/{constants.js → constants.mjs} +2 -0
- package/dist/effect.d.mts +17 -0
- package/dist/{effect.js → effect.mjs} +11 -4
- package/dist/helpers/is.d.mts +47 -0
- package/dist/helpers/is.mjs +55 -0
- package/dist/helpers/proxy.d.mts +15 -0
- package/dist/helpers/proxy.mjs +72 -0
- package/dist/helpers/value.d.mts +8 -0
- package/dist/helpers/{value.js → value.mjs} +4 -2
- package/dist/index.d.mts +10 -0
- package/dist/index.mjs +8 -0
- package/dist/models.d.mts +62 -0
- package/dist/models.mjs +1 -0
- package/dist/{mora.full.js → mora.full.mjs} +206 -106
- package/dist/subscription.d.mts +15 -0
- package/dist/{subscription.js → subscription.mjs} +2 -0
- package/dist/value/array.d.mts +156 -0
- package/dist/value/array.mjs +165 -0
- package/dist/value/computed.d.mts +21 -0
- package/dist/value/computed.mjs +68 -0
- package/dist/value/reactive.d.mts +40 -0
- package/dist/value/{reactive.js → reactive.mjs} +25 -2
- package/dist/value/signal.d.mts +44 -0
- package/dist/value/signal.mjs +59 -0
- package/dist/value/store.d.mts +112 -0
- package/dist/value/store.mjs +81 -0
- package/package.json +41 -35
- package/src/constants.ts +1 -6
- package/src/helpers/is.ts +1 -1
- package/src/helpers/proxy.ts +64 -17
- package/src/models.ts +3 -1
- package/src/value/array.ts +79 -23
- package/src/value/computed.ts +65 -30
- package/src/value/signal.ts +77 -9
- package/src/value/store.ts +101 -12
- package/dist/helpers/is.js +0 -23
- package/dist/helpers/proxy.js +0 -48
- package/dist/index.js +0 -8
- package/dist/models.js +0 -0
- package/dist/value/array.js +0 -100
- package/dist/value/computed.js +0 -36
- package/dist/value/signal.js +0 -24
- package/dist/value/store.js +0 -43
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Reactive } from "./reactive.mjs";
|
|
2
|
+
import { ReactiveOptions, Unsubscribe } from "../models.mjs";
|
|
3
|
+
import { Key, PlainObject } from "@oscarpalmer/atoms/models";
|
|
4
|
+
|
|
5
|
+
//#region src/value/store.d.ts
|
|
6
|
+
declare class Store<Value extends PlainObject> extends Reactive<Value, Value> {
|
|
7
|
+
#private;
|
|
8
|
+
constructor(value: Value, options?: ReactiveOptions<Value>);
|
|
9
|
+
/**
|
|
10
|
+
* @inheritdoc
|
|
11
|
+
*/
|
|
12
|
+
get(): Value;
|
|
13
|
+
/**
|
|
14
|
+
* Get a value by key
|
|
15
|
+
* @param key Key of the value to get
|
|
16
|
+
* @returns Value for the specified key, or `undefined` if it doesn't exist
|
|
17
|
+
*/
|
|
18
|
+
get<Key extends keyof Value>(key: Key): Value[Key];
|
|
19
|
+
/**
|
|
20
|
+
* Get a value by key
|
|
21
|
+
* @param key Key of the value to get
|
|
22
|
+
* @returns Value for the specified key, or `undefined` if it doesn't exist
|
|
23
|
+
*/
|
|
24
|
+
get(key: Key): unknown;
|
|
25
|
+
/**
|
|
26
|
+
* Notify dependents of changes
|
|
27
|
+
*
|
|
28
|
+
* _This bypasses equality checks and will immediately notify dependents.
|
|
29
|
+
* Use this only if you're modifying nested data that would be ignored by equality checks._
|
|
30
|
+
*/
|
|
31
|
+
notify(): void;
|
|
32
|
+
/**
|
|
33
|
+
* Get the value _(without reactivity)_
|
|
34
|
+
* @returns The current value
|
|
35
|
+
*/
|
|
36
|
+
peek(): Value;
|
|
37
|
+
/**
|
|
38
|
+
* Get a value by key _(without reactivity)_
|
|
39
|
+
* @param key Key of the value to get
|
|
40
|
+
* @returns Value for the specified key, or `undefined` if it doesn't exist
|
|
41
|
+
*/
|
|
42
|
+
peek<Key extends keyof Value>(key: Key): Value[Key];
|
|
43
|
+
/**
|
|
44
|
+
* Get a value by key _(without reactivity)_
|
|
45
|
+
* @param key Key of the value to get
|
|
46
|
+
* @returns Value for the specified key, or `undefined` if it doesn't exist
|
|
47
|
+
*/
|
|
48
|
+
peek(key: Key): unknown;
|
|
49
|
+
/**
|
|
50
|
+
* Set the value
|
|
51
|
+
* @param value New value _(defaults to an empty object)_
|
|
52
|
+
*/
|
|
53
|
+
set(value?: Value | (() => null | undefined | Value | Promise<null | undefined | Value>) | Promise<null | undefined | Value>): void;
|
|
54
|
+
/**
|
|
55
|
+
* Set a value by key
|
|
56
|
+
* @param key Key of the value to set
|
|
57
|
+
* @param value New value
|
|
58
|
+
*/
|
|
59
|
+
set<Key extends keyof Value>(key: Key, value: Value[Key] | (() => Value[Key] | Promise<Value[Key]>) | Promise<Value[Key]>): void;
|
|
60
|
+
/**
|
|
61
|
+
* Set a value by key
|
|
62
|
+
* @param key Key of the value to set
|
|
63
|
+
* @param value New value
|
|
64
|
+
*/
|
|
65
|
+
set(key: Key, value: unknown): void;
|
|
66
|
+
/**
|
|
67
|
+
* @inheritdoc
|
|
68
|
+
*/
|
|
69
|
+
subscribe(callback: (value: Value) => void): Unsubscribe;
|
|
70
|
+
/**
|
|
71
|
+
* Subscribe to changes for a specific key
|
|
72
|
+
* @param key Key of the value to subscribe to
|
|
73
|
+
* @param callback Callback for changes
|
|
74
|
+
* @returns Unsubscribe callback
|
|
75
|
+
*/
|
|
76
|
+
subscribe<Key extends keyof Value>(key: Key, callback: (value: Value[Key] | undefined) => void): Unsubscribe;
|
|
77
|
+
/**
|
|
78
|
+
* Subscribe to changes for a specific key
|
|
79
|
+
* @param key Key of the value to subscribe to
|
|
80
|
+
* @param callback Callback for changes
|
|
81
|
+
* @returns Unsubscribe callback
|
|
82
|
+
*/
|
|
83
|
+
subscribe(key: Key, callback: (value: unknown) => void): Unsubscribe;
|
|
84
|
+
/**
|
|
85
|
+
* Update the value _(based on the current value)_
|
|
86
|
+
* @param callback Callback to update the value
|
|
87
|
+
*/
|
|
88
|
+
update(callback: (value: Value) => Value): void;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Create a reactive store from a function result
|
|
92
|
+
* @param value Initial object value
|
|
93
|
+
* @param options Reactivity options
|
|
94
|
+
* @returns Reactive store
|
|
95
|
+
*/
|
|
96
|
+
declare function store<Value extends PlainObject>(value: () => Value | Promise<Value>, options?: ReactiveOptions<Value>): Store<Value>;
|
|
97
|
+
/**
|
|
98
|
+
* Create a reactive store from a promise
|
|
99
|
+
* @param value Initial object value
|
|
100
|
+
* @param options Reactivity options
|
|
101
|
+
* @returns Reactive store
|
|
102
|
+
*/
|
|
103
|
+
declare function store<Value extends PlainObject>(value: Promise<Value>, options?: ReactiveOptions<Value>): Store<Value>;
|
|
104
|
+
/**
|
|
105
|
+
* Create a reactive store
|
|
106
|
+
* @param value Initial object value
|
|
107
|
+
* @param options Reactivity options
|
|
108
|
+
* @returns Reactive store
|
|
109
|
+
*/
|
|
110
|
+
declare function store<Value extends PlainObject>(value: Value, options?: ReactiveOptions<Value>): Store<Value>;
|
|
111
|
+
//#endregion
|
|
112
|
+
export { Store, store };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { NAME_STORE } from "../constants.mjs";
|
|
2
|
+
import { startBatch, stopBatch } from "../batch.mjs";
|
|
3
|
+
import { noop, subscribe } from "../subscription.mjs";
|
|
4
|
+
import { Reactive } from "./reactive.mjs";
|
|
5
|
+
import { getValue } from "../helpers/value.mjs";
|
|
6
|
+
import { emityProxyValues, getReactiveValueInProxy, setProxyValue, setValueInProxy } from "../helpers/proxy.mjs";
|
|
7
|
+
import { isKey, isPlainObject } from "@oscarpalmer/atoms/is";
|
|
8
|
+
//#region src/value/store.ts
|
|
9
|
+
var Store = class extends Reactive {
|
|
10
|
+
#keyed = /* @__PURE__ */ new Map();
|
|
11
|
+
constructor(value, options) {
|
|
12
|
+
super(NAME_STORE, new Proxy(value, { set: (target, property, value) => setValueInProxy({
|
|
13
|
+
target,
|
|
14
|
+
property,
|
|
15
|
+
value,
|
|
16
|
+
isArray: false,
|
|
17
|
+
state: this.state
|
|
18
|
+
}) }), options);
|
|
19
|
+
}
|
|
20
|
+
get(key) {
|
|
21
|
+
return isKey(key) ? getReactiveValueInProxy(this, this.#keyed, key, false).get() : getValue(this.state);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Notify dependents of changes
|
|
25
|
+
*
|
|
26
|
+
* _This bypasses equality checks and will immediately notify dependents.
|
|
27
|
+
* Use this only if you're modifying nested data that would be ignored by equality checks._
|
|
28
|
+
*/
|
|
29
|
+
notify() {
|
|
30
|
+
emityProxyValues(this.state, this.#keyed);
|
|
31
|
+
}
|
|
32
|
+
peek(key) {
|
|
33
|
+
return isKey(key) ? this.state.value[key] : { ...this.state.value };
|
|
34
|
+
}
|
|
35
|
+
set(first, second) {
|
|
36
|
+
setProxyValue(false, this.state, isStoreObject, isKey, setObject, setProperty, first, second);
|
|
37
|
+
}
|
|
38
|
+
subscribe(first, second) {
|
|
39
|
+
if (isKey(first) && typeof second === "function") return getReactiveValueInProxy(this, this.#keyed, first, false).subscribe(second);
|
|
40
|
+
return typeof first === "function" ? subscribe(this.state, first) : noop;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Update the value _(based on the current value)_
|
|
44
|
+
* @param callback Callback to update the value
|
|
45
|
+
*/
|
|
46
|
+
update(callback) {
|
|
47
|
+
const updated = callback(this.state.value);
|
|
48
|
+
if (updated == null || isPlainObject(updated)) setObject(this.state, updated);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
function isStoreObject(value) {
|
|
52
|
+
return value == null || isPlainObject(value);
|
|
53
|
+
}
|
|
54
|
+
function setObject(state, value) {
|
|
55
|
+
startBatch();
|
|
56
|
+
const actual = value ?? {};
|
|
57
|
+
const proxy = state.value;
|
|
58
|
+
const proxyKeys = Object.keys(proxy);
|
|
59
|
+
const actualKeys = Object.keys(actual);
|
|
60
|
+
let { length } = proxyKeys;
|
|
61
|
+
for (let index = 0; index < length; index += 1) {
|
|
62
|
+
const key = proxyKeys[index];
|
|
63
|
+
proxy[key] = actualKeys.includes(key) ? actual[key] : void 0;
|
|
64
|
+
}
|
|
65
|
+
length = actualKeys.length;
|
|
66
|
+
for (let index = 0; index < length; index += 1) {
|
|
67
|
+
const key = actualKeys[index];
|
|
68
|
+
if (!proxyKeys.includes(key)) proxy[key] = actual[key];
|
|
69
|
+
}
|
|
70
|
+
stopBatch();
|
|
71
|
+
}
|
|
72
|
+
function setProperty(state, key, value) {
|
|
73
|
+
state.value[key] = value;
|
|
74
|
+
}
|
|
75
|
+
function store(value, options) {
|
|
76
|
+
const instance = new Store({}, options);
|
|
77
|
+
instance.set(value);
|
|
78
|
+
return instance;
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
export { Store, store };
|
package/package.json
CHANGED
|
@@ -1,49 +1,55 @@
|
|
|
1
1
|
{
|
|
2
|
+
"name": "@oscarpalmer/mora",
|
|
3
|
+
"version": "0.28.0",
|
|
4
|
+
"description": "Signals and stuff…",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"reactive",
|
|
7
|
+
"signal",
|
|
8
|
+
"signals"
|
|
9
|
+
],
|
|
10
|
+
"license": "MIT",
|
|
2
11
|
"author": {
|
|
3
12
|
"name": "Oscar Palmér",
|
|
4
13
|
"url": "https://oscarpalmer.se"
|
|
5
14
|
},
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
"description": "Signals and stuff…",
|
|
10
|
-
"devDependencies": {
|
|
11
|
-
"@types/node": "^25",
|
|
12
|
-
"@vitest/coverage-istanbul": "^4",
|
|
13
|
-
"jsdom": "^27.3",
|
|
14
|
-
"oxfmt": "^0.21",
|
|
15
|
-
"oxlint": "^1.36",
|
|
16
|
-
"rolldown": "1.0.0-beta.58",
|
|
17
|
-
"tslib": "^2.8",
|
|
18
|
-
"typescript": "^5.9",
|
|
19
|
-
"vite": "8.0.0-beta.5",
|
|
20
|
-
"vitest": "^4"
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/oscarpalmer/mora.git"
|
|
21
18
|
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"src",
|
|
22
|
+
"types"
|
|
23
|
+
],
|
|
24
|
+
"type": "module",
|
|
25
|
+
"module": "./dist/index.mjs",
|
|
26
|
+
"types": "./dist/index.d.mts",
|
|
22
27
|
"exports": {
|
|
23
28
|
"./package.json": "./package.json",
|
|
24
29
|
".": {
|
|
25
|
-
"types": "./
|
|
26
|
-
"default": "./dist/index.
|
|
30
|
+
"types": "./dist/index.d.mts",
|
|
31
|
+
"default": "./dist/index.mjs"
|
|
27
32
|
}
|
|
28
33
|
},
|
|
29
|
-
"files": ["dist", "src", "types"],
|
|
30
|
-
"keywords": ["signal", "signals", "reactive"],
|
|
31
|
-
"license": "MIT",
|
|
32
|
-
"module": "./dist/index.js",
|
|
33
|
-
"name": "@oscarpalmer/mora",
|
|
34
|
-
"repository": {
|
|
35
|
-
"type": "git",
|
|
36
|
-
"url": "git+https://github.com/oscarpalmer/mora.git"
|
|
37
|
-
},
|
|
38
34
|
"scripts": {
|
|
39
|
-
"build": "
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"test": "
|
|
44
|
-
"watch": "npx vite build --watch"
|
|
35
|
+
"build": "vpx vp run tsdown:build && vpx vp pack",
|
|
36
|
+
"tsdown:build": "vpx tsdown -c ./tsdown.config.ts",
|
|
37
|
+
"tsdown:watch": "vpx tsdown -c ./tsdown.config.ts --watch",
|
|
38
|
+
"test": "vpx vp test run --coverage",
|
|
39
|
+
"test:leak": "vpx vp test run --detect-async-leaks --coverage"
|
|
45
40
|
},
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@oscarpalmer/atoms": "^0.172"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/node": "^25.5",
|
|
46
|
+
"@vitest/coverage-istanbul": "^4.1",
|
|
47
|
+
"jsdom": "^29",
|
|
48
|
+
"tsdown": "^0.21",
|
|
49
|
+
"typescript": "^5.9",
|
|
50
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
|
|
51
|
+
"vite-plus": "latest",
|
|
52
|
+
"vitest": "npm:@voidzero-dev/vite-plus-test@latest"
|
|
53
|
+
},
|
|
54
|
+
"packageManager": "npm@11.11.1"
|
|
49
55
|
}
|
package/src/constants.ts
CHANGED
|
@@ -15,12 +15,7 @@ export const BATCH: Batch = {
|
|
|
15
15
|
handlers: new Set<Effect | Subscription>(),
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
export const METHODS_AFFECTING_LENGTH = new Set<string>([
|
|
19
|
-
'pop',
|
|
20
|
-
'push',
|
|
21
|
-
'shift',
|
|
22
|
-
'unshift',
|
|
23
|
-
]);
|
|
18
|
+
export const METHODS_AFFECTING_LENGTH = new Set<string>(['pop', 'push', 'shift', 'unshift']);
|
|
24
19
|
|
|
25
20
|
export const METHODS_UPDATE = new Set<string>([
|
|
26
21
|
...METHODS_AFFECTING_LENGTH,
|
package/src/helpers/is.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type {PlainObject} from '@oscarpalmer/atoms/models';
|
|
2
2
|
import {
|
|
3
|
+
NAME_ALL,
|
|
3
4
|
NAME_ARRAY,
|
|
4
5
|
NAME_COMPUTED,
|
|
5
6
|
NAME_EFFECT,
|
|
6
7
|
NAME_MORA,
|
|
7
8
|
NAME_SIGNAL,
|
|
8
9
|
NAME_STORE,
|
|
9
|
-
NAME_ALL,
|
|
10
10
|
} from '../constants';
|
|
11
11
|
import type {Effect} from '../effect';
|
|
12
12
|
import type {ReactiveArray} from '../value/array';
|
package/src/helpers/proxy.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type {ArrayOrPlainObject, Key, PlainObject} from '@oscarpalmer/atoms/models';
|
|
2
|
-
import {startBatch, stopBatch} from '../batch';
|
|
3
2
|
import {PROPERTY_LENGTH} from '../constants';
|
|
4
3
|
import type {InternalComputed, ReactiveState, SetValueInProxyParameters} from '../models';
|
|
5
4
|
import type {ReactiveArray} from '../value/array';
|
|
@@ -66,33 +65,81 @@ export function getReactiveValueInProxy(
|
|
|
66
65
|
return item;
|
|
67
66
|
}
|
|
68
67
|
|
|
69
|
-
export function setProxyValue
|
|
70
|
-
|
|
68
|
+
export function setProxyValue<Value, Item = Value>(
|
|
69
|
+
array: boolean,
|
|
70
|
+
state: ReactiveState<Value, Item>,
|
|
71
|
+
isObject: (value: unknown) => value is Value | undefined,
|
|
72
|
+
isProperty: (value: unknown) => boolean,
|
|
73
|
+
setObject: (state: ReactiveState<Value, Item>, value: Value | undefined) => void,
|
|
74
|
+
setProperty: (state: ReactiveState<Value, Item>, property: unknown, value: Item) => void,
|
|
75
|
+
first?: unknown,
|
|
76
|
+
second?: unknown,
|
|
77
|
+
): void {
|
|
78
|
+
if (array && first === PROPERTY_LENGTH) {
|
|
79
|
+
(state.value as unknown[]).length = second as number;
|
|
71
80
|
|
|
72
|
-
|
|
73
|
-
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
74
83
|
|
|
75
|
-
|
|
84
|
+
if (isObject(first)) {
|
|
85
|
+
setObject(state, first);
|
|
76
86
|
|
|
77
|
-
|
|
78
|
-
const key = proxyKeys[index];
|
|
79
|
-
|
|
80
|
-
(proxy as PlainObject)[key] = valueKeys.includes(key) ? (value as PlainObject)[key] : undefined;
|
|
87
|
+
return;
|
|
81
88
|
}
|
|
82
89
|
|
|
83
|
-
|
|
90
|
+
const property = isProperty(first);
|
|
84
91
|
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
if (array && property && Number.isNaN(first)) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
87
95
|
|
|
88
|
-
|
|
89
|
-
const keyedValue = (value as PlainObject)[key];
|
|
96
|
+
let actual = property ? second : first;
|
|
90
97
|
|
|
91
|
-
|
|
98
|
+
if (typeof actual === 'function') {
|
|
99
|
+
try {
|
|
100
|
+
actual = (actual as Function)();
|
|
101
|
+
} catch {
|
|
102
|
+
return;
|
|
92
103
|
}
|
|
93
104
|
}
|
|
94
105
|
|
|
95
|
-
|
|
106
|
+
if (actual instanceof Promise) {
|
|
107
|
+
if (property) {
|
|
108
|
+
state.promises ??= new Map();
|
|
109
|
+
|
|
110
|
+
state.promises.set(first as Key, actual as Promise<never>);
|
|
111
|
+
} else {
|
|
112
|
+
state.promise = actual as Promise<never>;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
void actual
|
|
116
|
+
.then(value => {
|
|
117
|
+
if (property && state.promises!.get(first as Key) === actual) {
|
|
118
|
+
state.promises!.delete(first as Key);
|
|
119
|
+
|
|
120
|
+
setProperty(state, first, value);
|
|
121
|
+
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (!property && isObject(value) && state.promise === actual) {
|
|
126
|
+
state.promise = undefined;
|
|
127
|
+
|
|
128
|
+
setObject(state, value);
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
.catch(() => {
|
|
132
|
+
if (property && state.promises!.get(first as Key) === actual) {
|
|
133
|
+
state.promises!.delete(first as Key);
|
|
134
|
+
} else if (!property && state.promise === actual) {
|
|
135
|
+
state.promise = undefined;
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
} else if (property) {
|
|
139
|
+
setProperty(state, first, actual as Item);
|
|
140
|
+
} else if (isObject(actual)) {
|
|
141
|
+
setObject(state, actual);
|
|
142
|
+
}
|
|
96
143
|
}
|
|
97
144
|
|
|
98
145
|
export function setValueInProxy<Value extends ArrayOrPlainObject, Equal>(
|
package/src/models.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {GenericCallback} from '@oscarpalmer/atoms/models';
|
|
1
|
+
import type {GenericCallback, Key} from '@oscarpalmer/atoms/models';
|
|
2
2
|
import type {Effect} from './effect';
|
|
3
3
|
import type {Subscription} from './subscription';
|
|
4
4
|
import type {Computed} from './value/computed';
|
|
@@ -47,6 +47,8 @@ export type ReactiveState<Value, Equal> = {
|
|
|
47
47
|
computeds: Set<Computed<unknown>>;
|
|
48
48
|
effects: Set<Effect>;
|
|
49
49
|
equal: (first: Equal, second: Equal) => boolean;
|
|
50
|
+
promise?: Promise<Value>;
|
|
51
|
+
promises?: Map<Key, Promise<never>>;
|
|
50
52
|
subscriptions: Map<GenericCallback, Subscription>;
|
|
51
53
|
value: Value;
|
|
52
54
|
};
|
package/src/value/array.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type {GenericCallback} from '@oscarpalmer/atoms/models';
|
|
2
2
|
import {METHODS_AFFECTING_LENGTH, METHODS_UPDATE, NAME_ARRAY, PROPERTY_LENGTH} from '../constants';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
emityProxyValues,
|
|
5
|
+
getReactiveValueInProxy,
|
|
6
|
+
setProxyValue,
|
|
7
|
+
setValueInProxy,
|
|
8
|
+
} from '../helpers/proxy';
|
|
4
9
|
import {emitValue, equalArrays, getValue} from '../helpers/value';
|
|
5
10
|
import type {ReactiveOptions, ReactiveState, Unsubscribe} from '../models';
|
|
6
11
|
import {noop, subscribe} from '../subscription';
|
|
@@ -85,7 +90,7 @@ export class ReactiveArray<Item> extends Reactive<Item[], Item> {
|
|
|
85
90
|
* Get the length of the array
|
|
86
91
|
* @returns Length of the array
|
|
87
92
|
*/
|
|
88
|
-
get(property:
|
|
93
|
+
get(property: typeof PROPERTY_LENGTH): number;
|
|
89
94
|
|
|
90
95
|
get(first?: unknown): unknown {
|
|
91
96
|
if (typeof first === 'number') {
|
|
@@ -130,14 +135,14 @@ export class ReactiveArray<Item> extends Reactive<Item[], Item> {
|
|
|
130
135
|
* Get the length of the array _(without reactivity)_
|
|
131
136
|
* @returns Length of the array
|
|
132
137
|
*/
|
|
133
|
-
override peek(property:
|
|
138
|
+
override peek(property: typeof PROPERTY_LENGTH): number;
|
|
134
139
|
|
|
135
140
|
override peek(value?: unknown): unknown {
|
|
136
|
-
if (value ===
|
|
141
|
+
if (value === PROPERTY_LENGTH) {
|
|
137
142
|
return this.#size.peek();
|
|
138
143
|
}
|
|
139
144
|
|
|
140
|
-
return typeof value === 'number' ? this.state.value.at(value) :
|
|
145
|
+
return typeof value === 'number' ? this.state.value.at(value) : this.state.value.slice();
|
|
141
146
|
}
|
|
142
147
|
|
|
143
148
|
/**
|
|
@@ -161,29 +166,37 @@ export class ReactiveArray<Item> extends Reactive<Item[], Item> {
|
|
|
161
166
|
* Set the value
|
|
162
167
|
* @param value New array of items _(defaults to an empty array)_
|
|
163
168
|
*/
|
|
164
|
-
set(
|
|
169
|
+
set(
|
|
170
|
+
value?:
|
|
171
|
+
| Item[]
|
|
172
|
+
| (() => null | undefined | Item[] | Promise<null | undefined | Item[]>)
|
|
173
|
+
| Promise<null | undefined | Item[]>,
|
|
174
|
+
): void;
|
|
165
175
|
|
|
166
176
|
/**
|
|
167
177
|
* Set the value at an index
|
|
168
178
|
* @param index Index of item to set __(if negative, starts from the end)_
|
|
169
179
|
* @param value New item
|
|
170
180
|
*/
|
|
171
|
-
set(index: number, value: Item): void;
|
|
181
|
+
set(index: number, value: Item | (() => Item | Promise<Item>) | Promise<Item>): void;
|
|
172
182
|
|
|
173
183
|
/**
|
|
174
184
|
* Set the length of the array
|
|
175
185
|
* @param value New array length
|
|
176
186
|
*/
|
|
177
|
-
set(property:
|
|
178
|
-
|
|
179
|
-
set(first?:
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
+
set(property: typeof PROPERTY_LENGTH, value: number): void;
|
|
188
|
+
|
|
189
|
+
set(first?: unknown, second?: unknown): void {
|
|
190
|
+
setProxyValue<Item[], Item>(
|
|
191
|
+
true,
|
|
192
|
+
this.state,
|
|
193
|
+
isArrayValue,
|
|
194
|
+
isArrayIndex,
|
|
195
|
+
setArray,
|
|
196
|
+
setAtIndex,
|
|
197
|
+
first,
|
|
198
|
+
second,
|
|
199
|
+
);
|
|
187
200
|
}
|
|
188
201
|
|
|
189
202
|
/**
|
|
@@ -248,14 +261,53 @@ export class ReactiveArray<Item> extends Reactive<Item[], Item> {
|
|
|
248
261
|
}
|
|
249
262
|
}
|
|
250
263
|
|
|
264
|
+
/**
|
|
265
|
+
* Create a reactive array from a function result
|
|
266
|
+
* @param value Initial array of items
|
|
267
|
+
* @param options Reactivity options
|
|
268
|
+
* @returns Reactive array
|
|
269
|
+
*/
|
|
270
|
+
export function array<Item>(
|
|
271
|
+
value: () => Item[] | Promise<Item[]>,
|
|
272
|
+
options?: ReactiveOptions<Item>,
|
|
273
|
+
): ReactiveArray<Item>;
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Create a reactive array from a promise
|
|
277
|
+
* @param value Initial array of items
|
|
278
|
+
* @param options Reactivity options
|
|
279
|
+
* @returns Reactive array
|
|
280
|
+
*/
|
|
281
|
+
export function array<Item>(
|
|
282
|
+
value: Promise<Item[]>,
|
|
283
|
+
options?: ReactiveOptions<Item>,
|
|
284
|
+
): ReactiveArray<Item>;
|
|
285
|
+
|
|
251
286
|
/**
|
|
252
287
|
* Create a reactive array
|
|
253
288
|
* @param value Initial array of items
|
|
254
289
|
* @param options Reactivity options
|
|
255
290
|
* @returns Reactive array
|
|
256
291
|
*/
|
|
257
|
-
export function array<Item>(value: Item[], options?: ReactiveOptions<Item>): ReactiveArray<Item
|
|
258
|
-
|
|
292
|
+
export function array<Item>(value: Item[], options?: ReactiveOptions<Item>): ReactiveArray<Item>;
|
|
293
|
+
|
|
294
|
+
export function array<Item>(
|
|
295
|
+
value: Item[] | (() => Item[] | Promise<Item[]>) | Promise<Item[]>,
|
|
296
|
+
options?: ReactiveOptions<Item>,
|
|
297
|
+
): ReactiveArray<Item> {
|
|
298
|
+
const instance = new ReactiveArray([], options);
|
|
299
|
+
|
|
300
|
+
instance.set(value);
|
|
301
|
+
|
|
302
|
+
return instance;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function isArrayIndex(value: unknown): boolean {
|
|
306
|
+
return typeof value === 'number';
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function isArrayValue<Item>(value: unknown): value is Item[] | undefined {
|
|
310
|
+
return value == null || Array.isArray(value);
|
|
259
311
|
}
|
|
260
312
|
|
|
261
313
|
function updateArray<Item>(
|
|
@@ -265,7 +317,7 @@ function updateArray<Item>(
|
|
|
265
317
|
length: Signal<number>,
|
|
266
318
|
): unknown {
|
|
267
319
|
const affectsLength = METHODS_AFFECTING_LENGTH.has(type);
|
|
268
|
-
const previousArray = affectsLength ? [] :
|
|
320
|
+
const previousArray = affectsLength ? [] : array.slice();
|
|
269
321
|
const previousLength = array.length;
|
|
270
322
|
|
|
271
323
|
return (...args: unknown[]): unknown => {
|
|
@@ -283,10 +335,14 @@ function updateArray<Item>(
|
|
|
283
335
|
};
|
|
284
336
|
}
|
|
285
337
|
|
|
286
|
-
function
|
|
287
|
-
|
|
338
|
+
function setArray<Item>(state: ReactiveState<Item[], Item>, value: Item[] | undefined): void {
|
|
339
|
+
state.value.splice(0, state.value.length, ...(value ?? []));
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function setAtIndex<Item>(state: ReactiveState<Item[], Item>, index: unknown, value: Item): void {
|
|
343
|
+
const actual = (index as number) < 0 ? state.value.length + (index as number) : (index as number);
|
|
288
344
|
|
|
289
345
|
if (actual > -1) {
|
|
290
|
-
|
|
346
|
+
state.value[actual] = value;
|
|
291
347
|
}
|
|
292
348
|
}
|