@remix-run/ui 0.1.0 → 0.1.2
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 +24 -3
- package/dist/animation/animate-layout-mixin.js +66 -66
- package/dist/animation/animate-layout-mixin.js.map +1 -1
- package/dist/components/accordion/accordion.d.ts +3 -3
- package/dist/components/accordion/accordion.js +0 -2
- package/dist/components/accordion/accordion.js.map +1 -1
- package/dist/components/accordion/demos/card.demo.d.ts +6 -0
- package/dist/components/accordion/demos/card.demo.js +58 -0
- package/dist/components/accordion/demos/card.demo.js.map +1 -0
- package/dist/components/accordion/demos/multiple.demo.d.ts +6 -0
- package/dist/components/accordion/demos/multiple.demo.js +19 -0
- package/dist/components/accordion/demos/multiple.demo.js.map +1 -0
- package/dist/components/accordion/demos/overview.demo.d.ts +6 -0
- package/dist/components/accordion/demos/overview.demo.js +19 -0
- package/dist/components/accordion/demos/overview.demo.js.map +1 -0
- package/dist/components/breadcrumbs/breadcrumbs.js +0 -2
- package/dist/components/breadcrumbs/breadcrumbs.js.map +1 -1
- package/dist/components/breadcrumbs/demos/basic.demo.d.ts +5 -0
- package/dist/components/breadcrumbs/demos/basic.demo.js +14 -0
- package/dist/components/breadcrumbs/demos/basic.demo.js.map +1 -0
- package/dist/components/breadcrumbs/demos/separator.demo.d.ts +5 -0
- package/dist/components/breadcrumbs/demos/separator.demo.js +14 -0
- package/dist/components/breadcrumbs/demos/separator.demo.js.map +1 -0
- package/dist/components/button/button.d.ts +82 -0
- package/dist/components/button/button.js +60 -2
- package/dist/components/button/button.js.map +1 -1
- package/dist/components/button/demos/aliases.demo.d.ts +6 -0
- package/dist/components/button/demos/aliases.demo.js +18 -0
- package/dist/components/button/demos/aliases.demo.js.map +1 -0
- package/dist/components/button/demos/basic.demo.d.ts +6 -0
- package/dist/components/button/demos/basic.demo.js +19 -0
- package/dist/components/button/demos/basic.demo.js.map +1 -0
- package/dist/components/button/demos/states.demo.d.ts +6 -0
- package/dist/components/button/demos/states.demo.js +29 -0
- package/dist/components/button/demos/states.demo.js.map +1 -0
- package/dist/components/combobox/combobox.d.ts +3 -3
- package/dist/components/combobox/combobox.js +0 -2
- package/dist/components/combobox/combobox.js.map +1 -1
- package/dist/components/combobox/demos/overview.demo.d.ts +6 -0
- package/dist/components/combobox/demos/overview.demo.js +153 -0
- package/dist/components/combobox/demos/overview.demo.js.map +1 -0
- package/dist/components/listbox/demos/overview.demo.d.ts +6 -0
- package/dist/components/listbox/demos/overview.demo.js +64 -0
- package/dist/components/listbox/demos/overview.demo.js.map +1 -0
- package/dist/components/listbox/listbox.d.ts +1 -6
- package/dist/components/listbox/listbox.js +21 -33
- package/dist/components/listbox/listbox.js.map +1 -1
- package/dist/components/menu/demos/bubbling.demo.d.ts +5 -0
- package/dist/components/menu/demos/bubbling.demo.js +15 -0
- package/dist/components/menu/demos/bubbling.demo.js.map +1 -0
- package/dist/components/menu/demos/overview.demo.d.ts +6 -0
- package/dist/components/menu/demos/overview.demo.js +32 -0
- package/dist/components/menu/demos/overview.demo.js.map +1 -0
- package/dist/components/menu/menu.d.ts +5 -14
- package/dist/components/menu/menu.js +23 -38
- package/dist/components/menu/menu.js.map +1 -1
- package/dist/components/popover/demos/overview.demo.d.ts +6 -0
- package/dist/components/popover/demos/overview.demo.js +64 -0
- package/dist/components/popover/demos/overview.demo.js.map +1 -0
- package/dist/components/select/demos/deconstructed.demo.d.ts +6 -0
- package/dist/components/select/demos/deconstructed.demo.js +53 -0
- package/dist/components/select/demos/deconstructed.demo.js.map +1 -0
- package/dist/components/select/demos/overview.demo.d.ts +5 -0
- package/dist/components/select/demos/overview.demo.js +27 -0
- package/dist/components/select/demos/overview.demo.js.map +1 -0
- package/dist/components/select/select.d.ts +3 -3
- package/dist/components/select/select.js +17 -26
- package/dist/components/select/select.js.map +1 -1
- package/dist/components/tabs/tabs.d.ts +3 -3
- package/dist/components/tabs/tabs.js +0 -2
- package/dist/components/tabs/tabs.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/runtime/component.d.ts +8 -5
- package/dist/runtime/component.js +84 -80
- package/dist/runtime/component.js.map +1 -1
- package/dist/runtime/core/attributes.d.ts +12 -0
- package/dist/runtime/core/attributes.js +85 -0
- package/dist/runtime/core/attributes.js.map +1 -0
- package/dist/runtime/core/children.d.ts +7 -0
- package/dist/runtime/core/children.js +30 -0
- package/dist/runtime/core/children.js.map +1 -0
- package/dist/runtime/core/props.d.ts +9 -0
- package/dist/runtime/core/props.js +169 -0
- package/dist/runtime/core/props.js.map +1 -0
- package/dist/runtime/core/vnode.d.ts +3 -0
- package/dist/runtime/core/vnode.js +39 -0
- package/dist/runtime/core/vnode.js.map +1 -0
- package/dist/runtime/create-element.js +2 -1
- package/dist/runtime/create-element.js.map +1 -1
- package/dist/runtime/diff-dom.js +47 -14
- package/dist/runtime/diff-dom.js.map +1 -1
- package/dist/runtime/diff-props.d.ts +1 -2
- package/dist/runtime/diff-props.js +2 -181
- package/dist/runtime/diff-props.js.map +1 -1
- package/dist/runtime/dom.d.ts +4 -0
- package/dist/runtime/frame.d.ts +23 -2
- package/dist/runtime/frame.js +51 -52
- package/dist/runtime/frame.js.map +1 -1
- package/dist/runtime/jsx.js +2 -27
- package/dist/runtime/jsx.js.map +1 -1
- package/dist/runtime/mixins/mixin.js +9 -4
- package/dist/runtime/mixins/mixin.js.map +1 -1
- package/dist/runtime/mixins/on-mixin.d.ts +15 -4
- package/dist/runtime/mixins/on-mixin.js +9 -2
- package/dist/runtime/mixins/on-mixin.js.map +1 -1
- package/dist/runtime/navigation.d.ts +6 -0
- package/dist/runtime/navigation.js +16 -3
- package/dist/runtime/navigation.js.map +1 -1
- package/dist/runtime/reconcile.js +444 -181
- package/dist/runtime/reconcile.js.map +1 -1
- package/dist/runtime/run.d.ts +17 -2
- package/dist/runtime/run.js.map +1 -1
- package/dist/runtime/scheduler.js +10 -0
- package/dist/runtime/scheduler.js.map +1 -1
- package/dist/runtime/stream-protocol.d.ts +8 -0
- package/dist/runtime/stream-protocol.js +20 -0
- package/dist/runtime/stream-protocol.js.map +1 -0
- package/dist/runtime/to-vnode.js +17 -18
- package/dist/runtime/to-vnode.js.map +1 -1
- package/dist/runtime/vnode.d.ts +5 -4
- package/dist/runtime/vnode.js +2 -3
- package/dist/runtime/vnode.js.map +1 -1
- package/dist/server/stream.js +80 -106
- package/dist/server/stream.js.map +1 -1
- package/dist/style/layers.d.ts +2 -0
- package/dist/style/layers.js +3 -0
- package/dist/style/layers.js.map +1 -0
- package/dist/style/stylesheet.d.ts +3 -1
- package/dist/style/stylesheet.js +69 -8
- package/dist/style/stylesheet.js.map +1 -1
- package/dist/theme/glyph-contract.d.ts +1 -1
- package/dist/theme/runtime.js +4 -1
- package/dist/theme/runtime.js.map +1 -1
- package/package.json +6 -7
- package/src/animation/README.md +314 -0
- package/src/animation/animate-layout-mixin.ts +79 -71
- package/src/components/accordion/README.md +166 -0
- package/src/components/accordion/accordion.tsx +8 -10
- package/src/components/accordion/demos/card.demo.tsx +113 -0
- package/src/components/accordion/demos/multiple.demo.tsx +56 -0
- package/src/components/accordion/demos/overview.demo.tsx +56 -0
- package/src/components/anchor/README.md +153 -0
- package/src/components/breadcrumbs/README.md +55 -0
- package/src/components/breadcrumbs/breadcrumbs.tsx +0 -2
- package/src/components/breadcrumbs/demos/basic.demo.tsx +17 -0
- package/src/components/breadcrumbs/demos/separator.demo.tsx +18 -0
- package/src/components/button/README.md +4 -4
- package/src/components/button/button.tsx +88 -2
- package/src/components/button/demos/aliases.demo.tsx +27 -0
- package/src/components/button/demos/basic.demo.tsx +30 -0
- package/src/components/button/demos/states.demo.tsx +52 -0
- package/src/components/combobox/combobox.tsx +8 -10
- package/src/components/combobox/demos/overview.demo.tsx +199 -0
- package/src/components/glyph/README.md +72 -0
- package/src/components/listbox/README.md +115 -0
- package/src/components/listbox/demos/overview.demo.tsx +101 -0
- package/src/components/listbox/listbox.ts +23 -32
- package/src/components/menu/README.md +96 -0
- package/src/components/menu/demos/bubbling.demo.tsx +34 -0
- package/src/components/menu/demos/overview.demo.tsx +76 -0
- package/src/components/menu/menu.tsx +35 -41
- package/src/components/popover/README.md +3 -3
- package/src/components/popover/demos/overview.demo.tsx +143 -0
- package/src/components/select/README.md +107 -0
- package/src/components/select/demos/deconstructed.demo.tsx +95 -0
- package/src/components/select/demos/overview.demo.tsx +51 -0
- package/src/components/select/select.tsx +28 -28
- package/src/components/tabs/tabs.tsx +10 -5
- package/src/index.ts +1 -1
- package/src/runtime/component.ts +94 -82
- package/src/runtime/core/attributes.ts +97 -0
- package/src/runtime/core/children.ts +42 -0
- package/src/runtime/core/props.ts +185 -0
- package/src/runtime/core/vnode.ts +48 -0
- package/src/runtime/create-element.ts +2 -1
- package/src/runtime/diff-dom.ts +59 -12
- package/src/runtime/diff-props.ts +2 -187
- package/src/runtime/dom.ts +4 -0
- package/src/runtime/frame.ts +84 -61
- package/src/runtime/jsx.ts +2 -30
- package/src/runtime/mixins/mixin.ts +9 -4
- package/src/runtime/mixins/on-mixin.ts +20 -12
- package/src/runtime/navigation.ts +24 -3
- package/src/runtime/reconcile.ts +552 -186
- package/src/runtime/run.ts +17 -1
- package/src/runtime/scheduler.ts +7 -0
- package/src/runtime/stream-protocol.ts +27 -0
- package/src/runtime/to-vnode.ts +15 -17
- package/src/runtime/vnode.ts +6 -9
- package/src/server/README.md +88 -0
- package/src/server/stream.ts +105 -117
- package/src/style/layers.ts +2 -0
- package/src/style/stylesheet.ts +75 -10
- package/src/test/README.md +107 -0
- package/src/test/utils.ts +3 -1
- package/src/theme/README.md +103 -0
- package/src/theme/runtime.ts +4 -1
- package/src/utils/scroll-lock/README.md +33 -0
- package/src/test/setup.ts +0 -7
package/src/runtime/reconcile.ts
CHANGED
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
findContextFromAncestry,
|
|
25
25
|
} from './vnode.ts'
|
|
26
26
|
import { invariant } from './invariant.ts'
|
|
27
|
-
import {
|
|
27
|
+
import { patchHostProps } from './core/props.ts'
|
|
28
28
|
import type { StyleManager } from '../style/index.ts'
|
|
29
29
|
import type { ElementProps } from './jsx.ts'
|
|
30
30
|
import { skipComments, logHydrationMismatch } from './client-entries.ts'
|
|
@@ -42,13 +42,10 @@ import {
|
|
|
42
42
|
type MixinRuntimeBinding,
|
|
43
43
|
type MixinRuntimeState,
|
|
44
44
|
} from './mixins/mixin.ts'
|
|
45
|
+
import { isOnMixinDescriptor, type OnMixinDescriptor } from './mixins/on-mixin.ts'
|
|
45
46
|
|
|
46
47
|
const SVG_NS = 'http://www.w3.org/2000/svg'
|
|
47
48
|
|
|
48
|
-
// Internal diffing flags (modeled after Preact)
|
|
49
|
-
const INSERT_VNODE = 1 << 0
|
|
50
|
-
const MATCHED = 1 << 1
|
|
51
|
-
|
|
52
49
|
let idCounter = 0
|
|
53
50
|
let persistedRemovalToken = 0
|
|
54
51
|
const persistedMixinNodes = new Set<CommittedHostNode>()
|
|
@@ -116,6 +113,20 @@ type ControlledReflectionState = {
|
|
|
116
113
|
onChange: () => void
|
|
117
114
|
}
|
|
118
115
|
|
|
116
|
+
type DirectEventBinding = {
|
|
117
|
+
type: string
|
|
118
|
+
handler: (event: Event, signal: AbortSignal) => void | Promise<void>
|
|
119
|
+
capture: boolean
|
|
120
|
+
stableHandler: ((event: Event) => void) | null
|
|
121
|
+
reentry: AbortController | null
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
type DirectEventState = {
|
|
125
|
+
bindings: DirectEventBinding[]
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const EMPTY_DIRECT_EVENT_DESCRIPTORS: OnMixinDescriptor[] = []
|
|
129
|
+
|
|
119
130
|
function shouldRestoreControlledReflectionOnInput(
|
|
120
131
|
node: CommittedHostNode,
|
|
121
132
|
state: ControlledReflectionState,
|
|
@@ -258,6 +269,18 @@ function resolveNodeMixProps(
|
|
|
258
269
|
state?: MixinRuntimeState,
|
|
259
270
|
): ElementProps {
|
|
260
271
|
let mix = node.props.mix
|
|
272
|
+
let directEventDescriptors = resolveDirectEventDescriptors(mix)
|
|
273
|
+
if (directEventDescriptors) {
|
|
274
|
+
if (state) {
|
|
275
|
+
teardownMixins(state)
|
|
276
|
+
}
|
|
277
|
+
node._mixState = undefined
|
|
278
|
+
node._mixedProps = node.props
|
|
279
|
+
node._directEventDescriptors = directEventDescriptors
|
|
280
|
+
return node.props
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
node._directEventDescriptors = undefined
|
|
261
284
|
if (state == null && (mix == null || (Array.isArray(mix) && mix.length === 0))) {
|
|
262
285
|
node._mixState = undefined
|
|
263
286
|
node._mixedProps = node.props
|
|
@@ -283,6 +306,22 @@ function resolveNodeMixProps(
|
|
|
283
306
|
return resolved.props
|
|
284
307
|
}
|
|
285
308
|
|
|
309
|
+
function resolveDirectEventDescriptors(mix: ElementProps['mix']): OnMixinDescriptor[] | null {
|
|
310
|
+
if (!mix) return EMPTY_DIRECT_EVENT_DESCRIPTORS
|
|
311
|
+
if (!Array.isArray(mix)) {
|
|
312
|
+
return isOnMixinDescriptor(mix) ? [mix] : null
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return areOnMixinDescriptors(mix) ? mix : null
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function areOnMixinDescriptors(descriptors: unknown[]): descriptors is OnMixinDescriptor[] {
|
|
319
|
+
for (let item of descriptors) {
|
|
320
|
+
if (!isOnMixinDescriptor(item)) return false
|
|
321
|
+
}
|
|
322
|
+
return true
|
|
323
|
+
}
|
|
324
|
+
|
|
286
325
|
function enqueueMixinBindingUpdate(
|
|
287
326
|
this: MixinRuntimeBinding,
|
|
288
327
|
done: (signal: AbortSignal) => void,
|
|
@@ -299,7 +338,7 @@ function enqueueMixinBindingUpdate(
|
|
|
299
338
|
dispatchMixinBeforeUpdate(state)
|
|
300
339
|
let prevProps = getHostProps(node)
|
|
301
340
|
let nextProps = resolveNodeMixProps(node, this.frame, this.scheduler, state)
|
|
302
|
-
|
|
341
|
+
patchHostProps(prevProps, nextProps, this.node)
|
|
303
342
|
|
|
304
343
|
dispatchMixinCommit(state)
|
|
305
344
|
done(state ? getMixinRuntimeSignal(state) : AbortSignal.abort())
|
|
@@ -332,6 +371,8 @@ function bindNodeMixRuntime(
|
|
|
332
371
|
}
|
|
333
372
|
|
|
334
373
|
function isHeadHostNode(node: HostNode): boolean {
|
|
374
|
+
if (node.type === 'head') return true
|
|
375
|
+
if (node.type.length !== 4) return false
|
|
335
376
|
return node.type.toLowerCase() === 'head'
|
|
336
377
|
}
|
|
337
378
|
|
|
@@ -460,8 +501,11 @@ function diffHost(
|
|
|
460
501
|
let mixState = curr._mixState as MixinRuntimeState | undefined
|
|
461
502
|
let currProps = getHostProps(curr)
|
|
462
503
|
let nextProps = resolveNodeMixProps(next, frame, scheduler, mixState)
|
|
463
|
-
|
|
464
|
-
|
|
504
|
+
let nextMixState = next._mixState as MixinRuntimeState | undefined
|
|
505
|
+
let shouldDispatchMixinLifecycle =
|
|
506
|
+
(nextMixState?.runners.length ?? 0) > 0 && shouldDispatchInlineMixinLifecycle(curr._dom)
|
|
507
|
+
if (shouldDispatchMixinLifecycle) {
|
|
508
|
+
dispatchMixinBeforeUpdate(nextMixState)
|
|
465
509
|
}
|
|
466
510
|
|
|
467
511
|
// Handle innerHTML prop BEFORE diffChildren to avoid clearing children
|
|
@@ -485,23 +529,25 @@ function diffHost(
|
|
|
485
529
|
next,
|
|
486
530
|
rootTarget,
|
|
487
531
|
)
|
|
488
|
-
|
|
532
|
+
patchHostProps(currProps, nextProps, curr._dom)
|
|
489
533
|
|
|
490
534
|
next._dom = curr._dom
|
|
491
535
|
next._parent = vParent
|
|
492
536
|
next._controller = curr._controller
|
|
537
|
+
next._directEventState = curr._directEventState
|
|
493
538
|
next._controlledState = curr._controlledState
|
|
539
|
+
syncDirectEventListeners(next as CommittedHostNode)
|
|
494
540
|
|
|
495
541
|
if (next._controlledState || shouldTrackControlledReflection(nextProps)) {
|
|
496
542
|
ensureControlledReflection(next as CommittedHostNode, scheduler)
|
|
497
543
|
syncControlledReflection(next as CommittedHostNode, nextProps)
|
|
498
544
|
}
|
|
499
545
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
])
|
|
546
|
+
if (next._mixState) {
|
|
547
|
+
bindNodeMixRuntime(next as CommittedHostNode, frame, scheduler, styles)
|
|
548
|
+
}
|
|
549
|
+
if (shouldDispatchMixinLifecycle) {
|
|
550
|
+
scheduler.enqueueCommitPhase([() => dispatchMixinCommit(nextMixState)])
|
|
505
551
|
}
|
|
506
552
|
|
|
507
553
|
return
|
|
@@ -512,12 +558,115 @@ function setupHostNode(node: HostNode, dom: Element, scheduler: Scheduler): void
|
|
|
512
558
|
let props = getHostProps(node)
|
|
513
559
|
let committedNode = node as CommittedHostNode
|
|
514
560
|
|
|
561
|
+
syncDirectEventListeners(committedNode)
|
|
562
|
+
|
|
515
563
|
if (shouldTrackControlledReflection(props)) {
|
|
516
564
|
ensureControlledReflection(committedNode, scheduler)
|
|
517
565
|
syncControlledReflection(committedNode, props)
|
|
518
566
|
}
|
|
519
567
|
}
|
|
520
568
|
|
|
569
|
+
function syncDirectEventListeners(node: CommittedHostNode): void {
|
|
570
|
+
let descriptors = node._directEventDescriptors as OnMixinDescriptor[] | undefined
|
|
571
|
+
if (!descriptors) {
|
|
572
|
+
teardownDirectEventListeners(node)
|
|
573
|
+
return
|
|
574
|
+
}
|
|
575
|
+
if (descriptors.length === 0) {
|
|
576
|
+
teardownDirectEventListeners(node)
|
|
577
|
+
return
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
let state = node._directEventState as DirectEventState | undefined
|
|
581
|
+
if (!state) {
|
|
582
|
+
state = { bindings: [] }
|
|
583
|
+
node._directEventState = state
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
let bindings = state.bindings
|
|
587
|
+
for (let index = 0; index < descriptors.length; index++) {
|
|
588
|
+
let descriptor = descriptors[index]
|
|
589
|
+
let [type, handler, captureBoolean = false] = descriptor.args
|
|
590
|
+
let binding = bindings[index]
|
|
591
|
+
|
|
592
|
+
if (!binding) {
|
|
593
|
+
binding = createDirectEventBinding(type, handler, captureBoolean)
|
|
594
|
+
bindings[index] = binding
|
|
595
|
+
attachDirectEventBinding(node._dom, binding)
|
|
596
|
+
continue
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (binding.type !== type || binding.capture !== captureBoolean) {
|
|
600
|
+
removeDirectEventBinding(node._dom, binding)
|
|
601
|
+
binding.type = type
|
|
602
|
+
binding.capture = captureBoolean
|
|
603
|
+
attachDirectEventBinding(node._dom, binding)
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
binding.handler = handler
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
for (let index = descriptors.length; index < bindings.length; index++) {
|
|
610
|
+
removeDirectEventBinding(node._dom, bindings[index])
|
|
611
|
+
}
|
|
612
|
+
bindings.length = descriptors.length
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function createDirectEventBinding(
|
|
616
|
+
type: string,
|
|
617
|
+
handler: DirectEventBinding['handler'],
|
|
618
|
+
capture: boolean,
|
|
619
|
+
): DirectEventBinding {
|
|
620
|
+
let binding: DirectEventBinding = {
|
|
621
|
+
type,
|
|
622
|
+
handler,
|
|
623
|
+
capture,
|
|
624
|
+
reentry: null,
|
|
625
|
+
stableHandler: null,
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
return binding
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function getStableDirectEventHandler(binding: DirectEventBinding): (event: Event) => void {
|
|
632
|
+
if (binding.stableHandler) return binding.stableHandler
|
|
633
|
+
|
|
634
|
+
binding.stableHandler = (event) => {
|
|
635
|
+
invokeDirectEventBinding(binding, event)
|
|
636
|
+
}
|
|
637
|
+
return binding.stableHandler
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
function attachDirectEventBinding(dom: Element, binding: DirectEventBinding): void {
|
|
641
|
+
dom.addEventListener(binding.type, getStableDirectEventHandler(binding), binding.capture)
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function removeDirectEventBinding(dom: Element, binding: DirectEventBinding): void {
|
|
645
|
+
if (binding.stableHandler) {
|
|
646
|
+
dom.removeEventListener(binding.type, binding.stableHandler, binding.capture)
|
|
647
|
+
}
|
|
648
|
+
binding.reentry?.abort(new DOMException('', 'AbortError'))
|
|
649
|
+
binding.reentry = null
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
function teardownDirectEventListeners(node: CommittedHostNode): void {
|
|
653
|
+
let state = node._directEventState as DirectEventState | undefined
|
|
654
|
+
if (!state) return
|
|
655
|
+
|
|
656
|
+
for (let binding of state.bindings) {
|
|
657
|
+
removeDirectEventBinding(node._dom, binding)
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
state.bindings.length = 0
|
|
661
|
+
node._directEventState = undefined
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
function invokeDirectEventBinding(binding: DirectEventBinding, event: Event): void {
|
|
665
|
+
binding.reentry?.abort(new DOMException('', 'EventReentry'))
|
|
666
|
+
binding.reentry = new AbortController()
|
|
667
|
+
void binding.handler(event, binding.reentry.signal)
|
|
668
|
+
}
|
|
669
|
+
|
|
521
670
|
function diffText(curr: CommittedTextNode, next: TextNode, vParent: VNode) {
|
|
522
671
|
if (curr._text !== next._text) {
|
|
523
672
|
curr._dom.textContent = next._text
|
|
@@ -618,9 +767,11 @@ function insert(
|
|
|
618
767
|
rootTarget,
|
|
619
768
|
childCursor,
|
|
620
769
|
)
|
|
621
|
-
|
|
770
|
+
patchHostProps({}, hostProps, targetHead)
|
|
622
771
|
setupHostNode(node, targetHead, scheduler)
|
|
623
|
-
|
|
772
|
+
if (node._mixState) {
|
|
773
|
+
bindNodeMixRuntime(node as CommittedHostNode, frame, scheduler, styles)
|
|
774
|
+
}
|
|
624
775
|
return cursor
|
|
625
776
|
}
|
|
626
777
|
}
|
|
@@ -638,7 +789,7 @@ function insert(
|
|
|
638
789
|
let cursorTag = node._svg ? cursor.tagName : cursor.tagName.toLowerCase()
|
|
639
790
|
if (cursorTag === node.type) {
|
|
640
791
|
let nextCursor = cursor.nextSibling
|
|
641
|
-
|
|
792
|
+
patchHostProps({}, hostProps, cursor)
|
|
642
793
|
|
|
643
794
|
// Handle innerHTML prop
|
|
644
795
|
if (hostProps.innerHTML != null) {
|
|
@@ -660,7 +811,9 @@ function insert(
|
|
|
660
811
|
}
|
|
661
812
|
|
|
662
813
|
setupHostNode(node, cursor, scheduler)
|
|
663
|
-
|
|
814
|
+
if (node._mixState) {
|
|
815
|
+
bindNodeMixRuntime(node as CommittedHostNode, frame, scheduler, styles)
|
|
816
|
+
}
|
|
664
817
|
return nextCursor
|
|
665
818
|
} else {
|
|
666
819
|
// Type mismatch - try single-advance retry to handle browser extension injections
|
|
@@ -671,7 +824,7 @@ function insert(
|
|
|
671
824
|
if (nextTag === node.type) {
|
|
672
825
|
let nextCursor = nextSibling.nextSibling
|
|
673
826
|
// Found a match after skipping - adopt it and leave skipped node in place
|
|
674
|
-
|
|
827
|
+
patchHostProps({}, hostProps, nextSibling)
|
|
675
828
|
|
|
676
829
|
if (hostProps.innerHTML != null) {
|
|
677
830
|
nextSibling.innerHTML = hostProps.innerHTML as string
|
|
@@ -691,7 +844,9 @@ function insert(
|
|
|
691
844
|
}
|
|
692
845
|
|
|
693
846
|
setupHostNode(node, nextSibling, scheduler)
|
|
694
|
-
|
|
847
|
+
if (node._mixState) {
|
|
848
|
+
bindNodeMixRuntime(node as CommittedHostNode, frame, scheduler, styles)
|
|
849
|
+
}
|
|
695
850
|
return nextCursor
|
|
696
851
|
}
|
|
697
852
|
}
|
|
@@ -703,7 +858,7 @@ function insert(
|
|
|
703
858
|
let dom = node._svg
|
|
704
859
|
? document.createElementNS(SVG_NS, node.type)
|
|
705
860
|
: document.createElement(node.type)
|
|
706
|
-
|
|
861
|
+
patchHostProps({}, hostProps, dom)
|
|
707
862
|
|
|
708
863
|
// Handle innerHTML prop
|
|
709
864
|
if (hostProps.innerHTML != null) {
|
|
@@ -713,7 +868,9 @@ function insert(
|
|
|
713
868
|
}
|
|
714
869
|
|
|
715
870
|
setupHostNode(node, dom, scheduler)
|
|
716
|
-
|
|
871
|
+
if (node._mixState) {
|
|
872
|
+
bindNodeMixRuntime(node as CommittedHostNode, frame, scheduler, styles, false, domParent)
|
|
873
|
+
}
|
|
717
874
|
doInsert(dom)
|
|
718
875
|
return cursor
|
|
719
876
|
}
|
|
@@ -1223,6 +1380,7 @@ function cleanupDescendants(node: VNode, scheduler: Scheduler, styles: StyleMana
|
|
|
1223
1380
|
}
|
|
1224
1381
|
|
|
1225
1382
|
teardownMixins(node._mixState as MixinRuntimeState | undefined)
|
|
1383
|
+
teardownDirectEventListeners(node)
|
|
1226
1384
|
teardownControlledReflection(node)
|
|
1227
1385
|
if (node._controller) node._controller.abort()
|
|
1228
1386
|
return
|
|
@@ -1320,6 +1478,7 @@ function performHostNodeRemoval(
|
|
|
1320
1478
|
}
|
|
1321
1479
|
|
|
1322
1480
|
teardownMixins(node._mixState as MixinRuntimeState | undefined)
|
|
1481
|
+
teardownDirectEventListeners(node)
|
|
1323
1482
|
teardownControlledReflection(node)
|
|
1324
1483
|
// Never remove the real document.head node when reconciling a <head> vnode.
|
|
1325
1484
|
if (!isHeadHostNode(node)) {
|
|
@@ -1340,42 +1499,12 @@ function diffChildren(
|
|
|
1340
1499
|
cursor?: Node | null,
|
|
1341
1500
|
anchor?: Node,
|
|
1342
1501
|
) {
|
|
1343
|
-
let
|
|
1344
|
-
|
|
1345
|
-
// Warn when duplicate keys are present among siblings. Duplicate keys are
|
|
1346
|
-
// still processed (last one wins), but they make keyed diffing ambiguous.
|
|
1347
|
-
let hasKeys = false
|
|
1348
|
-
let seenKeys: Set<string> | undefined
|
|
1349
|
-
let duplicateKeys: Set<string> | undefined
|
|
1350
|
-
for (let i = 0; i < nextLength; i++) {
|
|
1351
|
-
let node = next[i]
|
|
1352
|
-
if (node && node.key != null) {
|
|
1353
|
-
hasKeys = true
|
|
1354
|
-
if (!seenKeys) {
|
|
1355
|
-
seenKeys = new Set([node.key])
|
|
1356
|
-
continue
|
|
1357
|
-
}
|
|
1358
|
-
if (seenKeys.has(node.key)) {
|
|
1359
|
-
if (!duplicateKeys) {
|
|
1360
|
-
duplicateKeys = new Set()
|
|
1361
|
-
}
|
|
1362
|
-
duplicateKeys.add(node.key)
|
|
1363
|
-
} else {
|
|
1364
|
-
seenKeys.add(node.key)
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1502
|
+
let hasKeys = hasKeyedChildren(next)
|
|
1368
1503
|
|
|
1369
|
-
if (duplicateKeys?.size) {
|
|
1370
|
-
let quotedKeys = Array.from(duplicateKeys, (key) => `"${key}"`)
|
|
1371
|
-
console.warn(
|
|
1372
|
-
`Duplicate keys detected in siblings: ${quotedKeys.join(', ')}. Keys should be unique.`,
|
|
1373
|
-
)
|
|
1374
|
-
}
|
|
1375
|
-
|
|
1376
|
-
// Initial mount / hydration: delegate to insert() for each child so that
|
|
1377
|
-
// hydration cursors and creation logic remain centralized there.
|
|
1378
1504
|
if (curr === null) {
|
|
1505
|
+
if (hasKeys) {
|
|
1506
|
+
warnDuplicateKeys(next)
|
|
1507
|
+
}
|
|
1379
1508
|
for (let node of next) {
|
|
1380
1509
|
cursor = insert(
|
|
1381
1510
|
node,
|
|
@@ -1393,14 +1522,23 @@ function diffChildren(
|
|
|
1393
1522
|
return cursor
|
|
1394
1523
|
}
|
|
1395
1524
|
|
|
1396
|
-
|
|
1525
|
+
if (
|
|
1526
|
+
next.length === 0 &&
|
|
1527
|
+
anchor === undefined &&
|
|
1528
|
+
!parentUsesInnerHTML(vParent) &&
|
|
1529
|
+
canBulkClearChildren(curr)
|
|
1530
|
+
) {
|
|
1531
|
+
for (let node of curr) {
|
|
1532
|
+
cleanupDescendants(node, scheduler, styles)
|
|
1533
|
+
}
|
|
1534
|
+
domParent.textContent = ''
|
|
1535
|
+
vParent._children = next
|
|
1536
|
+
return
|
|
1537
|
+
}
|
|
1397
1538
|
|
|
1398
|
-
// Detect if any keys are present in the new children. If not, we can fall
|
|
1399
|
-
// back to the simpler index-based diff which is cheaper and matches
|
|
1400
|
-
// pre-existing behavior.
|
|
1401
1539
|
if (!hasKeys) {
|
|
1402
|
-
for (let i = 0; i <
|
|
1403
|
-
let currentNode = i <
|
|
1540
|
+
for (let i = 0; i < next.length; i++) {
|
|
1541
|
+
let currentNode = i < curr.length ? curr[i] : null
|
|
1404
1542
|
diffVNodes(
|
|
1405
1543
|
currentNode,
|
|
1406
1544
|
next[i],
|
|
@@ -1415,8 +1553,8 @@ function diffChildren(
|
|
|
1415
1553
|
)
|
|
1416
1554
|
}
|
|
1417
1555
|
|
|
1418
|
-
if (
|
|
1419
|
-
for (let i =
|
|
1556
|
+
if (curr.length > next.length) {
|
|
1557
|
+
for (let i = next.length; i < curr.length; i++) {
|
|
1420
1558
|
let node = curr[i]
|
|
1421
1559
|
if (node) remove(node, domParent, scheduler, styles)
|
|
1422
1560
|
}
|
|
@@ -1426,128 +1564,137 @@ function diffChildren(
|
|
|
1426
1564
|
return
|
|
1427
1565
|
}
|
|
1428
1566
|
|
|
1429
|
-
|
|
1567
|
+
patchKeyedChildren(
|
|
1568
|
+
curr,
|
|
1569
|
+
next,
|
|
1570
|
+
domParent,
|
|
1571
|
+
frame,
|
|
1572
|
+
scheduler,
|
|
1573
|
+
styles,
|
|
1574
|
+
vParent,
|
|
1575
|
+
rootTarget,
|
|
1576
|
+
cursor,
|
|
1577
|
+
anchor,
|
|
1578
|
+
)
|
|
1430
1579
|
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
let remainingOldChildren = oldChildrenLength
|
|
1580
|
+
return
|
|
1581
|
+
}
|
|
1434
1582
|
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
oldKeyMap.set(c.key, i)
|
|
1443
|
-
}
|
|
1444
|
-
}
|
|
1583
|
+
function parentUsesInnerHTML(parent: VNode): boolean {
|
|
1584
|
+
return isHostNode(parent) && getHostProps(parent).innerHTML != null
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
function canBulkClearChildren(children: VNode[]): boolean {
|
|
1588
|
+
for (let child of children) {
|
|
1589
|
+
if (!canBulkClearNode(child)) return false
|
|
1445
1590
|
}
|
|
1591
|
+
return true
|
|
1592
|
+
}
|
|
1446
1593
|
|
|
1447
|
-
|
|
1448
|
-
|
|
1594
|
+
function canBulkClearNode(node: VNode): boolean {
|
|
1595
|
+
if (isCommittedTextNode(node)) return true
|
|
1449
1596
|
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
let
|
|
1453
|
-
|
|
1454
|
-
newChildren[i] = childVNode
|
|
1455
|
-
continue
|
|
1597
|
+
if (isCommittedHostNode(node)) {
|
|
1598
|
+
if (node._mixState) return false
|
|
1599
|
+
for (let child of node._children) {
|
|
1600
|
+
if (!canBulkClearNode(child)) return false
|
|
1456
1601
|
}
|
|
1602
|
+
return true
|
|
1603
|
+
}
|
|
1457
1604
|
|
|
1458
|
-
|
|
1459
|
-
|
|
1605
|
+
if (isFragmentNode(node)) {
|
|
1606
|
+
return canBulkClearChildren(node._children)
|
|
1607
|
+
}
|
|
1460
1608
|
|
|
1461
|
-
|
|
1462
|
-
|
|
1609
|
+
if (isCommittedComponentNode(node)) {
|
|
1610
|
+
return canBulkClearNode(node._content)
|
|
1611
|
+
}
|
|
1463
1612
|
|
|
1464
|
-
|
|
1465
|
-
|
|
1613
|
+
return false
|
|
1614
|
+
}
|
|
1466
1615
|
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
if (candidate && (candidateFlags & MATCHED) === 0 && candidate.type === type) {
|
|
1474
|
-
matchingIndex = mapIndex
|
|
1475
|
-
}
|
|
1476
|
-
}
|
|
1477
|
-
} else {
|
|
1478
|
-
// Non-keyed children use positional identity only - no searching
|
|
1479
|
-
let searchVNode = oldChildren[skewedIndex]
|
|
1480
|
-
let searchFlags = searchVNode?._flags ?? 0
|
|
1481
|
-
let available = searchVNode != null && (searchFlags & MATCHED) === 0
|
|
1482
|
-
if (available && searchVNode.key == null && type === searchVNode.type) {
|
|
1483
|
-
matchingIndex = skewedIndex
|
|
1484
|
-
}
|
|
1485
|
-
}
|
|
1616
|
+
function hasKeyedChildren(children: VNode[]): boolean {
|
|
1617
|
+
for (let node of children) {
|
|
1618
|
+
if (node.key != null) return true
|
|
1619
|
+
}
|
|
1620
|
+
return false
|
|
1621
|
+
}
|
|
1486
1622
|
|
|
1487
|
-
|
|
1623
|
+
function warnDuplicateKeys(children: VNode[]): void {
|
|
1624
|
+
let seenKeys: Set<string> | undefined
|
|
1625
|
+
let duplicateKeys: Set<string> | undefined
|
|
1488
1626
|
|
|
1489
|
-
|
|
1490
|
-
if (
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
}
|
|
1627
|
+
for (let node of children) {
|
|
1628
|
+
if (node.key == null) continue
|
|
1629
|
+
|
|
1630
|
+
if (!seenKeys) {
|
|
1631
|
+
seenKeys = new Set([node.key])
|
|
1632
|
+
continue
|
|
1496
1633
|
}
|
|
1497
1634
|
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
skew--
|
|
1506
|
-
} else if (nextLength < oldChildrenLength) {
|
|
1507
|
-
skew++
|
|
1508
|
-
}
|
|
1509
|
-
}
|
|
1635
|
+
if (seenKeys.has(node.key)) {
|
|
1636
|
+
duplicateKeys ??= new Set()
|
|
1637
|
+
duplicateKeys.add(node.key)
|
|
1638
|
+
} else {
|
|
1639
|
+
seenKeys.add(node.key)
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1510
1642
|
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1643
|
+
if (duplicateKeys?.size) {
|
|
1644
|
+
let quotedKeys = Array.from(duplicateKeys, (key) => `"${key}"`)
|
|
1645
|
+
console.warn(
|
|
1646
|
+
`Duplicate keys detected in siblings: ${quotedKeys.join(', ')}. Keys should be unique.`,
|
|
1647
|
+
)
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
function patchKeyedChildren(
|
|
1652
|
+
curr: VNode[],
|
|
1653
|
+
next: VNode[],
|
|
1654
|
+
domParent: ParentNode,
|
|
1655
|
+
frame: FrameHandle,
|
|
1656
|
+
scheduler: Scheduler,
|
|
1657
|
+
styles: StyleManager,
|
|
1658
|
+
vParent: VNode,
|
|
1659
|
+
rootTarget: EventTarget,
|
|
1660
|
+
cursor?: Node | null,
|
|
1661
|
+
anchor?: Node,
|
|
1662
|
+
): void {
|
|
1663
|
+
let matches =
|
|
1664
|
+
matchKeyedChildrenInOrder(curr, next) ??
|
|
1665
|
+
matchKeyedChildrenAfterSingleRemoval(curr, next) ??
|
|
1666
|
+
matchKeyedChildrenAfterPairSwap(curr, next)
|
|
1667
|
+
if (!matches) {
|
|
1668
|
+
warnDuplicateKeys(next)
|
|
1669
|
+
matches = matchKeyedChildren(curr, next)
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
let matchAnalysis = analyzeKeyedChildMatches(curr.length, matches)
|
|
1673
|
+
if (matchAnalysis.hasRemovals) {
|
|
1674
|
+
let usedOldIndexes = new Uint8Array(curr.length)
|
|
1675
|
+
|
|
1676
|
+
for (let match of matches) {
|
|
1677
|
+
if (match.oldIndex >= 0) {
|
|
1678
|
+
usedOldIndexes[match.oldIndex] = 1
|
|
1521
1679
|
}
|
|
1522
1680
|
}
|
|
1523
|
-
}
|
|
1524
1681
|
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
let oldVNode = oldChildren[i]
|
|
1529
|
-
if (oldVNode && ((oldVNode._flags ?? 0) & MATCHED) === 0) {
|
|
1530
|
-
remove(oldVNode, domParent, scheduler, styles)
|
|
1682
|
+
for (let oldIndex = 0; oldIndex < curr.length; oldIndex++) {
|
|
1683
|
+
if (usedOldIndexes[oldIndex] === 0) {
|
|
1684
|
+
remove(curr[oldIndex], domParent, scheduler, styles)
|
|
1531
1685
|
}
|
|
1532
1686
|
}
|
|
1533
1687
|
}
|
|
1534
1688
|
|
|
1535
|
-
|
|
1536
|
-
// order, similar to Preact's diffChildren + insert.
|
|
1537
|
-
vParent._children = newChildren
|
|
1689
|
+
vParent._children = next
|
|
1538
1690
|
|
|
1539
|
-
let
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
let childVNode = newChildren[i]
|
|
1543
|
-
if (!childVNode) continue
|
|
1544
|
-
|
|
1545
|
-
let idx = childVNode._index ?? -1
|
|
1546
|
-
let oldVNode = idx >= 0 ? oldChildren[idx] : null
|
|
1691
|
+
for (let index = 0; index < next.length; index++) {
|
|
1692
|
+
let match = matches[index]
|
|
1693
|
+
let oldNode = match.oldIndex >= 0 ? curr[match.oldIndex] : null
|
|
1547
1694
|
|
|
1548
1695
|
diffVNodes(
|
|
1549
|
-
|
|
1550
|
-
|
|
1696
|
+
oldNode,
|
|
1697
|
+
next[index],
|
|
1551
1698
|
domParent,
|
|
1552
1699
|
frame,
|
|
1553
1700
|
scheduler,
|
|
@@ -1557,40 +1704,255 @@ function diffChildren(
|
|
|
1557
1704
|
anchor,
|
|
1558
1705
|
cursor,
|
|
1559
1706
|
)
|
|
1707
|
+
}
|
|
1560
1708
|
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1709
|
+
if (matchAnalysis.canSkipPlacement) {
|
|
1710
|
+
return
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
let stableIndexes = lisMatches(matches)
|
|
1714
|
+
let stableCursor = stableIndexes.length - 1
|
|
1715
|
+
let placementAnchor: Node | null = anchor ?? null
|
|
1716
|
+
|
|
1717
|
+
for (let index = next.length - 1; index >= 0; index--) {
|
|
1718
|
+
let nextNode = next[index]
|
|
1719
|
+
let isStable = stableIndexes[stableCursor] === index
|
|
1720
|
+
|
|
1721
|
+
if (isStable) {
|
|
1722
|
+
stableCursor--
|
|
1723
|
+
} else {
|
|
1724
|
+
placeVNode(nextNode, domParent, placementAnchor)
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
placementAnchor = findFirstDomAnchor(nextNode) ?? placementAnchor
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
function matchKeyedChildren(curr: VNode[], next: VNode[]): Array<{ oldIndex: number }> {
|
|
1732
|
+
let oldKeyMap = new Map<string, number>()
|
|
1733
|
+
let usedOldIndexes = new Set<number>()
|
|
1734
|
+
let unkeyedSearchStart = 0
|
|
1735
|
+
|
|
1736
|
+
for (let index = 0; index < curr.length; index++) {
|
|
1737
|
+
let key = curr[index].key
|
|
1738
|
+
if (key != null) oldKeyMap.set(key, index)
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
return next.map((nextNode) => {
|
|
1742
|
+
let oldIndex = -1
|
|
1743
|
+
|
|
1744
|
+
if (nextNode.key != null) {
|
|
1745
|
+
let keyedOldIndex = oldKeyMap.get(nextNode.key)
|
|
1746
|
+
if (keyedOldIndex !== undefined) {
|
|
1747
|
+
let oldNode = curr[keyedOldIndex]
|
|
1748
|
+
if (!usedOldIndexes.has(keyedOldIndex) && oldNode.type === nextNode.type) {
|
|
1749
|
+
oldIndex = keyedOldIndex
|
|
1571
1750
|
}
|
|
1572
|
-
} else {
|
|
1573
|
-
target = lastPlaced.nextSibling
|
|
1574
1751
|
}
|
|
1752
|
+
} else {
|
|
1753
|
+
for (let index = unkeyedSearchStart; index < curr.length; index++) {
|
|
1754
|
+
let oldNode = curr[index]
|
|
1755
|
+
if (usedOldIndexes.has(index) || oldNode.key != null || oldNode.type !== nextNode.type) {
|
|
1756
|
+
continue
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
oldIndex = index
|
|
1760
|
+
unkeyedSearchStart = index + 1
|
|
1761
|
+
break
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
if (oldIndex >= 0) usedOldIndexes.add(oldIndex)
|
|
1766
|
+
return { oldIndex }
|
|
1767
|
+
})
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
function matchKeyedChildrenInOrder(
|
|
1771
|
+
curr: VNode[],
|
|
1772
|
+
next: VNode[],
|
|
1773
|
+
): Array<{ oldIndex: number }> | null {
|
|
1774
|
+
let length = Math.min(curr.length, next.length)
|
|
1775
|
+
let matches: Array<{ oldIndex: number }> = []
|
|
1776
|
+
|
|
1777
|
+
for (let index = 0; index < length; index++) {
|
|
1778
|
+
let nextNode = next[index]
|
|
1779
|
+
if (nextNode.key == null) return null
|
|
1780
|
+
|
|
1781
|
+
let oldNode = curr[index]
|
|
1782
|
+
if (oldNode.key !== nextNode.key || oldNode.type !== nextNode.type) {
|
|
1783
|
+
return null
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
matches.push({ oldIndex: index })
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
for (let index = length; index < next.length; index++) {
|
|
1790
|
+
if (next[index].key == null) return null
|
|
1791
|
+
matches.push({ oldIndex: -1 })
|
|
1792
|
+
}
|
|
1575
1793
|
|
|
1576
|
-
|
|
1794
|
+
return matches
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
function matchKeyedChildrenAfterSingleRemoval(
|
|
1798
|
+
curr: VNode[],
|
|
1799
|
+
next: VNode[],
|
|
1800
|
+
): Array<{ oldIndex: number }> | null {
|
|
1801
|
+
if (curr.length !== next.length + 1) return null
|
|
1802
|
+
|
|
1803
|
+
let matches: Array<{ oldIndex: number }> = []
|
|
1804
|
+
let oldIndex = 0
|
|
1805
|
+
let skippedOldNode = false
|
|
1806
|
+
|
|
1807
|
+
for (let nextIndex = 0; nextIndex < next.length; nextIndex++) {
|
|
1808
|
+
let nextNode = next[nextIndex]
|
|
1809
|
+
if (nextNode.key == null) return null
|
|
1810
|
+
|
|
1811
|
+
let oldNode = curr[oldIndex]
|
|
1812
|
+
if (oldNode.key === nextNode.key && oldNode.type === nextNode.type) {
|
|
1813
|
+
matches.push({ oldIndex })
|
|
1814
|
+
oldIndex++
|
|
1815
|
+
continue
|
|
1816
|
+
}
|
|
1577
1817
|
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1818
|
+
if (skippedOldNode) return null
|
|
1819
|
+
skippedOldNode = true
|
|
1820
|
+
oldIndex++
|
|
1821
|
+
|
|
1822
|
+
oldNode = curr[oldIndex]
|
|
1823
|
+
if (oldNode.key !== nextNode.key || oldNode.type !== nextNode.type) {
|
|
1824
|
+
return null
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
matches.push({ oldIndex })
|
|
1828
|
+
oldIndex++
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
return matches
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
function matchKeyedChildrenAfterPairSwap(
|
|
1835
|
+
curr: VNode[],
|
|
1836
|
+
next: VNode[],
|
|
1837
|
+
): Array<{ oldIndex: number }> | null {
|
|
1838
|
+
if (curr.length !== next.length) return null
|
|
1839
|
+
|
|
1840
|
+
let matches: Array<{ oldIndex: number }> = []
|
|
1841
|
+
let firstMismatch = -1
|
|
1842
|
+
let secondMismatch = -1
|
|
1843
|
+
|
|
1844
|
+
for (let index = 0; index < next.length; index++) {
|
|
1845
|
+
let nextNode = next[index]
|
|
1846
|
+
if (nextNode.key == null) return null
|
|
1847
|
+
|
|
1848
|
+
let oldNode = curr[index]
|
|
1849
|
+
if (oldNode.key === nextNode.key && oldNode.type === nextNode.type) {
|
|
1850
|
+
matches.push({ oldIndex: index })
|
|
1851
|
+
continue
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
if (firstMismatch === -1) {
|
|
1855
|
+
firstMismatch = index
|
|
1856
|
+
} else if (secondMismatch === -1) {
|
|
1857
|
+
secondMismatch = index
|
|
1858
|
+
} else {
|
|
1859
|
+
return null
|
|
1860
|
+
}
|
|
1861
|
+
matches.push({ oldIndex: -1 })
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
if (firstMismatch === -1) return matches
|
|
1865
|
+
if (secondMismatch === -1) return null
|
|
1866
|
+
|
|
1867
|
+
let firstOldNode = curr[firstMismatch]
|
|
1868
|
+
let secondOldNode = curr[secondMismatch]
|
|
1869
|
+
let firstNextNode = next[firstMismatch]
|
|
1870
|
+
let secondNextNode = next[secondMismatch]
|
|
1871
|
+
|
|
1872
|
+
if (
|
|
1873
|
+
firstOldNode.key !== secondNextNode.key ||
|
|
1874
|
+
firstOldNode.type !== secondNextNode.type ||
|
|
1875
|
+
secondOldNode.key !== firstNextNode.key ||
|
|
1876
|
+
secondOldNode.type !== firstNextNode.type
|
|
1877
|
+
) {
|
|
1878
|
+
return null
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
matches[firstMismatch] = { oldIndex: secondMismatch }
|
|
1882
|
+
matches[secondMismatch] = { oldIndex: firstMismatch }
|
|
1883
|
+
return matches
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
function analyzeKeyedChildMatches(
|
|
1887
|
+
currentLength: number,
|
|
1888
|
+
matches: ReadonlyArray<{ oldIndex: number }>,
|
|
1889
|
+
): { hasRemovals: boolean; canSkipPlacement: boolean } {
|
|
1890
|
+
let hasRemovals = matches.length !== currentLength
|
|
1891
|
+
let canSkipPlacement = true
|
|
1892
|
+
let lastOldIndex = -1
|
|
1893
|
+
let sawNewNode = false
|
|
1894
|
+
|
|
1895
|
+
for (let match of matches) {
|
|
1896
|
+
if (match.oldIndex < 0) {
|
|
1897
|
+
hasRemovals = true
|
|
1898
|
+
sawNewNode = true
|
|
1899
|
+
continue
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
if (sawNewNode || match.oldIndex < lastOldIndex) {
|
|
1903
|
+
canSkipPlacement = false
|
|
1904
|
+
}
|
|
1905
|
+
lastOldIndex = match.oldIndex
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
return { hasRemovals, canSkipPlacement }
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
function lisMatches(matches: ReadonlyArray<{ oldIndex: number }>): number[] {
|
|
1912
|
+
let predecessors = Array.from<number>({ length: matches.length })
|
|
1913
|
+
let tails: number[] = []
|
|
1914
|
+
|
|
1915
|
+
for (let index = 0; index < matches.length; index++) {
|
|
1916
|
+
let value = matches[index].oldIndex + 1
|
|
1917
|
+
if (value === 0) continue
|
|
1918
|
+
|
|
1919
|
+
let low = 0
|
|
1920
|
+
let high = tails.length
|
|
1921
|
+
|
|
1922
|
+
while (low < high) {
|
|
1923
|
+
let middle = (low + high) >> 1
|
|
1924
|
+
|
|
1925
|
+
if (matches[tails[middle]].oldIndex + 1 < value) {
|
|
1926
|
+
low = middle + 1
|
|
1927
|
+
} else {
|
|
1928
|
+
high = middle
|
|
1583
1929
|
}
|
|
1584
1930
|
}
|
|
1585
1931
|
|
|
1586
|
-
|
|
1932
|
+
predecessors[index] = low > 0 ? tails[low - 1] : -1
|
|
1933
|
+
tails[low] = index
|
|
1934
|
+
}
|
|
1587
1935
|
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1936
|
+
let cursor = tails.at(-1) ?? -1
|
|
1937
|
+
|
|
1938
|
+
for (let index = tails.length - 1; index >= 0; index--) {
|
|
1939
|
+
tails[index] = cursor
|
|
1940
|
+
cursor = predecessors[cursor] ?? -1
|
|
1591
1941
|
}
|
|
1592
1942
|
|
|
1593
|
-
return
|
|
1943
|
+
return tails
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
function placeVNode(node: VNode, domParent: ParentNode, anchor: Node | null): void {
|
|
1947
|
+
let firstDom = findFirstDomAnchor(node)
|
|
1948
|
+
if (!firstDom || firstDom.parentNode !== domParent) return
|
|
1949
|
+
|
|
1950
|
+
let lastDom = findLastDomAnchor(node)
|
|
1951
|
+
if (!lastDom) return
|
|
1952
|
+
if (anchor && domRangeContainsNode(firstDom, lastDom, anchor)) return
|
|
1953
|
+
if (firstDom === anchor) return
|
|
1954
|
+
|
|
1955
|
+
moveDomRange(domParent, firstDom, lastDom, anchor)
|
|
1594
1956
|
}
|
|
1595
1957
|
|
|
1596
1958
|
export function findFirstDomAnchor(node: VNode | null | undefined): Node | null {
|
|
@@ -1686,6 +2048,7 @@ function reclaimPersistedMixinNode(
|
|
|
1686
2048
|
newNode._parent = vParent
|
|
1687
2049
|
newNode._controller = persistedNode._controller
|
|
1688
2050
|
newNode._mixState = persistedNode._mixState
|
|
2051
|
+
newNode._directEventState = persistedNode._directEventState
|
|
1689
2052
|
newNode._controlledState = persistedNode._controlledState
|
|
1690
2053
|
|
|
1691
2054
|
let prevProps = getHostProps(persistedNode)
|
|
@@ -1698,7 +2061,8 @@ function reclaimPersistedMixinNode(
|
|
|
1698
2061
|
if (shouldDispatchInlineMixinLifecycle(persistedNode._dom)) {
|
|
1699
2062
|
dispatchMixinBeforeUpdate(newNode._mixState as MixinRuntimeState | undefined)
|
|
1700
2063
|
}
|
|
1701
|
-
|
|
2064
|
+
patchHostProps(prevProps, nextProps, persistedNode._dom)
|
|
2065
|
+
syncDirectEventListeners(newNode as CommittedHostNode)
|
|
1702
2066
|
ensureControlledReflection(newNode as CommittedHostNode, scheduler)
|
|
1703
2067
|
syncControlledReflection(newNode as CommittedHostNode, nextProps)
|
|
1704
2068
|
|
|
@@ -1713,7 +2077,9 @@ function reclaimPersistedMixinNode(
|
|
|
1713
2077
|
rootTarget,
|
|
1714
2078
|
)
|
|
1715
2079
|
|
|
1716
|
-
|
|
2080
|
+
if (newNode._mixState) {
|
|
2081
|
+
bindNodeMixRuntime(newNode as CommittedHostNode, frame, scheduler, styles, true)
|
|
2082
|
+
}
|
|
1717
2083
|
if (shouldDispatchInlineMixinLifecycle(persistedNode._dom)) {
|
|
1718
2084
|
scheduler.enqueueCommitPhase([
|
|
1719
2085
|
() => dispatchMixinCommit(newNode._mixState as MixinRuntimeState | undefined),
|