@evgkch/reactive-dom 0.1.0 → 0.2.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.
Files changed (41) hide show
  1. package/README.md +71 -92
  2. package/dist/create-element.js +2 -2
  3. package/dist/index.d.ts +2 -2
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +1 -1
  6. package/dist/list.js +4 -4
  7. package/dist/log.d.ts +9 -14
  8. package/dist/log.d.ts.map +1 -1
  9. package/dist/log.js +20 -78
  10. package/dist/slot.js +4 -4
  11. package/dist/unmount.js +2 -2
  12. package/examples/index.html +4 -4
  13. package/examples/pages/console/index.css +73 -0
  14. package/examples/pages/console/index.html +13 -0
  15. package/examples/pages/console/index.ts +11 -0
  16. package/examples/pages/console/model/state.ts +25 -0
  17. package/examples/pages/console/ui/App.ts +57 -0
  18. package/examples/pages/console/ui/LineItem.ts +13 -0
  19. package/examples/pages/monitor/index.css +297 -0
  20. package/examples/pages/monitor/index.html +13 -0
  21. package/examples/pages/monitor/index.ts +24 -0
  22. package/examples/pages/monitor/model/state.ts +30 -0
  23. package/examples/pages/monitor/ui/App.ts +207 -0
  24. package/examples/pages/monitor/ui/TaskItem.ts +41 -0
  25. package/examples/{stream/styles.css → pages/stream/index.css} +1 -75
  26. package/examples/pages/stream/index.html +13 -0
  27. package/examples/pages/stream/index.ts +15 -0
  28. package/examples/pages/stream/model/state.ts +45 -0
  29. package/examples/pages/stream/ui/App.ts +70 -0
  30. package/examples/pages/stream/ui/LogLine.ts +27 -0
  31. package/examples/shared/lib/index.ts +19 -0
  32. package/examples/{console/styles.css → shared/styles/common.css} +5 -77
  33. package/examples/tsconfig.json +13 -0
  34. package/package.json +10 -8
  35. package/examples/console/app.js +0 -97
  36. package/examples/console/index.html +0 -23
  37. package/examples/monitor/app.js +0 -260
  38. package/examples/monitor/index.html +0 -23
  39. package/examples/monitor/styles.css +0 -425
  40. package/examples/stream/app.js +0 -136
  41. package/examples/stream/index.html +0 -23
package/README.md CHANGED
@@ -25,11 +25,11 @@ One context handles subscriptions. One function unmounts everything.
25
25
  npm install @evgkch/reactive @evgkch/reactive-dom
26
26
  ```
27
27
 
28
- **Peer:** `@evgkch/reactive` ^1.0.3. In 1.0.3 the reactive package added **debug logging**: `configure({ log: true })` or `configure({ log: "verbose" })` to log Batch runs and Watch patches; `getObjectId(obj)` returns a stable id (e.g. `"t1"`) for correlating in DevTools. See [@evgkch/reactive](https://www.npmjs.com/package/@evgkch/reactive).
28
+ **Peer:** `@evgkch/reactive` ^1.1.0.
29
29
 
30
30
  ```ts
31
- import * as R from "@evgkch/reactive"; // data
32
- import * as UI from "@evgkch/reactive-dom"; // DOM
31
+ import * as R from "@evgkch/reactive";
32
+ import * as UI from "@evgkch/reactive-dom";
33
33
  ```
34
34
 
35
35
  ---
@@ -40,12 +40,10 @@ Creates a reusable factory from an HTML template. The template is parsed **once*
40
40
 
41
41
  ```ts
