riftt 1.0.0 → 1.0.1
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/rift.js +18 -0
- package/package.json +2 -2
- package/rollup.config.js +2 -3
- package/src/index.js +0 -56
- package/src/modules.js +0 -111
- package/src/router.js +0 -535
- package/src/sprae/core.js +0 -220
- package/src/sprae/directive/aria.js +0 -9
- package/src/sprae/directive/class.js +0 -28
- package/src/sprae/directive/data.js +0 -5
- package/src/sprae/directive/default.js +0 -210
- package/src/sprae/directive/each.js +0 -100
- package/src/sprae/directive/fx.js +0 -3
- package/src/sprae/directive/if.js +0 -46
- package/src/sprae/directive/ref.js +0 -13
- package/src/sprae/directive/style.js +0 -22
- package/src/sprae/directive/text.js +0 -13
- package/src/sprae/directive/value.js +0 -100
- package/src/sprae/directive/with.js +0 -20
- package/src/sprae/persist.js +0 -214
- package/src/sprae/signal.js +0 -106
- package/src/sprae/sprae.js +0 -20
- package/src/sprae/store.js +0 -197
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import sprae, { parse } from "../core.js";
|
|
2
|
-
import { dir } from "../core.js";
|
|
3
|
-
import { setter } from "../store.js";
|
|
4
|
-
import { attr } from "./default.js";
|
|
5
|
-
|
|
6
|
-
dir("value", (el, state, expr) => {
|
|
7
|
-
const type = el.type || "";
|
|
8
|
-
const tag = el.tagName;
|
|
9
|
-
|
|
10
|
-
const setText = (value) => {
|
|
11
|
-
const v = value ?? "";
|
|
12
|
-
el.value = v;
|
|
13
|
-
el.setAttribute("value", v);
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const textUpdate = (value) => setText(value);
|
|
17
|
-
|
|
18
|
-
const textareaUpdate = (value) => {
|
|
19
|
-
const from = el.selectionStart;
|
|
20
|
-
const to = el.selectionEnd;
|
|
21
|
-
setText(value);
|
|
22
|
-
if (from) el.setSelectionRange(from, to);
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const checkboxUpdate = (value) => {
|
|
26
|
-
el.checked = value;
|
|
27
|
-
attr(el, "checked", value);
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const radioUpdate = (value) => {
|
|
31
|
-
if (el.value === value) {
|
|
32
|
-
el.checked = value;
|
|
33
|
-
attr(el, "checked", value);
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const selectOneUpdate = (value) => {
|
|
38
|
-
for (const o of el.options) {
|
|
39
|
-
if (o.value == value) o.setAttribute("selected", "");
|
|
40
|
-
else o.removeAttribute("selected");
|
|
41
|
-
}
|
|
42
|
-
el.value = value;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const selectMultipleUpdate = (value) => {
|
|
46
|
-
for (const o of el.options) o.removeAttribute("selected");
|
|
47
|
-
for (const v of value) el.querySelector(`[value="${v}"]`)?.setAttribute("selected", "");
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const defaultUpdate = (value) => {
|
|
51
|
-
el.value = value;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const pickUpdater = () => {
|
|
55
|
-
if (tag === "TEXTAREA") return textareaUpdate;
|
|
56
|
-
switch (type) {
|
|
57
|
-
case "text":
|
|
58
|
-
case "":
|
|
59
|
-
return textUpdate;
|
|
60
|
-
case "checkbox":
|
|
61
|
-
return checkboxUpdate;
|
|
62
|
-
case "radio":
|
|
63
|
-
return radioUpdate;
|
|
64
|
-
case "select-one":
|
|
65
|
-
return selectOneUpdate;
|
|
66
|
-
case "select-multiple":
|
|
67
|
-
return selectMultipleUpdate;
|
|
68
|
-
default:
|
|
69
|
-
return defaultUpdate;
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const update = pickUpdater();
|
|
74
|
-
|
|
75
|
-
try {
|
|
76
|
-
const set = setter(expr);
|
|
77
|
-
|
|
78
|
-
const handleChange =
|
|
79
|
-
type === "checkbox"
|
|
80
|
-
? () => set(state, el.checked)
|
|
81
|
-
: type === "select-multiple"
|
|
82
|
-
? () =>
|
|
83
|
-
set(
|
|
84
|
-
state,
|
|
85
|
-
[...el.selectedOptions].map((o) => o.value),
|
|
86
|
-
)
|
|
87
|
-
: () => set(state, el.selectedIndex < 0 ? null : el.value);
|
|
88
|
-
|
|
89
|
-
el.oninput = el.onchange = handleChange;
|
|
90
|
-
|
|
91
|
-
if (type?.startsWith("select")) {
|
|
92
|
-
new MutationObserver(handleChange).observe(el, { childList: true, subtree: true, attributes: true });
|
|
93
|
-
sprae(el, state);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
parse(expr)(state) ?? handleChange();
|
|
97
|
-
} catch {}
|
|
98
|
-
|
|
99
|
-
return update;
|
|
100
|
-
});
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import sprae, { dir } from "../core.js";
|
|
2
|
-
import { untracked } from "../signal.js";
|
|
3
|
-
import store from "../store.js";
|
|
4
|
-
|
|
5
|
-
dir("with", (el, rootState) => {
|
|
6
|
-
let local;
|
|
7
|
-
|
|
8
|
-
return (values) => {
|
|
9
|
-
if (!local) {
|
|
10
|
-
// force untracked to avoid accidental subscriptions during setup
|
|
11
|
-
untracked(() => {
|
|
12
|
-
local = store(values, rootState);
|
|
13
|
-
sprae(el, local);
|
|
14
|
-
});
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
sprae(el, values);
|
|
19
|
-
};
|
|
20
|
-
});
|
package/src/sprae/persist.js
DELETED
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
import { effect } from "./signal.js";
|
|
2
|
-
import { _signals, _change } from "./store.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* symbol for accessing persistence configuration on a store
|
|
6
|
-
* @type {symbol}
|
|
7
|
-
*/
|
|
8
|
-
export const _persist = Symbol("persist");
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* persistence configuration options
|
|
12
|
-
* @typedef {Object} PersistOptions
|
|
13
|
-
* @property {string} [key] - localStorage key (defaults to store name or auto-generated)
|
|
14
|
-
* @property {Storage} [storage=localStorage] - storage interface (localStorage, sessionStorage, or custom)
|
|
15
|
-
* @property {string[]} [paths] - specific paths to persist (defaults to all)
|
|
16
|
-
* @property {number} [debounce=300] - debounce delay in milliseconds for writes
|
|
17
|
-
* @property {function} [serializer] - custom serializer for complex types
|
|
18
|
-
* @property {function} [deserializer] - custom deserializer for complex types
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* default serializer that handles common types
|
|
23
|
-
* @param {any} value - value to serialize
|
|
24
|
-
* @returns {string} serialized value
|
|
25
|
-
*/
|
|
26
|
-
const defaultSerializer = (value) => {
|
|
27
|
-
try {
|
|
28
|
-
return JSON.stringify(value, (key, val) => {
|
|
29
|
-
if (typeof val === "function") return undefined;
|
|
30
|
-
if (val && typeof val === "object" && val.constructor !== Object && !Array.isArray(val)) {
|
|
31
|
-
return undefined;
|
|
32
|
-
}
|
|
33
|
-
return val;
|
|
34
|
-
});
|
|
35
|
-
} catch {
|
|
36
|
-
return "{}";
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* default deserializer
|
|
42
|
-
* @param {string} value - serialized value
|
|
43
|
-
* @returns {any} deserialized value
|
|
44
|
-
*/
|
|
45
|
-
const defaultDeserializer = (value) => {
|
|
46
|
-
try {
|
|
47
|
-
return JSON.parse(value);
|
|
48
|
-
} catch {
|
|
49
|
-
return {};
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* creates a debounced function that delays execution
|
|
55
|
-
* @param {function} fn - function to debounce
|
|
56
|
-
* @param {number} delay - delay in milliseconds
|
|
57
|
-
* @returns {function} debounced function
|
|
58
|
-
*/
|
|
59
|
-
const debounce = (fn, delay) => {
|
|
60
|
-
let timeoutId;
|
|
61
|
-
return (...args) => {
|
|
62
|
-
clearTimeout(timeoutId);
|
|
63
|
-
timeoutId = setTimeout(() => fn(...args), delay);
|
|
64
|
-
};
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* extracts values from store at specified paths
|
|
69
|
-
* @param {Object} store - the store proxy
|
|
70
|
-
* @param {string[]} paths - paths to extract
|
|
71
|
-
* @returns {Object} extracted values
|
|
72
|
-
*/
|
|
73
|
-
const extractPaths = (store, paths) => {
|
|
74
|
-
const result = {};
|
|
75
|
-
|
|
76
|
-
if (!paths) {
|
|
77
|
-
const signals = store[_signals];
|
|
78
|
-
for (const key in signals) {
|
|
79
|
-
const signal = signals[key];
|
|
80
|
-
if (signal && !signal._set && key[0] !== "_") {
|
|
81
|
-
result[key] = store[key];
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
} else {
|
|
85
|
-
for (const path of paths) {
|
|
86
|
-
if (path in store) {
|
|
87
|
-
result[path] = store[path];
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return result;
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* checks if storage is available
|
|
97
|
-
* @param {Storage} storage - storage to test
|
|
98
|
-
* @returns {boolean} whether storage is available
|
|
99
|
-
*/
|
|
100
|
-
const isStorageAvailable = (storage) => {
|
|
101
|
-
try {
|
|
102
|
-
const testKey = "__storage_test__";
|
|
103
|
-
storage.setItem(testKey, "test");
|
|
104
|
-
storage.removeItem(testKey);
|
|
105
|
-
return true;
|
|
106
|
-
} catch {
|
|
107
|
-
return false;
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* creates persistence functionality for a store
|
|
113
|
-
* @param {Object} store - the store proxy
|
|
114
|
-
* @param {string} storeKey - unique key for the store
|
|
115
|
-
* @param {PersistOptions} options - persistence configuration
|
|
116
|
-
* @returns {function} cleanup function to stop persistence
|
|
117
|
-
*/
|
|
118
|
-
export const createPersistence = (store, storeKey, options = {}) => {
|
|
119
|
-
const config = {
|
|
120
|
-
key: storeKey,
|
|
121
|
-
storage: globalThis.localStorage,
|
|
122
|
-
paths: null,
|
|
123
|
-
debounce: 0,
|
|
124
|
-
serializer: defaultSerializer,
|
|
125
|
-
deserializer: defaultDeserializer,
|
|
126
|
-
...options,
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
if (!isStorageAvailable(config.storage)) {
|
|
130
|
-
console.warn(`persistence: ${config.storage === globalThis.localStorage ? "localStorage" : "storage"} is not available`);
|
|
131
|
-
return () => {};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
let isHydrating = false;
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* saves current store state to storage
|
|
138
|
-
*/
|
|
139
|
-
const saveState = debounce(() => {
|
|
140
|
-
if (isHydrating) return;
|
|
141
|
-
|
|
142
|
-
try {
|
|
143
|
-
const state = extractPaths(store, config.paths);
|
|
144
|
-
const serialized = config.serializer(state);
|
|
145
|
-
config.storage.setItem(config.key, serialized);
|
|
146
|
-
} catch (error) {
|
|
147
|
-
console.warn("persistence: failed to save state", error);
|
|
148
|
-
}
|
|
149
|
-
}, config.debounce);
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* restores state from storage
|
|
153
|
-
*/
|
|
154
|
-
const restoreState = () => {
|
|
155
|
-
try {
|
|
156
|
-
const stored = config.storage.getItem(config.key);
|
|
157
|
-
if (!stored) return;
|
|
158
|
-
|
|
159
|
-
const state = config.deserializer(stored);
|
|
160
|
-
if (!state || typeof state !== "object") return;
|
|
161
|
-
|
|
162
|
-
isHydrating = true;
|
|
163
|
-
|
|
164
|
-
for (const key in state) {
|
|
165
|
-
if (key in store[_signals] && !store[_signals][key]?._set) {
|
|
166
|
-
store[key] = state[key];
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
isHydrating = false;
|
|
171
|
-
} catch (error) {
|
|
172
|
-
console.warn("persistence: failed to restore state", error);
|
|
173
|
-
isHydrating = false;
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
restoreState();
|
|
178
|
-
|
|
179
|
-
const cleanup = effect(() => {
|
|
180
|
-
if (config.paths) {
|
|
181
|
-
for (const path of config.paths) {
|
|
182
|
-
store[path];
|
|
183
|
-
}
|
|
184
|
-
} else {
|
|
185
|
-
const signals = store[_signals];
|
|
186
|
-
for (const key in signals) {
|
|
187
|
-
const signal = signals[key];
|
|
188
|
-
if (signal && !signal._set && key[0] !== "_") {
|
|
189
|
-
store[key];
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
saveState();
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
store[_persist] = { config, saveState, restoreState };
|
|
197
|
-
|
|
198
|
-
return cleanup;
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* applies persistence to a store
|
|
203
|
-
* @param {Object} store - the store to persist
|
|
204
|
-
* @param {string} key - unique key for persistence
|
|
205
|
-
* @param {PersistOptions|boolean} options - persistence options or true for defaults
|
|
206
|
-
* @returns {function} cleanup function
|
|
207
|
-
*/
|
|
208
|
-
export const persist = (store, key, options = {}) => {
|
|
209
|
-
if (options === true) {
|
|
210
|
-
options = {};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return createPersistence(store, key, options);
|
|
214
|
-
};
|
package/src/sprae/signal.js
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
let current;
|
|
2
|
-
|
|
3
|
-
export let signal = (initialValue) => {
|
|
4
|
-
let value = initialValue;
|
|
5
|
-
const observers = new Set();
|
|
6
|
-
|
|
7
|
-
const signalObject = {
|
|
8
|
-
get value() {
|
|
9
|
-
if (current) {
|
|
10
|
-
current.deps.push(observers.add(current));
|
|
11
|
-
}
|
|
12
|
-
return value;
|
|
13
|
-
},
|
|
14
|
-
|
|
15
|
-
set value(newValue) {
|
|
16
|
-
if (newValue === value) return;
|
|
17
|
-
value = newValue;
|
|
18
|
-
for (const subscriber of observers) {
|
|
19
|
-
subscriber();
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
|
|
23
|
-
peek() {
|
|
24
|
-
return value;
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
signalObject.toJSON = () => signalObject.value;
|
|
29
|
-
signalObject.then = () => signalObject.value;
|
|
30
|
-
signalObject.toString = () => signalObject.value;
|
|
31
|
-
signalObject.valueOf = () => signalObject.value;
|
|
32
|
-
|
|
33
|
-
return signalObject;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export let effect = (effectFunction) => {
|
|
37
|
-
let teardownFunction;
|
|
38
|
-
let previousCurrent;
|
|
39
|
-
|
|
40
|
-
const effectExecutor = () => {
|
|
41
|
-
if (teardownFunction) {
|
|
42
|
-
teardownFunction.call();
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
previousCurrent = current;
|
|
46
|
-
current = effectExecutor;
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
teardownFunction = effectFunction();
|
|
50
|
-
} finally {
|
|
51
|
-
current = previousCurrent;
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
effectExecutor.deps = [];
|
|
56
|
-
effectExecutor();
|
|
57
|
-
|
|
58
|
-
return () => {
|
|
59
|
-
if (teardownFunction) {
|
|
60
|
-
teardownFunction.call();
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
let dependency;
|
|
64
|
-
while ((dependency = effectExecutor.deps.pop())) {
|
|
65
|
-
dependency.delete(effectExecutor);
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
export let computed = (computeFunction) => {
|
|
71
|
-
const internalSignal = signal();
|
|
72
|
-
let effectCleanup;
|
|
73
|
-
|
|
74
|
-
const computedObject = {
|
|
75
|
-
get value() {
|
|
76
|
-
if (!effectCleanup) {
|
|
77
|
-
effectCleanup = effect(() => {
|
|
78
|
-
internalSignal.value = computeFunction();
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
return internalSignal.value;
|
|
82
|
-
},
|
|
83
|
-
|
|
84
|
-
peek: internalSignal.peek,
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
computedObject.toJSON = () => computedObject.value;
|
|
88
|
-
computedObject.then = () => computedObject.value;
|
|
89
|
-
computedObject.toString = () => computedObject.value;
|
|
90
|
-
computedObject.valueOf = () => computedObject.value;
|
|
91
|
-
|
|
92
|
-
return computedObject;
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
export let batch = (fn) => fn();
|
|
96
|
-
|
|
97
|
-
export let untracked = batch;
|
|
98
|
-
|
|
99
|
-
// signals adapter - allows switching signals implementation and not depend on core
|
|
100
|
-
export let use = (impl) => {
|
|
101
|
-
signal = impl.signal;
|
|
102
|
-
effect = impl.effect;
|
|
103
|
-
computed = impl.computed;
|
|
104
|
-
batch = impl.batch || batch;
|
|
105
|
-
untracked = impl.untracked || untracked;
|
|
106
|
-
};
|
package/src/sprae/sprae.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import sprae from "./core.js";
|
|
2
|
-
|
|
3
|
-
// default directives
|
|
4
|
-
import "./directive/if.js";
|
|
5
|
-
import "./directive/each.js";
|
|
6
|
-
import "./directive/ref.js";
|
|
7
|
-
import "./directive/with.js";
|
|
8
|
-
import "./directive/text.js";
|
|
9
|
-
import "./directive/class.js";
|
|
10
|
-
import "./directive/style.js";
|
|
11
|
-
import "./directive/value.js";
|
|
12
|
-
import "./directive/fx.js";
|
|
13
|
-
import "./directive/default.js";
|
|
14
|
-
import "./directive/aria.js";
|
|
15
|
-
import "./directive/data.js";
|
|
16
|
-
|
|
17
|
-
// default compiler (indirect new Function to avoid detector)
|
|
18
|
-
sprae.use({ compile: (expr) => sprae.constructor(`with (arguments[0]) { return ${expr} };`) });
|
|
19
|
-
|
|
20
|
-
export default sprae;
|
package/src/sprae/store.js
DELETED
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
// signals-based proxy
|
|
2
|
-
import { signal, computed, batch } from "./signal.js";
|
|
3
|
-
import { parse } from "./core.js";
|
|
4
|
-
|
|
5
|
-
export const _signals = Symbol("signals");
|
|
6
|
-
export const _change = Symbol("change");
|
|
7
|
-
export const _stash = "__";
|
|
8
|
-
|
|
9
|
-
// length changing methods
|
|
10
|
-
const mutatingMethods = ["push", "pop", "shift", "unshift", "splice"];
|
|
11
|
-
|
|
12
|
-
// set/update signal value
|
|
13
|
-
const set = (signals, key, value) => {
|
|
14
|
-
const existingSignal = signals[key];
|
|
15
|
-
const currentValue = existingSignal?.peek();
|
|
16
|
-
|
|
17
|
-
if (key[0] === "_") {
|
|
18
|
-
signals[key] = value;
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (!existingSignal) {
|
|
23
|
-
signals[key] = value?.peek ? value : signal(store(value));
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (value === currentValue) {
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (existingSignal._set) {
|
|
32
|
-
existingSignal._set(value);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (Array.isArray(value) && Array.isArray(currentValue)) {
|
|
37
|
-
if (currentValue[_change]) {
|
|
38
|
-
batch(() => {
|
|
39
|
-
for (let i = 0; i < value.length; i++) {
|
|
40
|
-
currentValue[i] = value[i];
|
|
41
|
-
}
|
|
42
|
-
currentValue.length = value.length;
|
|
43
|
-
});
|
|
44
|
-
} else {
|
|
45
|
-
existingSignal.value = value;
|
|
46
|
-
}
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
existingSignal.value = store(value);
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
// object store is not lazy
|
|
54
|
-
export const store = (values, parent) => {
|
|
55
|
-
if (!values) {
|
|
56
|
-
return values;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (values[_signals] || values[Symbol.toStringTag]) {
|
|
60
|
-
return values;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (values.constructor !== Object) {
|
|
64
|
-
return Array.isArray(values) ? list(values) : values;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const signals = Object.create(parent?.[_signals] || {});
|
|
68
|
-
const lengthSignal = signal(Object.keys(values).length);
|
|
69
|
-
let stash;
|
|
70
|
-
|
|
71
|
-
const state = new Proxy(signals, {
|
|
72
|
-
get(target, key) {
|
|
73
|
-
if (key === _change) return lengthSignal;
|
|
74
|
-
if (key === _signals) return signals;
|
|
75
|
-
if (key === _stash) return stash;
|
|
76
|
-
if (key in signals) return signals[key]?.valueOf();
|
|
77
|
-
return globalThis[key];
|
|
78
|
-
},
|
|
79
|
-
|
|
80
|
-
set(target, key, value) {
|
|
81
|
-
if (key === _stash) {
|
|
82
|
-
stash = value;
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const existed = key in signals;
|
|
87
|
-
set(signals, key, value);
|
|
88
|
-
if (!existed) {
|
|
89
|
-
lengthSignal.value++;
|
|
90
|
-
}
|
|
91
|
-
return true;
|
|
92
|
-
},
|
|
93
|
-
|
|
94
|
-
deleteProperty(target, key) {
|
|
95
|
-
const signalToDelete = signals[key];
|
|
96
|
-
if (signalToDelete) {
|
|
97
|
-
signalToDelete[Symbol.dispose]?.();
|
|
98
|
-
delete signals[key];
|
|
99
|
-
lengthSignal.value--;
|
|
100
|
-
}
|
|
101
|
-
return true;
|
|
102
|
-
},
|
|
103
|
-
|
|
104
|
-
ownKeys(target) {
|
|
105
|
-
lengthSignal.value;
|
|
106
|
-
return Reflect.ownKeys(signals);
|
|
107
|
-
},
|
|
108
|
-
|
|
109
|
-
has(target, key) {
|
|
110
|
-
return true;
|
|
111
|
-
},
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
const descriptors = Object.getOwnPropertyDescriptors(values);
|
|
115
|
-
|
|
116
|
-
for (const key in values) {
|
|
117
|
-
const descriptor = descriptors[key];
|
|
118
|
-
|
|
119
|
-
if (descriptor?.get) {
|
|
120
|
-
const computedSignal = computed(descriptor.get.bind(state));
|
|
121
|
-
computedSignal._set = descriptor.set?.bind(state);
|
|
122
|
-
signals[key] = computedSignal;
|
|
123
|
-
} else {
|
|
124
|
-
signals[key] = null;
|
|
125
|
-
set(signals, key, values[key]);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return state;
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
// array store - signals are lazy since arrays can be very large & expensive
|
|
133
|
-
export const list = (values) => {
|
|
134
|
-
let lastAccessedProperty;
|
|
135
|
-
const lengthSignal = signal(values.length);
|
|
136
|
-
const signals = Array(values.length).fill();
|
|
137
|
-
|
|
138
|
-
const state = new Proxy(signals, {
|
|
139
|
-
get(target, key) {
|
|
140
|
-
if (typeof key === "symbol") {
|
|
141
|
-
if (key === _change) return lengthSignal;
|
|
142
|
-
if (key === _signals) return signals;
|
|
143
|
-
return signals[key];
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (key === "length") {
|
|
147
|
-
return mutatingMethods.includes(lastAccessedProperty) ? lengthSignal.peek() : lengthSignal.value;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
lastAccessedProperty = key;
|
|
151
|
-
|
|
152
|
-
if (!signals[key]) {
|
|
153
|
-
signals[key] = signal(store(values[key]));
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return signals[key].valueOf();
|
|
157
|
-
},
|
|
158
|
-
|
|
159
|
-
set(target, key, value) {
|
|
160
|
-
if (key === "length") {
|
|
161
|
-
for (let i = value; i < signals.length; i++) {
|
|
162
|
-
delete state[i];
|
|
163
|
-
}
|
|
164
|
-
lengthSignal.value = signals.length = value;
|
|
165
|
-
return true;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
set(signals, key, value);
|
|
169
|
-
|
|
170
|
-
if (key >= lengthSignal.peek()) {
|
|
171
|
-
lengthSignal.value = signals.length = +key + 1;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return true;
|
|
175
|
-
},
|
|
176
|
-
|
|
177
|
-
deleteProperty(target, key) {
|
|
178
|
-
signals[key]?.[Symbol.dispose]?.();
|
|
179
|
-
delete signals[key];
|
|
180
|
-
return true;
|
|
181
|
-
},
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
return state;
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
// create expression setter, reflecting value back to state
|
|
188
|
-
export const setter = (expression) => {
|
|
189
|
-
const fn = parse(`${expression}=${_stash}`);
|
|
190
|
-
|
|
191
|
-
return (state, value) => {
|
|
192
|
-
state[_stash] = value;
|
|
193
|
-
fn(state);
|
|
194
|
-
};
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
export default store;
|