what-core 0.5.6 → 0.6.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/dist/dom.js CHANGED
@@ -1,13 +1,6 @@
1
- import { effect, batch, untrack, signal } from './reactive.js';
2
- import { reportError, _injectGetCurrentComponent } from './components.js';
1
+ import { effect, batch, untrack, signal, __DEV__, __devtools } from './reactive.js';
2
+ import { reportError, _injectGetCurrentComponent, shallowEqual } from './components.js';
3
3
  import { _setComponentRef } from './helpers.js';
4
- if (typeof customElements !== 'undefined' && !customElements.get('what-c')) {
5
- customElements.define('what-c', class extends HTMLElement {
6
- connectedCallback() {
7
- this.style.display = 'contents';
8
- }
9
- });
10
- }
11
4
  const SVG_ELEMENTS = new Set([
12
5
  'svg', 'path', 'circle', 'rect', 'line', 'polyline', 'polygon', 'ellipse',
13
6
  'g', 'defs', 'use', 'symbol', 'clipPath', 'mask', 'pattern', 'image',
@@ -31,9 +24,21 @@ return !!value && typeof value === 'object' && (value._vnode === true || 'tag' i
31
24
  function disposeComponent(ctx) {
32
25
  if (ctx.disposed) return;
33
26
  ctx.disposed = true;
27
+ if (ctx.cleanups) {
28
+ for (const cleanup of ctx.cleanups) {
29
+ try { cleanup(); } catch (e) { console.error('[what] cleanup error:', e); }
30
+ }
31
+ }
32
+ if (ctx.effects) {
33
+ for (const dispose of ctx.effects) {
34
+ try { dispose(); } catch (e) { }
35
+ }
36
+ }
37
+ if (ctx.hooks) {
34
38
  for (const hook of ctx.hooks) {
35
- if (hook && typeof hook === 'object' && 'cleanup' in hook && hook.cleanup) {
36
- try { hook.cleanup(); } catch (e) { console.error('[what] cleanup error:', e); }
39
+ if (hook && typeof hook.cleanup === 'function') {
40
+ try { hook.cleanup(); } catch (e) { console.error('[what] hook cleanup error:', e); }
41
+ }
37
42
  }
38
43
  }
39
44
  if (ctx._cleanupCallbacks) {
@@ -41,9 +46,7 @@ for (const fn of ctx._cleanupCallbacks) {
41
46
  try { fn(); } catch (e) { console.error('[what] onCleanup error:', e); }
42
47
  }
43
48
  }
44
- for (const dispose of ctx.effects) {
45
- try { dispose(); } catch (e) { }
46
- }
49
+ if (__DEV__ && __devtools?.onComponentUnmount) __devtools.onComponentUnmount(ctx);
47
50
  mountedComponents.delete(ctx);
48
51
  }
49
52
  export function disposeTree(node) {
@@ -54,6 +57,11 @@ disposeComponent(node._componentCtx);
54
57
  if (node._dispose) {
55
58
  try { node._dispose(); } catch (e) { }
56
59
  }
60
+ if (node._propEffects) {
61
+ for (const key in node._propEffects) {
62
+ try { node._propEffects[key](); } catch (e) { }
63
+ }
64
+ }
57
65
  if (node.childNodes) {
58
66
  for (const child of node.childNodes) {
59
67
  disposeTree(child);
@@ -84,25 +92,29 @@ if (isDomNode(vnode)) {
84
92
  return vnode;
85
93
  }
86
94
  if (typeof vnode === 'function') {
87
- const wrapper = document.createElement('what-c');
88
- let mounted = false;
95
+ const container = document.createDocumentFragment ? document.createElement('span') : document.createElement('span');
96
+ container.style.display = 'contents';
97
+ let currentNodes = [];
89
98
  const dispose = effect(() => {
90
99
  const val = vnode();
91
100
  const vnodes = (val == null || val === false || val === true)
92
101
  ? []
93
102
  : Array.isArray(val) ? val : [val];
94
- if (!mounted) {
95
- mounted = true;
103
+ for (const old of currentNodes) {
104
+ disposeTree(old);
105
+ if (old.parentNode === container) container.removeChild(old);
106
+ }
107
+ currentNodes = [];
96
108
  for (const v of vnodes) {
97
- const node = createDOM(v, wrapper, parent?._isSvg);
98
- if (node) wrapper.appendChild(node);
109
+ const node = createDOM(v, container, parent?._isSvg);
110
+ if (node) {
111
+ container.appendChild(node);
112
+ currentNodes.push(node);
99
113
  }
100
- } else {
101
- reconcileChildren(wrapper, vnodes);
102
114
  }
103
115
  });
104
- wrapper._dispose = dispose;
105
- return wrapper;
116
+ container._dispose = dispose;
117
+ return container;
106
118
  }
107
119
  if (Array.isArray(vnode)) {
108
120
  const frag = document.createDocumentFragment();
@@ -112,29 +124,13 @@ if (node) frag.appendChild(node);
112
124
  }
113
125
  return frag;
114
126
  }
115
- if (!isVNode(vnode)) {
116
- return document.createTextNode(String(vnode));
117
- }
118
- if (typeof vnode.tag === 'function') {
127
+ if (isVNode(vnode) && typeof vnode.tag === 'function') {
119
128
  return createComponent(vnode, parent, isSvg);
120
129
  }
121
- const svgContext = isSvg || vnode.tag === 'svg' || SVG_ELEMENTS.has(vnode.tag);
122
- const el = svgContext
123
- ? document.createElementNS(SVG_NS, vnode.tag)
124
- : document.createElement(vnode.tag);
125
- applyProps(el, vnode.props, {}, svgContext);
126
- const hasRawHtml = vnode.props && (
127
- Object.prototype.hasOwnProperty.call(vnode.props, 'dangerouslySetInnerHTML') ||
128
- Object.prototype.hasOwnProperty.call(vnode.props, 'innerHTML')
129
- );
130
- if (!hasRawHtml) {
131
- for (const child of vnode.children) {
132
- const node = createDOM(child, el, svgContext && vnode.tag !== 'foreignObject');
133
- if (node) el.appendChild(node);
134
- }
130
+ if (isVNode(vnode)) {
131
+ return createElementFromVNode(vnode, parent, isSvg);
135
132
  }
136
- el._vnode = vnode;
137
- return el;
133
+ return document.createTextNode(String(vnode));
138
134
  }
139
135
  const componentStack = [];
140
136
  export function getCurrentComponent() {
@@ -146,7 +142,16 @@ export function getComponentStack() {
146
142
  return componentStack;
147
143
  }
148
144
  function createComponent(vnode, parent, isSvg) {
149
- const { tag: Component, props, children } = vnode;
145
+ let { tag: Component, props, children } = vnode;
146
+ if (typeof Component === 'function' &&
147
+ (Component.prototype?.isReactComponent || Component.prototype?.render)) {
148
+ const ClassComp = Component;
149
+ Component = function ClassComponentBridge(props) {
150
+ const instance = new ClassComp(props);
151
+ return instance.render();
152
+ };
153
+ Component.displayName = ClassComp.displayName || ClassComp.name || 'ClassComponent';
154
+ }
150
155
  if (Component === '__errorBoundary' || vnode.tag === '__errorBoundary') {
151
156
  return createErrorBoundary(vnode, parent);
152
157
  }
@@ -154,7 +159,7 @@ if (Component === '__suspense' || vnode.tag === '__suspense') {
154
159
  return createSuspenseBoundary(vnode, parent);
155
160
  }
156
161
  if (Component === '__portal' || vnode.tag === '__portal') {
157
- return createPortal(vnode, parent);
162
+ return createPortalDOM(vnode, parent);
158
163
  }
159
164
  const ctx = {
160
165
  hooks: [],
@@ -163,7 +168,7 @@ effects: [],
163
168
  cleanups: [],
164
169
  mounted: false,
165
170
  disposed: false,
166
- Component,
171
+ Component,
167
172
  _parentCtx: componentStack[componentStack.length - 1] || null,
168
173
  _errorBoundary: (() => {
169
174
  let p = componentStack[componentStack.length - 1];
@@ -174,21 +179,16 @@ p = p._parentCtx;
174
179
  return null;
175
180
  })()
176
181
  };
177
- let wrapper;
178
- if (isSvg) {
179
- wrapper = document.createElementNS(SVG_NS, 'g');
180
- } else {
181
- wrapper = document.createElement('what-c');
182
- }
183
- wrapper._componentCtx = ctx;
184
- wrapper._isSvg = !!isSvg;
185
- ctx._wrapper = wrapper;
182
+ const container = document.createElement('span');
183
+ container.style.display = 'contents';
184
+ container._componentCtx = ctx;
185
+ container._isSvg = !!isSvg;
186
+ ctx._wrapper = container;
186
187
  mountedComponents.add(ctx);
187
- const propsSignal = signal({ ...props, children });
188
+ if (__DEV__ && __devtools?.onComponentMount) __devtools.onComponentMount(ctx);
189
+ const propsChildren = children.length === 0 ? undefined : children.length === 1 ? children[0] : children;
190
+ const propsSignal = signal({ ...props, children: propsChildren });
188
191
  ctx._propsSignal = propsSignal;
189
- const dispose = effect(() => {
190
- if (ctx.disposed) return;
191
- ctx.hookIndex = 0;
192
192
  componentStack.push(ctx);
193
193
  let result;
194
194
  try {
@@ -199,11 +199,9 @@ if (!reportError(error, ctx)) {
199
199
  console.error('[what] Uncaught error in component:', Component.name || 'Anonymous', error);
200
200
  throw error;
201
201
  }
202
- return;
202
+ return container;
203
203
  }
204
204
  componentStack.pop();
205
- const vnodes = Array.isArray(result) ? result : [result];
206
- if (!ctx.mounted) {
207
205
  ctx.mounted = true;
208
206
  if (ctx._mountCallbacks) {
209
207
  queueMicrotask(() => {
@@ -213,22 +211,18 @@ try { fn(); } catch (e) { console.error('[what] onMount error:', e); }
213
211
  }
214
212
  });
215
213
  }
214
+ const vnodes = Array.isArray(result) ? result : [result];
216
215
  for (const v of vnodes) {
217
- const node = createDOM(v, wrapper, isSvg);
218
- if (node) wrapper.appendChild(node);
219
- }
220
- } else {
221
- reconcileChildren(wrapper, vnodes);
216
+ const node = createDOM(v, container, isSvg);
217
+ if (node) container.appendChild(node);
222
218
  }
223
- });
224
- ctx.effects.push(dispose);
225
- wrapper._vnode = vnode;
226
- return wrapper;
219
+ container._vnode = vnode;
220
+ return container;
227
221
  }
228
222
  function createErrorBoundary(vnode, parent) {
229
223
  const { errorState, handleError, fallback, reset } = vnode.props;
230
224
  const children = vnode.children;
231
- const wrapper = document.createElement('what-c');
225
+ const wrapper = document.createElement('span');
232
226
  wrapper.style.display = 'contents';
233
227
  const boundaryCtx = {
234
228
  hooks: [], hookIndex: 0, effects: [], cleanups: [],
@@ -240,22 +234,22 @@ wrapper._componentCtx = boundaryCtx;
240
234
  const dispose = effect(() => {
241
235
  const error = errorState();
242
236
  componentStack.push(boundaryCtx);
237
+ while (wrapper.firstChild) {
238
+ disposeTree(wrapper.firstChild);
239
+ wrapper.removeChild(wrapper.firstChild);
240
+ }
243
241
  let vnodes;
244
242
  if (error) {
245
243
  vnodes = typeof fallback === 'function' ? [fallback({ error, reset })] : [fallback];
246
244
  } else {
247
245
  vnodes = children;
248
246
  }
249
- componentStack.pop();
250
247
  vnodes = Array.isArray(vnodes) ? vnodes : [vnodes];
251
- if (wrapper.childNodes.length === 0) {
252
248
  for (const v of vnodes) {
253
249
  const node = createDOM(v, wrapper);
254
250
  if (node) wrapper.appendChild(node);
255
251
  }
256
- } else {
257
- reconcileChildren(wrapper, vnodes);
258
- }
252
+ componentStack.pop();
259
253
  });
260
254
  boundaryCtx.effects.push(dispose);
261
255
  return wrapper;
@@ -263,7 +257,7 @@ return wrapper;
263
257
  function createSuspenseBoundary(vnode, parent) {
264
258
  const { boundary, fallback, loading } = vnode.props;
265
259
  const children = vnode.children;
266
- const wrapper = document.createElement('what-c');
260
+ const wrapper = document.createElement('span');
267
261
  wrapper.style.display = 'contents';
268
262
  const boundaryCtx = {
269
263
  hooks: [], hookIndex: 0, effects: [], cleanups: [],
@@ -275,19 +269,21 @@ const dispose = effect(() => {
275
269
  const isLoading = loading();
276
270
  const vnodes = isLoading ? [fallback] : children;
277
271
  const normalized = Array.isArray(vnodes) ? vnodes : [vnodes];
278
- if (wrapper.childNodes.length === 0) {
272
+ componentStack.push(boundaryCtx);
273
+ while (wrapper.firstChild) {
274
+ disposeTree(wrapper.firstChild);
275
+ wrapper.removeChild(wrapper.firstChild);
276
+ }
279
277
  for (const v of normalized) {
280
278
  const node = createDOM(v, wrapper);
281
279
  if (node) wrapper.appendChild(node);
282
280
  }
283
- } else {
284
- reconcileChildren(wrapper, normalized);
285
- }
281
+ componentStack.pop();
286
282
  });
287
283
  boundaryCtx.effects.push(dispose);
288
284
  return wrapper;
289
285
  }
290
- function createPortal(vnode, parent) {
286
+ function createPortalDOM(vnode, parent) {
291
287
  const { container } = vnode.props;
292
288
  const children = vnode.children;
293
289
  if (!container) {
@@ -317,365 +313,69 @@ if (node.parentNode) node.parentNode.removeChild(node);
317
313
  }];
318
314
  return placeholder;
319
315
  }
320
- function reconcile(parent, oldNodes, newVNodes, beforeMarker) {
321
- if (!parent) return;
322
- const hasKeys = newVNodes.some(v => v && typeof v === 'object' && v.key != null);
323
- if (hasKeys) {
324
- reconcileKeyed(parent, oldNodes, newVNodes, beforeMarker);
325
- } else {
326
- reconcileUnkeyed(parent, oldNodes, newVNodes, beforeMarker);
327
- }
328
- }
329
- function reconcileUnkeyed(parent, oldNodes, newVNodes, beforeMarker) {
330
- const maxLen = Math.max(oldNodes.length, newVNodes.length);
331
- const newNodes = [];
332
- for (let i = 0; i < maxLen; i++) {
333
- const oldNode = oldNodes[i];
334
- const newVNode = newVNodes[i];
335
- if (i >= newVNodes.length) {
336
- if (oldNode && oldNode.parentNode) {
337
- disposeTree(oldNode);
338
- oldNode.parentNode.removeChild(oldNode);
339
- }
340
- continue;
341
- }
342
- if (i >= oldNodes.length) {
343
- const node = createDOM(newVNode, parent);
344
- if (node) {
345
- const ref = getInsertionRef(oldNodes, beforeMarker);
346
- parent.insertBefore(node, ref);
347
- newNodes.push(node);
348
- }
349
- continue;
350
- }
351
- const patched = patchNode(parent, oldNode, newVNode);
352
- newNodes.push(patched);
353
- }
354
- oldNodes.length = 0;
355
- oldNodes.push(...newNodes);
356
- }
357
- function reconcileKeyed(parent, oldNodes, newVNodes, beforeMarker) {
358
- const oldKeyMap = new Map();
359
- for (let i = 0; i < oldNodes.length; i++) {
360
- const node = oldNodes[i];
361
- const key = node._vnode?.key;
362
- if (key != null) {
363
- oldKeyMap.set(key, { node, index: i });
364
- }
365
- }
366
- const newNodes = [];
367
- const newLen = newVNodes.length;
368
- const sources = new Array(newLen).fill(-1);
369
- const reused = new Set();
370
- for (let i = 0; i < newLen; i++) {
371
- const vnode = newVNodes[i];
372
- const key = vnode?.key;
373
- if (key != null && oldKeyMap.has(key)) {
374
- const { node: oldNode, index: oldIndex } = oldKeyMap.get(key);
375
- sources[i] = oldIndex;
376
- reused.add(oldIndex);
377
- }
378
- }
379
- for (let i = 0; i < oldNodes.length; i++) {
380
- if (!reused.has(i) && oldNodes[i]?.parentNode) {
381
- disposeTree(oldNodes[i]);
382
- oldNodes[i].parentNode.removeChild(oldNodes[i]);
383
- }
384
- }
385
- const filtered = [];
386
- const filteredToOriginal = [];
387
- for (let j = 0; j < sources.length; j++) {
388
- if (sources[j] !== -1) {
389
- filteredToOriginal.push(j);
390
- filtered.push(sources[j]);
391
- }
392
- }
393
- const lis = longestIncreasingSubsequence(filtered);
394
- const lisSet = new Set(lis.map(i => filteredToOriginal[i]));
395
- let lastInserted = beforeMarker?.nextSibling || null;
396
- for (let i = newLen - 1; i >= 0; i--) {
397
- const vnode = newVNodes[i];
398
- const key = vnode?.key;
399
- const oldEntry = key != null ? oldKeyMap.get(key) : null;
400
- if (oldEntry && sources[i] !== -1) {
401
- const oldNode = oldEntry.node;
402
- const patched = patchNode(parent, oldNode, vnode);
403
- newNodes[i] = patched;
404
- if (!lisSet.has(i) && patched.parentNode) {
405
- parent.insertBefore(patched, lastInserted);
406
- }
407
- lastInserted = patched;
408
- } else {
409
- const node = createDOM(vnode, parent);
410
- if (node) {
411
- parent.insertBefore(node, lastInserted);
412
- lastInserted = node;
413
- }
414
- newNodes[i] = node;
415
- }
416
- }
417
- oldNodes.length = 0;
418
- oldNodes.push(...newNodes.filter(Boolean));
419
- }
420
- function longestIncreasingSubsequence(arr) {
421
- if (arr.length === 0) return [];
422
- const n = arr.length;
423
- const dp = new Array(n).fill(1);
424
- const parent = new Array(n).fill(-1);
425
- const tails = [0];
426
- for (let i = 1; i < n; i++) {
427
- if (arr[i] > arr[tails[tails.length - 1]]) {
428
- parent[i] = tails[tails.length - 1];
429
- tails.push(i);
430
- } else {
431
- let lo = 0, hi = tails.length - 1;
432
- while (lo < hi) {
433
- const mid = (lo + hi) >> 1;
434
- if (arr[tails[mid]] < arr[i]) lo = mid + 1;
435
- else hi = mid;
436
- }
437
- if (arr[i] < arr[tails[lo]]) {
438
- if (lo > 0) parent[i] = tails[lo - 1];
439
- tails[lo] = i;
440
- }
441
- }
442
- }
443
- const result = [];
444
- let k = tails[tails.length - 1];
445
- while (k !== -1) {
446
- result.push(k);
447
- k = parent[k];
448
- }
449
- return result.reverse();
450
- }
451
- function getInsertionRef(nodes, marker) {
452
- if (nodes.length > 0) {
453
- const last = nodes[nodes.length - 1];
454
- return last.nextSibling;
455
- }
456
- return marker ? marker.nextSibling : null;
457
- }
458
- function cleanupArrayMarkers(parent, startMarker) {
459
- const endMarker = startMarker._arrayEnd;
460
- if (!endMarker) return null;
461
- let node = startMarker.nextSibling;
462
- while (node && node !== endMarker) {
463
- const next = node.nextSibling;
464
- disposeTree(node);
465
- parent.removeChild(node);
466
- node = next;
467
- }
468
- if (endMarker.parentNode) parent.removeChild(endMarker);
469
- return startMarker;
470
- }
471
- function patchNode(parent, domNode, vnode) {
472
- if (vnode == null || vnode === false || vnode === true) {
473
- if (domNode && domNode.nodeType === 8 && domNode._arrayEnd) {
474
- cleanupArrayMarkers(parent, domNode);
475
- const placeholder = document.createComment('');
476
- parent.replaceChild(placeholder, domNode);
477
- return placeholder;
478
- }
479
- if (domNode && domNode.nodeType === 8 && !domNode._componentCtx) {
480
- return domNode;
481
- }
482
- const placeholder = document.createComment('');
483
- if (domNode && domNode.parentNode) {
484
- disposeTree(domNode);
485
- parent.replaceChild(placeholder, domNode);
486
- }
487
- return placeholder;
488
- }
489
- if (typeof vnode === 'function') {
490
- const wrapper = document.createElement('what-c');
491
- let mounted = false;
492
- const dispose = effect(() => {
493
- const val = vnode();
494
- const vnodes = (val == null || val === false || val === true)
495
- ? []
496
- : Array.isArray(val) ? val : [val];
497
- if (!mounted) {
498
- mounted = true;
499
- for (const v of vnodes) {
500
- const node = createDOM(v, wrapper);
501
- if (node) wrapper.appendChild(node);
502
- }
503
- } else {
504
- reconcileChildren(wrapper, vnodes);
505
- }
506
- });
507
- wrapper._dispose = dispose;
508
- if (domNode && domNode.parentNode) {
509
- disposeTree(domNode);
510
- parent.replaceChild(wrapper, domNode);
511
- }
512
- return wrapper;
513
- }
514
- if (isDomNode(vnode)) {
515
- if (domNode === vnode) return domNode;
516
- if (domNode && domNode.parentNode) {
517
- disposeTree(domNode);
518
- parent.replaceChild(vnode, domNode);
519
- }
520
- return vnode;
521
- }
522
- if (typeof vnode === 'string' || typeof vnode === 'number') {
523
- const text = String(vnode);
524
- if (domNode && domNode.nodeType === 8 && domNode._arrayEnd) {
525
- cleanupArrayMarkers(parent, domNode);
526
- const newNode = document.createTextNode(text);
527
- parent.replaceChild(newNode, domNode);
528
- return newNode;
529
- }
530
- if (domNode.nodeType === 3) {
531
- if (domNode.textContent !== text) domNode.textContent = text;
532
- return domNode;
533
- }
534
- const newNode = document.createTextNode(text);
535
- disposeTree(domNode);
536
- parent.replaceChild(newNode, domNode);
537
- return newNode;
538
- }
539
- if (Array.isArray(vnode)) {
540
- if (domNode && domNode.nodeType === 8 && domNode._arrayEnd) {
541
- const endMarker = domNode._arrayEnd;
542
- const oldChildren = [];
543
- let node = domNode.nextSibling;
544
- while (node && node !== endMarker) {
545
- oldChildren.push(node);
546
- node = node.nextSibling;
547
- }
548
- const maxLen = Math.max(oldChildren.length, vnode.length);
549
- for (let i = 0; i < maxLen; i++) {
550
- if (i >= vnode.length) {
551
- if (oldChildren[i]?.parentNode) {
552
- disposeTree(oldChildren[i]);
553
- parent.removeChild(oldChildren[i]);
554
- }
555
- } else if (i >= oldChildren.length) {
556
- const newNode = createDOM(vnode[i], parent);
557
- if (newNode) parent.insertBefore(newNode, endMarker);
558
- } else {
559
- patchNode(parent, oldChildren[i], vnode[i]);
560
- }
561
- }
562
- return domNode;
563
- }
564
- const startMarker = document.createComment('[');
565
- const endMarker = document.createComment(']');
566
- disposeTree(domNode);
567
- parent.replaceChild(endMarker, domNode);
568
- parent.insertBefore(startMarker, endMarker);
569
- for (const v of vnode) {
570
- const node = createDOM(v, parent);
571
- if (node) parent.insertBefore(node, endMarker);
572
- }
573
- startMarker._arrayEnd = endMarker;
574
- return startMarker;
575
- }
576
- if (!isVNode(vnode)) {
577
- const text = String(vnode);
578
- if (domNode.nodeType === 3) {
579
- if (domNode.textContent !== text) domNode.textContent = text;
580
- return domNode;
581
- }
582
- const newNode = document.createTextNode(text);
583
- disposeTree(domNode);
584
- parent.replaceChild(newNode, domNode);
585
- return newNode;
586
- }
587
- if (typeof vnode.tag === 'function') {
588
- if (domNode._componentCtx && !domNode._componentCtx.disposed
589
- && domNode._componentCtx.Component === vnode.tag) {
590
- domNode._componentCtx._propsSignal.set({ ...vnode.props, children: vnode.children });
591
- domNode._vnode = vnode;
592
- return domNode;
593
- }
594
- disposeTree(domNode);
595
- const node = createComponent(vnode, parent);
596
- parent.replaceChild(node, domNode);
597
- return node;
598
- }
599
- if (domNode.nodeType === 1 && domNode.tagName.toLowerCase() === vnode.tag) {
600
- const oldProps = domNode._vnode?.props || {};
601
- const nextProps = vnode.props || {};
602
- const hadRawHtml = Object.prototype.hasOwnProperty.call(oldProps, 'dangerouslySetInnerHTML')
603
- || Object.prototype.hasOwnProperty.call(oldProps, 'innerHTML');
604
- const hasRawHtml = Object.prototype.hasOwnProperty.call(nextProps, 'dangerouslySetInnerHTML')
605
- || Object.prototype.hasOwnProperty.call(nextProps, 'innerHTML');
606
- if (hasRawHtml && !hadRawHtml) {
607
- for (const child of Array.from(domNode.childNodes)) {
608
- disposeTree(child);
609
- }
610
- }
611
- applyProps(domNode, nextProps, oldProps);
612
- if (!hasRawHtml) {
613
- reconcileChildren(domNode, vnode.children);
614
- }
615
- domNode._vnode = vnode;
616
- return domNode;
617
- }
618
- const newNode = createDOM(vnode, parent);
619
- disposeTree(domNode);
620
- parent.replaceChild(newNode, domNode);
621
- return newNode;
622
- }
623
- function reconcileChildren(parent, newChildVNodes) {
624
- const oldChildren = Array.from(parent.childNodes);
625
- const hasKeys = newChildVNodes.some(v => v && typeof v === 'object' && v.key != null);
626
- if (hasKeys) {
627
- reconcileKeyed(parent, oldChildren, newChildVNodes, null);
628
- } else {
629
- const maxLen = Math.max(oldChildren.length, newChildVNodes.length);
630
- for (let i = 0; i < maxLen; i++) {
631
- if (i >= newChildVNodes.length) {
632
- if (oldChildren[i]?.parentNode) {
633
- disposeTree(oldChildren[i]);
634
- parent.removeChild(oldChildren[i]);
635
- }
636
- continue;
637
- }
638
- if (i >= oldChildren.length) {
639
- const node = createDOM(newChildVNodes[i], parent);
640
- if (node) parent.appendChild(node);
641
- continue;
642
- }
643
- patchNode(parent, oldChildren[i], newChildVNodes[i]);
316
+ function createElementFromVNode(vnode, parent, isSvg) {
317
+ const { tag, props, children } = vnode;
318
+ const svgContext = isSvg || SVG_ELEMENTS.has(tag);
319
+ const el = svgContext
320
+ ? document.createElementNS(SVG_NS, tag)
321
+ : document.createElement(tag);
322
+ if (props) {
323
+ applyProps(el, props, {}, svgContext);
644
324
  }
325
+ for (const child of children) {
326
+ const node = createDOM(child, el, svgContext && tag !== 'foreignObject');
327
+ if (node) el.appendChild(node);
645
328
  }
329
+ el._vnode = vnode;
330
+ return el;
646
331
  }
647
332
  function applyProps(el, newProps, oldProps, isSvg) {
648
333
  newProps = newProps || {};
649
334
  oldProps = oldProps || {};
650
- for (const key in oldProps) {
651
- if (key === 'key' || key === 'ref' || key === 'children') continue;
652
- if (!(key in newProps)) {
653
- removeProp(el, key, oldProps[key]);
654
- }
655
- }
656
335
  for (const key in newProps) {
657
- if (key === 'key' || key === 'ref' || key === 'children') continue;
658
- if (newProps[key] !== oldProps[key]) {
659
- setProp(el, key, newProps[key], isSvg);
660
- }
661
- }
662
- if (newProps.ref && newProps.ref !== oldProps.ref) {
336
+ if (key === 'key' || key === 'children') continue;
337
+ if (key === 'ref') {
663
338
  if (typeof newProps.ref === 'function') newProps.ref(el);
664
- else newProps.ref.current = el;
339
+ else if (newProps.ref) newProps.ref.current = el;
340
+ continue;
341
+ }
342
+ setProp(el, key, newProps[key], isSvg);
665
343
  }
666
344
  }
667
345
  function setProp(el, key, value, isSvg) {
346
+ if (typeof value === 'function' && !(key.startsWith('on') && key.length > 2) && key !== 'ref') {
347
+ if (!el._propEffects) el._propEffects = {};
348
+ if (el._propEffects[key]) {
349
+ try { el._propEffects[key](); } catch (e) { }
350
+ }
351
+ el._propEffects[key] = effect(() => {
352
+ const resolved = value();
353
+ setProp(el, key, resolved, isSvg);
354
+ });
355
+ return;
356
+ }
668
357
  if (key.startsWith('on') && key.length > 2) {
669
- const event = key.slice(2).toLowerCase();
670
- const old = el._events?.[event];
358
+ let eventName = key.slice(2);
359
+ let useCapture = false;
360
+ if (eventName.endsWith('Capture')) {
361
+ eventName = eventName.slice(0, -7);
362
+ useCapture = true;
363
+ }
364
+ const event = eventName.toLowerCase();
365
+ const storageKey = useCapture ? event + '_capture' : event;
366
+ const old = el._events?.[storageKey];
671
367
  if (old && old._original === value) return;
672
- if (old) el.removeEventListener(event, old);
368
+ if (old) el.removeEventListener(event, old, useCapture);
369
+ if (value == null) return;
673
370
  if (!el._events) el._events = {};
674
- const wrappedHandler = (e) => untrack(() => value(e));
371
+ const wrappedHandler = (e) => {
372
+ if (!e.nativeEvent) e.nativeEvent = e;
373
+ return untrack(() => value(e));
374
+ };
675
375
  wrappedHandler._original = value;
676
- el._events[event] = wrappedHandler;
376
+ el._events[storageKey] = wrappedHandler;
677
377
  const eventOpts = value._eventOpts;
678
- el.addEventListener(event, wrappedHandler, eventOpts || undefined);
378
+ el.addEventListener(event, wrappedHandler, eventOpts || useCapture || undefined);
679
379
  return;
680
380
  }
681
381
  if (key === 'className' || key === 'class') {
@@ -736,28 +436,4 @@ el[key] = value;
736
436
  } else {
737
437
  el.setAttribute(key, value);
738
438
  }
739
- }
740
- function removeProp(el, key, oldValue) {
741
- if (key.startsWith('on') && key.length > 2) {
742
- const event = key.slice(2).toLowerCase();
743
- if (el._events?.[event]) {
744
- el.removeEventListener(event, el._events[event]);
745
- delete el._events[event];
746
- }
747
- return;
748
- }
749
- if (key === 'className' || key === 'class') {
750
- el.className = '';
751
- return;
752
- }
753
- if (key === 'style') {
754
- el.style.cssText = '';
755
- el._prevStyle = null;
756
- return;
757
- }
758
- if (key === 'dangerouslySetInnerHTML' || key === 'innerHTML') {
759
- el.innerHTML = '';
760
- return;
761
- }
762
- el.removeAttribute(key);
763
439
  }