42
42
  const UserCard = UI.Struct(
43
- `
44
- <div class="card">
45
- <span data-ref="name"></span>
46
- <span data-ref="age"></span>
47
- </div>
48
- `,
43
+ `<div class="card">
44
+ <span data-ref="name"></span>
45
+ <span data-ref="age"></span>
46
+ </div>`,
49
47
  (props: { user: User; theme: string }, refs, ctx) => {
50
48
  ctx.batch(() => {
51
49
  refs.name.textContent = props.user.name;
@@ -65,10 +63,10 @@ document.body.appendChild(el);
65
63
 
66
64
  `ctx.batch` runs **synchronously on first call** — the element is fully rendered before it enters the DOM. On subsequent reactive changes it re-runs automatically.
67
65
 
68
- For manual removal:
66
+ To remove an element and stop its subscriptions:
69
67
 
70
68
  ```ts
71
- UI.remove(el); // unmount all subscriptions + el.remove()
69
+ UI.remove(el);
72
70
  ```
73
71
 
74
72
  ---
@@ -79,13 +77,11 @@ Same signature as `UI.Struct`, but the factory has two modes depending on how it
79
77
 
80
78
  ```ts
81
79
  const TaskItem = UI.List(
82
- `
83
- <li>
84
- <input type="checkbox" data-ref="check" />
85
- <span data-ref="text"></span>
86
- <button data-ref="remove">✕</button>
87
- </li>
88
- `,
80
+ `<li>
81
+ <input type="checkbox" data-ref="check" />
82
+ <span data-ref="text"></span>
83
+ <button data-ref="remove">✕</button>
84
+ </li>`,
89
85
  (props: { item: Task }, refs, ctx) => {
90
86
  ctx.batch(() => {
91
87
  refs.text.textContent = props.item.text;
@@ -124,19 +120,19 @@ In list mode the library uses `Watch` to track operations on the source:
124
120
 
125
121
  `onAdd` fires only for elements added **after** initial mount — not for the initial list. Use it for enter animations, not for rendering.
126
122
 
127
- `onRemove` receives `done` — the element is removed from DOM only after `done()` is called, enabling exit animations.
123
+ `onRemove` receives `done` — the element is removed from the DOM only after `done()` is called, enabling exit animations.
128
124
 
129
125
  ---
130
126
 
131
127
  ## Context
132
128
 
133
- Third argument in every setup after `props` and `refs`. Every subscription registered through `ctx` is automatically stopped when the component is unmounted.
129
+ Third argument in every setup, after `props` and `refs`. Every subscription registered through `ctx` is automatically stopped when the component is unmounted.
134
130
 
135
131
  ```ts
136
- ctx.batch(fn) // reactive effect
137
- ctx.watch(source, fn) // operation handler (sync, with patch)
138
- ctx.list(container, source, propsFactory, factory, hooks?) // nested list
139
- ctx.slot(container, getter) // conditional render
132
+ ctx.batch(fn) // reactive effect
133
+ ctx.watch(source, fn) // operation handler (sync, with patch)
134
+ ctx.list(container, source, propsFactory, factory, hooks?) // nested list
135
+ ctx.slot(container, getter) // conditional render
140
136
  ```
141
137
 
142
138
  There is no need to manage stop functions manually — `ctx` collects them all.
@@ -149,18 +145,15 @@ There is no need to manage stop functions manually — `ctx` collects them all.
149
145
 
150
146
  ```ts
151
147
  const ColumnItem = UI.Struct(
152
- `
153
- <div class="column">
154
- <h3 data-ref="title"></h3>
155
- <ul data-ref="tasks"></ul>
156
- </div>
157
- `,
148
+ `<div class="column">
149
+ <h3 data-ref="title"></h3>
150
+ <ul data-ref="tasks"></ul>
151
+ </div>`,
158
152
  (props: { col: Column }, refs, ctx) => {
159
153
  ctx.batch(() => {
160
154
  refs.title.textContent = props.col.title;
161
155
  });
162
156
  ctx.list(refs.tasks, props.col.tasks, (item) => ({ item }), TaskItem);
163
- // TaskItem's stop is registered in ctx — unmounted automatically with ColumnItem
164
157
  },
165
158
  );
166
159
  ```
@@ -178,7 +171,6 @@ const App = UI.Struct(`<div><div data-ref="panel"></div></div>`, (props, refs, c
178
171
 
179
172
  ```ts
180
173
  const Item = UI.Struct(`<div data-ref="el"></div>`, (props: { item: Task }, refs, ctx) => {
181
- // ctx.batch reads from multiple reactive sources — tracks all of them
182
174
  ctx.batch(() => {
183
175
  refs.el.className = [
184
176
  props.item.done ? "done" : "",
@@ -200,40 +192,36 @@ Conditional render outside a component:
200
192
  const stop = UI.Slot(container, () => (isOpen.get() ? Modal({ title }) : null));
201
193
  ```
202
194
 
203
- An anchor `Comment` node fixes the position in DOM. When the getter result changes — previous element is unmounted, new one is inserted.
195
+ An anchor `Comment` node fixes the position in the DOM. When the getter result changes — the previous element is unmounted, the new one is inserted.
204
196
 
205
197
  ---
206
198
 
207
199
  ## UI.remove / UI.unmount
208
200
 
209
201
  ```ts
210
- UI.remove(el); // unmount all subscriptions + el.remove()
211
- UI.unmount(el); // unmount only element stays in DOM
202
+ UI.remove(el); // stop all subscriptions + el.remove()
203
+ UI.unmount(el); // stop all subscriptions, element stays in DOM
212
204
  ```
213
205
 
214
206
  `UI.List` and `UI.Slot` call `UI.remove` automatically on their elements. For manually mounted elements call it explicitly.
215
207
 
216
208
  ---
217
209
 
218
- ## Debug logging (component logger)
210
+ ## Debug logging
219
211
 
220
- Turn on component lifecycle logs to see mount, list add/remove, slot swap, and unmount in the console. Uses a `[reactive-dom]` prefix and stable ids (`c1`, `c2`, …) so you can correlate with DevTools.
212
+ Turn on component lifecycle logs to see mount, list add/remove, slot swap, and unmount. Uses a `[reactive-dom]` prefix; meta is the element at the call site same pattern as `@evgkch/reactive`.
221
213
 
222
214
  ```ts
223
- import * as UI from "@evgkch/reactive-dom";
224
-
225
215
  UI.configure({ log: true });
226
- // mount c1 <div>
227
- // list mount c2
228
- // list add c3
229
- // unmount c3
216
+ // [reactive-dom] [struct:mount] <element>
217
+ // [reactive-dom] [list:mount] <element>
218
+ // [reactive-dom] [list:add] <element>
219
+ // [reactive-dom] [struct:unmount] <element>
230
220
  ```
231
221
 
232
- - `configure({ log: true })` — mount, unmount, list mount/add/remove, slot mount/swap/stop.
233
- - `configure({ log: false })` — off (default).
234
- - `getComponentId(el)` — returns the stable id for a component root (e.g. `"c1"`), creates one if missing.
235
-
236
- In the **examples** (monitor, console, stream), both the reactive logger (`R.configure({ log: true })`) and the component logger (`UI.configure({ log: true })`) are enabled when you open the app in the browser, so you see Batch/Watch from reactive and mount/list/slot/unmount from reactive-dom together.
222
+ - `configure({ log: true })` — use the default console logger.
223
+ - `configure({ log: logger })` — your logger `{ log(message: string, meta?: unknown): void }` (same shape as `@evgkch/reactive`).
224
+ - `configure({ log: null })` — off (default). No overhead: one guard, no work when disabled.
237
225
 
238
226
  ---
239
227
 
@@ -263,7 +251,7 @@ class TaskItemEl extends UI.ReactiveElement {
263
251
  customElements.define("task-item", TaskItemEl);
264
252
  ```
265
253
 
266
- `disconnectedCallback` calls `unmount` automatically. This means `UI.List` needs no manual stop — `el.remove()` triggers `disconnectedCallback` which cleans up:
254
+ `disconnectedCallback` calls `unmount` automatically `UI.List` needs no manual stop:
267
255
 
268
256
  ```ts
269
257
  TaskItem(ul, tasks, (item) => {
@@ -290,20 +278,17 @@ const tasks = R.List([
290
278
  R.Struct({ text: "extract data", done: true }),
291
279
  ]);
292
280
 
293
- // live metrics
294
281
  setInterval(() => {
295
282
  cpu.set(Math.max(10, Math.min(99, cpu.get() + Math.floor(Math.random() * 11) - 5)));
296
283
  }, 1000);
297
284
 
298
285
  const TaskItem = UI.List(
299
- `
300
- <li>
301
- <input type="checkbox" data-ref="check" />
302
- <span data-ref="text"></span>
303
- <span data-ref="status"></span>
304
- <button data-ref="remove">kill</button>
305
- </li>
306
- `,
286
+ `<li>
287
+ <input type="checkbox" data-ref="check" />
288
+ <span data-ref="text"></span>
289
+ <span data-ref="status"></span>
290
+ <button data-ref="remove">kill</button>
291
+ </li>`,
307
292
  (props: { item: Task }, refs, ctx) => {
308
293
  ctx.batch(() => {
309
294
  refs.text.textContent = props.item.text;
@@ -323,24 +308,22 @@ const TaskItem = UI.List(
323
308
  );
324
309
 
325
310
  const App = UI.Struct(
326
- `
327
- <div class="monitor">
328
- <div class="cpu-bar" data-ref="cpu-bar"></div>
329
- <div class="prompt">
330
- <span>$</span>
331
- <input data-ref="input" placeholder="spawn process..." />
332
- </div>
333
- <ul data-ref="list"></ul>
334
- <div class="statusbar">
335
- <span data-ref="count"></span>
336
- <div data-ref="filters">
337
- <button data-filter="all">all</button>
338
- <button data-filter="active">running</button>
339
- <button data-filter="done">done</button>
340
- </div>
341
- </div>
342
- </div>
343
- `,
311
+ `<div class="monitor">
312
+ <div class="cpu-bar" data-ref="cpu-bar"></div>
313
+ <div class="prompt">
314
+ <span>$</span>
315
+ <input data-ref="input" placeholder="spawn process..." />
316
+ </div>
317
+ <ul data-ref="list"></ul>
318
+ <div class="statusbar">
319
+ <span data-ref="count"></span>
320
+ <div data-ref="filters">
321
+ <button data-filter="all">all</button>
322
+ <button data-filter="active">running</button>
323
+ <button data-filter="done">done</button>
324
+ </div>
325
+ </div>
326
+ </div>`,
344
327
  (props, refs, ctx) => {
345
328
  ctx.list(refs.list, tasks, (item) => ({ item }), TaskItem, {
346
329
  onAdd: (el) => el.animate([{ opacity: 0 }, { opacity: 1 }], 150),
@@ -377,7 +360,6 @@ const App = UI.Struct(
377
360
  input.value = "";
378
361
  });
379
362
 
380
- // hotkeys
381
363
  document.addEventListener("keydown", (e) => {
382
364
  if (document.activeElement === input) return;
383
365
  if (e.key === "1") filter.set("all");
@@ -395,26 +377,23 @@ document.getElementById("app")!.appendChild(App({}));
395
377
 
396
378
  ## API reference
397
379
 
398
- | API | Description |
399
- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
400
- | `Struct(html, setup)` | Factory `(props) => Element`. Setup: `(props, refs, ctx)`. Template parsed once. |
401
- | `List(html, setup)` | Dual factory. `(props) => Element` or `(container, source, propsFactory, hooks?) => stop`. Setup: `(props, refs, ctx)`. `onAdd` fires only for new items, not initial mount. |
402
- | `Slot(container, getter)` | Conditional slot. Swaps element on change. Returns `stop`. |
403
- | `remove(el)` | `unmount(el)` + `el.remove()`. |
404
- | `unmount(el)` | Stop all subscriptions. Element stays in DOM. |
405
- | `ReactiveElement` | Base class for Custom Elements. Override `mount(ctx)`, call `invalidate()` from setters. |
406
- | `configure({ log? })` | Component logger: `log: true` logs mount/unmount, list add/remove, slot mount/swap/stop. Default `false`. |
407
- | `getComponentId(el)` | Stable id for a component root (e.g. `"c1"`). Use to correlate with logs or DevTools. |
380
+ | API | Description |
381
+ | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
382
+ | `Struct(html, setup)` | Factory `(props) => Element`. Setup: `(props, refs, ctx)`. Template parsed once. |
383
+ | `List(html, setup)` | Dual factory. `(props) => Element` or `(container, source, propsFactory, hooks?) => stop`. `onAdd` fires only for items added after initial mount. |
384
+ | `Slot(container, getter)` | Conditional slot outside a component. Swaps element on change. Returns `stop`. |
385
+ | `remove(el)` | Stop all subscriptions + `el.remove()`. |
386
+ | `unmount(el)` | Stop all subscriptions. Element stays in DOM. |
387
+ | `ReactiveElement` | Base class for Custom Elements. Override `mount(ctx)`, call `invalidate()` from setters. |
388
+ | `configure({ log? })` | `true` = console logger, `{ log(msg, meta?) }` = your logger, `null` = off. Same pattern as `@evgkch/reactive`. |
408
389
 
409
390
  ---
410
391
 
411
- ## GitHub Pages
412
-
413
- Examples are deployed automatically on push to `main`. The workflow builds the lib, fills `docs/` (gitignored) with the site, and deploys it — no duplicated code in the repo.
392
+ ## Examples
414
393
 
415
- **One-time setup:** **Settings → Pages → Build and deployment → Source** must be **GitHub Actions** (not "Deploy from a branch"). If you see 404 for `dist/index.js` or `lib/index.js`, the live site is coming from the branch; switch Source to **GitHub Actions** and re-run the workflow.
394
+ Live: `https://evgkch.github.io/reactive-dom/` monitor, console, stream.
416
395
 
417
- Site: `https://<username>.github.io/<repo>/`. To test the build locally: `npm run build:gh-pages` then serve the `docs/` folder.
396
+ Local: `npm run examples`
418
397
 
419
398
  ---
420
399
 
@@ -1,5 +1,5 @@
1
1
  import { Context } from "./ctx.js";
2
- import { logMount } from "./log.js";
2
+ import { getLogger, PREFIX } from "./log.js";
3
3
  import { setComponentStop } from "./unmount.js";
4
4
  export function createElement(tmpl, setup, props) {
5
5
  const fragment = tmpl.content.cloneNode(true);
@@ -22,7 +22,7 @@ export function createElement(tmpl, setup, props) {
22
22
  s();
23
23
  };
24
24
  setComponentStop(el, stop);
25
- logMount(el);
25
+ getLogger()?.log(`${PREFIX} [struct:mount]`, el);
26
26
  return el;
27
27
  }
28
28
  export function parseTemplate(html) {
package/dist/index.d.ts CHANGED
@@ -6,6 +6,6 @@ export type { Watchable, ListSetup } from "./list.js";
6
6
  export { Slot } from "./slot.js";
7
7
  export { unmount, remove } from "./unmount.js";
8
8
  export { ReactiveElement } from "./element.js";
9
- export { configure, getComponentId, setLogLevel } from "./log.js";
10
- export type { LogLevel, ConfigureOptions } from "./log.js";
9
+ export { configure, setLogger, getLogger } from "./log.js";
10
+ export type { ReactiveDomLogger, ConfigureOptions } from "./log.js";
11
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC9C,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACrD,YAAY,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAClE,YAAY,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC9C,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACrD,YAAY,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC3D,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js CHANGED
@@ -3,4 +3,4 @@ export { List } from "./list.js";
3
3
  export { Slot } from "./slot.js";
4
4
  export { unmount, remove } from "./unmount.js";
5
5
  export { ReactiveElement } from "./element.js";
6
- export { configure, getComponentId, setLogLevel } from "./log.js";
6
+ export { configure, setLogger, getLogger } from "./log.js";
package/dist/list.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createElement } from "./create-element.js";
2
2
  import { parseTemplate } from "./create-element.js";
3
- import { logListAdd, logListMount, logListRemove } from "./log.js";
3
+ import { getLogger, PREFIX } from "./log.js";
4
4
  import { unmount } from "./unmount.js";
5
5
  /**
6
6
  * Mount reactive list into container. Patches via Watch.
@@ -13,7 +13,7 @@ export function mountList(container, source, factory, hooks) {
13
13
  nodes.set(item, el);
14
14
  container.insertBefore(el, ref);
15
15
  if (runOnAdd) {
16
- logListAdd(el);
16
+ getLogger()?.log(`${PREFIX} [list:add]`, el);
17
17
  hooks?.onAdd?.(el);
18
18
  }
19
19
  return el;
@@ -23,7 +23,7 @@ export function mountList(container, source, factory, hooks) {
23
23
  if (!el)
24
24
  return;
25
25
  nodes.delete(item);
26
- logListRemove(el);
26
+ getLogger()?.log(`${PREFIX} [list:remove]`, el);
27
27
  unmount(el);
28
28
  if (hooks?.onRemove) {
29
29
  hooks.onRemove(el, () => el.remove());
@@ -32,7 +32,7 @@ export function mountList(container, source, factory, hooks) {
32
32
  el.remove();
33
33
  }
34
34
  }
35
- logListMount(container);
35
+ getLogger()?.log(`${PREFIX} [list:mount]`, container);
36
36
  // Initial mount: no onAdd so items are visible immediately (onAdd often animates from opacity 0)
37
37
  source.forEach((item) => addItem(item, null, false));
38
38
  const stopWatch = source.watch(({ start, removed, added }) => {
package/dist/log.d.ts CHANGED
@@ -1,18 +1,13 @@
1
- export type LogLevel = false | true | "verbose";
2
- export declare function setLogLevel(level: LogLevel): void;
1
+ /** Same shape as @evgkch/reactive: one logger, inline at call site, no overhead when null. */
2
+ export declare const PREFIX = "[reactive-dom]";
3
+ export interface ReactiveDomLogger {
4
+ log(message: string, meta?: unknown): void;
5
+ }
6
+ export declare function setLogger(lg: ReactiveDomLogger | null): void;
7
+ export declare function getLogger(): ReactiveDomLogger | null;
3
8
  export interface ConfigureOptions {
4
- log?: LogLevel;
9
+ /** `null` = off, `true` = default console, or your logger. No overhead when null. */
10
+ log?: ReactiveDomLogger | null | true;
5
11
  }
6
12
  export declare function configure(opts?: ConfigureOptions): void;
7
- export declare function getLogLevel(): LogLevel;
8
- /** Stable id for a component root element — use in console to find references. */
9
- export declare function getComponentId(el: Element): string;
10
- export declare function logMount(el: Element): void;
11
- export declare function logUnmount(el: Element): void;
12
- export declare function logListMount(container: Element): void;
13
- export declare function logListAdd(el: Element): void;
14
- export declare function logListRemove(el: Element): void;
15
- export declare function logSlotMount(container: Element): void;
16
- export declare function logSlotSwap(container: Element, prev: Element | null, next: Element | null): void;
17
- export declare function logSlotStop(container: Element): void;
18
13
  //# sourceMappingURL=log.d.ts.map
package/dist/log.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,IAAI,GAAG,SAAS,CAAC;AAOhD,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAEjD;AAED,MAAM,WAAW,gBAAgB;IAC7B,GAAG,CAAC,EAAE,QAAQ,CAAC;CAClB;AAED,wBAAgB,SAAS,CAAC,IAAI,GAAE,gBAAqB,GAAG,IAAI,CAE3D;AAED,wBAAgB,WAAW,IAAI,QAAQ,CAEtC;AAED,kFAAkF;AAClF,wBAAgB,cAAc,CAAC,EAAE,EAAE,OAAO,GAAG,MAAM,CAOlD;AAUD,wBAAgB,QAAQ,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAK1C;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAI5C;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CAIrD;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAI5C;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAI/C;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CAIrD;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI,CAMhG;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CAIpD"}
1
+ {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,eAAO,MAAM,MAAM,mBAAmB,CAAC;AAEvC,MAAM,WAAW,iBAAiB;IAC9B,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC9C;AAID,wBAAgB,SAAS,CAAC,EAAE,EAAE,iBAAiB,GAAG,IAAI,GAAG,IAAI,CAE5D;AAED,wBAAgB,SAAS,IAAI,iBAAiB,GAAG,IAAI,CAEpD;AAED,MAAM,WAAW,gBAAgB;IAC7B,qFAAqF;IACrF,GAAG,CAAC,EAAE,iBAAiB,GAAG,IAAI,GAAG,IAAI,CAAC;CACzC;AAED,wBAAgB,SAAS,CAAC,IAAI,GAAE,gBAAqB,GAAG,IAAI,CAe3D"}
package/dist/log.js CHANGED
@@ -1,84 +1,26 @@
1
- const PREFIX = "[reactive-dom]";
2
- let logLevel = false;
3
- const componentIds = new WeakMap();
4
- let nextId = 0;
5
- export function setLogLevel(level) {
6
- logLevel = level;
1
+ /** Same shape as @evgkch/reactive: one logger, inline at call site, no overhead when null. */
2
+ export const PREFIX = "[reactive-dom]";
3
+ let logger = null;
4
+ export function setLogger(lg) {
5
+ logger = lg;
7
6
  }
8
- export function configure(opts = {}) {
9
- if (opts.log !== undefined)
10
- setLogLevel(opts.log);
11
- }
12
- export function getLogLevel() {
13
- return logLevel;
14
- }
15
- /** Stable id for a component root element — use in console to find references. */
16
- export function getComponentId(el) {
17
- let id = componentIds.get(el);
18
- if (id === undefined) {
19
- id = `c${++nextId}`;
20
- componentIds.set(el, id);
21
- }
22
- return id;
23
- }
24
- function log(msg, id, extra) {
25
- if (!logLevel)
26
- return;
27
- const parts = [PREFIX, msg];
28
- if (id)
29
- parts.push(id);
30
- if (extra)
31
- parts.push(extra);
32
- console.log(parts.join(" "));
7
+ export function getLogger() {
8
+ return logger;
33
9
  }
34
- export function logMount(el) {
35
- if (!logLevel)
36
- return;
37
- const id = getComponentId(el);
38
- const tag = el.tagName.toLowerCase();
39
- log("mount", id, `<${tag}>`);
40
- }
41
- export function logUnmount(el) {
42
- if (!logLevel)
43
- return;
44
- const id = getComponentId(el);
45
- log("unmount", id);
46
- }
47
- export function logListMount(container) {
48
- if (!logLevel)
49
- return;
50
- const id = getComponentId(container);
51
- log("list mount", id);
52
- }
53
- export function logListAdd(el) {
54
- if (!logLevel)
55
- return;
56
- const id = getComponentId(el);
57
- log("list add", id);
58
- }
59
- export function logListRemove(el) {
60
- if (!logLevel)
61
- return;
62
- const id = getComponentId(el);
63
- log("list remove", id);
64
- }
65
- export function logSlotMount(container) {
66
- if (!logLevel)
10
+ export function configure(opts = {}) {
11
+ if (opts.log === undefined)
67
12
  return;
68
- const id = getComponentId(container);
69
- log("slot mount", id);
70
- }
71
- export function logSlotSwap(container, prev, next) {
72
- if (!logLevel)
13
+ if (opts.log === null) {
14
+ logger = null;
73
15
  return;
74
- const id = getComponentId(container);
75
- const prevId = prev ? getComponentId(prev) : "-";
76
- const nextId = next ? getComponentId(next) : "-";
77
- log("slot swap", id, `${prevId} → ${nextId}`);
78
- }
79
- export function logSlotStop(container) {
80
- if (!logLevel)
16
+ }
17
+ if (opts.log === true) {
18
+ logger = {
19
+ log(msg, meta) {
20
+ console.log(msg, meta ?? "");
21
+ },
22
+ };
81
23
  return;
82
- const id = getComponentId(container);
83
- log("slot stop", id);
24
+ }
25
+ logger = opts.log;
84
26
  }
package/dist/slot.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Batch } from "@evgkch/reactive";
2
- import { logSlotMount, logSlotStop, logSlotSwap } from "./log.js";
2
+ import { getLogger, PREFIX } from "./log.js";
3
3
  import { unmount } from "./unmount.js";
4
4
  /**
5
5
  * Conditional slot. Getter returns Element | null.
@@ -8,7 +8,7 @@ import { unmount } from "./unmount.js";
8
8
  export function Slot(container, getter) {
9
9
  const anchor = document.createComment("slot");
10
10
  container.appendChild(anchor);
11
- logSlotMount(container);
11
+ getLogger()?.log(`${PREFIX} [slot:mount]`, container);
12
12
  let current = null;
13
13
  const stopBatch = Batch(() => {
14
14
  const next = getter();
@@ -21,12 +21,12 @@ export function Slot(container, getter) {
21
21
  if (next) {
22
22
  container.insertBefore(next, anchor);
23
23
  }
24
- logSlotSwap(container, current, next);
24
+ getLogger()?.log(`${PREFIX} [slot:swap]`, { container, prev: current, next });
25
25
  current = next;
26
26
  });
27
27
  const stop = () => {
28
28
  stopBatch();
29
- logSlotStop(container);
29
+ getLogger()?.log(`${PREFIX} [slot:stop]`, container);
30
30
  if (current) {
31
31
  unmount(current);
32
32
  current.remove();
package/dist/unmount.js CHANGED
@@ -1,4 +1,4 @@
1
- import { logUnmount } from "./log.js";
1
+ import { getLogger, PREFIX } from "./log.js";
2
2
  /**
3
3
  * WeakMap: Element → stop. No leak — GC collects with the element.
4
4
  */
@@ -10,7 +10,7 @@ export function setComponentStop(el, stop) {
10
10
  export function unmount(el) {
11
11
  const stop = componentMap.get(el);
12
12
  if (stop) {
13
- logUnmount(el);
13
+ getLogger()?.log(`${PREFIX} [struct:unmount]`, el);
14
14
  stop();
15
15
  componentMap.delete(el);
16
16
  }
@@ -9,18 +9,18 @@
9
9
  </head>
10
10
  <body>
11
11
  <h1>Examples</h1>
12
- <p>From repo root: <code>npm run build && npm run examples</code>, then open the links below.</p>
12
+ <p><code>npm run build && npm run examples</code>, then open the links below.</p>
13
13
  <ul>
14
14
  <li>
15
- <a href="./monitor/">Monitor</a>
15
+ <a href="./pages/monitor/">Monitor</a>
16
16
  <span>Fake process monitor: list, add, toggle, kill, CPU/MEM/procs, hotkeys</span>
17
17
  </li>
18
18
  <li>
19
- <a href="./console/">Console</a>
19
+ <a href="./pages/console/">Console</a>
20
20
  <span>REPL-style console: type help, ping, echo &lt;msg&gt;, clear</span>
21
21
  </li>
22
22
  <li>
23
- <a href="./stream/">Stream</a>
23
+ <a href="./pages/stream/">Stream</a>
24
24
  <span>Live log stream: filter by level (info/warn/err), clear</span>
25
25
  </li>
26
26
  </ul>