sia-reactor 0.0.25 → 0.0.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -37
- package/dist/{TimeTravelOverlay-Bz2v9hov.d.cts → TimeTravelOverlay-DJJY5B9x.d.cts} +2 -2
- package/dist/{TimeTravelOverlay-CvTDJWpP.d.ts → TimeTravelOverlay-D_D3EW6s.d.ts} +2 -2
- package/dist/adapters/react.cjs +78 -40
- package/dist/adapters/react.d.cts +3 -3
- package/dist/adapters/react.d.ts +3 -3
- package/dist/adapters/react.js +6 -4
- package/dist/adapters/vanilla.cjs +210 -172
- package/dist/adapters/vanilla.d.cts +4 -4
- package/dist/adapters/vanilla.d.ts +4 -4
- package/dist/adapters/vanilla.js +6 -4
- package/dist/{chunk-3UHI7CNE.js → chunk-2EIKOZAD.js} +30 -17
- package/dist/{chunk-MWC3R7QL.js → chunk-3LIKXZ7X.js} +40 -15
- package/dist/chunk-3OT72G7R.js +39 -0
- package/dist/{chunk-P37ADJMM.js → chunk-IBAPWB27.js} +0 -37
- package/dist/chunk-NG3WWQV4.js +117 -0
- package/dist/chunk-PLYS4CVP.js +0 -0
- package/dist/{index-CB-IiZIB.d.cts → index-BLpfq517.d.cts} +26 -4
- package/dist/{index-CB-IiZIB.d.ts → index-BLpfq517.d.ts} +26 -4
- package/dist/index.cjs +30 -17
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -1
- package/dist/modules.cjs +82 -46
- package/dist/modules.d.cts +8 -13
- package/dist/modules.d.ts +8 -13
- package/dist/modules.js +24 -103
- package/dist/styles/time-travel-overlay.css +26 -2
- package/dist/super.d.ts +33 -17
- package/dist/super.global.js +110 -52
- package/dist/{timeTravel-D7NiYqdD.d.ts → timeTravel-CFSDXKcs.d.ts} +3 -5
- package/dist/{timeTravel-CEc3grUE.d.cts → timeTravel-CeKr5nq0.d.cts} +3 -5
- package/dist/utils.d.cts +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +9 -7
- package/package.json +10 -7
- /package/dist/{chunk-5A44QFT6.js → chunk-RVYL3OLW.js} +0 -0
package/README.md
CHANGED
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
[](https://www.npmjs.com/package/sia-reactor)
|
|
7
7
|
[](https://bundlephobia.com/package/sia-reactor)
|
|
8
|
+
[](https://github.com/Tobi007-del/sia-reactor)
|
|
8
9
|
|
|
9
|
-
[Live Demo & Benchmarks](https://tobi007-del.github.io/
|
|
10
|
+
[Live Demo & Benchmarks](https://tobi007-del.github.io/sia-reactor/index.html) | [Report Bug](https://github.com/Tobi007-del/sia-reactor/issues)
|
|
10
11
|
|
|
11
12
|
[Chronicles](https://github.com/Tobi007-del/tmg-media-player/blob/main/CHRONICLES.md) | [Interaction Folklore](https://github.com/Tobi007-del/tmg-media-player/blob/main/FOLKLORE.md)
|
|
12
13
|
|
|
@@ -135,14 +136,14 @@ The primary way to use the reactor is to wrap an object using `reactive(target,
|
|
|
135
136
|
*NOTE: `.` and `*` are engine reserved so don't use them as object keys*
|
|
136
137
|
|
|
137
138
|
```javascript
|
|
138
|
-
const
|
|
139
|
+
const store = reactive({ player: { volume: 50 } }, { smartCloning: true, referenceTracking: true }); // name it something other than `state` if intents will exist.
|
|
139
140
|
|
|
140
141
|
// Public API Methods are attached directly to the object with `reactive()`!
|
|
141
|
-
|
|
142
|
-
|
|
142
|
+
store.set("player.volume", (val) => Math.min(val, 100));
|
|
143
|
+
store.on("player.volume", (e) => console.log(e.value));
|
|
143
144
|
|
|
144
|
-
|
|
145
|
-
getReactor(
|
|
145
|
+
store.player.volume = 150; // Triggers mediation, clamps to 100, fires listener.
|
|
146
|
+
getReactor(store); store.__Reactor__; // Reference to the underlying reactor
|
|
146
147
|
```
|
|
147
148
|
|
|
148
149
|
Alternatively, you can instantiate the `Reactor` class directly to keep the API from interfering with your data or [try this](#reactive-preferences-method-naming):
|
|
@@ -195,42 +196,42 @@ const data = reactive({
|
|
|
195
196
|
|
|
196
197
|
### React Hooks & Effects
|
|
197
198
|
|
|
198
|
-
The engine provides native React bindings utilizing `useSyncExternalStore` and an internal `Autotracker` for concurrent-safe, surgically precise component re-renders. All hooks natively accept a `Reactor` instance, a `reactive()` proxy, or a plain object (which will be auto-wrapped on the fly). Just import, your editor will reveal more details.
|
|
199
|
+
The engine provides native React bindings utilizing `useSyncExternalStore` and an internal `Autotracker` for concurrent-safe, surgically precise component re-renders. All hooks natively accept a `Reactor` instance, a `reactive()` proxy, or a plain object (which will be auto-wrapped on the fly). Just import, your editor will reveal more details all round.
|
|
199
200
|
|
|
200
201
|
```javascript
|
|
201
202
|
import { reactive } from "sia-reactor";
|
|
202
203
|
import { useReactor, useAnyReactor, useSelector, useAnySelector, usePath, effect } from "sia-reactor/adapters/react";
|
|
203
204
|
|
|
204
|
-
const
|
|
205
|
+
const store = reactive({ user: { name: "Kosi", age: 25 }, theme: "dark" });
|
|
205
206
|
|
|
206
207
|
// 1. The Tracked State (Valtio-style)
|
|
207
208
|
function Profile() {
|
|
208
|
-
const
|
|
209
|
+
const sameStore = useReactor(store); // `useReactorSnapshot()` if mutable issues arise
|
|
209
210
|
useAnyReactor(); // when you just want state from any reactor
|
|
210
|
-
// Only re-renders if
|
|
211
|
-
return <div>{
|
|
211
|
+
// Only re-renders if store.user.name mutates. Completely ignores age and theme!
|
|
212
|
+
return <div>{sameStore.user.name + otherStore.user.name}</div>;
|
|
212
213
|
} // no snapshots like Valtio, you can read or write to anything
|
|
213
214
|
|
|
214
215
|
// 2. The Slice Selector (Zustand-style)
|
|
215
216
|
function Theme() {
|
|
216
|
-
const theme = useSelector(
|
|
217
|
-
const newName = useAnySelector(() =>
|
|
217
|
+
const theme = useSelector(store, (s) => s.theme); // `useSelectorSnapshot()` if mutable issues arise
|
|
218
|
+
const newName = useAnySelector(() => store.user.name + spouseStore.user.name); // when you just want to derive any state from any reactor
|
|
218
219
|
return <div>Theme: {theme}</div>;
|
|
219
220
|
}
|
|
220
221
|
|
|
221
222
|
// 3. The Direct Path Observer
|
|
222
223
|
function AgeObserver() {
|
|
223
|
-
const age = usePath(
|
|
224
|
+
const age = usePath(store, "user.age"); // pass in a normal object for an auto-scoped instance
|
|
224
225
|
return <div>Age: {age}</div>;
|
|
225
226
|
}
|
|
226
227
|
|
|
227
228
|
// 4. Vanilla Side Effects (Runs anywhere, framework agnostic)
|
|
228
|
-
const stopTracking = effect(() => console.log("User name changed to:",
|
|
229
|
+
const stopTracking = effect(() => console.log("User name changed to:", store.user.name)); // read or write as you wish
|
|
229
230
|
```
|
|
230
231
|
|
|
231
232
|
### Modules: The Extension Port
|
|
232
233
|
|
|
233
|
-
The `Reactor` is designed to be a lightweight core. Extended capabilities are attached via Modules.
|
|
234
|
+
The `Reactor` is designed to be a lightweight core. Extended capabilities are attached via Modules. Use `.attach(target: Reactor | Reactive<T>, id)` to chain reactors then `.setup(target, id)` which the `Reactor.use()` also calls to init, the `id` param will be direct keys on the final object, pass dotted paths to manipulate the shape.
|
|
234
235
|
|
|
235
236
|
#### The Persistence Module
|
|
236
237
|
Automatically syncs your State to LocalStorage, SessionStorage, Memory or IndexedDB. Always use this module first to avoid re-initialization issues.
|
|
@@ -239,31 +240,37 @@ Automatically syncs your State to LocalStorage, SessionStorage, Memory or Indexe
|
|
|
239
240
|
import { reactive, Reactor, getReactor } from "sia-reactor";
|
|
240
241
|
import { PersistModule, LocalStorageAdapter, IndexedDBAdapter, SessionStorageAdapter, CookieAdapter, MemoryAdapter } from "sia-reactor/modules";
|
|
241
242
|
|
|
242
|
-
const
|
|
243
|
-
const persist = new PersistModule({
|
|
244
|
-
key: "
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
243
|
+
const store = reactive({ theme: "dark", settings: { volume: 50, brightness: 30 } });
|
|
244
|
+
const persist = new PersistModule({
|
|
245
|
+
key: "APP_GLOBAL_STORE",
|
|
246
|
+
whitelist: ["theme", "settings.brightness"], // all paths if omitted, use object if multiple reactors
|
|
247
|
+
blacklist: ["settings.debug"], // optional excluded paths
|
|
248
|
+
throttle: 2500, // ms between saves
|
|
249
|
+
fanout: true, // async hydration use leaf writes to sync initialized listeners.
|
|
248
250
|
adapter: new IndexedDBAdapter({ dbName: "Session", version: 1, onversionchange: () => location.reload(), useSnapshot: true }) // or `LocalStorageAdapter` (instance or signature)
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
+
}, getReactor(store)); // `Reactor` in second arg for path inference
|
|
252
|
+
store.use(persist); // calls `.setup()`, use after all attachments, `id` is the second param too.
|
|
253
|
+
|
|
254
|
+
// Seperate attach sample if multiple reactors desired
|
|
255
|
+
persist.attach(uiStore, "ui").setup(appStore, "app"); // or paths: "app.ui"
|
|
256
|
+
persist.config.whitelist = { ui: ["settings.theme"], app: ["settings.volume"] }; // Multi-reactor filtering by id key. If you didn't pass ids, use implicit index keys: { "0": [...], "1": [...] }
|
|
257
|
+
|
|
251
258
|
```
|
|
252
259
|
|
|
253
260
|
#### The Time Travel Module
|
|
254
|
-
Record state frames, step through history, and optionally attach a ready-to-use vanilla debug overlay.
|
|
261
|
+
Record state frames, step through history, and optionally attach a ready-to-use vanilla debug overlay. Beware of paradoxes, seperate intent from state even in time.
|
|
255
262
|
|
|
256
263
|
```javascript
|
|
257
264
|
import { TimeTravelModule } from "sia-reactor/modules";
|
|
258
265
|
import { effect, TimeTravelOverlay } from "sia-reactor/adapters/vanilla";
|
|
259
266
|
import "sia-reactor/css/time-travel-overlay.css";
|
|
260
267
|
|
|
261
|
-
const time = new TimeTravelModule({ maxHistory: 300, loop: false, rate: 150 });
|
|
262
|
-
|
|
268
|
+
const time = new TimeTravelModule({ maxHistory: 300, loop: false, rate: 150, whitelist: ["store.playing", "store.currentTime"] });
|
|
269
|
+
store.use(time);
|
|
263
270
|
|
|
264
271
|
// If persist uses an async adapter (e.g. IndexedDB), wait till after hydration:
|
|
265
|
-
persist.state.once("hydrated", () =>
|
|
266
|
-
effect(() => persist.state.hydrated &&
|
|
272
|
+
persist.state.once("hydrated", () => store.use(time)); // starts `false`, one-time stall until it flips
|
|
273
|
+
effect(() => persist.state.hydrated && store.use(time), { once: true }) // same logic, different look :)
|
|
267
274
|
|
|
268
275
|
const overlay = new TimeTravelOverlay(time, { color: "#e26e02", startOpen: false, devOnly: true, container: document.body }); // optional debug interface for visulazation
|
|
269
276
|
```
|
|
@@ -308,7 +315,7 @@ const state = reactive(
|
|
|
308
315
|
suffix: 'Now',
|
|
309
316
|
whitelist: ['set', 'get', 'on', 'off'] // keys you're sure won't interfere with your own key names
|
|
310
317
|
}
|
|
311
|
-
);
|
|
318
|
+
); // name `state` as no intents will exist
|
|
312
319
|
// Whitelisted methods keep original names
|
|
313
320
|
state.set('count', (v) => v + 1);
|
|
314
321
|
state.get('count', (v) => v);
|
|
@@ -368,7 +375,7 @@ Because there is no "bubbling" or "event wave" yet, these methods do not receive
|
|
|
368
375
|
rtr.set("user.age", (value, terminated, payload) => {
|
|
369
376
|
console.log(payload.type); // "set" | "get" | "delete"
|
|
370
377
|
console.log(payload.target); // The exact anatomy of the mutation (see below)
|
|
371
|
-
console.log(payload.root); // Reference to the entire
|
|
378
|
+
console.log(payload.root); // Reference to the entire state tree
|
|
372
379
|
console.log(payload.terminated); // Boolean: Did a previous mediator kill this action?
|
|
373
380
|
console.log(payload.rejectable); // Boolean: Is this target wrapped in `intent()`?
|
|
374
381
|
}); // you could use external callbacks but typed with `Payload<T, "user.age">`
|
|
@@ -406,7 +413,7 @@ rtr.set("user.age", (value) => {
|
|
|
406
413
|
### 2. The Asynchronous Dimension: The S.I.A. Event Loop
|
|
407
414
|
When you use `.on()` or `.once()` (Listeners), you are sitting in the **Microtask Queue**. The memory has already been safely written, the Proxy traps have closed, and the engine is now broadcasting a DOM-Style "Mutation Wave" across the state tree.
|
|
408
415
|
|
|
409
|
-
If you mutate `
|
|
416
|
+
If you mutate `store.user.profile.name = "Kosi"`, the event wave travels like this:
|
|
410
417
|
1. **Capture Phase:** `*` (Root) ➔ `user` ➔ `user.profile`
|
|
411
418
|
2. **Target Phase:** `user.profile.name`
|
|
412
419
|
3. **Bubble Phase:** `user.profile` ➔ `user` ➔ `*` (Root)
|
|
@@ -457,8 +464,8 @@ rtr.on("intent.playing", (e) => {
|
|
|
457
464
|
When you listen to a parent object (like `"user.profile"`), you will naturally catch all mutations to its children.
|
|
458
465
|
|
|
459
466
|
To help you instantly differentiate between the object *itself* being replaced, versus a *child* property mutating deep inside of it, the Reactor intelligently morphs the `e.type`:
|
|
460
|
-
* If `
|
|
461
|
-
* If `
|
|
467
|
+
* If `store.user.profile = {}` happens, the listener receives `e.type === "set"`.
|
|
468
|
+
* If `store.user.profile.name = "Kosi"` happens, the parent listener receives `e.type === "update"`.
|
|
462
469
|
|
|
463
470
|
This allows for highly fine-grained syncing bridges across your application without writing heavy for-loop diffing algorithms! Use `{ depth: n }` to control how deep the path bubbles you see are, i.e.
|
|
464
471
|
|
|
@@ -531,7 +538,7 @@ S.I.A. Reactor synthesizes core concepts from the heavyweights of web and media
|
|
|
531
538
|
|
|
532
539
|
No fancy screenshots here. True engineers look at performance metrics.
|
|
533
540
|
|
|
534
|
-
To see the Reactor handle deep DAG mutations, DOM-style event routing, and microtask batching in real-time, visit the **[Live Demo](https://tobi007-del.github.io/
|
|
541
|
+
To see the Reactor handle deep DAG mutations, DOM-style event routing, and microtask batching in real-time, visit the **[Live Demo](https://tobi007-del.github.io/sia-reactor/index.html)**, open your DevTools console, and run the built-in Grand Master Stress Suite directly on your own CPU.
|
|
535
542
|
|
|
536
543
|
*NOTE: The reactor is progressively enhanced so it's performance depends on how you use it and the options you toggle, it's base form is incredibly light.*
|
|
537
544
|
|
|
@@ -540,7 +547,7 @@ To see the Reactor handle deep DAG mutations, DOM-style event routing, and micro
|
|
|
540
547
|
## Author
|
|
541
548
|
|
|
542
549
|
- Architect & Developer - [Oketade Oluwatobiloba (Tobi007-del)](https://github.com/Tobi007-del)
|
|
543
|
-
- Project - [t007-tools](https://github.com/Tobi007-del/
|
|
550
|
+
- Project - [t007-tools](https://github.com/Tobi007-del/sia-reactor)
|
|
544
551
|
|
|
545
552
|
## Acknowledgments
|
|
546
553
|
|
|
@@ -550,6 +557,6 @@ Designed to bring absolute architectural dominance and rendering efficiency to c
|
|
|
550
557
|
|
|
551
558
|
If you find this project useful, please consider giving it a star! ⭐
|
|
552
559
|
|
|
553
|
-
[](https://github.com/Tobi007-del/sia-reactor)
|
|
554
561
|
|
|
555
562
|
**[⬆ Back to Top](#sia-reactor)**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { m as TimeTravelModule } from './timeTravel-
|
|
1
|
+
import { e as Reactive } from './index-BLpfq517.cjs';
|
|
2
|
+
import { m as TimeTravelModule } from './timeTravel-CeKr5nq0.cjs';
|
|
3
3
|
|
|
4
4
|
/** Reactive options for the TimeTravel overlay instance. */
|
|
5
5
|
interface TimeTravelOverlayConfig {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { m as TimeTravelModule } from './timeTravel-
|
|
1
|
+
import { e as Reactive } from './index-BLpfq517.js';
|
|
2
|
+
import { m as TimeTravelModule } from './timeTravel-CFSDXKcs.js';
|
|
3
3
|
|
|
4
4
|
/** Reactive options for the TimeTravel overlay instance. */
|
|
5
5
|
interface TimeTravelOverlayConfig {
|
package/dist/adapters/react.cjs
CHANGED
|
@@ -498,28 +498,37 @@ var Reactor = class {
|
|
|
498
498
|
}
|
|
499
499
|
mediate(path, payload, type, cords) {
|
|
500
500
|
let terminated = false, value = payload.target.value;
|
|
501
|
-
const isGet = type === "get", isSet = type === "set", mediators = isGet ? this.getters : isSet ? this.setters : this.deleters;
|
|
502
|
-
for (let i = !isGet ? 0 :
|
|
503
|
-
const cord =
|
|
501
|
+
const isGet = type === "get", isSet = type === "set", mediators = isGet ? this.getters : isSet ? this.setters : this.deleters, scords = cords.slice();
|
|
502
|
+
for (let slen = scords.length, i = !isGet ? 0 : slen - 1, len = !isGet ? slen : -1; i !== len; i += !isGet ? 1 : -1) {
|
|
503
|
+
const cord = scords[i], response = isGet ? cord.cb(value, payload) : isSet ? cord.cb(value, terminated, payload) : cord.cb(terminated, payload);
|
|
504
504
|
if (isGet || !(terminated ||= payload.terminated = response === TERMINATOR)) value = response;
|
|
505
|
-
if (cord.once)
|
|
505
|
+
if (cord.once) {
|
|
506
|
+
const idx = cords.indexOf(cord);
|
|
507
|
+
if (idx !== -1) cords.splice(idx, 1), !cords.length && mediators.delete(path);
|
|
508
|
+
}
|
|
506
509
|
}
|
|
507
510
|
return value;
|
|
508
511
|
}
|
|
509
512
|
notify(path, payload) {
|
|
510
513
|
if (this.watchers) {
|
|
511
|
-
const wildcords = this.watchers.get("*"), cords = this.watchers.get(path);
|
|
514
|
+
const wildcords = this.watchers.get("*"), wildscords = wildcords?.slice(), cords = this.watchers.get(path), scords = cords?.slice();
|
|
512
515
|
if (cords)
|
|
513
|
-
for (let i = 0, len =
|
|
514
|
-
const cord =
|
|
516
|
+
for (let i = 0, len = scords.length; i < len; i++) {
|
|
517
|
+
const cord = scords[i];
|
|
515
518
|
cord.cb(payload.target.value, payload);
|
|
516
|
-
if (cord.once)
|
|
519
|
+
if (cord.once) {
|
|
520
|
+
const idx = cords.indexOf(cord);
|
|
521
|
+
if (idx !== -1) cords.splice(idx, 1), !cords.length && this.watchers.delete(path);
|
|
522
|
+
}
|
|
517
523
|
}
|
|
518
524
|
if (wildcords)
|
|
519
|
-
for (let i = 0, len =
|
|
520
|
-
const wildcord =
|
|
525
|
+
for (let i = 0, len = wildscords.length; i < len; i++) {
|
|
526
|
+
const wildcord = wildscords[i];
|
|
521
527
|
wildcord.cb(payload.target.value, payload);
|
|
522
|
-
if (wildcord.once)
|
|
528
|
+
if (wildcord.once) {
|
|
529
|
+
const idx = wildcords.indexOf(wildcord);
|
|
530
|
+
if (idx !== -1) wildcords.splice(idx, 1), !wildcords.length && this.watchers.delete("*");
|
|
531
|
+
}
|
|
523
532
|
}
|
|
524
533
|
}
|
|
525
534
|
this.listeners && this.schedule(path, payload);
|
|
@@ -557,8 +566,9 @@ var Reactor = class {
|
|
|
557
566
|
if (!cords) return;
|
|
558
567
|
e.type = path !== e.target.path ? "update" : e.staticType;
|
|
559
568
|
e.currentTarget = { path, value, oldValue: e.type !== "update" ? e.target.oldValue : void 0, key: e.type !== "update" ? path : path.slice(path.lastIndexOf(".") + 1) || "", hadKey: e.type !== "update" ? e.target.hadKey : true, object };
|
|
560
|
-
|
|
561
|
-
|
|
569
|
+
const scords = cords.slice();
|
|
570
|
+
for (let i = 0, len = scords.length, tDepth; i < len; i++) {
|
|
571
|
+
const cord = scords[i];
|
|
562
572
|
if (e.immediatePropagationStopped) break;
|
|
563
573
|
if (cord.capture !== isCapture) continue;
|
|
564
574
|
if (cord.depth !== void 0) {
|
|
@@ -566,7 +576,10 @@ var Reactor = class {
|
|
|
566
576
|
if (tDepth > cord.lDepth + cord.depth) continue;
|
|
567
577
|
}
|
|
568
578
|
cord.cb(e);
|
|
569
|
-
if (cord.once)
|
|
579
|
+
if (cord.once) {
|
|
580
|
+
const idx = cords.indexOf(cord);
|
|
581
|
+
if (idx !== -1) cords.splice(idx, 1), !cords.length && this.listeners.delete(path);
|
|
582
|
+
}
|
|
570
583
|
}
|
|
571
584
|
}
|
|
572
585
|
/**
|
|
@@ -663,7 +676,7 @@ var Reactor = class {
|
|
|
663
676
|
if (!cords) return void 0;
|
|
664
677
|
for (let i = 0, len = cords.length; i < len; i++) {
|
|
665
678
|
const cord = cords[i];
|
|
666
|
-
if (Object.is(cord.cb, cb)) return cord.sclup(), cords.splice(
|
|
679
|
+
if (Object.is(cord.cb, cb)) return cord.sclup(), cords.splice(i, 1), !cords.length && store.delete(path), true;
|
|
667
680
|
}
|
|
668
681
|
return false;
|
|
669
682
|
}
|
|
@@ -817,10 +830,10 @@ var Reactor = class {
|
|
|
817
830
|
off(path, callback, options) {
|
|
818
831
|
const cords = this.listeners?.get(path);
|
|
819
832
|
if (!cords) return void 0;
|
|
820
|
-
const { capture } = parseEvtOpts(options, EVT_OPTS.LISTENER);
|
|
833
|
+
const { capture = false } = parseEvtOpts(options, EVT_OPTS.LISTENER);
|
|
821
834
|
for (let i = 0, len = cords.length; i < len; i++) {
|
|
822
835
|
const cord = cords[i];
|
|
823
|
-
if (Object.is(cord.cb, callback) && cord.capture === capture) return cord.sclup(), cords.splice(
|
|
836
|
+
if (Object.is(cord.cb, callback) && cord.capture === capture) return cord.sclup(), cords.splice(i, 1), !cords.length && this.listeners.delete(path), true;
|
|
824
837
|
}
|
|
825
838
|
return false;
|
|
826
839
|
}
|
|
@@ -1100,6 +1113,26 @@ function usePath(target, path, options = NIL, build) {
|
|
|
1100
1113
|
// src/ts/adapters/react/TimeTravelOverlay.tsx
|
|
1101
1114
|
var import_react5 = require("react");
|
|
1102
1115
|
|
|
1116
|
+
// src/ts/modules/base.ts
|
|
1117
|
+
var wpArr = ["*"];
|
|
1118
|
+
|
|
1119
|
+
// src/ts/utils/dom.ts
|
|
1120
|
+
function createEl(tag, props, dataset, styles, el = tag ? document?.createElement(tag) : null) {
|
|
1121
|
+
return assignEl(el, props, dataset, styles), el;
|
|
1122
|
+
}
|
|
1123
|
+
function assignEl(el, props, dataset, styles) {
|
|
1124
|
+
if (!el) return;
|
|
1125
|
+
if (props) {
|
|
1126
|
+
for (const k of Object.keys(props)) if (props[k] !== void 0) el[k] = props[k];
|
|
1127
|
+
}
|
|
1128
|
+
if (dataset) {
|
|
1129
|
+
for (const k of Object.keys(dataset)) if (dataset[k] !== void 0) el.dataset[k] = String(dataset[k]);
|
|
1130
|
+
}
|
|
1131
|
+
if (styles) {
|
|
1132
|
+
for (const k of Object.keys(styles)) if (styles[k] !== void 0) el.style[k] = styles[k];
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1103
1136
|
// src/ts/utils/keys.ts
|
|
1104
1137
|
function stringifyKeyEvent(e) {
|
|
1105
1138
|
const parts = [];
|
|
@@ -1154,31 +1187,17 @@ function parseForARIAKS(s, formatted = true) {
|
|
|
1154
1187
|
return (formatted && !Array.isArray(s) ? s : formatKeyForDisplay(s)).toLowerCase().replace(/[()]/g, "").replace(/\bor\b/g, " ").replace(/\w+/g, (k) => m[k] || k).replace(/\s+/g, " ").trim();
|
|
1155
1188
|
}
|
|
1156
1189
|
|
|
1157
|
-
// src/ts/utils/dom.ts
|
|
1158
|
-
function createEl(tag, props, dataset, styles, el = tag ? document?.createElement(tag) : null) {
|
|
1159
|
-
return assignEl(el, props, dataset, styles), el;
|
|
1160
|
-
}
|
|
1161
|
-
function assignEl(el, props, dataset, styles) {
|
|
1162
|
-
if (!el) return;
|
|
1163
|
-
if (props) {
|
|
1164
|
-
for (const k of Object.keys(props)) if (props[k] !== void 0) el[k] = props[k];
|
|
1165
|
-
}
|
|
1166
|
-
if (dataset) {
|
|
1167
|
-
for (const k of Object.keys(dataset)) if (dataset[k] !== void 0) el.dataset[k] = String(dataset[k]);
|
|
1168
|
-
}
|
|
1169
|
-
if (styles) {
|
|
1170
|
-
for (const k of Object.keys(styles)) if (styles[k] !== void 0) el.style[k] = styles[k];
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
1190
|
// src/ts/adapters/vanilla/effect.ts
|
|
1175
|
-
function effect(callback, options) {
|
|
1191
|
+
function effect(callback, options = NIL) {
|
|
1176
1192
|
const atrkr = new Autotracker();
|
|
1177
1193
|
let destroyed = false;
|
|
1194
|
+
const cleanup = () => (destroyed = true, atrkr.destroy());
|
|
1178
1195
|
(function execute() {
|
|
1179
|
-
if (
|
|
1196
|
+
if (destroyed) return;
|
|
1197
|
+
withTracker(atrkr, callback);
|
|
1198
|
+
options.once ? cleanup() : atrkr.callback(execute, options);
|
|
1180
1199
|
})();
|
|
1181
|
-
return
|
|
1200
|
+
return cleanup;
|
|
1182
1201
|
}
|
|
1183
1202
|
|
|
1184
1203
|
// src/ts/adapters/vanilla/TimeTravelOverlay.ts
|
|
@@ -1203,9 +1222,11 @@ var TimeTravelOverlay = class _TimeTravelOverlay {
|
|
|
1203
1222
|
this.time = time;
|
|
1204
1223
|
this.config = reactive({ title: `Time Travel Overlay ${this.index = ++_TimeTravelOverlay.count}`, ...build });
|
|
1205
1224
|
this.state.open = !!this.config.startOpen;
|
|
1206
|
-
|
|
1225
|
+
let wlLive = false, blLive = false;
|
|
1226
|
+
const s = this.time.state, host = createEl("div", { className: "tt-overlay-host" }), toggle = createEl("button", { className: "tt-overlay-toggle", type: "button", onclick: () => this.state.open = !this.state.open }), panel = createEl("aside", { className: "tt-overlay", ariaLabel: "time travel overlay" }), title = createEl("div", { className: "title" }), frame = createEl("span", { className: "muted" }), clrHistory = createEl("button", { textContent: `Clear History${formatKeyForDisplay(keys.shortcuts.clrHistory)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.clrHistory, false), onclick: () => (this.time.clear(), this.state.import = "") }), undo = createEl("button", { textContent: `Undo${formatKeyForDisplay(keys.shortcuts.undo[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.undo, false), onclick: this.time.undo }), redo = createEl("button", { textContent: `Redo${formatKeyForDisplay(keys.shortcuts.redo[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.redo, false), onclick: this.time.redo }), genesis = createEl("button", { textContent: `Genesis${formatKeyForDisplay(keys.shortcuts.genesis)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.genesis, false), onclick: () => this.time.jumpTo(0) }), playPause = createEl("button", { onclick: () => this.time[s.paused ? "play" : "pause"](), ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.playPause, false) }), rewind = createEl("button", { textContent: `Rewind${formatKeyForDisplay(keys.shortcuts.rewind)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.rewind, false), onclick: this.time.rewind }), range = createEl("input", { type: "range", min: "0", max: "0", value: "0", title: "time travel frame", ariaLabel: "time travel frame", oninput: () => this.time.jumpTo(Number(range.value)) }), exp = createEl("button", { textContent: `Export${formatKeyForDisplay(keys.shortcuts.export)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.export, false), onclick: () => this.state.import = this.time.export(null, 2) }), imp = createEl("button", { textContent: `Import${formatKeyForDisplay(keys.shortcuts.import)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.import, false), onclick: () => this.state.import.trim().length && this.time.import(this.state.import) }), clr = createEl("button", { textContent: `Clear${formatKeyForDisplay(keys.shortcuts.clear)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.clear, false), onclick: () => this.state.import = "" }), payload = createEl("textarea", { className: "tt-io", readOnly: true, placeholder: "current payload json", title: "Current History Entry" }), io = createEl("textarea", { className: "tt-io", placeholder: "timeline payload json", title: "Time History", oninput: () => this.state.import = io.value }), foot = createEl("p", { className: "tt-footnote", textContent: "Want this in your app? " }), link = createEl("a", { target: "_blank", rel: "noreferrer noopener", textContent: "sia-reactor", href: "https://www.npmjs.com/package/sia-reactor" }), box = createEl("div", { className: "tt-status-box" }), status = createEl("div", { className: "tt-status-row" }), filters = createEl("div", { className: "tt-status-row" }), filterBox = createEl("div", { className: "tt-status-box" }), whitelistLabel = createEl("span", { className: "muted", textContent: "Whitelist:" }), blacklistLabel = createEl("span", { className: "muted", textContent: "Blacklist:" }), whitelist = createEl("input", { className: "tt-filter-input tt-io", placeholder: 'a.b, c.d or {"0":["a.b"]}', title: "Whitelist paths", onfocus: () => wlLive = true, onblur: () => (wlLive = false, whitelist.value = formatPaths(this.time.config.whitelist, "*")), oninput: (_, parsed = parsePaths(whitelist.value)) => parsed !== null && (this.time.config.whitelist = parsed) }), blacklist = createEl("input", { className: "tt-filter-input tt-io", placeholder: 'a.b, c.d or {"0":["a.b"]}', title: "Blacklist paths", onfocus: () => blLive = true, onblur: () => (blLive = false, blacklist.value = formatPaths(this.time.config.blacklist, "")), oninput: (_, parsed = parsePaths(blacklist.value, true)) => parsed !== null && (this.time.config.blacklist = parsed) }), filterRow1 = createEl("div", { className: "tt-filter-row" }), filterRow2 = createEl("div", { className: "tt-filter-row" }), row1 = createEl("div", { className: "tt-row" }), row2 = createEl("div", { className: "tt-row" }), row3 = createEl("div", { className: "tt-row" });
|
|
1207
1227
|
status.append((box.append(frame), box), clrHistory);
|
|
1208
|
-
|
|
1228
|
+
filters.append((filterBox.append((filterRow1.append(whitelistLabel, whitelist), filterRow1), (filterRow2.append(blacklistLabel, blacklist), filterRow2)), filterBox));
|
|
1229
|
+
panel.append(title, status, (row1.append(undo, redo, genesis), row1), (row2.append(playPause, rewind), row2), payload, range, filters, (row3.append(exp, imp, clr), row3), io, (foot.appendChild(link), foot));
|
|
1209
1230
|
host.append(toggle, panel);
|
|
1210
1231
|
this.els = { host, toggle, panel, title, frame, clrHistory, undo, redo, genesis, playPause, rewind, range, exp, imp, clr, payload, io };
|
|
1211
1232
|
this.keyup = (e) => {
|
|
@@ -1235,7 +1256,8 @@ var TimeTravelOverlay = class _TimeTravelOverlay {
|
|
|
1235
1256
|
effect(() => {
|
|
1236
1257
|
clr.disabled = imp.disabled = !this.state.import.trim().length;
|
|
1237
1258
|
io.value !== this.state.import && (io.value = this.state.import);
|
|
1238
|
-
})
|
|
1259
|
+
}),
|
|
1260
|
+
effect(() => (!wlLive && (whitelist.value = formatPaths(this.time.config.whitelist, "*")), !blLive && (blacklist.value = formatPaths(this.time.config.blacklist, ""))))
|
|
1239
1261
|
];
|
|
1240
1262
|
this.clups.push(...sync);
|
|
1241
1263
|
}
|
|
@@ -1257,6 +1279,22 @@ function getDock(container) {
|
|
|
1257
1279
|
const dock = getDirChild(layer, "tt-overlay-dock") || createEl("div", { className: "tt-overlay-dock" });
|
|
1258
1280
|
return dock.parentElement !== layer && layer.appendChild(dock), dock;
|
|
1259
1281
|
}
|
|
1282
|
+
function formatPaths(paths, emptyText) {
|
|
1283
|
+
return !paths ? emptyText : Array.isArray(paths) ? paths.length ? paths.join(", ") : emptyText : "object" === typeof paths ? JSON.stringify(paths) : String(paths);
|
|
1284
|
+
}
|
|
1285
|
+
function parsePaths(raw, allowEmpty = false) {
|
|
1286
|
+
const text = raw.trim();
|
|
1287
|
+
if (!text) return allowEmpty ? void 0 : wpArr;
|
|
1288
|
+
if (text[0] === "{")
|
|
1289
|
+
try {
|
|
1290
|
+
const parsed = JSON.parse(text);
|
|
1291
|
+
if (parsed && "object" === typeof parsed) return parsed;
|
|
1292
|
+
} catch {
|
|
1293
|
+
return null;
|
|
1294
|
+
}
|
|
1295
|
+
const list = text.split(",").map((p) => p.trim()).filter(Boolean);
|
|
1296
|
+
return list.length ? list : allowEmpty ? void 0 : wpArr;
|
|
1297
|
+
}
|
|
1260
1298
|
|
|
1261
1299
|
// src/ts/adapters/react/TimeTravelOverlay.tsx
|
|
1262
1300
|
function TimeTravelOverlay2(props) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { E as EffectOptions,
|
|
1
|
+
import { E as EffectOptions, a as Reactor, e as Reactive, x as ReactorBuild, D as DeepReadonly, W as WildPaths, f as PathValue } from '../index-BLpfq517.cjs';
|
|
2
2
|
import { useLayoutEffect } from 'react';
|
|
3
|
-
import { m as TimeTravelModule } from '../timeTravel-
|
|
4
|
-
import { a as TimeTravelOverlayConfig } from '../TimeTravelOverlay-
|
|
3
|
+
import { m as TimeTravelModule } from '../timeTravel-CeKr5nq0.cjs';
|
|
4
|
+
import { a as TimeTravelOverlayConfig } from '../TimeTravelOverlay-DJJY5B9x.cjs';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Subscribes a component to desired Reactor state and returns it.
|
package/dist/adapters/react.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { E as EffectOptions,
|
|
1
|
+
import { E as EffectOptions, a as Reactor, e as Reactive, x as ReactorBuild, D as DeepReadonly, W as WildPaths, f as PathValue } from '../index-BLpfq517.js';
|
|
2
2
|
import { useLayoutEffect } from 'react';
|
|
3
|
-
import { m as TimeTravelModule } from '../timeTravel-
|
|
4
|
-
import { a as TimeTravelOverlayConfig } from '../TimeTravelOverlay-
|
|
3
|
+
import { m as TimeTravelModule } from '../timeTravel-CFSDXKcs.js';
|
|
4
|
+
import { a as TimeTravelOverlayConfig } from '../TimeTravelOverlay-D_D3EW6s.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Subscribes a component to desired Reactor state and returns it.
|
package/dist/adapters/react.js
CHANGED
|
@@ -2,12 +2,14 @@ import {
|
|
|
2
2
|
Autotracker,
|
|
3
3
|
TimeTravelOverlay,
|
|
4
4
|
withTracker
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-3LIKXZ7X.js";
|
|
6
|
+
import "../chunk-PLYS4CVP.js";
|
|
7
|
+
import "../chunk-NG3WWQV4.js";
|
|
6
8
|
import {
|
|
7
9
|
getReactor
|
|
8
|
-
} from "../chunk-
|
|
9
|
-
import "../chunk-
|
|
10
|
-
import "../chunk-
|
|
10
|
+
} from "../chunk-2EIKOZAD.js";
|
|
11
|
+
import "../chunk-RVYL3OLW.js";
|
|
12
|
+
import "../chunk-3OT72G7R.js";
|
|
11
13
|
import {
|
|
12
14
|
CTX,
|
|
13
15
|
NIL,
|