microtags 0.0.3 → 0.0.5

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 CHANGED
@@ -9,9 +9,9 @@
9
9
  [![gzip size](https://img.shields.io/bundlephobia/minzip/microtags?style=flat-square)](https://bundlephobia.com/package/microtags)
10
10
  [![license](https://img.shields.io/badge/license-Big_Time-blue?style=flat-square)](LICENSE)
11
11
 
12
- Web component helper with reactive props, automatic cleanup,
13
- and no Shadow DOM. Reactive via
14
- [alien-signals](https://github.com/stackblitz/alien-signals).
12
+ Web component helper with reactive props, automatic cleanup, and no
13
+ [Shadow DOM](https://gomakethings.com/the-shadow-dom-is-an-antipattern/).
14
+ Reactive via [alien-signals](https://github.com/stackblitz/alien-signals).
15
15
 
16
16
  **Size**: 3.41 kB with all dependencies, minified and brotlied
17
17
 
@@ -36,13 +36,17 @@ Inspired by [nanotags](https://nanotags.psdcoder.dev/). Reactive props use
36
36
  * [Refs](#refs)
37
37
  + [`withRefs`](#withrefs)
38
38
  + [Element types](#element-types)
39
- * [define](#define)
40
- * [withContexts](#withcontexts)
41
- * [setup and ctx](#setup-and-ctx)
39
+ * [`define`](#define)
40
+ * [`withContexts`](#withcontexts)
41
+ * [`setup` and `ctx`](#setup-and-ctx)
42
42
  + [peek and untracked](#peek-and-untracked)
43
- - [Subpaths](#subpaths)
44
- * [microtags/context](#microtagscontext)
45
- * [microtags/render](#microtagsrender)
43
+ - [Templates](#templates)
44
+ - [Subpath Exports](#subpath-exports)
45
+ * [`microtags/context`](#microtagscontext)
46
+ * [`microtags/render`](#microtagsrender)
47
+ + [`renderList`](#renderlist)
48
+ + [`render`](#render)
49
+ + [Templates](#templates-1)
46
50
  - [Divergence from nanotags](#divergence-from-nanotags)
47
51
 
48
52
  <!-- tocstop -->
@@ -356,8 +360,8 @@ it instead. Queries are resolved relative to the host element, during
356
360
 
357
361
  ```ts
358
362
  .withRefs(r => ({
359
- button: r.one(), // resolves [data-ref="button"] — throws if missing
360
- items: r.all(), // resolves all [data-ref="items"] — empty array if none
363
+ button: r.one(), // resolves [data-ref="button"] (throws if missing)
364
+ items: r.all(), // resolves all [data-ref="items"] (empty array if none)
361
365
  }))
362
366
  ```
363
367
 
@@ -407,46 +411,83 @@ constrained to `HTMLElement`, so cast at the use site if you need one
407
411
  (e.g. `ctx.refs.icon as unknown as SVGSVGElement`).
408
412
 
409
413
 
410
- ### define
414
+ ### `define`
411
415
 
412
416
  ```ts
413
417
  import { define } from 'microtags'
414
418
 
415
- define(tagName: string): ComponentBuilder
419
+ define(tagName:string):ComponentBuilder
416
420
  ```
417
421
 
418
422
  Returns a fluent builder. Call `.setup()` to register the custom element.
419
423
  All builder methods are chainable and fully type-inferred.
420
424
 
421
- ### withContexts
425
+ ### `withContexts`
426
+
427
+ Context is for the case where a parent component defines some state, e.g. a
428
+ `theme`, and the top-level (application) knows nothing about the theme. But the
429
+ top level application does determine what children the parent renders
430
+ (it passes in children), and the children need to know the theme.
431
+
432
+ In that case, there is no way for the "parent" component to pass the theme to
433
+ the children, and the root level (application) doesn't know what the theme is.
434
+
435
+ Context gives us a "pull" based way to model this. The context-provider
436
+ publishes the value, and any descendants opt in (or pull values) with
437
+ `withContexts`, and the unknown-consumer and late-arrival problems are handled.
438
+
439
+ >
440
+ > [!IMPORTANT]
441
+ > Context is only relevant on the client-side.
442
+ > Context gives you nothing for first paint or the no-JS case.
443
+ >
444
+
422
445
 
423
446
  ```ts
424
- import { createContext } from 'microtags/context'
447
+ import { define } from 'microtags'
448
+ import { createContext, provide } from 'microtags/context'
425
449
 
426
450
  const ThemeToken = createContext<string>()
427
451
 
428
- define('my-child')
452
+ // Parent: owns the theme and publishes it to its descendants. It does
453
+ // not reference the children below. The application composes those in.
454
+ define('theme-provider')
455
+ .setup(ctx => {
456
+ ctx.onCleanup(provide(ctx.host, ThemeToken, 'dark'))
457
+ })
458
+
459
+ // Child: opts in to the theme and reads whichever provider it is
460
+ // composed into. It never receives the value as a prop.
461
+ define('themed-card')
429
462
  .withContexts(() => ({
430
463
  theme: ThemeToken,
431
464
  }))
432
465
  .setup(ctx => {
433
466
  ctx.effect(() => {
434
- console.log('theme is', ctx.contexts.theme)
467
+ ctx.host.dataset.theme = ctx.contexts.theme
435
468
  })
436
469
  })
437
470
  ```
438
471
 
472
+ ```html
473
+ <!-- The application writes this markup. It nests <themed-card> inside
474
+ <theme-provider> but never sets the theme itself. -->
475
+ <theme-provider>
476
+ <themed-card>Card content</themed-card>
477
+ </theme-provider>
478
+ ```
479
+
439
480
  `setup()` is deferred until all required contexts resolve. See
440
- [microtags/context](#microtagscontext) for the provider API.
481
+ [`microtags/context`](#microtagscontext) for the provider API.
441
482
 
442
- ### setup and ctx
483
+ ### `setup` and `ctx`
443
484
 
444
485
  ```ts
445
486
  .setup(ctx => {
446
- // ctx.host — the HTMLElement itself
447
- // ctx.props — callable signals for each declared prop
448
- // ctx.refs — resolved DOM refs
449
- // ctx.contexts — consumed context values (reactive)
487
+ // ctx.host the HTMLElement itself
488
+ // ctx.props callable signals for each declared prop
489
+ // ctx.refs resolved DOM refs
490
+ // ctx.contexts consumed context values (reactive)
450
491
 
451
492
  // Run a tracked effect; auto-disposed on disconnect.
452
493
  ctx.effect(() => {
@@ -486,46 +527,265 @@ ctx.effect(() => {
486
527
  })
487
528
  ```
488
529
 
489
- ## Subpaths
530
+ ## Templates
531
+
532
+ [`render` and `renderList`](#microtagsrender) both take a `<template>`
533
+ element as their second argument. A
534
+ [`<template>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/template)
535
+ is a native HTML element that holds markup the browser parses but does not
536
+ render. Its contents are inert. Nothing inside paints, scripts do not run,
537
+ images and other resources do not load, and the nodes stay out of the
538
+ document until you clone them.
490
539
 
491
- ### microtags/context
540
+ See [./example/ssr.ts, line 89](./example/ssr.ts#L89).
492
541
 
493
- Cross-component context backed by alien-signals.
542
+ You write the markup for an item inside the template, and the
543
+ reconciler stamps out a copy per item by cloning `template.content`. The
544
+ template's first element child is the unit that gets cloned, so give each
545
+ template a single root element.
546
+
547
+ ```html
548
+ <item-list>
549
+ <ul></ul>
550
+ <template>
551
+ <li>
552
+ <span class="name"></span>
553
+ <span class="count"></span>
554
+ </li>
555
+ </template>
556
+ </item-list>
557
+ ```
558
+
559
+ The component resolves the list and the template as refs, then calls
560
+ `renderList` from inside an effect so the list re-renders whenever the
561
+ data signal changes.
494
562
 
495
563
  ```ts
496
- import { createContext, provide, resolveContextSignal } from 'microtags/context'
564
+ import { define } from 'microtags'
565
+ import { signal } from 'alien-signals'
566
+ import { renderList } from 'microtags/render'
567
+
568
+ type Item = { id:string; name:string; count:number }
569
+
570
+ define('item-list')
571
+ .withRefs(r => ({
572
+ list: r.one('ul'),
573
+ row: r.one('template'), // typed as HTMLTemplateElement
574
+ }))
575
+ .setup(ctx => {
576
+ const items = signal<Item[]>([
577
+ { id: 'a', name: 'Alice', count: 1 },
578
+ { id: 'b', name: 'Bob', count: 2 },
579
+ ])
580
+
581
+ // Re-runs on every items() change; only changed rows are touched.
582
+ ctx.effect(() => {
583
+ renderList(ctx.refs.list, ctx.refs.row, {
584
+ data: items(),
585
+ key: item => item.id,
586
+ update: (el, item) => {
587
+ el.querySelector('.name')!.textContent = item.name
588
+ el.querySelector('.count')!.textContent =
589
+ String(item.count)
590
+ },
591
+ })
592
+ })
593
+ })
594
+ ```
595
+
596
+ Because the template lives in your HTML instead of a JavaScript string, each
597
+ render clones real DOM nodes rather than re-parsing HTML, and the `update`
598
+ callback function mutates the DOM through properties like `textContent`.
599
+ Nothing concatenates strings into markup, so there is no string-injection
600
+ XSS vector.
601
+
602
+ ## Subpath Exports
603
+
604
+ ### `microtags/context`
605
+
606
+ Cross-component context backed by alien-signals. A provider shares a value
607
+ with everything nested inside it; a consumer reads the value from its nearest
608
+ provider ancestor and reacts when it changes.
609
+
610
+ `provide` seeds a signal the first time it runs and updates that same signal
611
+ on later calls, so re-providing inside an effect pushes changes to consumers.
612
+
613
+ ```ts
614
+ import { define } from 'microtags'
615
+ import { signal } from 'alien-signals'
616
+ import { createContext, provide } from 'microtags/context'
497
617
 
498
618
  const ThemeToken = createContext<string>()
499
619
 
500
- // In a provider component's setup():
501
- const cleanup = provide(ctx.host, ThemeToken, 'dark')
502
- ctx.onCleanup(cleanup)
620
+ // Provider: owns the theme and shares it with its descendants.
621
+ define('theme-provider')
622
+ .withRefs(r => ({
623
+ toggle: r.one('button'),
624
+ }))
625
+ .setup(ctx => {
626
+ const theme = signal('light')
627
+
628
+ // Share with descendants; remove the entry on disconnect.
629
+ ctx.onCleanup(provide(ctx.host, ThemeToken, theme()))
630
+
631
+ // Re-provide on change. provide() updates the existing signal,
632
+ // so consumers that read this value re-run.
633
+ ctx.effect(() => provide(ctx.host, ThemeToken, theme()))
634
+
635
+ ctx.on(ctx.refs.toggle, 'click', () => {
636
+ theme(theme() === 'light' ? 'dark' : 'light')
637
+ })
638
+ })
639
+
640
+ // Consumer: reads the nearest provider's theme, reactively.
641
+ define('themed-card')
642
+ .withContexts(() => ({
643
+ theme: ThemeToken,
644
+ }))
645
+ .setup(ctx => {
646
+ // Reading ctx.contexts.theme inside an effect subscribes to it,
647
+ // so this re-runs whenever the provider toggles the value.
648
+ ctx.effect(() => {
649
+ ctx.host.dataset.theme = ctx.contexts.theme
650
+ })
651
+ })
652
+ ```
653
+
654
+ ```html
655
+ <theme-provider>
656
+ <button>Toggle theme</button>
657
+ <themed-card>Card A</themed-card>
658
+ <themed-card>Card B</themed-card>
659
+ </theme-provider>
660
+ ```
661
+
662
+ Both cards resolve to the same nearest `<theme-provider>`, so one click
663
+ updates both. `resolveContextSignal(element, token)` is the low-level lookup
664
+ those reads use under the hood; reach for it only when you need the provider
665
+ signal imperatively.
666
+
667
+ ---
668
+
669
+ ### `microtags/render`
670
+
671
+ A keyed reconciler for rendering lists and single items from a
672
+ `<template>`, with no virtual DOM and no diffing library.
673
+
674
+ Microtags' state is reactive. When a signal changes, you want the DOM to follow.
675
+ A way to do that for a list is to rebuild the markup on every change,
676
+ e.g. `container.innerHTML = items.map(toHTML).join('')`.
503
677
 
504
- // In a consumer component via withContexts:
505
- ctx.contexts.theme // 'dark' (reactive — re-runs effects when value changes)
678
+ But rebuilding the markup on every change
679
+ **throws away and recreates every node on every update**, losing any DOM
680
+ state, like focus, text selection, scroll position, and the internal state of
681
+ elements like `<video>`, `<details>`, and `<iframe>`. It also re-parses HTML on
682
+ every pass, which creates a vector for string-concatenation XSS.
683
+
684
+ **This library exports a `renderList` and a `render` function**.
685
+ They diff against the live DOM. See
686
+ [example/render-demo.ts](./example/render-demo.ts) for a working example.
687
+
688
+ #### `renderList`
689
+
690
+ Each item is matched to an existing element by its `key` attribute, so nodes
691
+ are reused across renders rather than being recreated. Nodes whose keys
692
+ disappear from the data are removed, surviving nodes are moved into place
693
+ with the fewest DOM operations, and your `update` callback runs only for
694
+ items whose value actually changed (compared by identity). Focus state and other
695
+ DOM state survives a re-render, and changing one row touches only that row.
696
+
697
+ ```ts
698
+ function renderList<T, E extends Element = Element> (
699
+ container:Element,
700
+ template:HTMLTemplateElement,
701
+ options:{
702
+ data:readonly T[];
703
+ key:(item:T, index:number) => string|number;
704
+ update:(el:E, item:T) => void;
705
+ }
706
+ ):void
506
707
  ```
507
708
 
508
- ### microtags/render
709
+ ##### `renderList` Example
509
710
 
510
- DOM diffing helpers for rendering lists and single items without a virtual DOM.
711
+ The markup is the list container plus a `<template>` for one row:
712
+
713
+ ```html
714
+ <ul></ul>
715
+ <template>
716
+ <li>
717
+ <span class="name"></span>
718
+ </li>
719
+ </template>
720
+ ```
721
+
722
+ Call `renderList` from inside an effect so it re-runs whenever the data
723
+ signal changes:
511
724
 
512
725
  ```ts
513
- import { render, renderList } from 'microtags/render'
726
+ ctx.effect(() => {
727
+ renderList(ctx.refs.list, ctx.refs.row, {
728
+ data: items(),
729
+ key: item => item.id,
730
+ update: (el, item) => {
731
+ el.querySelector('.name')!.textContent = item.name
732
+ },
733
+ })
734
+ })
735
+ ```
736
+
737
+ #### `render`
738
+
739
+ `render` is the same machinery for a single item. It keys on the template, so
740
+ repeated calls update the same element in place instead of replacing it.
741
+
742
+ ```ts
743
+ function render<T, E extends Element = Element> (
744
+ container:Element,
745
+ template:HTMLTemplateElement,
746
+ options?:{
747
+ data?:T;
748
+ update?:(el:E, item:T) => void;
749
+ }
750
+ ):void
751
+ ```
514
752
 
515
- // Render a single item into a container from a <template>.
753
+ ##### `render` Example
754
+
755
+ The markup is a container plus a single-item `<template>`:
756
+
757
+ ```html
758
+ <div class="item"></div>
759
+ <template>
760
+ <div>
761
+ <span></span>
762
+ </div>
763
+ </template>
764
+ ```
765
+
766
+ ```ts
767
+ import { render } from 'microtags/render'
768
+
769
+ // Render a single item into the container from the <template>.
516
770
  render(container, template, {
517
771
  data: item,
518
- update: (el, item) => { el.querySelector('span')!.textContent = item.name },
519
- })
520
-
521
- // Render a keyed list with minimal DOM mutations.
522
- renderList(container, template, {
523
- data: items,
524
- key: item => item.id,
525
- update: (el, item) => { /* update el from item */ },
772
+ update: (el, item) => {
773
+ el.querySelector('span')!.textContent = item.name
774
+ },
526
775
  })
527
776
  ```
528
777
 
778
+ For a runnable version, see `example/render-demo.ts`. It keeps an editable
779
+ `<input>` in every row and a single item panel rendered with `render`, then
780
+ re-renders on a timer and reorders on demand to show that focus, caret, and
781
+ typed text survive: keyed nodes are reused and at most moved, never rebuilt.
782
+
783
+ #### Templates
784
+
785
+
786
+
787
+ ---
788
+
529
789
  ## Divergence from nanotags
530
790
 
531
791
  | nanotags (nanostores) | microtags (alien-signals) |
@@ -536,8 +796,4 @@ renderList(container, template, {
536
796
  | `ctx.props.$count` (store) | `ctx.props.count` (callable signal) |
537
797
 
538
798
  Props declared with `withProps` are native alien-signals callable signals.
539
- There is no `$` prefix convention — all prop names are plain identifiers.
540
-
541
- ```
542
- /ed3d-plan-and-execute:execute-implementation-plan /Users/nick/code/_microtags/docs/implementation-plans/2026-06-28-zod-validation-example/ /Users/nick/code/_microtags/
543
- ```
799
+ There is no `$` prefix convention; all prop names are plain identifiers.
package/dist/meta.json CHANGED
@@ -133,7 +133,7 @@
133
133
  "format": "esm"
134
134
  },
135
135
  "src/render.ts": {
136
- "bytes": 2601,
136
+ "bytes": 2457,
137
137
  "imports": [
138
138
  {
139
139
  "path": "<runtime>",
@@ -192,7 +192,7 @@
192
192
  "imports": [],
193
193
  "exports": [],
194
194
  "inputs": {},
195
- "bytes": 4204
195
+ "bytes": 4056
196
196
  },
197
197
  "dist/render.js": {
198
198
  "imports": [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/render.ts"],
4
- "sourcesContent": ["export type RenderListOptions<T, E extends Element = Element> = {\n data:readonly T[];\n key:(item:T, index:number) => string | number;\n update:(el:E, item:T) => void;\n}\n\nexport type RenderOptions<T, E extends Element = Element> = {\n data?:T;\n update?:(el:E, item:T) => void;\n}\n\nconst elementData = new WeakMap<Element, unknown>()\nconst elementKeys = new WeakMap<Element, string | number>()\n\nexport function renderList<T, E extends Element = Element> (\n container:Element,\n template:HTMLTemplateElement,\n options:RenderListOptions<T, E>\n):void {\n const { data, key: getKey, update } = options\n const existingByKey = new Map<string | number, E>()\n\n for (const child of Array.from(container.children)) {\n const key = elementKeys.get(child)\n if (key !== undefined) existingByKey.set(key, child as E)\n }\n\n const dataKeys = new Set(data.map(getKey))\n\n for (const child of Array.from(container.children)) {\n const key = elementKeys.get(child)\n if (key === undefined || !dataKeys.has(key)) {\n child.remove()\n elementKeys.delete(child)\n elementData.delete(child)\n }\n }\n\n let prev:E | null = null\n for (let i = 0; i < data.length; i++) {\n const item = data[i]\n const key = getKey(item, i)\n let el = existingByKey.get(key)\n\n if (!el) {\n el = (template.content.cloneNode(true) as Element)\n .firstElementChild as E\n elementKeys.set(el, key)\n }\n\n if (elementData.get(el) !== item) {\n update(el, item)\n elementData.set(el, item)\n }\n\n const expected = prev ?\n prev.nextElementSibling :\n container.firstElementChild\n\n if (el !== expected) {\n if (prev) prev.after(el)\n else container.prepend(el)\n }\n\n prev = el\n }\n}\n\nlet _tplCounter = 0\nconst tplIds = new WeakMap<HTMLTemplateElement, number>()\n\nexport function render<T, E extends Element = Element> (\n container:Element,\n template:HTMLTemplateElement,\n options?:RenderOptions<T, E>\n):void {\n renderList<T, E>(container, template, {\n data: [\n options !== undefined && 'data' in options ?\n options.data as T :\n {} as T\n ],\n key: () => {\n let id = tplIds.get(template)\n if (id === undefined) {\n id = _tplCounter++\n tplIds.set(template, id)\n }\n return id\n },\n update: options?.update ?? (() => {}),\n })\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWA,IAAM,cAAc,oBAAI,QAA0B;AAClD,IAAM,cAAc,oBAAI,QAAkC;AAEnD,SAAS,WACZ,WACA,UACA,SACG;AACH,QAAM,EAAE,MAAM,KAAK,QAAQ,OAAO,IAAI;AACtC,QAAM,gBAAgB,oBAAI,IAAwB;AAElD,aAAW,SAAS,MAAM,KAAK,UAAU,QAAQ,GAAG;AAChD,UAAM,MAAM,YAAY,IAAI,KAAK;AACjC,QAAI,QAAQ,OAAW,eAAc,IAAI,KAAK,KAAU;AAAA,EAC5D;AAEA,QAAM,WAAW,IAAI,IAAI,KAAK,IAAI,MAAM,CAAC;AAEzC,aAAW,SAAS,MAAM,KAAK,UAAU,QAAQ,GAAG;AAChD,UAAM,MAAM,YAAY,IAAI,KAAK;AACjC,QAAI,QAAQ,UAAa,CAAC,SAAS,IAAI,GAAG,GAAG;AACzC,YAAM,OAAO;AACb,kBAAY,OAAO,KAAK;AACxB,kBAAY,OAAO,KAAK;AAAA,IAC5B;AAAA,EACJ;AAEA,MAAI,OAAgB;AACpB,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AAClC,UAAM,OAAO,KAAK,CAAC;AACnB,UAAM,MAAM,OAAO,MAAM,CAAC;AAC1B,QAAI,KAAK,cAAc,IAAI,GAAG;AAE9B,QAAI,CAAC,IAAI;AACL,WAAM,SAAS,QAAQ,UAAU,IAAI,EAChC;AACL,kBAAY,IAAI,IAAI,GAAG;AAAA,IAC3B;AAEA,QAAI,YAAY,IAAI,EAAE,MAAM,MAAM;AAC9B,aAAO,IAAI,IAAI;AACf,kBAAY,IAAI,IAAI,IAAI;AAAA,IAC5B;AAEA,UAAM,WAAW,OACb,KAAK,qBACL,UAAU;AAEd,QAAI,OAAO,UAAU;AACjB,UAAI,KAAM,MAAK,MAAM,EAAE;AAAA,UAClB,WAAU,QAAQ,EAAE;AAAA,IAC7B;AAEA,WAAO;AAAA,EACX;AACJ;AApDgB;AAsDhB,IAAI,cAAc;AAClB,IAAM,SAAS,oBAAI,QAAqC;AAEjD,SAAS,OACZ,WACA,UACA,SACG;AACH,aAAiB,WAAW,UAAU;AAAA,IAClC,MAAM;AAAA,MACF,YAAY,UAAa,UAAU,UAC/B,QAAQ,OACR,CAAC;AAAA,IACT;AAAA,IACA,KAAK,6BAAM;AACP,UAAI,KAAK,OAAO,IAAI,QAAQ;AAC5B,UAAI,OAAO,QAAW;AAClB,aAAK;AACL,eAAO,IAAI,UAAU,EAAE;AAAA,MAC3B;AACA,aAAO;AAAA,IACX,GAPK;AAAA,IAQL,QAAQ,SAAS,WAAW,MAAM;AAAA,IAAC;AAAA,EACvC,CAAC;AACL;AArBgB;",
4
+ "sourcesContent": ["const elementData = new WeakMap<Element, unknown>()\nconst elementKeys = new WeakMap<Element, string | number>()\n\nexport function renderList<T, E extends Element = Element> (\n container:Element,\n template:HTMLTemplateElement,\n options:{\n data:readonly T[];\n key:(item:T, index:number) => string|number;\n update:(el:E, item:T) => void;\n }\n):void {\n const { data, key: getKey, update } = options\n const existingByKey = new Map<string | number, E>()\n\n for (const child of Array.from(container.children)) {\n const key = elementKeys.get(child)\n if (key !== undefined) existingByKey.set(key, child as E)\n }\n\n const dataKeys = new Set(data.map(getKey))\n\n for (const child of Array.from(container.children)) {\n const key = elementKeys.get(child)\n if (key === undefined || !dataKeys.has(key)) {\n child.remove()\n elementKeys.delete(child)\n elementData.delete(child)\n }\n }\n\n let prev:E | null = null\n for (let i = 0; i < data.length; i++) {\n const item = data[i]\n const key = getKey(item, i)\n let el = existingByKey.get(key)\n\n if (!el) {\n el = (template.content.cloneNode(true) as Element)\n .firstElementChild as E\n elementKeys.set(el, key)\n }\n\n if (elementData.get(el) !== item) {\n update(el, item)\n elementData.set(el, item)\n }\n\n const expected = prev ?\n prev.nextElementSibling :\n container.firstElementChild\n\n if (el !== expected) {\n if (prev) prev.after(el)\n else container.prepend(el)\n }\n\n prev = el\n }\n}\n\nlet _tplCounter = 0\nconst tplIds = new WeakMap<HTMLTemplateElement, number>()\n\nexport function render<T, E extends Element = Element> (\n container:Element,\n template:HTMLTemplateElement,\n options?:{\n data?:T;\n update?:(el:E, item:T) => void;\n }\n):void {\n renderList<T, E>(container, template, {\n data: [\n options !== undefined && 'data' in options ?\n options.data as T :\n {} as T\n ],\n key: () => {\n let id = tplIds.get(template)\n if (id === undefined) {\n id = _tplCounter++\n tplIds.set(template, id)\n }\n return id\n },\n update: options?.update ?? (() => {}),\n })\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAM,cAAc,oBAAI,QAA0B;AAClD,IAAM,cAAc,oBAAI,QAAkC;AAEnD,SAAS,WACZ,WACA,UACA,SAKG;AACH,QAAM,EAAE,MAAM,KAAK,QAAQ,OAAO,IAAI;AACtC,QAAM,gBAAgB,oBAAI,IAAwB;AAElD,aAAW,SAAS,MAAM,KAAK,UAAU,QAAQ,GAAG;AAChD,UAAM,MAAM,YAAY,IAAI,KAAK;AACjC,QAAI,QAAQ,OAAW,eAAc,IAAI,KAAK,KAAU;AAAA,EAC5D;AAEA,QAAM,WAAW,IAAI,IAAI,KAAK,IAAI,MAAM,CAAC;AAEzC,aAAW,SAAS,MAAM,KAAK,UAAU,QAAQ,GAAG;AAChD,UAAM,MAAM,YAAY,IAAI,KAAK;AACjC,QAAI,QAAQ,UAAa,CAAC,SAAS,IAAI,GAAG,GAAG;AACzC,YAAM,OAAO;AACb,kBAAY,OAAO,KAAK;AACxB,kBAAY,OAAO,KAAK;AAAA,IAC5B;AAAA,EACJ;AAEA,MAAI,OAAgB;AACpB,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AAClC,UAAM,OAAO,KAAK,CAAC;AACnB,UAAM,MAAM,OAAO,MAAM,CAAC;AAC1B,QAAI,KAAK,cAAc,IAAI,GAAG;AAE9B,QAAI,CAAC,IAAI;AACL,WAAM,SAAS,QAAQ,UAAU,IAAI,EAChC;AACL,kBAAY,IAAI,IAAI,GAAG;AAAA,IAC3B;AAEA,QAAI,YAAY,IAAI,EAAE,MAAM,MAAM;AAC9B,aAAO,IAAI,IAAI;AACf,kBAAY,IAAI,IAAI,IAAI;AAAA,IAC5B;AAEA,UAAM,WAAW,OACb,KAAK,qBACL,UAAU;AAEd,QAAI,OAAO,UAAU;AACjB,UAAI,KAAM,MAAK,MAAM,EAAE;AAAA,UAClB,WAAU,QAAQ,EAAE;AAAA,IAC7B;AAEA,WAAO;AAAA,EACX;AACJ;AAxDgB;AA0DhB,IAAI,cAAc;AAClB,IAAM,SAAS,oBAAI,QAAqC;AAEjD,SAAS,OACZ,WACA,UACA,SAIG;AACH,aAAiB,WAAW,UAAU;AAAA,IAClC,MAAM;AAAA,MACF,YAAY,UAAa,UAAU,UAC/B,QAAQ,OACR,CAAC;AAAA,IACT;AAAA,IACA,KAAK,6BAAM;AACP,UAAI,KAAK,OAAO,IAAI,QAAQ;AAC5B,UAAI,OAAO,QAAW;AAClB,aAAK;AACL,eAAO,IAAI,UAAU,EAAE;AAAA,MAC3B;AACA,aAAO;AAAA,IACX,GAPK;AAAA,IAQL,QAAQ,SAAS,WAAW,MAAM;AAAA,IAAC;AAAA,EACvC,CAAC;AACL;AAxBgB;",
6
6
  "names": []
7
7
  }
package/dist/render.d.ts CHANGED
@@ -1,12 +1,10 @@
1
- export type RenderListOptions<T, E extends Element = Element> = {
1
+ export declare function renderList<T, E extends Element = Element>(container: Element, template: HTMLTemplateElement, options: {
2
2
  data: readonly T[];
3
3
  key: (item: T, index: number) => string | number;
4
4
  update: (el: E, item: T) => void;
5
- };
6
- export type RenderOptions<T, E extends Element = Element> = {
5
+ }): void;
6
+ export declare function render<T, E extends Element = Element>(container: Element, template: HTMLTemplateElement, options?: {
7
7
  data?: T;
8
8
  update?: (el: E, item: T) => void;
9
- };
10
- export declare function renderList<T, E extends Element = Element>(container: Element, template: HTMLTemplateElement, options: RenderListOptions<T, E>): void;
11
- export declare function render<T, E extends Element = Element>(container: Element, template: HTMLTemplateElement, options?: RenderOptions<T, E>): void;
9
+ }): void;
12
10
  //# sourceMappingURL=render.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,CAAC,CAAC,EAAE,CAAC,SAAS,OAAO,GAAG,OAAO,IAAI;IAC5D,IAAI,EAAC,SAAS,CAAC,EAAE,CAAC;IAClB,GAAG,EAAC,CAAC,IAAI,EAAC,CAAC,EAAE,KAAK,EAAC,MAAM,KAAK,MAAM,GAAG,MAAM,CAAC;IAC9C,MAAM,EAAC,CAAC,EAAE,EAAC,CAAC,EAAE,IAAI,EAAC,CAAC,KAAK,IAAI,CAAC;CACjC,CAAA;AAED,MAAM,MAAM,aAAa,CAAC,CAAC,EAAE,CAAC,SAAS,OAAO,GAAG,OAAO,IAAI;IACxD,IAAI,CAAC,EAAC,CAAC,CAAC;IACR,MAAM,CAAC,EAAC,CAAC,EAAE,EAAC,CAAC,EAAE,IAAI,EAAC,CAAC,KAAK,IAAI,CAAC;CAClC,CAAA;AAKD,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,SAAS,OAAO,GAAG,OAAO,EACrD,SAAS,EAAC,OAAO,EACjB,QAAQ,EAAC,mBAAmB,EAC5B,OAAO,EAAC,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,GACjC,IAAI,CAgDL;AAKD,wBAAgB,MAAM,CAAC,CAAC,EAAE,CAAC,SAAS,OAAO,GAAG,OAAO,EACjD,SAAS,EAAC,OAAO,EACjB,QAAQ,EAAC,mBAAmB,EAC5B,OAAO,CAAC,EAAC,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,GAC9B,IAAI,CAiBL"}
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAGA,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,SAAS,OAAO,GAAG,OAAO,EACrD,SAAS,EAAC,OAAO,EACjB,QAAQ,EAAC,mBAAmB,EAC5B,OAAO,EAAC;IACJ,IAAI,EAAC,SAAS,CAAC,EAAE,CAAC;IAClB,GAAG,EAAC,CAAC,IAAI,EAAC,CAAC,EAAE,KAAK,EAAC,MAAM,KAAK,MAAM,GAAC,MAAM,CAAC;IAC5C,MAAM,EAAC,CAAC,EAAE,EAAC,CAAC,EAAE,IAAI,EAAC,CAAC,KAAK,IAAI,CAAC;CACjC,GACH,IAAI,CAgDL;AAKD,wBAAgB,MAAM,CAAC,CAAC,EAAE,CAAC,SAAS,OAAO,GAAG,OAAO,EACjD,SAAS,EAAC,OAAO,EACjB,QAAQ,EAAC,mBAAmB,EAC5B,OAAO,CAAC,EAAC;IACL,IAAI,CAAC,EAAC,CAAC,CAAC;IACR,MAAM,CAAC,EAAC,CAAC,EAAE,EAAC,CAAC,EAAE,IAAI,EAAC,CAAC,KAAK,IAAI,CAAC;CAClC,GACH,IAAI,CAiBL"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/render.ts"],
4
- "sourcesContent": ["export type RenderListOptions<T, E extends Element = Element> = {\n data:readonly T[];\n key:(item:T, index:number) => string | number;\n update:(el:E, item:T) => void;\n}\n\nexport type RenderOptions<T, E extends Element = Element> = {\n data?:T;\n update?:(el:E, item:T) => void;\n}\n\nconst elementData = new WeakMap<Element, unknown>()\nconst elementKeys = new WeakMap<Element, string | number>()\n\nexport function renderList<T, E extends Element = Element> (\n container:Element,\n template:HTMLTemplateElement,\n options:RenderListOptions<T, E>\n):void {\n const { data, key: getKey, update } = options\n const existingByKey = new Map<string | number, E>()\n\n for (const child of Array.from(container.children)) {\n const key = elementKeys.get(child)\n if (key !== undefined) existingByKey.set(key, child as E)\n }\n\n const dataKeys = new Set(data.map(getKey))\n\n for (const child of Array.from(container.children)) {\n const key = elementKeys.get(child)\n if (key === undefined || !dataKeys.has(key)) {\n child.remove()\n elementKeys.delete(child)\n elementData.delete(child)\n }\n }\n\n let prev:E | null = null\n for (let i = 0; i < data.length; i++) {\n const item = data[i]\n const key = getKey(item, i)\n let el = existingByKey.get(key)\n\n if (!el) {\n el = (template.content.cloneNode(true) as Element)\n .firstElementChild as E\n elementKeys.set(el, key)\n }\n\n if (elementData.get(el) !== item) {\n update(el, item)\n elementData.set(el, item)\n }\n\n const expected = prev ?\n prev.nextElementSibling :\n container.firstElementChild\n\n if (el !== expected) {\n if (prev) prev.after(el)\n else container.prepend(el)\n }\n\n prev = el\n }\n}\n\nlet _tplCounter = 0\nconst tplIds = new WeakMap<HTMLTemplateElement, number>()\n\nexport function render<T, E extends Element = Element> (\n container:Element,\n template:HTMLTemplateElement,\n options?:RenderOptions<T, E>\n):void {\n renderList<T, E>(container, template, {\n data: [\n options !== undefined && 'data' in options ?\n options.data as T :\n {} as T\n ],\n key: () => {\n let id = tplIds.get(template)\n if (id === undefined) {\n id = _tplCounter++\n tplIds.set(template, id)\n }\n return id\n },\n update: options?.update ?? (() => {}),\n })\n}\n"],
5
- "mappings": ";;;;;AAWA,IAAM,cAAc,oBAAI,QAA0B;AAClD,IAAM,cAAc,oBAAI,QAAkC;AAEnD,SAAS,WACZ,WACA,UACA,SACG;AACH,QAAM,EAAE,MAAM,KAAK,QAAQ,OAAO,IAAI;AACtC,QAAM,gBAAgB,oBAAI,IAAwB;AAElD,aAAW,SAAS,MAAM,KAAK,UAAU,QAAQ,GAAG;AAChD,UAAM,MAAM,YAAY,IAAI,KAAK;AACjC,QAAI,QAAQ,OAAW,eAAc,IAAI,KAAK,KAAU;AAAA,EAC5D;AAEA,QAAM,WAAW,IAAI,IAAI,KAAK,IAAI,MAAM,CAAC;AAEzC,aAAW,SAAS,MAAM,KAAK,UAAU,QAAQ,GAAG;AAChD,UAAM,MAAM,YAAY,IAAI,KAAK;AACjC,QAAI,QAAQ,UAAa,CAAC,SAAS,IAAI,GAAG,GAAG;AACzC,YAAM,OAAO;AACb,kBAAY,OAAO,KAAK;AACxB,kBAAY,OAAO,KAAK;AAAA,IAC5B;AAAA,EACJ;AAEA,MAAI,OAAgB;AACpB,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AAClC,UAAM,OAAO,KAAK,CAAC;AACnB,UAAM,MAAM,OAAO,MAAM,CAAC;AAC1B,QAAI,KAAK,cAAc,IAAI,GAAG;AAE9B,QAAI,CAAC,IAAI;AACL,WAAM,SAAS,QAAQ,UAAU,IAAI,EAChC;AACL,kBAAY,IAAI,IAAI,GAAG;AAAA,IAC3B;AAEA,QAAI,YAAY,IAAI,EAAE,MAAM,MAAM;AAC9B,aAAO,IAAI,IAAI;AACf,kBAAY,IAAI,IAAI,IAAI;AAAA,IAC5B;AAEA,UAAM,WAAW,OACb,KAAK,qBACL,UAAU;AAEd,QAAI,OAAO,UAAU;AACjB,UAAI,KAAM,MAAK,MAAM,EAAE;AAAA,UAClB,WAAU,QAAQ,EAAE;AAAA,IAC7B;AAEA,WAAO;AAAA,EACX;AACJ;AApDgB;AAsDhB,IAAI,cAAc;AAClB,IAAM,SAAS,oBAAI,QAAqC;AAEjD,SAAS,OACZ,WACA,UACA,SACG;AACH,aAAiB,WAAW,UAAU;AAAA,IAClC,MAAM;AAAA,MACF,YAAY,UAAa,UAAU,UAC/B,QAAQ,OACR,CAAC;AAAA,IACT;AAAA,IACA,KAAK,6BAAM;AACP,UAAI,KAAK,OAAO,IAAI,QAAQ;AAC5B,UAAI,OAAO,QAAW;AAClB,aAAK;AACL,eAAO,IAAI,UAAU,EAAE;AAAA,MAC3B;AACA,aAAO;AAAA,IACX,GAPK;AAAA,IAQL,QAAQ,SAAS,WAAW,MAAM;AAAA,IAAC;AAAA,EACvC,CAAC;AACL;AArBgB;",
4
+ "sourcesContent": ["const elementData = new WeakMap<Element, unknown>()\nconst elementKeys = new WeakMap<Element, string | number>()\n\nexport function renderList<T, E extends Element = Element> (\n container:Element,\n template:HTMLTemplateElement,\n options:{\n data:readonly T[];\n key:(item:T, index:number) => string|number;\n update:(el:E, item:T) => void;\n }\n):void {\n const { data, key: getKey, update } = options\n const existingByKey = new Map<string | number, E>()\n\n for (const child of Array.from(container.children)) {\n const key = elementKeys.get(child)\n if (key !== undefined) existingByKey.set(key, child as E)\n }\n\n const dataKeys = new Set(data.map(getKey))\n\n for (const child of Array.from(container.children)) {\n const key = elementKeys.get(child)\n if (key === undefined || !dataKeys.has(key)) {\n child.remove()\n elementKeys.delete(child)\n elementData.delete(child)\n }\n }\n\n let prev:E | null = null\n for (let i = 0; i < data.length; i++) {\n const item = data[i]\n const key = getKey(item, i)\n let el = existingByKey.get(key)\n\n if (!el) {\n el = (template.content.cloneNode(true) as Element)\n .firstElementChild as E\n elementKeys.set(el, key)\n }\n\n if (elementData.get(el) !== item) {\n update(el, item)\n elementData.set(el, item)\n }\n\n const expected = prev ?\n prev.nextElementSibling :\n container.firstElementChild\n\n if (el !== expected) {\n if (prev) prev.after(el)\n else container.prepend(el)\n }\n\n prev = el\n }\n}\n\nlet _tplCounter = 0\nconst tplIds = new WeakMap<HTMLTemplateElement, number>()\n\nexport function render<T, E extends Element = Element> (\n container:Element,\n template:HTMLTemplateElement,\n options?:{\n data?:T;\n update?:(el:E, item:T) => void;\n }\n):void {\n renderList<T, E>(container, template, {\n data: [\n options !== undefined && 'data' in options ?\n options.data as T :\n {} as T\n ],\n key: () => {\n let id = tplIds.get(template)\n if (id === undefined) {\n id = _tplCounter++\n tplIds.set(template, id)\n }\n return id\n },\n update: options?.update ?? (() => {}),\n })\n}\n"],
5
+ "mappings": ";;;;;AAAA,IAAM,cAAc,oBAAI,QAA0B;AAClD,IAAM,cAAc,oBAAI,QAAkC;AAEnD,SAAS,WACZ,WACA,UACA,SAKG;AACH,QAAM,EAAE,MAAM,KAAK,QAAQ,OAAO,IAAI;AACtC,QAAM,gBAAgB,oBAAI,IAAwB;AAElD,aAAW,SAAS,MAAM,KAAK,UAAU,QAAQ,GAAG;AAChD,UAAM,MAAM,YAAY,IAAI,KAAK;AACjC,QAAI,QAAQ,OAAW,eAAc,IAAI,KAAK,KAAU;AAAA,EAC5D;AAEA,QAAM,WAAW,IAAI,IAAI,KAAK,IAAI,MAAM,CAAC;AAEzC,aAAW,SAAS,MAAM,KAAK,UAAU,QAAQ,GAAG;AAChD,UAAM,MAAM,YAAY,IAAI,KAAK;AACjC,QAAI,QAAQ,UAAa,CAAC,SAAS,IAAI,GAAG,GAAG;AACzC,YAAM,OAAO;AACb,kBAAY,OAAO,KAAK;AACxB,kBAAY,OAAO,KAAK;AAAA,IAC5B;AAAA,EACJ;AAEA,MAAI,OAAgB;AACpB,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AAClC,UAAM,OAAO,KAAK,CAAC;AACnB,UAAM,MAAM,OAAO,MAAM,CAAC;AAC1B,QAAI,KAAK,cAAc,IAAI,GAAG;AAE9B,QAAI,CAAC,IAAI;AACL,WAAM,SAAS,QAAQ,UAAU,IAAI,EAChC;AACL,kBAAY,IAAI,IAAI,GAAG;AAAA,IAC3B;AAEA,QAAI,YAAY,IAAI,EAAE,MAAM,MAAM;AAC9B,aAAO,IAAI,IAAI;AACf,kBAAY,IAAI,IAAI,IAAI;AAAA,IAC5B;AAEA,UAAM,WAAW,OACb,KAAK,qBACL,UAAU;AAEd,QAAI,OAAO,UAAU;AACjB,UAAI,KAAM,MAAK,MAAM,EAAE;AAAA,UAClB,WAAU,QAAQ,EAAE;AAAA,IAC7B;AAEA,WAAO;AAAA,EACX;AACJ;AAxDgB;AA0DhB,IAAI,cAAc;AAClB,IAAM,SAAS,oBAAI,QAAqC;AAEjD,SAAS,OACZ,WACA,UACA,SAIG;AACH,aAAiB,WAAW,UAAU;AAAA,IAClC,MAAM;AAAA,MACF,YAAY,UAAa,UAAU,UAC/B,QAAQ,OACR,CAAC;AAAA,IACT;AAAA,IACA,KAAK,6BAAM;AACP,UAAI,KAAK,OAAO,IAAI,QAAQ;AAC5B,UAAI,OAAO,QAAW;AAClB,aAAK;AACL,eAAO,IAAI,UAAU,EAAE;AAAA,MAC3B;AACA,aAAO;AAAA,IACX,GAPK;AAAA,IAQL,QAAQ,SAAS,WAAW,MAAM;AAAA,IAAC;AAAA,EACvC,CAAC;AACL;AAxBgB;",
6
6
  "names": []
7
7
  }
@@ -1,2 +1,2 @@
1
- var g=Object.defineProperty;var a=(d,i)=>g(d,"name",{value:i,configurable:!0});var o=new WeakMap,m=new WeakMap;function x(d,i,s){let{data:l,key:E,update:T}=s,p=new Map;for(let e of Array.from(d.children)){let t=m.get(e);t!==void 0&&p.set(t,e)}let c=new Set(l.map(E));for(let e of Array.from(d.children)){let t=m.get(e);(t===void 0||!c.has(t))&&(e.remove(),m.delete(e),o.delete(e))}let r=null;for(let e=0;e<l.length;e++){let t=l[e],f=E(t,e),n=p.get(f);n||(n=i.content.cloneNode(!0).firstElementChild,m.set(n,f)),o.get(n)!==t&&(T(n,t),o.set(n,t));let y=r?r.nextElementSibling:d.firstElementChild;n!==y&&(r?r.after(n):d.prepend(n)),r=n}}a(x,"renderList");var k=0,u=new WeakMap;function M(d,i,s){x(d,i,{data:[s!==void 0&&"data"in s?s.data:{}],key:a(()=>{let l=u.get(i);return l===void 0&&(l=k++,u.set(i,l)),l},"key"),update:s?.update??(()=>{})})}a(M,"render");export{M as render,x as renderList};
1
+ var g=Object.defineProperty;var s=(l,a)=>g(l,"name",{value:a,configurable:!0});var o=new WeakMap,m=new WeakMap;function k(l,a,i){let{data:d,key:E,update:p}=i,f=new Map;for(let e of Array.from(l.children)){let t=m.get(e);t!==void 0&&f.set(t,e)}let T=new Set(d.map(E));for(let e of Array.from(l.children)){let t=m.get(e);(t===void 0||!T.has(t))&&(e.remove(),m.delete(e),o.delete(e))}let r=null;for(let e=0;e<d.length;e++){let t=d[e],u=E(t,e),n=f.get(u);n||(n=a.content.cloneNode(!0).firstElementChild,m.set(n,u)),o.get(n)!==t&&(p(n,t),o.set(n,t));let y=r?r.nextElementSibling:l.firstElementChild;n!==y&&(r?r.after(n):l.prepend(n)),r=n}}s(k,"renderList");var h=0,c=new WeakMap;function M(l,a,i){k(l,a,{data:[i!==void 0&&"data"in i?i.data:{}],key:s(()=>{let d=c.get(a);return d===void 0&&(d=h++,c.set(a,d)),d},"key"),update:i?.update??(()=>{})})}s(M,"render");export{M as render,k as renderList};
2
2
  //# sourceMappingURL=render.min.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/render.ts"],
4
- "sourcesContent": ["export type RenderListOptions<T, E extends Element = Element> = {\n data:readonly T[];\n key:(item:T, index:number) => string | number;\n update:(el:E, item:T) => void;\n}\n\nexport type RenderOptions<T, E extends Element = Element> = {\n data?:T;\n update?:(el:E, item:T) => void;\n}\n\nconst elementData = new WeakMap<Element, unknown>()\nconst elementKeys = new WeakMap<Element, string | number>()\n\nexport function renderList<T, E extends Element = Element> (\n container:Element,\n template:HTMLTemplateElement,\n options:RenderListOptions<T, E>\n):void {\n const { data, key: getKey, update } = options\n const existingByKey = new Map<string | number, E>()\n\n for (const child of Array.from(container.children)) {\n const key = elementKeys.get(child)\n if (key !== undefined) existingByKey.set(key, child as E)\n }\n\n const dataKeys = new Set(data.map(getKey))\n\n for (const child of Array.from(container.children)) {\n const key = elementKeys.get(child)\n if (key === undefined || !dataKeys.has(key)) {\n child.remove()\n elementKeys.delete(child)\n elementData.delete(child)\n }\n }\n\n let prev:E | null = null\n for (let i = 0; i < data.length; i++) {\n const item = data[i]\n const key = getKey(item, i)\n let el = existingByKey.get(key)\n\n if (!el) {\n el = (template.content.cloneNode(true) as Element)\n .firstElementChild as E\n elementKeys.set(el, key)\n }\n\n if (elementData.get(el) !== item) {\n update(el, item)\n elementData.set(el, item)\n }\n\n const expected = prev ?\n prev.nextElementSibling :\n container.firstElementChild\n\n if (el !== expected) {\n if (prev) prev.after(el)\n else container.prepend(el)\n }\n\n prev = el\n }\n}\n\nlet _tplCounter = 0\nconst tplIds = new WeakMap<HTMLTemplateElement, number>()\n\nexport function render<T, E extends Element = Element> (\n container:Element,\n template:HTMLTemplateElement,\n options?:RenderOptions<T, E>\n):void {\n renderList<T, E>(container, template, {\n data: [\n options !== undefined && 'data' in options ?\n options.data as T :\n {} as T\n ],\n key: () => {\n let id = tplIds.get(template)\n if (id === undefined) {\n id = _tplCounter++\n tplIds.set(template, id)\n }\n return id\n },\n update: options?.update ?? (() => {}),\n })\n}\n"],
5
- "mappings": "+EAWA,IAAMA,EAAc,IAAI,QAClBC,EAAc,IAAI,QAEjB,SAASC,EACZC,EACAC,EACAC,EACG,CACH,GAAM,CAAE,KAAAC,EAAM,IAAKC,EAAQ,OAAAC,CAAO,EAAIH,EAChCI,EAAgB,IAAI,IAE1B,QAAWC,KAAS,MAAM,KAAKP,EAAU,QAAQ,EAAG,CAChD,IAAMQ,EAAMV,EAAY,IAAIS,CAAK,EAC7BC,IAAQ,QAAWF,EAAc,IAAIE,EAAKD,CAAU,CAC5D,CAEA,IAAME,EAAW,IAAI,IAAIN,EAAK,IAAIC,CAAM,CAAC,EAEzC,QAAWG,KAAS,MAAM,KAAKP,EAAU,QAAQ,EAAG,CAChD,IAAMQ,EAAMV,EAAY,IAAIS,CAAK,GAC7BC,IAAQ,QAAa,CAACC,EAAS,IAAID,CAAG,KACtCD,EAAM,OAAO,EACbT,EAAY,OAAOS,CAAK,EACxBV,EAAY,OAAOU,CAAK,EAEhC,CAEA,IAAIG,EAAgB,KACpB,QAASC,EAAI,EAAGA,EAAIR,EAAK,OAAQQ,IAAK,CAClC,IAAMC,EAAOT,EAAKQ,CAAC,EACbH,EAAMJ,EAAOQ,EAAMD,CAAC,EACtBE,EAAKP,EAAc,IAAIE,CAAG,EAEzBK,IACDA,EAAMZ,EAAS,QAAQ,UAAU,EAAI,EAChC,kBACLH,EAAY,IAAIe,EAAIL,CAAG,GAGvBX,EAAY,IAAIgB,CAAE,IAAMD,IACxBP,EAAOQ,EAAID,CAAI,EACff,EAAY,IAAIgB,EAAID,CAAI,GAG5B,IAAME,EAAWJ,EACbA,EAAK,mBACLV,EAAU,kBAEVa,IAAOC,IACHJ,EAAMA,EAAK,MAAMG,CAAE,EAClBb,EAAU,QAAQa,CAAE,GAG7BH,EAAOG,CACX,CACJ,CApDgBE,EAAAhB,EAAA,cAsDhB,IAAIiB,EAAc,EACZC,EAAS,IAAI,QAEZ,SAASC,EACZlB,EACAC,EACAC,EACG,CACHH,EAAiBC,EAAWC,EAAU,CAClC,KAAM,CACFC,IAAY,QAAa,SAAUA,EAC/BA,EAAQ,KACR,CAAC,CACT,EACA,IAAKa,EAAA,IAAM,CACP,IAAII,EAAKF,EAAO,IAAIhB,CAAQ,EAC5B,OAAIkB,IAAO,SACPA,EAAKH,IACLC,EAAO,IAAIhB,EAAUkB,CAAE,GAEpBA,CACX,EAPK,OAQL,OAAQjB,GAAS,SAAW,IAAM,CAAC,EACvC,CAAC,CACL,CArBgBa,EAAAG,EAAA",
4
+ "sourcesContent": ["const elementData = new WeakMap<Element, unknown>()\nconst elementKeys = new WeakMap<Element, string | number>()\n\nexport function renderList<T, E extends Element = Element> (\n container:Element,\n template:HTMLTemplateElement,\n options:{\n data:readonly T[];\n key:(item:T, index:number) => string|number;\n update:(el:E, item:T) => void;\n }\n):void {\n const { data, key: getKey, update } = options\n const existingByKey = new Map<string | number, E>()\n\n for (const child of Array.from(container.children)) {\n const key = elementKeys.get(child)\n if (key !== undefined) existingByKey.set(key, child as E)\n }\n\n const dataKeys = new Set(data.map(getKey))\n\n for (const child of Array.from(container.children)) {\n const key = elementKeys.get(child)\n if (key === undefined || !dataKeys.has(key)) {\n child.remove()\n elementKeys.delete(child)\n elementData.delete(child)\n }\n }\n\n let prev:E | null = null\n for (let i = 0; i < data.length; i++) {\n const item = data[i]\n const key = getKey(item, i)\n let el = existingByKey.get(key)\n\n if (!el) {\n el = (template.content.cloneNode(true) as Element)\n .firstElementChild as E\n elementKeys.set(el, key)\n }\n\n if (elementData.get(el) !== item) {\n update(el, item)\n elementData.set(el, item)\n }\n\n const expected = prev ?\n prev.nextElementSibling :\n container.firstElementChild\n\n if (el !== expected) {\n if (prev) prev.after(el)\n else container.prepend(el)\n }\n\n prev = el\n }\n}\n\nlet _tplCounter = 0\nconst tplIds = new WeakMap<HTMLTemplateElement, number>()\n\nexport function render<T, E extends Element = Element> (\n container:Element,\n template:HTMLTemplateElement,\n options?:{\n data?:T;\n update?:(el:E, item:T) => void;\n }\n):void {\n renderList<T, E>(container, template, {\n data: [\n options !== undefined && 'data' in options ?\n options.data as T :\n {} as T\n ],\n key: () => {\n let id = tplIds.get(template)\n if (id === undefined) {\n id = _tplCounter++\n tplIds.set(template, id)\n }\n return id\n },\n update: options?.update ?? (() => {}),\n })\n}\n"],
5
+ "mappings": "+EAAA,IAAMA,EAAc,IAAI,QAClBC,EAAc,IAAI,QAEjB,SAASC,EACZC,EACAC,EACAC,EAKG,CACH,GAAM,CAAE,KAAAC,EAAM,IAAKC,EAAQ,OAAAC,CAAO,EAAIH,EAChCI,EAAgB,IAAI,IAE1B,QAAWC,KAAS,MAAM,KAAKP,EAAU,QAAQ,EAAG,CAChD,IAAMQ,EAAMV,EAAY,IAAIS,CAAK,EAC7BC,IAAQ,QAAWF,EAAc,IAAIE,EAAKD,CAAU,CAC5D,CAEA,IAAME,EAAW,IAAI,IAAIN,EAAK,IAAIC,CAAM,CAAC,EAEzC,QAAWG,KAAS,MAAM,KAAKP,EAAU,QAAQ,EAAG,CAChD,IAAMQ,EAAMV,EAAY,IAAIS,CAAK,GAC7BC,IAAQ,QAAa,CAACC,EAAS,IAAID,CAAG,KACtCD,EAAM,OAAO,EACbT,EAAY,OAAOS,CAAK,EACxBV,EAAY,OAAOU,CAAK,EAEhC,CAEA,IAAIG,EAAgB,KACpB,QAASC,EAAI,EAAGA,EAAIR,EAAK,OAAQQ,IAAK,CAClC,IAAMC,EAAOT,EAAKQ,CAAC,EACbH,EAAMJ,EAAOQ,EAAMD,CAAC,EACtBE,EAAKP,EAAc,IAAIE,CAAG,EAEzBK,IACDA,EAAMZ,EAAS,QAAQ,UAAU,EAAI,EAChC,kBACLH,EAAY,IAAIe,EAAIL,CAAG,GAGvBX,EAAY,IAAIgB,CAAE,IAAMD,IACxBP,EAAOQ,EAAID,CAAI,EACff,EAAY,IAAIgB,EAAID,CAAI,GAG5B,IAAME,EAAWJ,EACbA,EAAK,mBACLV,EAAU,kBAEVa,IAAOC,IACHJ,EAAMA,EAAK,MAAMG,CAAE,EAClBb,EAAU,QAAQa,CAAE,GAG7BH,EAAOG,CACX,CACJ,CAxDgBE,EAAAhB,EAAA,cA0DhB,IAAIiB,EAAc,EACZC,EAAS,IAAI,QAEZ,SAASC,EACZlB,EACAC,EACAC,EAIG,CACHH,EAAiBC,EAAWC,EAAU,CAClC,KAAM,CACFC,IAAY,QAAa,SAAUA,EAC/BA,EAAQ,KACR,CAAC,CACT,EACA,IAAKa,EAAA,IAAM,CACP,IAAII,EAAKF,EAAO,IAAIhB,CAAQ,EAC5B,OAAIkB,IAAO,SACPA,EAAKH,IACLC,EAAO,IAAIhB,EAAUkB,CAAE,GAEpBA,CACX,EAPK,OAQL,OAAQjB,GAAS,SAAW,IAAM,CAAC,EACvC,CAAC,CACL,CAxBgBa,EAAAG,EAAA",
6
6
  "names": ["elementData", "elementKeys", "renderList", "container", "template", "options", "data", "getKey", "update", "existingByKey", "child", "key", "dataKeys", "prev", "i", "item", "el", "expected", "__name", "_tplCounter", "tplIds", "render", "id"]
7
7
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "microtags",
3
3
  "type": "module",
4
- "version": "0.0.3",
4
+ "version": "0.0.5",
5
5
  "sideEffects": [
6
6
  "./example/**"
7
7
  ],