@remix-run/ui 0.1.1 → 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 +41 -47
- package/dist/runtime/frame.js.map +1 -1
- package/dist/runtime/jsx.js +2 -28
- 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.js +9 -1
- package/dist/runtime/navigation.js.map +1 -1
- package/dist/runtime/reconcile.js +433 -192
- 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/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 +5 -9
- 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 +76 -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 +3 -3
- 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 +71 -55
- package/src/runtime/jsx.ts +2 -29
- package/src/runtime/mixins/mixin.ts +9 -4
- package/src/runtime/mixins/on-mixin.ts +20 -12
- package/src/runtime/navigation.ts +10 -1
- package/src/runtime/reconcile.ts +540 -209
- package/src/runtime/run.ts +17 -1
- package/src/runtime/stream-protocol.ts +27 -0
- package/src/runtime/to-vnode.ts +5 -8
- package/src/runtime/vnode.ts +6 -9
- package/src/server/README.md +88 -0
- package/src/server/stream.ts +99 -116
- 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/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())
|
|
@@ -490,19 +529,23 @@ function diffHost(
|
|
|
490
529
|
next,
|
|
491
530
|
rootTarget,
|
|
492
531
|
)
|
|
493
|
-
|
|
532
|
+
patchHostProps(currProps, nextProps, curr._dom)
|
|
494
533
|
|
|
495
534
|
next._dom = curr._dom
|
|
496
535
|
next._parent = vParent
|
|
497
536
|
next._controller = curr._controller
|
|
537
|
+
next._directEventState = curr._directEventState
|
|
498
538
|
next._controlledState = curr._controlledState
|
|
539
|
+
syncDirectEventListeners(next as CommittedHostNode)
|
|
499
540
|
|
|
500
541
|
if (next._controlledState || shouldTrackControlledReflection(nextProps)) {
|
|
501
542
|
ensureControlledReflection(next as CommittedHostNode, scheduler)
|
|
502
543
|
syncControlledReflection(next as CommittedHostNode, nextProps)
|
|
503
544
|
}
|
|
504
545
|
|
|
505
|
-
|
|
546
|
+
if (next._mixState) {
|
|
547
|
+
bindNodeMixRuntime(next as CommittedHostNode, frame, scheduler, styles)
|
|
548
|
+
}
|
|
506
549
|
if (shouldDispatchMixinLifecycle) {
|
|
507
550
|
scheduler.enqueueCommitPhase([() => dispatchMixinCommit(nextMixState)])
|
|
508
551
|
}
|
|
@@ -515,12 +558,115 @@ function setupHostNode(node: HostNode, dom: Element, scheduler: Scheduler): void
|
|
|
515
558
|
let props = getHostProps(node)
|
|
516
559
|
let committedNode = node as CommittedHostNode
|
|
517
560
|
|
|
561
|
+
syncDirectEventListeners(committedNode)
|
|
562
|
+
|
|
518
563
|
if (shouldTrackControlledReflection(props)) {
|
|
519
564
|
ensureControlledReflection(committedNode, scheduler)
|
|
520
565
|
syncControlledReflection(committedNode, props)
|
|
521
566
|
}
|
|
522
567
|
}
|
|
523
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
|
+
|
|
524
670
|
function diffText(curr: CommittedTextNode, next: TextNode, vParent: VNode) {
|
|
525
671
|
if (curr._text !== next._text) {
|
|
526
672
|
curr._dom.textContent = next._text
|
|
@@ -621,9 +767,11 @@ function insert(
|
|
|
621
767
|
rootTarget,
|
|
622
768
|
childCursor,
|
|
623
769
|
)
|
|
624
|
-
|
|
770
|
+
patchHostProps({}, hostProps, targetHead)
|
|
625
771
|
setupHostNode(node, targetHead, scheduler)
|
|
626
|
-
|
|
772
|
+
if (node._mixState) {
|
|
773
|
+
bindNodeMixRuntime(node as CommittedHostNode, frame, scheduler, styles)
|
|
774
|
+
}
|
|
627
775
|
return cursor
|
|
628
776
|
}
|
|
629
777
|
}
|
|
@@ -641,7 +789,7 @@ function insert(
|
|
|
641
789
|
let cursorTag = node._svg ? cursor.tagName : cursor.tagName.toLowerCase()
|
|
642
790
|
if (cursorTag === node.type) {
|
|
643
791
|
let nextCursor = cursor.nextSibling
|
|
644
|
-
|
|
792
|
+
patchHostProps({}, hostProps, cursor)
|
|
645
793
|
|
|
646
794
|
// Handle innerHTML prop
|
|
647
795
|
if (hostProps.innerHTML != null) {
|
|
@@ -663,7 +811,9 @@ function insert(
|
|
|
663
811
|
}
|
|
664
812
|
|
|
665
813
|
setupHostNode(node, cursor, scheduler)
|
|
666
|
-
|
|
814
|
+
if (node._mixState) {
|
|
815
|
+
bindNodeMixRuntime(node as CommittedHostNode, frame, scheduler, styles)
|
|
816
|
+
}
|
|
667
817
|
return nextCursor
|
|
668
818
|
} else {
|
|
669
819
|
// Type mismatch - try single-advance retry to handle browser extension injections
|
|
@@ -674,7 +824,7 @@ function insert(
|
|
|
674
824
|
if (nextTag === node.type) {
|
|
675
825
|
let nextCursor = nextSibling.nextSibling
|
|
676
826
|
// Found a match after skipping - adopt it and leave skipped node in place
|
|
677
|
-
|
|
827
|
+
patchHostProps({}, hostProps, nextSibling)
|
|
678
828
|
|
|
679
829
|
if (hostProps.innerHTML != null) {
|
|
680
830
|
nextSibling.innerHTML = hostProps.innerHTML as string
|
|
@@ -694,7 +844,9 @@ function insert(
|
|
|
694
844
|
}
|
|
695
845
|
|
|
696
846
|
setupHostNode(node, nextSibling, scheduler)
|
|
697
|
-
|
|
847
|
+
if (node._mixState) {
|
|
848
|
+
bindNodeMixRuntime(node as CommittedHostNode, frame, scheduler, styles)
|
|
849
|
+
}
|
|
698
850
|
return nextCursor
|
|
699
851
|
}
|
|
700
852
|
}
|
|
@@ -706,7 +858,7 @@ function insert(
|
|
|
706
858
|
let dom = node._svg
|
|
707
859
|
? document.createElementNS(SVG_NS, node.type)
|
|
708
860
|
: document.createElement(node.type)
|
|
709
|
-
|
|
861
|
+
patchHostProps({}, hostProps, dom)
|
|
710
862
|
|
|
711
863
|
// Handle innerHTML prop
|
|
712
864
|
if (hostProps.innerHTML != null) {
|
|
@@ -716,7 +868,9 @@ function insert(
|
|
|
716
868
|
}
|
|
717
869
|
|
|
718
870
|
setupHostNode(node, dom, scheduler)
|
|
719
|
-
|
|
871
|
+
if (node._mixState) {
|
|
872
|
+
bindNodeMixRuntime(node as CommittedHostNode, frame, scheduler, styles, false, domParent)
|
|
873
|
+
}
|
|
720
874
|
doInsert(dom)
|
|
721
875
|
return cursor
|
|
722
876
|
}
|
|
@@ -1226,6 +1380,7 @@ function cleanupDescendants(node: VNode, scheduler: Scheduler, styles: StyleMana
|
|
|
1226
1380
|
}
|
|
1227
1381
|
|
|
1228
1382
|
teardownMixins(node._mixState as MixinRuntimeState | undefined)
|
|
1383
|
+
teardownDirectEventListeners(node)
|
|
1229
1384
|
teardownControlledReflection(node)
|
|
1230
1385
|
if (node._controller) node._controller.abort()
|
|
1231
1386
|
return
|
|
@@ -1323,6 +1478,7 @@ function performHostNodeRemoval(
|
|
|
1323
1478
|
}
|
|
1324
1479
|
|
|
1325
1480
|
teardownMixins(node._mixState as MixinRuntimeState | undefined)
|
|
1481
|
+
teardownDirectEventListeners(node)
|
|
1326
1482
|
teardownControlledReflection(node)
|
|
1327
1483
|
// Never remove the real document.head node when reconciling a <head> vnode.
|
|
1328
1484
|
if (!isHeadHostNode(node)) {
|
|
@@ -1343,42 +1499,12 @@ function diffChildren(
|
|
|
1343
1499
|
cursor?: Node | null,
|
|
1344
1500
|
anchor?: Node,
|
|
1345
1501
|
) {
|
|
1346
|
-
let
|
|
1502
|
+
let hasKeys = hasKeyedChildren(next)
|
|
1347
1503
|
|
|
1348
|
-
// Warn when duplicate keys are present among siblings. Duplicate keys are
|
|
1349
|
-
// still processed (last one wins), but they make keyed diffing ambiguous.
|
|
1350
|
-
let hasKeys = false
|
|
1351
|
-
let seenKeys: Set<string> | undefined
|
|
1352
|
-
let duplicateKeys: Set<string> | undefined
|
|
1353
|
-
for (let i = 0; i < nextLength; i++) {
|
|
1354
|
-
let node = next[i]
|
|
1355
|
-
if (node && node.key != null) {
|
|
1356
|
-
hasKeys = true
|
|
1357
|
-
if (!seenKeys) {
|
|
1358
|
-
seenKeys = new Set([node.key])
|
|
1359
|
-
continue
|
|
1360
|
-
}
|
|
1361
|
-
if (seenKeys.has(node.key)) {
|
|
1362
|
-
if (!duplicateKeys) {
|
|
1363
|
-
duplicateKeys = new Set()
|
|
1364
|
-
}
|
|
1365
|
-
duplicateKeys.add(node.key)
|
|
1366
|
-
} else {
|
|
1367
|
-
seenKeys.add(node.key)
|
|
1368
|
-
}
|
|
1369
|
-
}
|
|
1370
|
-
}
|
|
1371
|
-
|
|
1372
|
-
if (duplicateKeys?.size) {
|
|
1373
|
-
let quotedKeys = Array.from(duplicateKeys, (key) => `"${key}"`)
|
|
1374
|
-
console.warn(
|
|
1375
|
-
`Duplicate keys detected in siblings: ${quotedKeys.join(', ')}. Keys should be unique.`,
|
|
1376
|
-
)
|
|
1377
|
-
}
|
|
1378
|
-
|
|
1379
|
-
// Initial mount / hydration: delegate to insert() for each child so that
|
|
1380
|
-
// hydration cursors and creation logic remain centralized there.
|
|
1381
1504
|
if (curr === null) {
|
|
1505
|
+
if (hasKeys) {
|
|
1506
|
+
warnDuplicateKeys(next)
|
|
1507
|
+
}
|
|
1382
1508
|
for (let node of next) {
|
|
1383
1509
|
cursor = insert(
|
|
1384
1510
|
node,
|
|
@@ -1396,14 +1522,23 @@ function diffChildren(
|
|
|
1396
1522
|
return cursor
|
|
1397
1523
|
}
|
|
1398
1524
|
|
|
1399
|
-
|
|
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
|
+
}
|
|
1400
1538
|
|
|
1401
|
-
// Detect if any keys are present in the new children. If not, we can fall
|
|
1402
|
-
// back to the simpler index-based diff which is cheaper and matches
|
|
1403
|
-
// pre-existing behavior.
|
|
1404
1539
|
if (!hasKeys) {
|
|
1405
|
-
for (let i = 0; i <
|
|
1406
|
-
let currentNode = i <
|
|
1540
|
+
for (let i = 0; i < next.length; i++) {
|
|
1541
|
+
let currentNode = i < curr.length ? curr[i] : null
|
|
1407
1542
|
diffVNodes(
|
|
1408
1543
|
currentNode,
|
|
1409
1544
|
next[i],
|
|
@@ -1418,8 +1553,8 @@ function diffChildren(
|
|
|
1418
1553
|
)
|
|
1419
1554
|
}
|
|
1420
1555
|
|
|
1421
|
-
if (
|
|
1422
|
-
for (let i =
|
|
1556
|
+
if (curr.length > next.length) {
|
|
1557
|
+
for (let i = next.length; i < curr.length; i++) {
|
|
1423
1558
|
let node = curr[i]
|
|
1424
1559
|
if (node) remove(node, domParent, scheduler, styles)
|
|
1425
1560
|
}
|
|
@@ -1429,160 +1564,137 @@ function diffChildren(
|
|
|
1429
1564
|
return
|
|
1430
1565
|
}
|
|
1431
1566
|
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1567
|
+
patchKeyedChildren(
|
|
1568
|
+
curr,
|
|
1569
|
+
next,
|
|
1570
|
+
domParent,
|
|
1571
|
+
frame,
|
|
1572
|
+
scheduler,
|
|
1573
|
+
styles,
|
|
1574
|
+
vParent,
|
|
1575
|
+
rootTarget,
|
|
1576
|
+
cursor,
|
|
1577
|
+
anchor,
|
|
1578
|
+
)
|
|
1442
1579
|
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
diffVNodes(
|
|
1446
|
-
curr[i],
|
|
1447
|
-
next[i],
|
|
1448
|
-
domParent,
|
|
1449
|
-
frame,
|
|
1450
|
-
scheduler,
|
|
1451
|
-
styles,
|
|
1452
|
-
vParent,
|
|
1453
|
-
rootTarget,
|
|
1454
|
-
anchor,
|
|
1455
|
-
cursor,
|
|
1456
|
-
)
|
|
1457
|
-
}
|
|
1580
|
+
return
|
|
1581
|
+
}
|
|
1458
1582
|
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
}
|
|
1583
|
+
function parentUsesInnerHTML(parent: VNode): boolean {
|
|
1584
|
+
return isHostNode(parent) && getHostProps(parent).innerHTML != null
|
|
1585
|
+
}
|
|
1463
1586
|
|
|
1464
|
-
|
|
1587
|
+
function canBulkClearChildren(children: VNode[]): boolean {
|
|
1588
|
+
for (let child of children) {
|
|
1589
|
+
if (!canBulkClearNode(child)) return false
|
|
1590
|
+
}
|
|
1591
|
+
return true
|
|
1592
|
+
}
|
|
1465
1593
|
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
let remainingOldChildren = oldChildrenLength
|
|
1594
|
+
function canBulkClearNode(node: VNode): boolean {
|
|
1595
|
+
if (isCommittedTextNode(node)) return true
|
|
1469
1596
|
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
if (c) {
|
|
1475
|
-
c._flags = 0
|
|
1476
|
-
if (c.key != null) {
|
|
1477
|
-
oldKeyMap.set(c.key, i)
|
|
1478
|
-
}
|
|
1597
|
+
if (isCommittedHostNode(node)) {
|
|
1598
|
+
if (node._mixState) return false
|
|
1599
|
+
for (let child of node._children) {
|
|
1600
|
+
if (!canBulkClearNode(child)) return false
|
|
1479
1601
|
}
|
|
1602
|
+
return true
|
|
1480
1603
|
}
|
|
1481
1604
|
|
|
1482
|
-
|
|
1483
|
-
|
|
1605
|
+
if (isFragmentNode(node)) {
|
|
1606
|
+
return canBulkClearChildren(node._children)
|
|
1607
|
+
}
|
|
1484
1608
|
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
if (!childVNode) {
|
|
1489
|
-
newChildren[i] = childVNode
|
|
1490
|
-
continue
|
|
1491
|
-
}
|
|
1609
|
+
if (isCommittedComponentNode(node)) {
|
|
1610
|
+
return canBulkClearNode(node._content)
|
|
1611
|
+
}
|
|
1492
1612
|
|
|
1493
|
-
|
|
1494
|
-
|
|
1613
|
+
return false
|
|
1614
|
+
}
|
|
1495
1615
|
|
|
1496
|
-
|
|
1497
|
-
|
|
1616
|
+
function hasKeyedChildren(children: VNode[]): boolean {
|
|
1617
|
+
for (let node of children) {
|
|
1618
|
+
if (node.key != null) return true
|
|
1619
|
+
}
|
|
1620
|
+
return false
|
|
1621
|
+
}
|
|
1498
1622
|
|
|
1499
|
-
|
|
1500
|
-
|
|
1623
|
+
function warnDuplicateKeys(children: VNode[]): void {
|
|
1624
|
+
let seenKeys: Set<string> | undefined
|
|
1625
|
+
let duplicateKeys: Set<string> | undefined
|
|
1501
1626
|
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
let mapIndex = oldKeyMap.get(key)
|
|
1505
|
-
if (mapIndex !== undefined) {
|
|
1506
|
-
let candidate = oldChildren[mapIndex]
|
|
1507
|
-
let candidateFlags = candidate?._flags ?? 0
|
|
1508
|
-
if (candidate && (candidateFlags & MATCHED) === 0 && candidate.type === type) {
|
|
1509
|
-
matchingIndex = mapIndex
|
|
1510
|
-
}
|
|
1511
|
-
}
|
|
1512
|
-
} else {
|
|
1513
|
-
// Non-keyed children use positional identity only - no searching
|
|
1514
|
-
let searchVNode = oldChildren[skewedIndex]
|
|
1515
|
-
let searchFlags = searchVNode?._flags ?? 0
|
|
1516
|
-
let available = searchVNode != null && (searchFlags & MATCHED) === 0
|
|
1517
|
-
if (available && searchVNode.key == null && type === searchVNode.type) {
|
|
1518
|
-
matchingIndex = skewedIndex
|
|
1519
|
-
}
|
|
1520
|
-
}
|
|
1627
|
+
for (let node of children) {
|
|
1628
|
+
if (node.key == null) continue
|
|
1521
1629
|
|
|
1522
|
-
|
|
1630
|
+
if (!seenKeys) {
|
|
1631
|
+
seenKeys = new Set([node.key])
|
|
1632
|
+
continue
|
|
1633
|
+
}
|
|
1523
1634
|
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
matchedOldVNode._flags = (matchedOldVNode._flags ?? 0) | MATCHED
|
|
1530
|
-
}
|
|
1635
|
+
if (seenKeys.has(node.key)) {
|
|
1636
|
+
duplicateKeys ??= new Set()
|
|
1637
|
+
duplicateKeys.add(node.key)
|
|
1638
|
+
} else {
|
|
1639
|
+
seenKeys.add(node.key)
|
|
1531
1640
|
}
|
|
1641
|
+
}
|
|
1532
1642
|
|
|
1533
|
-
|
|
1534
|
-
let
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
skew--
|
|
1541
|
-
} else if (nextLength < oldChildrenLength) {
|
|
1542
|
-
skew++
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
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
|
+
}
|
|
1545
1650
|
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
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
|
|
1556
1679
|
}
|
|
1557
1680
|
}
|
|
1558
|
-
}
|
|
1559
1681
|
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
let oldVNode = oldChildren[i]
|
|
1564
|
-
if (oldVNode && ((oldVNode._flags ?? 0) & MATCHED) === 0) {
|
|
1565
|
-
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)
|
|
1566
1685
|
}
|
|
1567
1686
|
}
|
|
1568
1687
|
}
|
|
1569
1688
|
|
|
1570
|
-
|
|
1571
|
-
// order, similar to Preact's diffChildren + insert.
|
|
1572
|
-
vParent._children = newChildren
|
|
1573
|
-
|
|
1574
|
-
let lastPlaced: Node | null = null
|
|
1575
|
-
|
|
1576
|
-
for (let i = 0; i < nextLength; i++) {
|
|
1577
|
-
let childVNode = newChildren[i]
|
|
1578
|
-
if (!childVNode) continue
|
|
1689
|
+
vParent._children = next
|
|
1579
1690
|
|
|
1580
|
-
|
|
1581
|
-
let
|
|
1691
|
+
for (let index = 0; index < next.length; index++) {
|
|
1692
|
+
let match = matches[index]
|
|
1693
|
+
let oldNode = match.oldIndex >= 0 ? curr[match.oldIndex] : null
|
|
1582
1694
|
|
|
1583
1695
|
diffVNodes(
|
|
1584
|
-
|
|
1585
|
-
|
|
1696
|
+
oldNode,
|
|
1697
|
+
next[index],
|
|
1586
1698
|
domParent,
|
|
1587
1699
|
frame,
|
|
1588
1700
|
scheduler,
|
|
@@ -1592,40 +1704,255 @@ function diffChildren(
|
|
|
1592
1704
|
anchor,
|
|
1593
1705
|
cursor,
|
|
1594
1706
|
)
|
|
1707
|
+
}
|
|
1595
1708
|
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
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
|
|
1606
1750
|
}
|
|
1607
|
-
} else {
|
|
1608
|
-
target = lastPlaced.nextSibling
|
|
1609
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
|
+
}
|
|
1793
|
+
|
|
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
|
|
1610
1810
|
|
|
1611
|
-
|
|
1811
|
+
let oldNode = curr[oldIndex]
|
|
1812
|
+
if (oldNode.key === nextNode.key && oldNode.type === nextNode.type) {
|
|
1813
|
+
matches.push({ oldIndex })
|
|
1814
|
+
oldIndex++
|
|
1815
|
+
continue
|
|
1816
|
+
}
|
|
1817
|
+
|
|
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
|
+
}
|
|
1612
1853
|
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
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
|
|
1618
1929
|
}
|
|
1619
1930
|
}
|
|
1620
1931
|
|
|
1621
|
-
|
|
1932
|
+
predecessors[index] = low > 0 ? tails[low - 1] : -1
|
|
1933
|
+
tails[low] = index
|
|
1934
|
+
}
|
|
1622
1935
|
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
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
|
|
1626
1941
|
}
|
|
1627
1942
|
|
|
1628
|
-
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)
|
|
1629
1956
|
}
|
|
1630
1957
|
|
|
1631
1958
|
export function findFirstDomAnchor(node: VNode | null | undefined): Node | null {
|
|
@@ -1721,6 +2048,7 @@ function reclaimPersistedMixinNode(
|
|
|
1721
2048
|
newNode._parent = vParent
|
|
1722
2049
|
newNode._controller = persistedNode._controller
|
|
1723
2050
|
newNode._mixState = persistedNode._mixState
|
|
2051
|
+
newNode._directEventState = persistedNode._directEventState
|
|
1724
2052
|
newNode._controlledState = persistedNode._controlledState
|
|
1725
2053
|
|
|
1726
2054
|
let prevProps = getHostProps(persistedNode)
|
|
@@ -1733,7 +2061,8 @@ function reclaimPersistedMixinNode(
|
|
|
1733
2061
|
if (shouldDispatchInlineMixinLifecycle(persistedNode._dom)) {
|
|
1734
2062
|
dispatchMixinBeforeUpdate(newNode._mixState as MixinRuntimeState | undefined)
|
|
1735
2063
|
}
|
|
1736
|
-
|
|
2064
|
+
patchHostProps(prevProps, nextProps, persistedNode._dom)
|
|
2065
|
+
syncDirectEventListeners(newNode as CommittedHostNode)
|
|
1737
2066
|
ensureControlledReflection(newNode as CommittedHostNode, scheduler)
|
|
1738
2067
|
syncControlledReflection(newNode as CommittedHostNode, nextProps)
|
|
1739
2068
|
|
|
@@ -1748,7 +2077,9 @@ function reclaimPersistedMixinNode(
|
|
|
1748
2077
|
rootTarget,
|
|
1749
2078
|
)
|
|
1750
2079
|
|
|
1751
|
-
|
|
2080
|
+
if (newNode._mixState) {
|
|
2081
|
+
bindNodeMixRuntime(newNode as CommittedHostNode, frame, scheduler, styles, true)
|
|
2082
|
+
}
|
|
1752
2083
|
if (shouldDispatchInlineMixinLifecycle(persistedNode._dom)) {
|
|
1753
2084
|
scheduler.enqueueCommitPhase([
|
|
1754
2085
|
() => dispatchMixinCommit(newNode._mixState as MixinRuntimeState | undefined),
|