micra.js 1.1.0 → 2.1.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.
package/src/dom/events.ts CHANGED
@@ -13,7 +13,7 @@
13
13
 
14
14
  import type { InternalInstance, MicraElement, StateRecord } from '../types'
15
15
  import { warn } from '../utils/expr'
16
- import { queryOwn, queryAll } from './query'
16
+ import { queryOwn, queryOwnAll, queryAll } from './query'
17
17
 
18
18
  /** @internal Attach a DOM listener and track it on the instance for destroy(). */
19
19
  function track<S extends StateRecord>(
@@ -94,7 +94,7 @@ export function bindAtEvents<S extends StateRecord>(
94
94
  const isFragment = root.nodeType === 11
95
95
  const all = isFragment
96
96
  ? queryAll(root as unknown as ParentNode, '*')
97
- : queryAll(root, '*')
97
+ : queryOwnAll(root, '*')
98
98
 
99
99
  // Include root itself for the regular-element case
100
100
  if (!isFragment && !all.includes(root)) all.unshift(root)
package/src/dom/query.ts CHANGED
@@ -25,7 +25,21 @@ export function queryAll(root: ParentNode, sel: string): Element[] {
25
25
  * owned by that nested component, not by root's component — so we skip it.
26
26
  */
27
27
  export function queryOwn(root: Element, attr: string): Element[] {
28
- return queryAll(root, `[${attr}]`).filter(el => {
28
+ return filterOwn(root, queryAll(root, `[${attr}]`))
29
+ }
30
+
31
+ /**
32
+ * Like queryOwn but accepts an arbitrary CSS selector. Used by bindAtEvents
33
+ * which scans `*` for `@`-prefixed attribute names (no attribute selector exists
34
+ * for those).
35
+ */
36
+ export function queryOwnAll(root: Element, sel: string): Element[] {
37
+ return filterOwn(root, queryAll(root, sel))
38
+ }
39
+
40
+ /** @internal Shared subtree-ownership filter. */
41
+ function filterOwn(root: Element, els: Element[]): Element[] {
42
+ return els.filter(el => {
29
43
  let node: Element | null = el.parentElement
30
44
  while (node && node !== root) {
31
45
  if (node.hasAttribute('data-component')) return false
package/src/index.ts CHANGED
@@ -17,7 +17,7 @@
17
17
  * - SSR-friendly: Micra.start() is safe to call multiple times
18
18
  * - Directive cache: O(1) re-renders after first mount
19
19
  *
20
- * Size target: < 5 KB minified+gzipped
20
+ * Size target: < 5.5 KB minified+gzipped
21
21
  *
22
22
  * @module Micra
23
23
  */
package/src/types.ts CHANGED
@@ -138,6 +138,15 @@ export interface CachedBinding {
138
138
  expr: string
139
139
  }
140
140
 
141
+ /**
142
+ * @internal data-if binding — like CachedBinding but also carries the
143
+ * placeholder Comment that takes the element's slot in the DOM while the
144
+ * element is detached (unmounted).
145
+ */
146
+ export interface CachedIfBinding extends CachedBinding {
147
+ placeholder?: Comment
148
+ }
149
+
141
150
  /**
142
151
  * @internal Per-element directive binding with pre-parsed pairs.
143
152
  * Used by `data-bind` and `data-class` — both share the
@@ -159,7 +168,7 @@ export interface CachedPairBinding {
159
168
  export interface DirectiveCache {
160
169
  text: CachedBinding[]
161
170
  html: CachedBinding[]
162
- if: CachedBinding[]
171
+ if: CachedIfBinding[]
163
172
  show: CachedBinding[]
164
173
  bind: CachedPairBinding[]
165
174
  model: CachedBinding[]
@@ -77,18 +77,20 @@ export async function micraFetch(url: string, options: FetchOptions = {}): Promi
77
77
  if (method === 'GET' || method === 'HEAD') {
78
78
  const params: Record<string, string> = {}
79
79
  for (const [k, v] of Object.entries(options)) {
80
- if (k !== 'method' && k !== 'headers' && v != null) params[k] = String(v)
80
+ if (k !== 'method' && k !== 'headers' && k !== 'signal' && v != null)
81
+ params[k] = String(v)
81
82
  }
82
83
  if (Object.keys(params).length)
83
84
  finalUrl += (url.includes('?') ? '&' : '?') + new URLSearchParams(params)
84
- } else {
85
+ } else if (options.body !== undefined) {
85
86
  headers['Content-Type'] = 'application/json'
86
- body = JSON.stringify(options.body !== undefined ? options.body : options)
87
+ body = JSON.stringify(options.body)
87
88
  }
88
89
 
89
90
  const res = await fetch(finalUrl, {
90
91
  method,
91
92
  headers,
93
+ ...(options.signal !== undefined ? { signal: options.signal as AbortSignal } : {}),
92
94
  ...(body !== undefined ? { body } : {}),
93
95
  })
94
96