sia-reactor 0.0.26 → 0.0.28
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 +30 -30
- package/dist/{TimeTravelOverlay-_6k5wu1I.d.cts → TimeTravelOverlay-DJJY5B9x.d.cts} +2 -2
- package/dist/{TimeTravelOverlay-fun5VLIo.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-2jKy98op.d.cts → index-BLpfq517.d.cts} +23 -9
- package/dist/{index-2jKy98op.d.ts → index-BLpfq517.d.ts} +23 -9
- 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 +76 -53
- package/dist/modules.d.cts +8 -17
- package/dist/modules.d.ts +8 -17
- package/dist/modules.js +19 -111
- package/dist/styles/time-travel-overlay.css +41 -2
- package/dist/super.d.ts +28 -29
- package/dist/super.global.js +104 -59
- package/dist/{timeTravel-CsbQ8qhP.d.ts → timeTravel-CFSDXKcs.d.ts} +3 -9
- package/dist/{timeTravel-DzgX8BKQ.d.cts → timeTravel-CeKr5nq0.d.cts} +3 -9
- package/dist/utils.d.cts +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +9 -7
- package/package.json +2 -2
- /package/dist/{chunk-5A44QFT6.js → chunk-RVYL3OLW.js} +0 -0
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
[](https://bundlephobia.com/package/sia-reactor)
|
|
8
8
|
[](https://github.com/Tobi007-del/sia-reactor)
|
|
9
9
|
|
|
10
|
-
[Live Demo & Benchmarks](https://tobi007-del.github.io/sia-reactor/
|
|
10
|
+
[Live Demo & Benchmarks](https://tobi007-del.github.io/sia-reactor/index.html) | [Report Bug](https://github.com/Tobi007-del/sia-reactor/issues)
|
|
11
11
|
|
|
12
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)
|
|
13
13
|
|
|
@@ -136,14 +136,14 @@ The primary way to use the reactor is to wrap an object using `reactive(target,
|
|
|
136
136
|
*NOTE: `.` and `*` are engine reserved so don't use them as object keys*
|
|
137
137
|
|
|
138
138
|
```javascript
|
|
139
|
-
const
|
|
139
|
+
const store = reactive({ player: { volume: 50 } }, { smartCloning: true, referenceTracking: true }); // name it something other than `state` if intents will exist.
|
|
140
140
|
|
|
141
141
|
// Public API Methods are attached directly to the object with `reactive()`!
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
store.set("player.volume", (val) => Math.min(val, 100));
|
|
143
|
+
store.on("player.volume", (e) => console.log(e.value));
|
|
144
144
|
|
|
145
|
-
|
|
146
|
-
getReactor(
|
|
145
|
+
store.player.volume = 150; // Triggers mediation, clamps to 100, fires listener.
|
|
146
|
+
getReactor(store); store.__Reactor__; // Reference to the underlying reactor
|
|
147
147
|
```
|
|
148
148
|
|
|
149
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):
|
|
@@ -202,31 +202,31 @@ The engine provides native React bindings utilizing `useSyncExternalStore` and a
|
|
|
202
202
|
import { reactive } from "sia-reactor";
|
|
203
203
|
import { useReactor, useAnyReactor, useSelector, useAnySelector, usePath, effect } from "sia-reactor/adapters/react";
|
|
204
204
|
|
|
205
|
-
const
|
|
205
|
+
const store = reactive({ user: { name: "Kosi", age: 25 }, theme: "dark" });
|
|
206
206
|
|
|
207
207
|
// 1. The Tracked State (Valtio-style)
|
|
208
208
|
function Profile() {
|
|
209
|
-
const
|
|
209
|
+
const sameStore = useReactor(store); // `useReactorSnapshot()` if mutable issues arise
|
|
210
210
|
useAnyReactor(); // when you just want state from any reactor
|
|
211
|
-
// Only re-renders if
|
|
212
|
-
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>;
|
|
213
213
|
} // no snapshots like Valtio, you can read or write to anything
|
|
214
214
|
|
|
215
215
|
// 2. The Slice Selector (Zustand-style)
|
|
216
216
|
function Theme() {
|
|
217
|
-
const theme = useSelector(
|
|
218
|
-
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
|
|
219
219
|
return <div>Theme: {theme}</div>;
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
// 3. The Direct Path Observer
|
|
223
223
|
function AgeObserver() {
|
|
224
|
-
const age = usePath(
|
|
224
|
+
const age = usePath(store, "user.age"); // pass in a normal object for an auto-scoped instance
|
|
225
225
|
return <div>Age: {age}</div>;
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
// 4. Vanilla Side Effects (Runs anywhere, framework agnostic)
|
|
229
|
-
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
|
|
230
230
|
```
|
|
231
231
|
|
|
232
232
|
### Modules: The Extension Port
|
|
@@ -240,37 +240,37 @@ Automatically syncs your State to LocalStorage, SessionStorage, Memory or Indexe
|
|
|
240
240
|
import { reactive, Reactor, getReactor } from "sia-reactor";
|
|
241
241
|
import { PersistModule, LocalStorageAdapter, IndexedDBAdapter, SessionStorageAdapter, CookieAdapter, MemoryAdapter } from "sia-reactor/modules";
|
|
242
242
|
|
|
243
|
-
const
|
|
243
|
+
const store = reactive({ theme: "dark", settings: { volume: 50, brightness: 30 } });
|
|
244
244
|
const persist = new PersistModule({
|
|
245
|
-
key: "
|
|
245
|
+
key: "APP_GLOBAL_STORE",
|
|
246
246
|
whitelist: ["theme", "settings.brightness"], // all paths if omitted, use object if multiple reactors
|
|
247
247
|
blacklist: ["settings.debug"], // optional excluded paths
|
|
248
248
|
throttle: 2500, // ms between saves
|
|
249
249
|
fanout: true, // async hydration use leaf writes to sync initialized listeners.
|
|
250
250
|
adapter: new IndexedDBAdapter({ dbName: "Session", version: 1, onversionchange: () => location.reload(), useSnapshot: true }) // or `LocalStorageAdapter` (instance or signature)
|
|
251
|
-
}, getReactor(
|
|
252
|
-
|
|
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
253
|
|
|
254
254
|
// Seperate attach sample if multiple reactors desired
|
|
255
|
-
persist.attach(
|
|
255
|
+
persist.attach(uiStore, "ui").setup(appStore, "app"); // or paths: "app.ui"
|
|
256
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
257
|
|
|
258
258
|
```
|
|
259
259
|
|
|
260
260
|
#### The Time Travel Module
|
|
261
|
-
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.
|
|
262
262
|
|
|
263
263
|
```javascript
|
|
264
264
|
import { TimeTravelModule } from "sia-reactor/modules";
|
|
265
265
|
import { effect, TimeTravelOverlay } from "sia-reactor/adapters/vanilla";
|
|
266
266
|
import "sia-reactor/css/time-travel-overlay.css";
|
|
267
267
|
|
|
268
|
-
const time = new TimeTravelModule({ maxHistory: 300, loop: false, rate: 150, whitelist: ["
|
|
269
|
-
|
|
268
|
+
const time = new TimeTravelModule({ maxHistory: 300, loop: false, rate: 150, whitelist: ["store.playing", "store.currentTime"] });
|
|
269
|
+
store.use(time);
|
|
270
270
|
|
|
271
271
|
// If persist uses an async adapter (e.g. IndexedDB), wait till after hydration:
|
|
272
|
-
persist.state.once("hydrated", () =>
|
|
273
|
-
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 :)
|
|
274
274
|
|
|
275
275
|
const overlay = new TimeTravelOverlay(time, { color: "#e26e02", startOpen: false, devOnly: true, container: document.body }); // optional debug interface for visulazation
|
|
276
276
|
```
|
|
@@ -315,7 +315,7 @@ const state = reactive(
|
|
|
315
315
|
suffix: 'Now',
|
|
316
316
|
whitelist: ['set', 'get', 'on', 'off'] // keys you're sure won't interfere with your own key names
|
|
317
317
|
}
|
|
318
|
-
);
|
|
318
|
+
); // name `state` as no intents will exist
|
|
319
319
|
// Whitelisted methods keep original names
|
|
320
320
|
state.set('count', (v) => v + 1);
|
|
321
321
|
state.get('count', (v) => v);
|
|
@@ -375,7 +375,7 @@ Because there is no "bubbling" or "event wave" yet, these methods do not receive
|
|
|
375
375
|
rtr.set("user.age", (value, terminated, payload) => {
|
|
376
376
|
console.log(payload.type); // "set" | "get" | "delete"
|
|
377
377
|
console.log(payload.target); // The exact anatomy of the mutation (see below)
|
|
378
|
-
console.log(payload.root); // Reference to the entire
|
|
378
|
+
console.log(payload.root); // Reference to the entire state tree
|
|
379
379
|
console.log(payload.terminated); // Boolean: Did a previous mediator kill this action?
|
|
380
380
|
console.log(payload.rejectable); // Boolean: Is this target wrapped in `intent()`?
|
|
381
381
|
}); // you could use external callbacks but typed with `Payload<T, "user.age">`
|
|
@@ -413,7 +413,7 @@ rtr.set("user.age", (value) => {
|
|
|
413
413
|
### 2. The Asynchronous Dimension: The S.I.A. Event Loop
|
|
414
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.
|
|
415
415
|
|
|
416
|
-
If you mutate `
|
|
416
|
+
If you mutate `store.user.profile.name = "Kosi"`, the event wave travels like this:
|
|
417
417
|
1. **Capture Phase:** `*` (Root) ➔ `user` ➔ `user.profile`
|
|
418
418
|
2. **Target Phase:** `user.profile.name`
|
|
419
419
|
3. **Bubble Phase:** `user.profile` ➔ `user` ➔ `*` (Root)
|
|
@@ -464,8 +464,8 @@ rtr.on("intent.playing", (e) => {
|
|
|
464
464
|
When you listen to a parent object (like `"user.profile"`), you will naturally catch all mutations to its children.
|
|
465
465
|
|
|
466
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`:
|
|
467
|
-
* If `
|
|
468
|
-
* 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"`.
|
|
469
469
|
|
|
470
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.
|
|
471
471
|
|
|
@@ -538,7 +538,7 @@ S.I.A. Reactor synthesizes core concepts from the heavyweights of web and media
|
|
|
538
538
|
|
|
539
539
|
No fancy screenshots here. True engineers look at performance metrics.
|
|
540
540
|
|
|
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/
|
|
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.
|
|
542
542
|
|
|
543
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.*
|
|
544
544
|
|
|
@@ -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,
|