@remix-run/ui 0.1.1 → 0.2.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/README.md +33 -10
- package/dist/animation/animate-layout-mixin.js +66 -66
- package/dist/animation/animate-layout-mixin.js.map +1 -1
- package/dist/animation/spring.d.ts +6 -35
- package/dist/animation/spring.js +9 -8
- package/dist/animation/spring.js.map +1 -1
- package/dist/components/accordion/accordion.d.ts +7 -7
- 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.d.ts +1 -1
- 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 +86 -4
- package/dist/components/button/button.js +63 -3
- 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 +11 -11
- 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 +4 -9
- 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 +15 -24
- 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/popover/popover.d.ts +5 -5
- package/dist/components/popover/popover.js.map +1 -1
- 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 +11 -11
- package/dist/components/select/select.js +17 -26
- package/dist/components/select/select.js.map +1 -1
- package/dist/components/tabs/tabs.d.ts +6 -6
- package/dist/components/tabs/tabs.js +0 -2
- package/dist/components/tabs/tabs.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/dist/interactions/keydown/keydown.d.ts +1 -1
- package/dist/interactions/outside-click/outside-click-mixin.d.ts +2 -1
- package/dist/interactions/typeahead/typeahead-mixin.d.ts +3 -1
- package/dist/runtime/component.d.ts +18 -12
- package/dist/runtime/component.js +93 -80
- package/dist/runtime/component.js.map +1 -1
- package/dist/runtime/core/attributes.d.ts +14 -0
- package/dist/runtime/core/attributes.js +103 -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 +170 -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 +20 -9
- 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.d.ts +1 -1
- package/dist/runtime/jsx.js +2 -28
- package/dist/runtime/jsx.js.map +1 -1
- package/dist/runtime/mixins/link-mixin.d.ts +5 -1
- package/dist/runtime/mixins/link-mixin.js.map +1 -1
- package/dist/runtime/mixins/mixin.d.ts +2 -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/mixins/ref-mixin.d.ts +2 -1
- package/dist/runtime/mixins/ref-mixin.js.map +1 -1
- package/dist/runtime/navigation.js +9 -1
- package/dist/runtime/navigation.js.map +1 -1
- package/dist/runtime/reconcile.d.ts +1 -1
- package/dist/runtime/reconcile.js +452 -197
- 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.d.ts +10 -10
- package/dist/runtime/scheduler.js +1 -1
- 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/svg-attributes.js +1 -0
- package/dist/runtime/svg-attributes.js.map +1 -1
- package/dist/runtime/to-vnode.js +7 -11
- package/dist/runtime/to-vnode.js.map +1 -1
- package/dist/runtime/vnode.d.ts +11 -5
- package/dist/runtime/vnode.js +6 -3
- package/dist/runtime/vnode.js.map +1 -1
- package/dist/server/stream.d.ts +2 -0
- package/dist/server/stream.js +154 -114
- package/dist/server/stream.js.map +1 -1
- package/dist/style/css-mixin.d.ts +2 -1
- package/dist/style/css-mixin.js.map +1 -1
- package/dist/style/index.d.ts +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 +13 -11
- package/dist/style/stylesheet.js +69 -8
- package/dist/style/stylesheet.js.map +1 -1
- package/dist/theme/contract.d.ts +24 -222
- package/dist/theme/contract.js +2 -2
- package/dist/theme/contract.js.map +1 -1
- package/dist/theme/glyph-contract.d.ts +1 -3
- package/dist/theme/glyph-contract.js.map +1 -1
- package/dist/theme/presets/rmx-01/index.d.ts +4 -2
- package/dist/theme/presets/rmx-01/index.js.map +1 -1
- package/dist/theme/runtime.js +4 -1
- package/dist/theme/runtime.js.map +1 -1
- package/dist/utils/scroll-lock.d.ts +3 -3
- package/dist/utils/scroll-lock.js.map +1 -1
- package/package.json +3 -3
- package/src/animation/README.md +316 -0
- package/src/animation/animate-layout-mixin.ts +79 -71
- package/src/animation/spring.ts +21 -9
- package/src/components/accordion/README.md +166 -0
- package/src/components/accordion/accordion.tsx +14 -14
- 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/anchor/anchor.ts +1 -1
- package/src/components/breadcrumbs/README.md +55 -0
- package/src/components/breadcrumbs/breadcrumbs.tsx +1 -3
- 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 +118 -20
- 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/README.md +1 -1
- package/src/components/combobox/combobox.tsx +37 -21
- package/src/components/combobox/demos/overview.demo.tsx +199 -0
- package/src/components/glyph/README.md +72 -0
- package/src/components/glyph/glyph.tsx +2 -2
- package/src/components/listbox/README.md +115 -0
- package/src/components/listbox/demos/overview.demo.tsx +101 -0
- package/src/components/listbox/listbox.ts +106 -110
- 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 +61 -55
- package/src/components/popover/README.md +3 -3
- package/src/components/popover/demos/overview.demo.tsx +143 -0
- package/src/components/popover/popover.ts +90 -79
- 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 +53 -36
- package/src/components/tabs/tabs.tsx +10 -5
- package/src/index.ts +2 -1
- package/src/runtime/component.ts +122 -88
- package/src/runtime/core/attributes.ts +118 -0
- package/src/runtime/core/children.ts +42 -0
- package/src/runtime/core/props.ts +187 -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 +20 -9
- package/src/runtime/frame.ts +71 -55
- package/src/runtime/jsx.ts +3 -30
- package/src/runtime/mixins/link-mixin.ts +88 -86
- package/src/runtime/mixins/mixin.ts +18 -5
- package/src/runtime/mixins/on-mixin.ts +20 -12
- package/src/runtime/mixins/ref-mixin.ts +4 -5
- package/src/runtime/navigation.ts +10 -1
- package/src/runtime/reconcile.ts +563 -223
- package/src/runtime/run.ts +17 -1
- package/src/runtime/scheduler.ts +20 -20
- package/src/runtime/stream-protocol.ts +27 -0
- package/src/runtime/svg-attributes.ts +1 -0
- package/src/runtime/to-vnode.ts +7 -10
- package/src/runtime/vnode.ts +16 -9
- package/src/server/README.md +90 -0
- package/src/server/stream.ts +176 -124
- package/src/style/css-mixin.ts +6 -1
- package/src/style/index.ts +1 -2
- package/src/style/layers.ts +2 -0
- package/src/style/stylesheet.ts +88 -11
- package/src/test/README.md +107 -0
- package/src/test/utils.ts +3 -1
- package/src/theme/README.md +103 -0
- package/src/theme/contract.ts +37 -5
- package/src/theme/glyph-contract.ts +3 -1
- package/src/theme/presets/rmx-01/index.ts +4 -2
- package/src/theme/runtime.ts +4 -1
- package/src/utils/scroll-lock/README.md +33 -0
- package/src/utils/scroll-lock.ts +7 -3
package/src/runtime/reconcile.ts
CHANGED
|
@@ -20,11 +20,12 @@ import {
|
|
|
20
20
|
isCommittedTextNode,
|
|
21
21
|
isFragmentNode,
|
|
22
22
|
isHostNode,
|
|
23
|
+
isNonRenderNode,
|
|
23
24
|
isTextNode,
|
|
24
25
|
findContextFromAncestry,
|
|
25
26
|
} from './vnode.ts'
|
|
26
27
|
import { invariant } from './invariant.ts'
|
|
27
|
-
import {
|
|
28
|
+
import { patchHostProps } from './core/props.ts'
|
|
28
29
|
import type { StyleManager } from '../style/index.ts'
|
|
29
30
|
import type { ElementProps } from './jsx.ts'
|
|
30
31
|
import { skipComments, logHydrationMismatch } from './client-entries.ts'
|
|
@@ -42,13 +43,10 @@ import {
|
|
|
42
43
|
type MixinRuntimeBinding,
|
|
43
44
|
type MixinRuntimeState,
|
|
44
45
|
} from './mixins/mixin.ts'
|
|
46
|
+
import { isOnMixinDescriptor, type OnMixinDescriptor } from './mixins/on-mixin.ts'
|
|
45
47
|
|
|
46
48
|
const SVG_NS = 'http://www.w3.org/2000/svg'
|
|
47
49
|
|
|
48
|
-
// Internal diffing flags (modeled after Preact)
|
|
49
|
-
const INSERT_VNODE = 1 << 0
|
|
50
|
-
const MATCHED = 1 << 1
|
|
51
|
-
|
|
52
50
|
let idCounter = 0
|
|
53
51
|
let persistedRemovalToken = 0
|
|
54
52
|
const persistedMixinNodes = new Set<CommittedHostNode>()
|
|
@@ -116,6 +114,20 @@ type ControlledReflectionState = {
|
|
|
116
114
|
onChange: () => void
|
|
117
115
|
}
|
|
118
116
|
|
|
117
|
+
type DirectEventBinding = {
|
|
118
|
+
type: string
|
|
119
|
+
handler: (event: Event, signal: AbortSignal) => void | Promise<void>
|
|
120
|
+
capture: boolean
|
|
121
|
+
stableHandler: ((event: Event) => void) | null
|
|
122
|
+
reentry: AbortController | null
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
type DirectEventState = {
|
|
126
|
+
bindings: DirectEventBinding[]
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const EMPTY_DIRECT_EVENT_DESCRIPTORS: OnMixinDescriptor[] = []
|
|
130
|
+
|
|
119
131
|
function shouldRestoreControlledReflectionOnInput(
|
|
120
132
|
node: CommittedHostNode,
|
|
121
133
|
state: ControlledReflectionState,
|
|
@@ -258,6 +270,18 @@ function resolveNodeMixProps(
|
|
|
258
270
|
state?: MixinRuntimeState,
|
|
259
271
|
): ElementProps {
|
|
260
272
|
let mix = node.props.mix
|
|
273
|
+
let directEventDescriptors = resolveDirectEventDescriptors(mix)
|
|
274
|
+
if (directEventDescriptors) {
|
|
275
|
+
if (state) {
|
|
276
|
+
teardownMixins(state)
|
|
277
|
+
}
|
|
278
|
+
node._mixState = undefined
|
|
279
|
+
node._mixedProps = node.props
|
|
280
|
+
node._directEventDescriptors = directEventDescriptors
|
|
281
|
+
return node.props
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
node._directEventDescriptors = undefined
|
|
261
285
|
if (state == null && (mix == null || (Array.isArray(mix) && mix.length === 0))) {
|
|
262
286
|
node._mixState = undefined
|
|
263
287
|
node._mixedProps = node.props
|
|
@@ -283,6 +307,22 @@ function resolveNodeMixProps(
|
|
|
283
307
|
return resolved.props
|
|
284
308
|
}
|
|
285
309
|
|
|
310
|
+
function resolveDirectEventDescriptors(mix: ElementProps['mix']): OnMixinDescriptor[] | null {
|
|
311
|
+
if (!mix) return EMPTY_DIRECT_EVENT_DESCRIPTORS
|
|
312
|
+
if (!Array.isArray(mix)) {
|
|
313
|
+
return isOnMixinDescriptor(mix) ? [mix] : null
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return areOnMixinDescriptors(mix) ? mix : null
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function areOnMixinDescriptors(descriptors: unknown[]): descriptors is OnMixinDescriptor[] {
|
|
320
|
+
for (let item of descriptors) {
|
|
321
|
+
if (!isOnMixinDescriptor(item)) return false
|
|
322
|
+
}
|
|
323
|
+
return true
|
|
324
|
+
}
|
|
325
|
+
|
|
286
326
|
function enqueueMixinBindingUpdate(
|
|
287
327
|
this: MixinRuntimeBinding,
|
|
288
328
|
done: (signal: AbortSignal) => void,
|
|
@@ -299,7 +339,7 @@ function enqueueMixinBindingUpdate(
|
|
|
299
339
|
dispatchMixinBeforeUpdate(state)
|
|
300
340
|
let prevProps = getHostProps(node)
|
|
301
341
|
let nextProps = resolveNodeMixProps(node, this.frame, this.scheduler, state)
|
|
302
|
-
|
|
342
|
+
patchHostProps(prevProps, nextProps, this.node)
|
|
303
343
|
|
|
304
344
|
dispatchMixinCommit(state)
|
|
305
345
|
done(state ? getMixinRuntimeSignal(state) : AbortSignal.abort())
|
|
@@ -382,6 +422,10 @@ export function diffVNodes(
|
|
|
382
422
|
return rootCursor
|
|
383
423
|
}
|
|
384
424
|
|
|
425
|
+
if (isNonRenderNode(curr) && isNonRenderNode(next)) {
|
|
426
|
+
return rootCursor
|
|
427
|
+
}
|
|
428
|
+
|
|
385
429
|
if (isCommittedTextNode(curr) && isTextNode(next)) {
|
|
386
430
|
diffText(curr, next, vParent)
|
|
387
431
|
return rootCursor
|
|
@@ -405,7 +449,7 @@ export function diffVNodes(
|
|
|
405
449
|
frame,
|
|
406
450
|
scheduler,
|
|
407
451
|
styles,
|
|
408
|
-
|
|
452
|
+
next,
|
|
409
453
|
rootTarget,
|
|
410
454
|
undefined,
|
|
411
455
|
anchor,
|
|
@@ -445,7 +489,7 @@ function replace(
|
|
|
445
489
|
return
|
|
446
490
|
}
|
|
447
491
|
|
|
448
|
-
let replacementAnchor = findNextSiblingDomAnchor(curr
|
|
492
|
+
let replacementAnchor = findNextSiblingDomAnchor(curr) ?? anchor
|
|
449
493
|
remove(curr, domParent, scheduler, styles)
|
|
450
494
|
insert(next, domParent, frame, scheduler, styles, vParent, rootTarget, replacementAnchor)
|
|
451
495
|
}
|
|
@@ -490,19 +534,23 @@ function diffHost(
|
|
|
490
534
|
next,
|
|
491
535
|
rootTarget,
|
|
492
536
|
)
|
|
493
|
-
|
|
537
|
+
patchHostProps(currProps, nextProps, curr._dom)
|
|
494
538
|
|
|
495
539
|
next._dom = curr._dom
|
|
496
540
|
next._parent = vParent
|
|
497
541
|
next._controller = curr._controller
|
|
542
|
+
next._directEventState = curr._directEventState
|
|
498
543
|
next._controlledState = curr._controlledState
|
|
544
|
+
syncDirectEventListeners(next as CommittedHostNode)
|
|
499
545
|
|
|
500
546
|
if (next._controlledState || shouldTrackControlledReflection(nextProps)) {
|
|
501
547
|
ensureControlledReflection(next as CommittedHostNode, scheduler)
|
|
502
548
|
syncControlledReflection(next as CommittedHostNode, nextProps)
|
|
503
549
|
}
|
|
504
550
|
|
|
505
|
-
|
|
551
|
+
if (next._mixState) {
|
|
552
|
+
bindNodeMixRuntime(next as CommittedHostNode, frame, scheduler, styles)
|
|
553
|
+
}
|
|
506
554
|
if (shouldDispatchMixinLifecycle) {
|
|
507
555
|
scheduler.enqueueCommitPhase([() => dispatchMixinCommit(nextMixState)])
|
|
508
556
|
}
|
|
@@ -515,12 +563,115 @@ function setupHostNode(node: HostNode, dom: Element, scheduler: Scheduler): void
|
|
|
515
563
|
let props = getHostProps(node)
|
|
516
564
|
let committedNode = node as CommittedHostNode
|
|
517
565
|
|
|
566
|
+
syncDirectEventListeners(committedNode)
|
|
567
|
+
|
|
518
568
|
if (shouldTrackControlledReflection(props)) {
|
|
519
569
|
ensureControlledReflection(committedNode, scheduler)
|
|
520
570
|
syncControlledReflection(committedNode, props)
|
|
521
571
|
}
|
|
522
572
|
}
|
|
523
573
|
|
|
574
|
+
function syncDirectEventListeners(node: CommittedHostNode): void {
|
|
575
|
+
let descriptors = node._directEventDescriptors as OnMixinDescriptor[] | undefined
|
|
576
|
+
if (!descriptors) {
|
|
577
|
+
teardownDirectEventListeners(node)
|
|
578
|
+
return
|
|
579
|
+
}
|
|
580
|
+
if (descriptors.length === 0) {
|
|
581
|
+
teardownDirectEventListeners(node)
|
|
582
|
+
return
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
let state = node._directEventState as DirectEventState | undefined
|
|
586
|
+
if (!state) {
|
|
587
|
+
state = { bindings: [] }
|
|
588
|
+
node._directEventState = state
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
let bindings = state.bindings
|
|
592
|
+
for (let index = 0; index < descriptors.length; index++) {
|
|
593
|
+
let descriptor = descriptors[index]
|
|
594
|
+
let [type, handler, captureBoolean = false] = descriptor.args
|
|
595
|
+
let binding = bindings[index]
|
|
596
|
+
|
|
597
|
+
if (!binding) {
|
|
598
|
+
binding = createDirectEventBinding(type, handler, captureBoolean)
|
|
599
|
+
bindings[index] = binding
|
|
600
|
+
attachDirectEventBinding(node._dom, binding)
|
|
601
|
+
continue
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (binding.type !== type || binding.capture !== captureBoolean) {
|
|
605
|
+
removeDirectEventBinding(node._dom, binding)
|
|
606
|
+
binding.type = type
|
|
607
|
+
binding.capture = captureBoolean
|
|
608
|
+
attachDirectEventBinding(node._dom, binding)
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
binding.handler = handler
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
for (let index = descriptors.length; index < bindings.length; index++) {
|
|
615
|
+
removeDirectEventBinding(node._dom, bindings[index])
|
|
616
|
+
}
|
|
617
|
+
bindings.length = descriptors.length
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
function createDirectEventBinding(
|
|
621
|
+
type: string,
|
|
622
|
+
handler: DirectEventBinding['handler'],
|
|
623
|
+
capture: boolean,
|
|
624
|
+
): DirectEventBinding {
|
|
625
|
+
let binding: DirectEventBinding = {
|
|
626
|
+
type,
|
|
627
|
+
handler,
|
|
628
|
+
capture,
|
|
629
|
+
reentry: null,
|
|
630
|
+
stableHandler: null,
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
return binding
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
function getStableDirectEventHandler(binding: DirectEventBinding): (event: Event) => void {
|
|
637
|
+
if (binding.stableHandler) return binding.stableHandler
|
|
638
|
+
|
|
639
|
+
binding.stableHandler = (event) => {
|
|
640
|
+
invokeDirectEventBinding(binding, event)
|
|
641
|
+
}
|
|
642
|
+
return binding.stableHandler
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
function attachDirectEventBinding(dom: Element, binding: DirectEventBinding): void {
|
|
646
|
+
dom.addEventListener(binding.type, getStableDirectEventHandler(binding), binding.capture)
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function removeDirectEventBinding(dom: Element, binding: DirectEventBinding): void {
|
|
650
|
+
if (binding.stableHandler) {
|
|
651
|
+
dom.removeEventListener(binding.type, binding.stableHandler, binding.capture)
|
|
652
|
+
}
|
|
653
|
+
binding.reentry?.abort(new DOMException('', 'AbortError'))
|
|
654
|
+
binding.reentry = null
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function teardownDirectEventListeners(node: CommittedHostNode): void {
|
|
658
|
+
let state = node._directEventState as DirectEventState | undefined
|
|
659
|
+
if (!state) return
|
|
660
|
+
|
|
661
|
+
for (let binding of state.bindings) {
|
|
662
|
+
removeDirectEventBinding(node._dom, binding)
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
state.bindings.length = 0
|
|
666
|
+
node._directEventState = undefined
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
function invokeDirectEventBinding(binding: DirectEventBinding, event: Event): void {
|
|
670
|
+
binding.reentry?.abort(new DOMException('', 'EventReentry'))
|
|
671
|
+
binding.reentry = new AbortController()
|
|
672
|
+
void binding.handler(event, binding.reentry.signal)
|
|
673
|
+
}
|
|
674
|
+
|
|
524
675
|
function diffText(curr: CommittedTextNode, next: TextNode, vParent: VNode) {
|
|
525
676
|
if (curr._text !== next._text) {
|
|
526
677
|
curr._dom.textContent = next._text
|
|
@@ -563,6 +714,10 @@ function insert(
|
|
|
563
714
|
? (dom: Node) => domParent.insertBefore(dom, anchor)
|
|
564
715
|
: (dom: Node) => domParent.appendChild(dom)
|
|
565
716
|
|
|
717
|
+
if (isNonRenderNode(node)) {
|
|
718
|
+
return cursor
|
|
719
|
+
}
|
|
720
|
+
|
|
566
721
|
if (isTextNode(node)) {
|
|
567
722
|
if (cursor instanceof Text) {
|
|
568
723
|
node._parent = vParent
|
|
@@ -621,9 +776,11 @@ function insert(
|
|
|
621
776
|
rootTarget,
|
|
622
777
|
childCursor,
|
|
623
778
|
)
|
|
624
|
-
|
|
779
|
+
patchHostProps({}, hostProps, targetHead)
|
|
625
780
|
setupHostNode(node, targetHead, scheduler)
|
|
626
|
-
|
|
781
|
+
if (node._mixState) {
|
|
782
|
+
bindNodeMixRuntime(node as CommittedHostNode, frame, scheduler, styles)
|
|
783
|
+
}
|
|
627
784
|
return cursor
|
|
628
785
|
}
|
|
629
786
|
}
|
|
@@ -641,7 +798,7 @@ function insert(
|
|
|
641
798
|
let cursorTag = node._svg ? cursor.tagName : cursor.tagName.toLowerCase()
|
|
642
799
|
if (cursorTag === node.type) {
|
|
643
800
|
let nextCursor = cursor.nextSibling
|
|
644
|
-
|
|
801
|
+
patchHostProps({}, hostProps, cursor)
|
|
645
802
|
|
|
646
803
|
// Handle innerHTML prop
|
|
647
804
|
if (hostProps.innerHTML != null) {
|
|
@@ -663,7 +820,9 @@ function insert(
|
|
|
663
820
|
}
|
|
664
821
|
|
|
665
822
|
setupHostNode(node, cursor, scheduler)
|
|
666
|
-
|
|
823
|
+
if (node._mixState) {
|
|
824
|
+
bindNodeMixRuntime(node as CommittedHostNode, frame, scheduler, styles)
|
|
825
|
+
}
|
|
667
826
|
return nextCursor
|
|
668
827
|
} else {
|
|
669
828
|
// Type mismatch - try single-advance retry to handle browser extension injections
|
|
@@ -674,7 +833,7 @@ function insert(
|
|
|
674
833
|
if (nextTag === node.type) {
|
|
675
834
|
let nextCursor = nextSibling.nextSibling
|
|
676
835
|
// Found a match after skipping - adopt it and leave skipped node in place
|
|
677
|
-
|
|
836
|
+
patchHostProps({}, hostProps, nextSibling)
|
|
678
837
|
|
|
679
838
|
if (hostProps.innerHTML != null) {
|
|
680
839
|
nextSibling.innerHTML = hostProps.innerHTML as string
|
|
@@ -694,7 +853,9 @@ function insert(
|
|
|
694
853
|
}
|
|
695
854
|
|
|
696
855
|
setupHostNode(node, nextSibling, scheduler)
|
|
697
|
-
|
|
856
|
+
if (node._mixState) {
|
|
857
|
+
bindNodeMixRuntime(node as CommittedHostNode, frame, scheduler, styles)
|
|
858
|
+
}
|
|
698
859
|
return nextCursor
|
|
699
860
|
}
|
|
700
861
|
}
|
|
@@ -706,7 +867,7 @@ function insert(
|
|
|
706
867
|
let dom = node._svg
|
|
707
868
|
? document.createElementNS(SVG_NS, node.type)
|
|
708
869
|
: document.createElement(node.type)
|
|
709
|
-
|
|
870
|
+
patchHostProps({}, hostProps, dom)
|
|
710
871
|
|
|
711
872
|
// Handle innerHTML prop
|
|
712
873
|
if (hostProps.innerHTML != null) {
|
|
@@ -716,7 +877,9 @@ function insert(
|
|
|
716
877
|
}
|
|
717
878
|
|
|
718
879
|
setupHostNode(node, dom, scheduler)
|
|
719
|
-
|
|
880
|
+
if (node._mixState) {
|
|
881
|
+
bindNodeMixRuntime(node as CommittedHostNode, frame, scheduler, styles, false, domParent)
|
|
882
|
+
}
|
|
720
883
|
doInsert(dom)
|
|
721
884
|
return cursor
|
|
722
885
|
}
|
|
@@ -724,17 +887,7 @@ function insert(
|
|
|
724
887
|
if (isFragmentNode(node)) {
|
|
725
888
|
// Insert fragment children in order before the same anchor
|
|
726
889
|
for (let child of node._children) {
|
|
727
|
-
cursor = insert(
|
|
728
|
-
child,
|
|
729
|
-
domParent,
|
|
730
|
-
frame,
|
|
731
|
-
scheduler,
|
|
732
|
-
styles,
|
|
733
|
-
vParent,
|
|
734
|
-
rootTarget,
|
|
735
|
-
anchor,
|
|
736
|
-
cursor,
|
|
737
|
-
)
|
|
890
|
+
cursor = insert(child, domParent, frame, scheduler, styles, node, rootTarget, anchor, cursor)
|
|
738
891
|
}
|
|
739
892
|
return cursor
|
|
740
893
|
}
|
|
@@ -1121,6 +1274,8 @@ export function renderComponent(
|
|
|
1121
1274
|
anchor?: Node,
|
|
1122
1275
|
cursor?: Node | null,
|
|
1123
1276
|
): Node | null | undefined {
|
|
1277
|
+
if (handle.isRemoved()) return cursor
|
|
1278
|
+
|
|
1124
1279
|
let [element, tasks] = handle.render(next.props)
|
|
1125
1280
|
let content = toVNode(element)
|
|
1126
1281
|
let newCursor = diffVNodes(
|
|
@@ -1226,6 +1381,7 @@ function cleanupDescendants(node: VNode, scheduler: Scheduler, styles: StyleMana
|
|
|
1226
1381
|
}
|
|
1227
1382
|
|
|
1228
1383
|
teardownMixins(node._mixState as MixinRuntimeState | undefined)
|
|
1384
|
+
teardownDirectEventListeners(node)
|
|
1229
1385
|
teardownControlledReflection(node)
|
|
1230
1386
|
if (node._controller) node._controller.abort()
|
|
1231
1387
|
return
|
|
@@ -1323,6 +1479,7 @@ function performHostNodeRemoval(
|
|
|
1323
1479
|
}
|
|
1324
1480
|
|
|
1325
1481
|
teardownMixins(node._mixState as MixinRuntimeState | undefined)
|
|
1482
|
+
teardownDirectEventListeners(node)
|
|
1326
1483
|
teardownControlledReflection(node)
|
|
1327
1484
|
// Never remove the real document.head node when reconciling a <head> vnode.
|
|
1328
1485
|
if (!isHeadHostNode(node)) {
|
|
@@ -1343,42 +1500,12 @@ function diffChildren(
|
|
|
1343
1500
|
cursor?: Node | null,
|
|
1344
1501
|
anchor?: Node,
|
|
1345
1502
|
) {
|
|
1346
|
-
let
|
|
1503
|
+
let hasKeys = hasKeyedChildren(next)
|
|
1347
1504
|
|
|
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
1505
|
if (curr === null) {
|
|
1506
|
+
if (hasKeys) {
|
|
1507
|
+
warnDuplicateKeys(next)
|
|
1508
|
+
}
|
|
1382
1509
|
for (let node of next) {
|
|
1383
1510
|
cursor = insert(
|
|
1384
1511
|
node,
|
|
@@ -1396,14 +1523,23 @@ function diffChildren(
|
|
|
1396
1523
|
return cursor
|
|
1397
1524
|
}
|
|
1398
1525
|
|
|
1399
|
-
|
|
1526
|
+
if (
|
|
1527
|
+
next.length === 0 &&
|
|
1528
|
+
anchor === undefined &&
|
|
1529
|
+
!parentUsesInnerHTML(vParent) &&
|
|
1530
|
+
canBulkClearChildren(curr)
|
|
1531
|
+
) {
|
|
1532
|
+
for (let node of curr) {
|
|
1533
|
+
cleanupDescendants(node, scheduler, styles)
|
|
1534
|
+
}
|
|
1535
|
+
domParent.textContent = ''
|
|
1536
|
+
vParent._children = next
|
|
1537
|
+
return
|
|
1538
|
+
}
|
|
1400
1539
|
|
|
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
1540
|
if (!hasKeys) {
|
|
1405
|
-
for (let i = 0; i <
|
|
1406
|
-
let currentNode = i <
|
|
1541
|
+
for (let i = 0; i < next.length; i++) {
|
|
1542
|
+
let currentNode = i < curr.length ? curr[i] : null
|
|
1407
1543
|
diffVNodes(
|
|
1408
1544
|
currentNode,
|
|
1409
1545
|
next[i],
|
|
@@ -1418,8 +1554,8 @@ function diffChildren(
|
|
|
1418
1554
|
)
|
|
1419
1555
|
}
|
|
1420
1556
|
|
|
1421
|
-
if (
|
|
1422
|
-
for (let i =
|
|
1557
|
+
if (curr.length > next.length) {
|
|
1558
|
+
for (let i = next.length; i < curr.length; i++) {
|
|
1423
1559
|
let node = curr[i]
|
|
1424
1560
|
if (node) remove(node, domParent, scheduler, styles)
|
|
1425
1561
|
}
|
|
@@ -1429,160 +1565,137 @@ function diffChildren(
|
|
|
1429
1565
|
return
|
|
1430
1566
|
}
|
|
1431
1567
|
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1568
|
+
patchKeyedChildren(
|
|
1569
|
+
curr,
|
|
1570
|
+
next,
|
|
1571
|
+
domParent,
|
|
1572
|
+
frame,
|
|
1573
|
+
scheduler,
|
|
1574
|
+
styles,
|
|
1575
|
+
vParent,
|
|
1576
|
+
rootTarget,
|
|
1577
|
+
cursor,
|
|
1578
|
+
anchor,
|
|
1579
|
+
)
|
|
1442
1580
|
|
|
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
|
-
}
|
|
1581
|
+
return
|
|
1582
|
+
}
|
|
1458
1583
|
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
}
|
|
1584
|
+
function parentUsesInnerHTML(parent: VNode): boolean {
|
|
1585
|
+
return isHostNode(parent) && getHostProps(parent).innerHTML != null
|
|
1586
|
+
}
|
|
1463
1587
|
|
|
1464
|
-
|
|
1588
|
+
function canBulkClearChildren(children: VNode[]): boolean {
|
|
1589
|
+
for (let child of children) {
|
|
1590
|
+
if (!canBulkClearNode(child)) return false
|
|
1591
|
+
}
|
|
1592
|
+
return true
|
|
1593
|
+
}
|
|
1465
1594
|
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
let remainingOldChildren = oldChildrenLength
|
|
1595
|
+
function canBulkClearNode(node: VNode): boolean {
|
|
1596
|
+
if (isCommittedTextNode(node)) return true
|
|
1469
1597
|
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
if (c) {
|
|
1475
|
-
c._flags = 0
|
|
1476
|
-
if (c.key != null) {
|
|
1477
|
-
oldKeyMap.set(c.key, i)
|
|
1478
|
-
}
|
|
1598
|
+
if (isCommittedHostNode(node)) {
|
|
1599
|
+
if (node._mixState) return false
|
|
1600
|
+
for (let child of node._children) {
|
|
1601
|
+
if (!canBulkClearNode(child)) return false
|
|
1479
1602
|
}
|
|
1603
|
+
return true
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
if (isFragmentNode(node)) {
|
|
1607
|
+
return canBulkClearChildren(node._children)
|
|
1480
1608
|
}
|
|
1481
1609
|
|
|
1482
|
-
|
|
1483
|
-
|
|
1610
|
+
if (isCommittedComponentNode(node)) {
|
|
1611
|
+
return canBulkClearNode(node._content)
|
|
1612
|
+
}
|
|
1484
1613
|
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
let childVNode = next[i]
|
|
1488
|
-
if (!childVNode) {
|
|
1489
|
-
newChildren[i] = childVNode
|
|
1490
|
-
continue
|
|
1491
|
-
}
|
|
1614
|
+
return false
|
|
1615
|
+
}
|
|
1492
1616
|
|
|
1493
|
-
|
|
1494
|
-
|
|
1617
|
+
function hasKeyedChildren(children: VNode[]): boolean {
|
|
1618
|
+
for (let node of children) {
|
|
1619
|
+
if (node.key != null) return true
|
|
1620
|
+
}
|
|
1621
|
+
return false
|
|
1622
|
+
}
|
|
1495
1623
|
|
|
1496
|
-
|
|
1497
|
-
|
|
1624
|
+
function warnDuplicateKeys(children: VNode[]): void {
|
|
1625
|
+
let seenKeys: Set<string> | undefined
|
|
1626
|
+
let duplicateKeys: Set<string> | undefined
|
|
1498
1627
|
|
|
1499
|
-
|
|
1500
|
-
|
|
1628
|
+
for (let node of children) {
|
|
1629
|
+
if (node.key == null) continue
|
|
1501
1630
|
|
|
1502
|
-
if (
|
|
1503
|
-
|
|
1504
|
-
|
|
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
|
-
}
|
|
1631
|
+
if (!seenKeys) {
|
|
1632
|
+
seenKeys = new Set([node.key])
|
|
1633
|
+
continue
|
|
1520
1634
|
}
|
|
1521
1635
|
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
remainingOldChildren--
|
|
1528
|
-
if (matchedOldVNode) {
|
|
1529
|
-
matchedOldVNode._flags = (matchedOldVNode._flags ?? 0) | MATCHED
|
|
1530
|
-
}
|
|
1636
|
+
if (seenKeys.has(node.key)) {
|
|
1637
|
+
duplicateKeys ??= new Set()
|
|
1638
|
+
duplicateKeys.add(node.key)
|
|
1639
|
+
} else {
|
|
1640
|
+
seenKeys.add(node.key)
|
|
1531
1641
|
}
|
|
1642
|
+
}
|
|
1532
1643
|
|
|
1533
|
-
|
|
1534
|
-
let
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
skew--
|
|
1541
|
-
} else if (nextLength < oldChildrenLength) {
|
|
1542
|
-
skew++
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1644
|
+
if (duplicateKeys?.size) {
|
|
1645
|
+
let quotedKeys = Array.from(duplicateKeys, (key) => `"${key}"`)
|
|
1646
|
+
console.warn(
|
|
1647
|
+
`Duplicate keys detected in siblings: ${quotedKeys.join(', ')}. Keys should be unique.`,
|
|
1648
|
+
)
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1545
1651
|
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1652
|
+
function patchKeyedChildren(
|
|
1653
|
+
curr: VNode[],
|
|
1654
|
+
next: VNode[],
|
|
1655
|
+
domParent: ParentNode,
|
|
1656
|
+
frame: FrameHandle,
|
|
1657
|
+
scheduler: Scheduler,
|
|
1658
|
+
styles: StyleManager,
|
|
1659
|
+
vParent: VNode,
|
|
1660
|
+
rootTarget: EventTarget,
|
|
1661
|
+
cursor?: Node | null,
|
|
1662
|
+
anchor?: Node,
|
|
1663
|
+
): void {
|
|
1664
|
+
let matches =
|
|
1665
|
+
matchKeyedChildrenInOrder(curr, next) ??
|
|
1666
|
+
matchKeyedChildrenAfterSingleRemoval(curr, next) ??
|
|
1667
|
+
matchKeyedChildrenAfterPairSwap(curr, next)
|
|
1668
|
+
if (!matches) {
|
|
1669
|
+
warnDuplicateKeys(next)
|
|
1670
|
+
matches = matchKeyedChildren(curr, next)
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
let matchAnalysis = analyzeKeyedChildMatches(curr.length, matches)
|
|
1674
|
+
if (matchAnalysis.hasRemovals) {
|
|
1675
|
+
let usedOldIndexes = new Uint8Array(curr.length)
|
|
1676
|
+
|
|
1677
|
+
for (let match of matches) {
|
|
1678
|
+
if (match.oldIndex >= 0) {
|
|
1679
|
+
usedOldIndexes[match.oldIndex] = 1
|
|
1556
1680
|
}
|
|
1557
1681
|
}
|
|
1558
|
-
}
|
|
1559
1682
|
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
let oldVNode = oldChildren[i]
|
|
1564
|
-
if (oldVNode && ((oldVNode._flags ?? 0) & MATCHED) === 0) {
|
|
1565
|
-
remove(oldVNode, domParent, scheduler, styles)
|
|
1683
|
+
for (let oldIndex = 0; oldIndex < curr.length; oldIndex++) {
|
|
1684
|
+
if (usedOldIndexes[oldIndex] === 0) {
|
|
1685
|
+
remove(curr[oldIndex], domParent, scheduler, styles)
|
|
1566
1686
|
}
|
|
1567
1687
|
}
|
|
1568
1688
|
}
|
|
1569
1689
|
|
|
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
|
|
1690
|
+
vParent._children = next
|
|
1579
1691
|
|
|
1580
|
-
|
|
1581
|
-
let
|
|
1692
|
+
for (let index = 0; index < next.length; index++) {
|
|
1693
|
+
let match = matches[index]
|
|
1694
|
+
let oldNode = match.oldIndex >= 0 ? curr[match.oldIndex] : null
|
|
1582
1695
|
|
|
1583
1696
|
diffVNodes(
|
|
1584
|
-
|
|
1585
|
-
|
|
1697
|
+
oldNode,
|
|
1698
|
+
next[index],
|
|
1586
1699
|
domParent,
|
|
1587
1700
|
frame,
|
|
1588
1701
|
scheduler,
|
|
@@ -1592,40 +1705,255 @@ function diffChildren(
|
|
|
1592
1705
|
anchor,
|
|
1593
1706
|
cursor,
|
|
1594
1707
|
)
|
|
1708
|
+
}
|
|
1595
1709
|
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1710
|
+
if (matchAnalysis.canSkipPlacement) {
|
|
1711
|
+
return
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
let stableIndexes = lisMatches(matches)
|
|
1715
|
+
let stableCursor = stableIndexes.length - 1
|
|
1716
|
+
let placementAnchor: Node | null = anchor ?? null
|
|
1717
|
+
|
|
1718
|
+
for (let index = next.length - 1; index >= 0; index--) {
|
|
1719
|
+
let nextNode = next[index]
|
|
1720
|
+
let isStable = stableIndexes[stableCursor] === index
|
|
1721
|
+
|
|
1722
|
+
if (isStable) {
|
|
1723
|
+
stableCursor--
|
|
1724
|
+
} else {
|
|
1725
|
+
placeVNode(nextNode, domParent, placementAnchor)
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
placementAnchor = findFirstDomAnchor(nextNode) ?? placementAnchor
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
function matchKeyedChildren(curr: VNode[], next: VNode[]): Array<{ oldIndex: number }> {
|
|
1733
|
+
let oldKeyMap = new Map<string, number>()
|
|
1734
|
+
let usedOldIndexes = new Set<number>()
|
|
1735
|
+
let unkeyedSearchStart = 0
|
|
1736
|
+
|
|
1737
|
+
for (let index = 0; index < curr.length; index++) {
|
|
1738
|
+
let key = curr[index].key
|
|
1739
|
+
if (key != null) oldKeyMap.set(key, index)
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
return next.map((nextNode) => {
|
|
1743
|
+
let oldIndex = -1
|
|
1744
|
+
|
|
1745
|
+
if (nextNode.key != null) {
|
|
1746
|
+
let keyedOldIndex = oldKeyMap.get(nextNode.key)
|
|
1747
|
+
if (keyedOldIndex !== undefined) {
|
|
1748
|
+
let oldNode = curr[keyedOldIndex]
|
|
1749
|
+
if (!usedOldIndexes.has(keyedOldIndex) && oldNode.type === nextNode.type) {
|
|
1750
|
+
oldIndex = keyedOldIndex
|
|
1606
1751
|
}
|
|
1607
|
-
} else {
|
|
1608
|
-
target = lastPlaced.nextSibling
|
|
1609
1752
|
}
|
|
1753
|
+
} else {
|
|
1754
|
+
for (let index = unkeyedSearchStart; index < curr.length; index++) {
|
|
1755
|
+
let oldNode = curr[index]
|
|
1756
|
+
if (usedOldIndexes.has(index) || oldNode.key != null || oldNode.type !== nextNode.type) {
|
|
1757
|
+
continue
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
oldIndex = index
|
|
1761
|
+
unkeyedSearchStart = index + 1
|
|
1762
|
+
break
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1610
1765
|
|
|
1611
|
-
|
|
1766
|
+
if (oldIndex >= 0) usedOldIndexes.add(oldIndex)
|
|
1767
|
+
return { oldIndex }
|
|
1768
|
+
})
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
function matchKeyedChildrenInOrder(
|
|
1772
|
+
curr: VNode[],
|
|
1773
|
+
next: VNode[],
|
|
1774
|
+
): Array<{ oldIndex: number }> | null {
|
|
1775
|
+
let length = Math.min(curr.length, next.length)
|
|
1776
|
+
let matches: Array<{ oldIndex: number }> = []
|
|
1777
|
+
|
|
1778
|
+
for (let index = 0; index < length; index++) {
|
|
1779
|
+
let nextNode = next[index]
|
|
1780
|
+
if (nextNode.key == null) return null
|
|
1781
|
+
|
|
1782
|
+
let oldNode = curr[index]
|
|
1783
|
+
if (oldNode.key !== nextNode.key || oldNode.type !== nextNode.type) {
|
|
1784
|
+
return null
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
matches.push({ oldIndex: index })
|
|
1788
|
+
}
|
|
1612
1789
|
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1790
|
+
for (let index = length; index < next.length; index++) {
|
|
1791
|
+
if (next[index].key == null) return null
|
|
1792
|
+
matches.push({ oldIndex: -1 })
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
return matches
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
function matchKeyedChildrenAfterSingleRemoval(
|
|
1799
|
+
curr: VNode[],
|
|
1800
|
+
next: VNode[],
|
|
1801
|
+
): Array<{ oldIndex: number }> | null {
|
|
1802
|
+
if (curr.length !== next.length + 1) return null
|
|
1803
|
+
|
|
1804
|
+
let matches: Array<{ oldIndex: number }> = []
|
|
1805
|
+
let oldIndex = 0
|
|
1806
|
+
let skippedOldNode = false
|
|
1807
|
+
|
|
1808
|
+
for (let nextIndex = 0; nextIndex < next.length; nextIndex++) {
|
|
1809
|
+
let nextNode = next[nextIndex]
|
|
1810
|
+
if (nextNode.key == null) return null
|
|
1811
|
+
|
|
1812
|
+
let oldNode = curr[oldIndex]
|
|
1813
|
+
if (oldNode.key === nextNode.key && oldNode.type === nextNode.type) {
|
|
1814
|
+
matches.push({ oldIndex })
|
|
1815
|
+
oldIndex++
|
|
1816
|
+
continue
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
if (skippedOldNode) return null
|
|
1820
|
+
skippedOldNode = true
|
|
1821
|
+
oldIndex++
|
|
1822
|
+
|
|
1823
|
+
oldNode = curr[oldIndex]
|
|
1824
|
+
if (oldNode.key !== nextNode.key || oldNode.type !== nextNode.type) {
|
|
1825
|
+
return null
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
matches.push({ oldIndex })
|
|
1829
|
+
oldIndex++
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
return matches
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
function matchKeyedChildrenAfterPairSwap(
|
|
1836
|
+
curr: VNode[],
|
|
1837
|
+
next: VNode[],
|
|
1838
|
+
): Array<{ oldIndex: number }> | null {
|
|
1839
|
+
if (curr.length !== next.length) return null
|
|
1840
|
+
|
|
1841
|
+
let matches: Array<{ oldIndex: number }> = []
|
|
1842
|
+
let firstMismatch = -1
|
|
1843
|
+
let secondMismatch = -1
|
|
1844
|
+
|
|
1845
|
+
for (let index = 0; index < next.length; index++) {
|
|
1846
|
+
let nextNode = next[index]
|
|
1847
|
+
if (nextNode.key == null) return null
|
|
1848
|
+
|
|
1849
|
+
let oldNode = curr[index]
|
|
1850
|
+
if (oldNode.key === nextNode.key && oldNode.type === nextNode.type) {
|
|
1851
|
+
matches.push({ oldIndex: index })
|
|
1852
|
+
continue
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
if (firstMismatch === -1) {
|
|
1856
|
+
firstMismatch = index
|
|
1857
|
+
} else if (secondMismatch === -1) {
|
|
1858
|
+
secondMismatch = index
|
|
1859
|
+
} else {
|
|
1860
|
+
return null
|
|
1861
|
+
}
|
|
1862
|
+
matches.push({ oldIndex: -1 })
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
if (firstMismatch === -1) return matches
|
|
1866
|
+
if (secondMismatch === -1) return null
|
|
1867
|
+
|
|
1868
|
+
let firstOldNode = curr[firstMismatch]
|
|
1869
|
+
let secondOldNode = curr[secondMismatch]
|
|
1870
|
+
let firstNextNode = next[firstMismatch]
|
|
1871
|
+
let secondNextNode = next[secondMismatch]
|
|
1872
|
+
|
|
1873
|
+
if (
|
|
1874
|
+
firstOldNode.key !== secondNextNode.key ||
|
|
1875
|
+
firstOldNode.type !== secondNextNode.type ||
|
|
1876
|
+
secondOldNode.key !== firstNextNode.key ||
|
|
1877
|
+
secondOldNode.type !== firstNextNode.type
|
|
1878
|
+
) {
|
|
1879
|
+
return null
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
matches[firstMismatch] = { oldIndex: secondMismatch }
|
|
1883
|
+
matches[secondMismatch] = { oldIndex: firstMismatch }
|
|
1884
|
+
return matches
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
function analyzeKeyedChildMatches(
|
|
1888
|
+
currentLength: number,
|
|
1889
|
+
matches: ReadonlyArray<{ oldIndex: number }>,
|
|
1890
|
+
): { hasRemovals: boolean; canSkipPlacement: boolean } {
|
|
1891
|
+
let hasRemovals = matches.length !== currentLength
|
|
1892
|
+
let canSkipPlacement = true
|
|
1893
|
+
let lastOldIndex = -1
|
|
1894
|
+
let sawNewNode = false
|
|
1895
|
+
|
|
1896
|
+
for (let match of matches) {
|
|
1897
|
+
if (match.oldIndex < 0) {
|
|
1898
|
+
hasRemovals = true
|
|
1899
|
+
sawNewNode = true
|
|
1900
|
+
continue
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
if (sawNewNode || match.oldIndex < lastOldIndex) {
|
|
1904
|
+
canSkipPlacement = false
|
|
1905
|
+
}
|
|
1906
|
+
lastOldIndex = match.oldIndex
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
return { hasRemovals, canSkipPlacement }
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
function lisMatches(matches: ReadonlyArray<{ oldIndex: number }>): number[] {
|
|
1913
|
+
let predecessors = Array.from<number>({ length: matches.length })
|
|
1914
|
+
let tails: number[] = []
|
|
1915
|
+
|
|
1916
|
+
for (let index = 0; index < matches.length; index++) {
|
|
1917
|
+
let value = matches[index].oldIndex + 1
|
|
1918
|
+
if (value === 0) continue
|
|
1919
|
+
|
|
1920
|
+
let low = 0
|
|
1921
|
+
let high = tails.length
|
|
1922
|
+
|
|
1923
|
+
while (low < high) {
|
|
1924
|
+
let middle = (low + high) >> 1
|
|
1925
|
+
|
|
1926
|
+
if (matches[tails[middle]].oldIndex + 1 < value) {
|
|
1927
|
+
low = middle + 1
|
|
1928
|
+
} else {
|
|
1929
|
+
high = middle
|
|
1618
1930
|
}
|
|
1619
1931
|
}
|
|
1620
1932
|
|
|
1621
|
-
|
|
1933
|
+
predecessors[index] = low > 0 ? tails[low - 1] : -1
|
|
1934
|
+
tails[low] = index
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
let cursor = tails.at(-1) ?? -1
|
|
1622
1938
|
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1939
|
+
for (let index = tails.length - 1; index >= 0; index--) {
|
|
1940
|
+
tails[index] = cursor
|
|
1941
|
+
cursor = predecessors[cursor] ?? -1
|
|
1626
1942
|
}
|
|
1627
1943
|
|
|
1628
|
-
return
|
|
1944
|
+
return tails
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
function placeVNode(node: VNode, domParent: ParentNode, anchor: Node | null): void {
|
|
1948
|
+
let firstDom = findFirstDomAnchor(node)
|
|
1949
|
+
if (!firstDom || firstDom.parentNode !== domParent) return
|
|
1950
|
+
|
|
1951
|
+
let lastDom = findLastDomAnchor(node)
|
|
1952
|
+
if (!lastDom) return
|
|
1953
|
+
if (anchor && domRangeContainsNode(firstDom, lastDom, anchor)) return
|
|
1954
|
+
if (firstDom === anchor) return
|
|
1955
|
+
|
|
1956
|
+
moveDomRange(domParent, firstDom, lastDom, anchor)
|
|
1629
1957
|
}
|
|
1630
1958
|
|
|
1631
1959
|
export function findFirstDomAnchor(node: VNode | null | undefined): Node | null {
|
|
@@ -1693,15 +2021,23 @@ function shouldDispatchInlineMixinLifecycle(node: Node): boolean {
|
|
|
1693
2021
|
return true
|
|
1694
2022
|
}
|
|
1695
2023
|
|
|
1696
|
-
export function findNextSiblingDomAnchor(curr: VNode
|
|
2024
|
+
export function findNextSiblingDomAnchor(curr: VNode): Node | null {
|
|
2025
|
+
let vParent = curr._parent
|
|
1697
2026
|
if (!vParent || !Array.isArray(vParent._children)) return null
|
|
1698
2027
|
let children = vParent._children
|
|
2028
|
+
if (children.length === 0) return findNextSiblingDomAnchor(vParent)
|
|
2029
|
+
|
|
1699
2030
|
let idx = children.indexOf(curr)
|
|
1700
2031
|
if (idx === -1) return null
|
|
1701
2032
|
for (let i = idx + 1; i < children.length; i++) {
|
|
1702
2033
|
let dom = findFirstDomAnchor(children[i])
|
|
1703
2034
|
if (dom) return dom
|
|
1704
2035
|
}
|
|
2036
|
+
|
|
2037
|
+
if (isFragmentNode(vParent)) {
|
|
2038
|
+
return findNextSiblingDomAnchor(vParent)
|
|
2039
|
+
}
|
|
2040
|
+
|
|
1705
2041
|
return null
|
|
1706
2042
|
}
|
|
1707
2043
|
|
|
@@ -1721,6 +2057,7 @@ function reclaimPersistedMixinNode(
|
|
|
1721
2057
|
newNode._parent = vParent
|
|
1722
2058
|
newNode._controller = persistedNode._controller
|
|
1723
2059
|
newNode._mixState = persistedNode._mixState
|
|
2060
|
+
newNode._directEventState = persistedNode._directEventState
|
|
1724
2061
|
newNode._controlledState = persistedNode._controlledState
|
|
1725
2062
|
|
|
1726
2063
|
let prevProps = getHostProps(persistedNode)
|
|
@@ -1733,7 +2070,8 @@ function reclaimPersistedMixinNode(
|
|
|
1733
2070
|
if (shouldDispatchInlineMixinLifecycle(persistedNode._dom)) {
|
|
1734
2071
|
dispatchMixinBeforeUpdate(newNode._mixState as MixinRuntimeState | undefined)
|
|
1735
2072
|
}
|
|
1736
|
-
|
|
2073
|
+
patchHostProps(prevProps, nextProps, persistedNode._dom)
|
|
2074
|
+
syncDirectEventListeners(newNode as CommittedHostNode)
|
|
1737
2075
|
ensureControlledReflection(newNode as CommittedHostNode, scheduler)
|
|
1738
2076
|
syncControlledReflection(newNode as CommittedHostNode, nextProps)
|
|
1739
2077
|
|
|
@@ -1748,7 +2086,9 @@ function reclaimPersistedMixinNode(
|
|
|
1748
2086
|
rootTarget,
|
|
1749
2087
|
)
|
|
1750
2088
|
|
|
1751
|
-
|
|
2089
|
+
if (newNode._mixState) {
|
|
2090
|
+
bindNodeMixRuntime(newNode as CommittedHostNode, frame, scheduler, styles, true)
|
|
2091
|
+
}
|
|
1752
2092
|
if (shouldDispatchInlineMixinLifecycle(persistedNode._dom)) {
|
|
1753
2093
|
scheduler.enqueueCommitPhase([
|
|
1754
2094
|
() => dispatchMixinCommit(newNode._mixState as MixinRuntimeState | undefined),
|