@tanstack/redact 0.0.20 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/attributes.js +2 -1
- package/dist/core/attributes.js.map +2 -2
- package/dist/core/browser.d.ts +9 -0
- package/dist/core/browser.js +35 -0
- package/dist/core/browser.js.map +7 -0
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +3 -3
- package/dist/core/internal.d.ts +31 -8
- package/dist/core/internal.js +3 -1
- package/dist/core/internal.js.map +2 -2
- package/dist/core/resource-hints.d.ts +7 -0
- package/dist/core/resource-hints.js +19 -0
- package/dist/core/resource-hints.js.map +7 -0
- package/dist/core/types.js +1 -1
- package/dist/dom/_all.d.ts +11 -16
- package/dist/dom/_all.js +9 -20
- package/dist/dom/_all.js.map +2 -2
- package/dist/dom/client.d.ts +4 -4
- package/dist/dom/client.js +4 -4
- package/dist/dom/commit.d.ts +23 -0
- package/dist/dom/commit.js +191 -0
- package/dist/dom/commit.js.map +7 -0
- package/dist/dom/dispatcher.d.ts +4 -22
- package/dist/dom/dispatcher.js +184 -126
- package/dist/dom/dispatcher.js.map +3 -3
- package/dist/dom/dom.d.ts +2 -0
- package/dist/dom/dom.js +72 -52
- package/dist/dom/dom.js.map +2 -2
- package/dist/dom/error-info.d.ts +2 -0
- package/dist/dom/error-info.js +16 -0
- package/dist/dom/error-info.js.map +7 -0
- package/dist/dom/event-replay.js +1 -1
- package/dist/dom/features/activity/full.d.ts +1 -0
- package/dist/dom/features/activity/full.js +162 -0
- package/dist/dom/features/activity/full.js.map +7 -0
- package/dist/dom/features/activity/index.d.ts +1 -0
- package/dist/dom/features/activity/index.js +3 -0
- package/dist/dom/features/activity/index.js.map +7 -0
- package/dist/dom/features/activity/stub.d.ts +1 -0
- package/dist/dom/features/activity/stub.js +12 -0
- package/dist/dom/features/activity/stub.js.map +7 -0
- package/dist/dom/features/class/full.js +87 -21
- package/dist/dom/features/class/full.js.map +2 -2
- package/dist/dom/features/class/index.d.ts +1 -1
- package/dist/dom/features/class/index.js +2 -2
- package/dist/dom/features/class/stub.js +3 -3
- package/dist/dom/features/context/full.d.ts +4 -1
- package/dist/dom/features/context/full.js +69 -9
- package/dist/dom/features/context/full.js.map +2 -2
- package/dist/dom/features/context/index.d.ts +1 -1
- package/dist/dom/features/context/index.js +2 -2
- package/dist/dom/features/context/stub.d.ts +4 -1
- package/dist/dom/features/context/stub.js +15 -4
- package/dist/dom/features/context/stub.js.map +2 -2
- package/dist/dom/features/forward-ref/full.js +12 -21
- package/dist/dom/features/forward-ref/full.js.map +2 -2
- package/dist/dom/features/forward-ref/index.d.ts +1 -1
- package/dist/dom/features/forward-ref/index.js +2 -2
- package/dist/dom/features/forward-ref/stub.js +4 -4
- package/dist/dom/features/fragment-refs/index.d.ts +1 -0
- package/dist/dom/features/fragment-refs/index.js +8 -0
- package/dist/dom/features/fragment-refs/index.js.map +7 -0
- package/dist/dom/features/fragment-refs/stub.d.ts +3 -0
- package/dist/dom/features/fragment-refs/stub.js +14 -0
- package/dist/dom/features/fragment-refs/stub.js.map +7 -0
- package/dist/dom/features/hydration/full.d.ts +11 -6
- package/dist/dom/features/hydration/full.js +76 -76
- package/dist/dom/features/hydration/full.js.map +2 -2
- package/dist/dom/features/hydration/index.d.ts +1 -1
- package/dist/dom/features/hydration/index.js +2 -2
- package/dist/dom/features/hydration/stub.d.ts +7 -2
- package/dist/dom/features/hydration/stub.js +1 -1
- package/dist/dom/features/hydration/stub.js.map +1 -1
- package/dist/dom/features/index.d.ts +8 -7
- package/dist/dom/features/index.js +9 -8
- package/dist/dom/features/index.js.map +2 -2
- package/dist/dom/features/lazy/full.js +4 -4
- package/dist/dom/features/lazy/index.d.ts +1 -1
- package/dist/dom/features/lazy/index.js +2 -2
- package/dist/dom/features/lazy/stub.js +4 -4
- package/dist/dom/features/memo/full.js +8 -6
- package/dist/dom/features/memo/full.js.map +2 -2
- package/dist/dom/features/memo/index.d.ts +1 -1
- package/dist/dom/features/memo/index.js +2 -2
- package/dist/dom/features/memo/stub.js +4 -4
- package/dist/dom/features/portal/full.js +4 -4
- package/dist/dom/features/portal/index.d.ts +1 -1
- package/dist/dom/features/portal/index.js +2 -2
- package/dist/dom/features/portal/stub.js +4 -4
- package/dist/dom/features/suspense/full.js +192 -47
- package/dist/dom/features/suspense/full.js.map +2 -2
- package/dist/dom/features/suspense/index.d.ts +1 -1
- package/dist/dom/features/suspense/index.js +2 -2
- package/dist/dom/features/suspense/stub.js +4 -4
- package/dist/dom/features/view-transition/full.d.ts +6 -0
- package/dist/dom/features/view-transition/full.js +280 -0
- package/dist/dom/features/view-transition/full.js.map +7 -0
- package/dist/dom/features/view-transition/index.d.ts +1 -0
- package/dist/dom/features/view-transition/index.js +3 -0
- package/dist/dom/features/view-transition/index.js.map +7 -0
- package/dist/dom/features/view-transition/resources.d.ts +11 -0
- package/dist/dom/features/view-transition/resources.js +108 -0
- package/dist/dom/features/view-transition/resources.js.map +7 -0
- package/dist/dom/features/view-transition/stub.d.ts +6 -0
- package/dist/dom/features/view-transition/stub.js +22 -0
- package/dist/dom/features/view-transition/stub.js.map +7 -0
- package/dist/dom/features/view-transition/visual.d.ts +14 -0
- package/dist/dom/features/view-transition/visual.js +356 -0
- package/dist/dom/features/view-transition/visual.js.map +7 -0
- package/dist/dom/fragment-instance.d.ts +40 -0
- package/dist/dom/fragment-instance.js +324 -0
- package/dist/dom/fragment-instance.js.map +7 -0
- package/dist/dom/index.d.ts +11 -11
- package/dist/dom/index.js +11 -18
- package/dist/dom/index.js.map +3 -3
- package/dist/dom/portal.d.ts +2 -2
- package/dist/dom/portal.js +2 -2
- package/dist/dom/portal.js.map +1 -1
- package/dist/dom/reconcile.d.ts +32 -3
- package/dist/dom/reconcile.js +686 -222
- package/dist/dom/reconcile.js.map +2 -2
- package/dist/dom/resource-hints.d.ts +6 -0
- package/dist/dom/resource-hints.js +115 -0
- package/dist/dom/resource-hints.js.map +7 -0
- package/dist/dom/resource-store.d.ts +13 -0
- package/dist/dom/resource-store.js +43 -0
- package/dist/dom/resource-store.js.map +7 -0
- package/dist/dom/resources.d.ts +1 -0
- package/dist/dom/resources.js +44 -0
- package/dist/dom/resources.js.map +7 -0
- package/dist/dom/retained-effects.d.ts +6 -0
- package/dist/dom/retained-effects.js +49 -0
- package/dist/dom/retained-effects.js.map +7 -0
- package/dist/dom/root-internal.d.ts +4 -4
- package/dist/dom/root-internal.js +2 -2
- package/dist/dom/root-internal.js.map +2 -2
- package/dist/dom/root.d.ts +5 -5
- package/dist/dom/root.js +11 -11
- package/dist/dom/root.js.map +2 -2
- package/dist/dom/test-utils.js +1 -1
- package/dist/react/activity.d.ts +7 -0
- package/dist/react/activity.js +8 -0
- package/dist/react/activity.js.map +7 -0
- package/dist/react/children.d.ts +1 -1
- package/dist/react/children.js +3 -3
- package/dist/react/children.js.map +1 -1
- package/dist/react/class.d.ts +1 -1
- package/dist/react/class.js +3 -3
- package/dist/react/class.js.map +1 -1
- package/dist/react/compiler-runtime.js +2 -2
- package/dist/react/context.d.ts +1 -1
- package/dist/react/context.js +3 -3
- package/dist/react/context.js.map +1 -1
- package/dist/react/element.d.ts +6 -3
- package/dist/react/element.js +2 -2
- package/dist/react/element.js.map +2 -2
- package/dist/react/hooks.d.ts +26 -7
- package/dist/react/hooks.js +13 -3
- package/dist/react/hooks.js.map +2 -2
- package/dist/react/index.d.ts +39 -24
- package/dist/react/index.js +39 -19
- package/dist/react/index.js.map +3 -3
- package/dist/react/jsx-runtime.d.ts +2 -2
- package/dist/react/jsx-runtime.js +3 -3
- package/dist/react/memo.d.ts +1 -1
- package/dist/react/memo.js +1 -1
- package/dist/react/portal.js +1 -1
- package/dist/react/shared-internals.d.ts +15 -5
- package/dist/react/shared-internals.js +2 -3
- package/dist/react/shared-internals.js.map +2 -2
- package/dist/react/suspense.js +1 -1
- package/dist/react/view-transition.d.ts +32 -0
- package/dist/react/view-transition.js +13 -0
- package/dist/react/view-transition.js.map +7 -0
- package/dist/scheduler/index.js +1 -1
- package/dist/server/bootstrap-script.d.ts +1 -23
- package/dist/server/bootstrap-script.js +7 -6
- package/dist/server/bootstrap-script.js.map +2 -2
- package/dist/server/dispatcher.d.ts +28 -6
- package/dist/server/dispatcher.js +33 -15
- package/dist/server/dispatcher.js.map +2 -2
- package/dist/server/escape.js +16 -15
- package/dist/server/escape.js.map +2 -2
- package/dist/server/index.d.ts +6 -6
- package/dist/server/index.js +7 -7
- package/dist/server/renderToString.d.ts +1 -1
- package/dist/server/renderToString.js +12 -8
- package/dist/server/renderToString.js.map +2 -2
- package/dist/server/resource-hints.d.ts +22 -0
- package/dist/server/resource-hints.js +148 -0
- package/dist/server/resource-hints.js.map +7 -0
- package/dist/server/stream.d.ts +15 -2
- package/dist/server/stream.js +155 -63
- package/dist/server/stream.js.map +2 -2
- package/dist/server/walk.d.ts +15 -2
- package/dist/server/walk.js +143 -96
- package/dist/server/walk.js.map +2 -2
- package/dist/vite/index.d.ts +6 -0
- package/dist/vite/index.js +22 -6
- package/dist/vite/index.js.map +2 -2
- package/package.json +1 -1
- package/src/core/attributes.ts +1 -0
- package/src/core/browser.ts +33 -0
- package/src/core/internal.ts +24 -6
- package/src/core/resource-hints.ts +36 -0
- package/src/dom/_all.ts +1 -7
- package/src/dom/commit.ts +186 -0
- package/src/dom/dispatcher.ts +190 -134
- package/src/dom/dom.ts +70 -54
- package/src/dom/error-info.ts +15 -0
- package/src/dom/features/activity/full.ts +166 -0
- package/src/dom/features/activity/index.ts +1 -0
- package/src/dom/features/activity/stub.ts +14 -0
- package/src/dom/features/class/full.ts +72 -21
- package/src/dom/features/context/full.ts +66 -4
- package/src/dom/features/context/stub.ts +6 -0
- package/src/dom/features/forward-ref/full.ts +9 -19
- package/src/dom/features/fragment-refs/index.ts +1 -0
- package/src/dom/features/fragment-refs/stub.ts +3 -0
- package/src/dom/features/hydration/full.ts +84 -82
- package/src/dom/features/hydration/stub.ts +1 -1
- package/src/dom/features/index.ts +1 -0
- package/src/dom/features/memo/full.ts +4 -3
- package/src/dom/features/suspense/full.ts +187 -50
- package/src/dom/features/view-transition/full.ts +288 -0
- package/src/dom/features/view-transition/index.ts +1 -0
- package/src/dom/features/view-transition/resources.ts +107 -0
- package/src/dom/features/view-transition/stub.ts +7 -0
- package/src/dom/features/view-transition/visual.ts +406 -0
- package/src/dom/fragment-instance.ts +337 -0
- package/src/dom/index.ts +6 -7
- package/src/dom/portal.ts +1 -1
- package/src/dom/reconcile.ts +691 -244
- package/src/dom/resource-hints.ts +115 -0
- package/src/dom/resource-store.ts +52 -0
- package/src/dom/resources.ts +39 -0
- package/src/dom/retained-effects.ts +38 -0
- package/src/dom/root-internal.ts +4 -4
- package/src/dom/root.ts +12 -14
- package/src/react/activity.ts +10 -0
- package/src/react/element.ts +6 -2
- package/src/react/hooks.ts +12 -3
- package/src/react/index.ts +18 -0
- package/src/react/shared-internals.ts +3 -3
- package/src/react/view-transition.ts +41 -0
- package/src/server/bootstrap-script.ts +36 -5
- package/src/server/dispatcher.ts +35 -13
- package/src/server/escape.ts +15 -12
- package/src/server/renderToString.ts +7 -5
- package/src/server/resource-hints.ts +165 -0
- package/src/server/stream.ts +154 -62
- package/src/server/walk.ts +72 -11
- package/src/vite/index.ts +30 -13
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import { FiberTag, type Fiber, type FiberRoot } from '../../../core'
|
|
2
|
+
import {
|
|
3
|
+
REACT_VIEW_TRANSITION_TYPE,
|
|
4
|
+
type ViewTransitionClass,
|
|
5
|
+
type ViewTransitionInstance,
|
|
6
|
+
type ViewTransitionProps,
|
|
7
|
+
type ViewTransitionPseudoElement,
|
|
8
|
+
} from '../../../react/view-transition'
|
|
9
|
+
import { transitionResources, type TransitionImages } from './resources'
|
|
10
|
+
|
|
11
|
+
type Kind = 'enter' | 'exit' | 'update' | 'share'
|
|
12
|
+
type Host = HTMLElement | SVGElement
|
|
13
|
+
type Rect = [number, number, number, number]
|
|
14
|
+
interface Boundary {
|
|
15
|
+
fiber: Fiber
|
|
16
|
+
props: ViewTransitionProps
|
|
17
|
+
hosts: Host[]
|
|
18
|
+
ancestors: Fiber[]
|
|
19
|
+
parent: Fiber | null
|
|
20
|
+
name: string
|
|
21
|
+
explicit: boolean
|
|
22
|
+
rects: Rect[]
|
|
23
|
+
content: string
|
|
24
|
+
inViewport: boolean
|
|
25
|
+
}
|
|
26
|
+
interface Snapshot {
|
|
27
|
+
boundaries: Map<Fiber, Boundary>
|
|
28
|
+
visible: Set<Fiber>
|
|
29
|
+
images: TransitionImages
|
|
30
|
+
}
|
|
31
|
+
interface Capture {
|
|
32
|
+
old?: Boundary | undefined
|
|
33
|
+
next?: Boundary | undefined
|
|
34
|
+
kind: Kind
|
|
35
|
+
name: string
|
|
36
|
+
oldHosts: Host[]
|
|
37
|
+
newEnabled: boolean
|
|
38
|
+
}
|
|
39
|
+
interface Event {
|
|
40
|
+
boundary: Boundary
|
|
41
|
+
kind: Kind
|
|
42
|
+
name: string
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const names = new WeakMap<Fiber, string>()
|
|
46
|
+
const styleProperties = ['view-transition-name', 'view-transition-class']
|
|
47
|
+
let nextName = 0
|
|
48
|
+
|
|
49
|
+
function byType(value: ViewTransitionClass | undefined, types: string[]): string | undefined {
|
|
50
|
+
if (typeof value === 'string') return value
|
|
51
|
+
if (!value) return undefined
|
|
52
|
+
let result: string | undefined
|
|
53
|
+
for (const type of types) {
|
|
54
|
+
const match = value[type]
|
|
55
|
+
if (match === 'none') return match
|
|
56
|
+
if (match != null) result = result == null ? match : result + ' ' + match
|
|
57
|
+
}
|
|
58
|
+
return result ?? value.default
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function className(props: ViewTransitionProps, kind: Kind, types: string[]): string | undefined {
|
|
62
|
+
const value = byType(props[kind], types) ?? byType(props.default, types)
|
|
63
|
+
return value === 'auto' ? undefined : value
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function visibleChildren(fiber: Fiber, visit: (child: Fiber) => void): void {
|
|
67
|
+
if (fiber.tag === FiberTag.Activity && fiber.pp?.mode === 'hidden') return
|
|
68
|
+
if (fiber.tag === FiberTag.Suspense && fiber.ms?.f) {
|
|
69
|
+
visit(fiber.ms.f)
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
for (let child = fiber.child; child; child = child.sibling) visit(child)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Ignore nested boundary content: its mutations belong to that boundary.
|
|
76
|
+
function contents(node: Node, nested: Set<Node>, first = false): string {
|
|
77
|
+
if (!first && nested.has(node)) return '|'
|
|
78
|
+
if (node.nodeType === 3) return JSON.stringify(node.nodeValue)
|
|
79
|
+
if (node.nodeType !== 1) return ''
|
|
80
|
+
const element = node as Host
|
|
81
|
+
let result = '<' + element.nodeName
|
|
82
|
+
for (const attribute of element.attributes) {
|
|
83
|
+
if (attribute.name !== 'style') result += ' ' + attribute.name + '=' + JSON.stringify(attribute.value)
|
|
84
|
+
}
|
|
85
|
+
for (let i = 0; i < element.style.length; i++) {
|
|
86
|
+
const property = element.style[i]!
|
|
87
|
+
if (property !== 'view-transition-name' && property !== 'view-transition-class') {
|
|
88
|
+
result += ';' + property + ':' + element.style.getPropertyValue(property) + '!' + element.style.getPropertyPriority(property)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if ('value' in element) result += '=' + (element as HTMLInputElement).value
|
|
92
|
+
if ('checked' in element) result += ':' + (element as HTMLInputElement).checked
|
|
93
|
+
result += '>'
|
|
94
|
+
for (let child = node.firstChild; child; child = child.nextSibling) result += contents(child, nested)
|
|
95
|
+
return result + '</' + element.nodeName + '>'
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function snapshot(roots: FiberRoot[], doc: Document, types: string[], connected = true): Snapshot {
|
|
99
|
+
const boundaries = new Map<Fiber, Boundary>()
|
|
100
|
+
const visible = new Set<Fiber>()
|
|
101
|
+
const images: TransitionImages = new Map()
|
|
102
|
+
const allHosts = new Set<Node>()
|
|
103
|
+
const empty: Boundary[] = []
|
|
104
|
+
const ancestors: Fiber[] = []
|
|
105
|
+
function walk(fiber: Fiber, parent: Fiber | null, pending: Boundary[]): void {
|
|
106
|
+
if (fiber.um) return
|
|
107
|
+
if (fiber.type === REACT_VIEW_TRANSITION_TYPE) {
|
|
108
|
+
const props: ViewTransitionProps = fiber.pp ?? {}
|
|
109
|
+
const explicitName = byType(props.name, types)
|
|
110
|
+
let name = explicitName
|
|
111
|
+
if (name == null || name === 'auto') {
|
|
112
|
+
name = names.get(fiber)
|
|
113
|
+
if (!name) names.set(fiber, name = '_redact_t_' + (++nextName).toString(32))
|
|
114
|
+
}
|
|
115
|
+
const boundary: Boundary = {
|
|
116
|
+
fiber, props, hosts: [], ancestors: ancestors.slice(), parent, name,
|
|
117
|
+
explicit: explicitName != null && explicitName !== 'auto', rects: [], content: '', inViewport: false,
|
|
118
|
+
}
|
|
119
|
+
boundaries.set(fiber, boundary)
|
|
120
|
+
pending = pending.concat(boundary)
|
|
121
|
+
parent = fiber
|
|
122
|
+
}
|
|
123
|
+
if (fiber.tag === FiberTag.Host) {
|
|
124
|
+
visible.add(fiber)
|
|
125
|
+
const dom = fiber.dom as Host | null
|
|
126
|
+
if (parent && fiber.type === 'img' && dom?.ownerDocument === doc) {
|
|
127
|
+
images.set(dom as HTMLImageElement, fiber.pp)
|
|
128
|
+
}
|
|
129
|
+
if (pending.length && dom?.ownerDocument === doc && (!connected || dom.isConnected) && dom.style) {
|
|
130
|
+
for (const boundary of pending) boundary.hosts.push(dom)
|
|
131
|
+
allHosts.add(dom)
|
|
132
|
+
}
|
|
133
|
+
pending = empty
|
|
134
|
+
ancestors.push(fiber)
|
|
135
|
+
}
|
|
136
|
+
visibleChildren(fiber, child => walk(child, parent, pending))
|
|
137
|
+
if (fiber.tag === FiberTag.Host) ancestors.pop()
|
|
138
|
+
}
|
|
139
|
+
for (const root of roots) walk(root.r, null, empty)
|
|
140
|
+
const view = doc.defaultView
|
|
141
|
+
if (connected) for (const boundary of boundaries.values()) {
|
|
142
|
+
boundary.rects = boundary.hosts.map(host => {
|
|
143
|
+
const rect = host.getBoundingClientRect()
|
|
144
|
+
return [rect.x, rect.y, rect.width, rect.height]
|
|
145
|
+
})
|
|
146
|
+
boundary.inViewport = boundary.rects.some(([x, y, width, height]) => width > 0 && height > 0 && x + width > 0 && y + height > 0 && x < (view?.innerWidth ?? Infinity) && y < (view?.innerHeight ?? Infinity))
|
|
147
|
+
boundary.content = boundary.hosts.map(host => contents(host, allHosts, true)).join('')
|
|
148
|
+
}
|
|
149
|
+
return { boundaries, visible, images }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function pseudo(doc: Document, kind: string, name: string): ViewTransitionPseudoElement {
|
|
153
|
+
const scope = doc.documentElement
|
|
154
|
+
const selector = '::view-transition-' + kind + '(' + nativeName(name) + ')'
|
|
155
|
+
return {
|
|
156
|
+
animate(keyframes, options) {
|
|
157
|
+
return scope.animate(keyframes, { ...(typeof options === 'number' ? { duration: options } : options), pseudoElement: selector })
|
|
158
|
+
},
|
|
159
|
+
getAnimations() {
|
|
160
|
+
return scope.getAnimations({ subtree: true }).filter(animation => {
|
|
161
|
+
const effect = animation.effect as KeyframeEffect | null
|
|
162
|
+
return effect?.target === scope && effect.pseudoElement === selector
|
|
163
|
+
})
|
|
164
|
+
},
|
|
165
|
+
getComputedStyle() { return doc.defaultView!.getComputedStyle(scope, selector) },
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function nativeName(name: string): string {
|
|
170
|
+
return CSS.escape(name) === name ? name : CSS.escape('r-' + btoa(name).replace(/=/g, ''))
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function instance(doc: Document, name: string): ViewTransitionInstance {
|
|
174
|
+
return {
|
|
175
|
+
name,
|
|
176
|
+
group: pseudo(doc, 'group', name),
|
|
177
|
+
imagePair: pseudo(doc, 'image-pair', name),
|
|
178
|
+
old: pseudo(doc, 'old', name),
|
|
179
|
+
new: pseudo(doc, 'new', name),
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function topLevel(boundary: Boundary, other: Snapshot, side: Snapshot): boolean {
|
|
184
|
+
// An inserted/deleted host wrapper owns its subtree's enter/exit animation.
|
|
185
|
+
if (boundary.ancestors.some(fiber => !other.visible.has(fiber))) return false
|
|
186
|
+
for (let parent = boundary.parent; parent; parent = side.boundaries.get(parent)?.parent ?? null) {
|
|
187
|
+
if (!other.boundaries.has(parent)) return false
|
|
188
|
+
}
|
|
189
|
+
return true
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function changed(before: Boundary, after: Boundary): boolean {
|
|
193
|
+
return before.content !== after.content || before.hosts.length !== after.hosts.length ||
|
|
194
|
+
before.hosts.some((host, i) => host !== after.hosts[i] || before.rects[i]!.some((value, axis) => value !== after.rects[i]![axis]))
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export interface PreparedViewTransition {
|
|
198
|
+
document: Document
|
|
199
|
+
exclude(root: FiberRoot): void
|
|
200
|
+
prepare(): Promise<void> | undefined
|
|
201
|
+
waitForResources(fontsWereLoaded: boolean): Promise<void> | undefined
|
|
202
|
+
beforeCommit(): boolean
|
|
203
|
+
afterCommit(): void
|
|
204
|
+
ready(): void
|
|
205
|
+
restore(): void
|
|
206
|
+
finish(): void
|
|
207
|
+
cancel(): void
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function prepareViewTransition(rootIterable: Iterable<FiberRoot>, types: string[]): PreparedViewTransition | null {
|
|
211
|
+
const roots = Array.from(rootIterable)
|
|
212
|
+
const container = roots[0]?.c
|
|
213
|
+
const doc = container?.nodeType === 9 ? container as unknown as Document : container?.ownerDocument
|
|
214
|
+
if (!doc?.documentElement) return null
|
|
215
|
+
const before = snapshot(roots, doc, types)
|
|
216
|
+
const saved = new Map<Host, string[]>()
|
|
217
|
+
const suppressed = new Set<string>()
|
|
218
|
+
const events: Event[] = []
|
|
219
|
+
const cleanups: (() => void)[] = []
|
|
220
|
+
const captures: Capture[] = []
|
|
221
|
+
let done = false
|
|
222
|
+
let after: Snapshot
|
|
223
|
+
let resources: ReturnType<typeof transitionResources> | undefined
|
|
224
|
+
|
|
225
|
+
function apply(host: Host, name: string, classes?: string): void {
|
|
226
|
+
let previous = saved.get(host)
|
|
227
|
+
if (!previous) saved.set(host, previous = [])
|
|
228
|
+
for (let index = 0; index < 2; index++) {
|
|
229
|
+
const property = styleProperties[index]!
|
|
230
|
+
const offset = index * 3
|
|
231
|
+
const current = host.style.getPropertyValue(property)
|
|
232
|
+
// A committed style prop may replace either temporary value.
|
|
233
|
+
if (current !== previous[offset + 2]) {
|
|
234
|
+
previous[offset] = current
|
|
235
|
+
previous[offset + 1] = host.style.getPropertyPriority(property)
|
|
236
|
+
}
|
|
237
|
+
const value = index ? classes === 'none' ? '' : classes ?? previous[offset]! : nativeName(name)
|
|
238
|
+
host.style.setProperty(property, previous[offset + 2] = value, 'important')
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function applyHosts(hosts: Host[], name: string, props: ViewTransitionProps, kind: Kind): void {
|
|
243
|
+
const classes = className(props, kind, types)
|
|
244
|
+
hosts.forEach((host, index) => apply(host, index ? name + '_' + index : name, classes))
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function suppress(hosts: Host[], name: string): void {
|
|
248
|
+
hosts.forEach((_, index) => suppressed.add(index ? name + '_' + index : name))
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function ownership(side: 'old' | 'next'): Map<Host, Capture> {
|
|
252
|
+
const owners = new Map<Host, Capture>()
|
|
253
|
+
for (const capture of captures) {
|
|
254
|
+
if (side === 'next' && !capture.newEnabled) continue
|
|
255
|
+
for (const host of capture[side]?.hosts ?? []) owners.set(host, capture)
|
|
256
|
+
}
|
|
257
|
+
return owners
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function capture(kind: Kind, old?: Boundary, next?: Boundary, enabled = true): void {
|
|
261
|
+
captures.push({ old, next, kind, name: (old ?? next)!.name, oldHosts: [], newEnabled: !!next && enabled })
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function restore(): void {
|
|
265
|
+
for (const [host, previous] of saved) {
|
|
266
|
+
for (let index = 0; index < 2; index++) {
|
|
267
|
+
const property = styleProperties[index]!
|
|
268
|
+
const offset = index * 3
|
|
269
|
+
if (host.style.getPropertyValue(property) === previous[offset + 2]) {
|
|
270
|
+
host.style.setProperty(property, previous[offset]!, previous[offset + 1])
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
saved.clear()
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function finish(): void {
|
|
278
|
+
if (done) return
|
|
279
|
+
done = true
|
|
280
|
+
resources?.cancel()
|
|
281
|
+
restore()
|
|
282
|
+
let error: unknown
|
|
283
|
+
let failed = false
|
|
284
|
+
for (const cleanup of cleanups.splice(0)) {
|
|
285
|
+
try { cleanup() } catch (caught) { if (!failed) { error = caught; failed = true } }
|
|
286
|
+
}
|
|
287
|
+
if (failed) throw error
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
document: doc,
|
|
292
|
+
exclude(root) {
|
|
293
|
+
const index = roots.indexOf(root)
|
|
294
|
+
if (index >= 0) roots.splice(index, 1)
|
|
295
|
+
for (const [fiber] of before.boundaries) if (fiber.root === root) before.boundaries.delete(fiber)
|
|
296
|
+
},
|
|
297
|
+
prepare() {
|
|
298
|
+
after = snapshot(roots, doc, types, false)
|
|
299
|
+
resources = transitionResources(doc, before.images, after.images)
|
|
300
|
+
return resources.before()
|
|
301
|
+
},
|
|
302
|
+
waitForResources(fontsWereLoaded) { if (!done) return resources?.after(fontsWereLoaded) },
|
|
303
|
+
beforeCommit() {
|
|
304
|
+
if (done) return false
|
|
305
|
+
const entering = new Map<string, Boundary>()
|
|
306
|
+
const paired = new Set<Fiber>()
|
|
307
|
+
for (const boundary of after.boundaries.values()) {
|
|
308
|
+
if (!before.boundaries.has(boundary.fiber) && boundary.explicit) entering.set(boundary.name, boundary)
|
|
309
|
+
}
|
|
310
|
+
for (const old of before.boundaries.values()) {
|
|
311
|
+
const current = after.boundaries.get(old.fiber)
|
|
312
|
+
if (current) {
|
|
313
|
+
if (className(current.props, 'update', types) !== 'none') capture('update', old, current)
|
|
314
|
+
} else {
|
|
315
|
+
const shared = old.explicit && old.inViewport ? entering.get(old.name) : undefined
|
|
316
|
+
if (shared) {
|
|
317
|
+
entering.delete(old.name)
|
|
318
|
+
if (className(old.props, 'share', types) !== 'none') {
|
|
319
|
+
paired.add(shared.fiber)
|
|
320
|
+
capture('share', old, shared, className(shared.props, 'share', types) !== 'none')
|
|
321
|
+
}
|
|
322
|
+
} else if (topLevel(old, after, before) && old.inViewport && className(old.props, 'exit', types) !== 'none') {
|
|
323
|
+
capture('exit', old)
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
for (const boundary of after.boundaries.values()) {
|
|
328
|
+
if (before.boundaries.has(boundary.fiber) || paired.has(boundary.fiber)) continue
|
|
329
|
+
if (topLevel(boundary, before, after) && className(boundary.props, 'enter', types) !== 'none') {
|
|
330
|
+
capture('enter', undefined, boundary)
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
// A directly nested boundary owns updates; an outer presence boundary
|
|
334
|
+
// owns entering/exiting hosts. Shared descendants take precedence.
|
|
335
|
+
captures.sort((a, b) => (a.kind === 'share' ? 2 : a.kind === 'update' ? 0 : 1) - (b.kind === 'share' ? 2 : b.kind === 'update' ? 0 : 1))
|
|
336
|
+
if (!captures.some(capture => capture.old?.hosts.length || (capture.newEnabled && capture.next?.hosts.length))) return false
|
|
337
|
+
const owners = ownership('old')
|
|
338
|
+
apply(doc.documentElement, 'none')
|
|
339
|
+
for (const boundary of before.boundaries.values()) for (const host of boundary.hosts) apply(host, 'none')
|
|
340
|
+
for (const capture of captures) {
|
|
341
|
+
capture.oldHosts = capture.old?.hosts.filter(host => owners.get(host) === capture) ?? []
|
|
342
|
+
const props = capture.kind === 'update' ? capture.next!.props : capture.old?.props
|
|
343
|
+
if (props) applyHosts(capture.oldHosts, capture.name, props, capture.kind)
|
|
344
|
+
}
|
|
345
|
+
return true
|
|
346
|
+
},
|
|
347
|
+
afterCommit() {
|
|
348
|
+
if (done) return
|
|
349
|
+
const after = snapshot(roots, doc, types)
|
|
350
|
+
for (const capture of captures) {
|
|
351
|
+
if (capture.next) capture.next = after.boundaries.get(capture.next.fiber)
|
|
352
|
+
if (capture.kind !== 'update' && !capture.next?.inViewport) capture.newEnabled = false
|
|
353
|
+
}
|
|
354
|
+
const owners = ownership('next')
|
|
355
|
+
for (const boundary of after.boundaries.values()) for (const host of boundary.hosts) apply(host, 'none')
|
|
356
|
+
for (const capture of captures) {
|
|
357
|
+
const newHosts = capture.next?.hosts.filter(host => owners.get(host) === capture) ?? []
|
|
358
|
+
if (capture.next) applyHosts(newHosts, capture.name, capture.next.props, capture.kind)
|
|
359
|
+
if (!capture.oldHosts.length && !newHosts.length) continue
|
|
360
|
+
if (capture.kind === 'update') {
|
|
361
|
+
if (capture.next && capture.old && changed(capture.old, capture.next)) events.push({ boundary: capture.next, name: capture.name, kind: 'update' })
|
|
362
|
+
else suppress(capture.oldHosts, capture.name)
|
|
363
|
+
} else {
|
|
364
|
+
const boundary = capture.kind === 'enter' ? capture.next : capture.old
|
|
365
|
+
if (boundary) events.push({ boundary, name: capture.name, kind: capture.kind })
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
},
|
|
369
|
+
ready() {
|
|
370
|
+
if (done) return
|
|
371
|
+
// These captures were needed before the synchronous commit could tell
|
|
372
|
+
// which boundaries changed. Cancel their native animations, not the DOM.
|
|
373
|
+
const nativeSuppressed = new Set([...suppressed].map(nativeName))
|
|
374
|
+
for (const animation of doc.documentElement.getAnimations({ subtree: true })) {
|
|
375
|
+
const effect = animation.effect as KeyframeEffect | null
|
|
376
|
+
const selector = effect?.pseudoElement
|
|
377
|
+
if (effect?.target !== doc.documentElement || !selector) continue
|
|
378
|
+
const start = selector.indexOf('(')
|
|
379
|
+
if (start >= 0 && nativeSuppressed.has(selector.slice(start + 1, -1))) animation.cancel()
|
|
380
|
+
}
|
|
381
|
+
for (const name of suppressed) {
|
|
382
|
+
const animation = pseudo(doc, 'group', name).animate({ opacity: 0 }, { duration: 0, fill: 'forwards' })
|
|
383
|
+
cleanups.push(() => animation.cancel())
|
|
384
|
+
}
|
|
385
|
+
let error: unknown
|
|
386
|
+
let failed = false
|
|
387
|
+
for (const event of events) {
|
|
388
|
+
if (done) break
|
|
389
|
+
const callback = event.boundary.props[('on' + event.kind[0]!.toUpperCase() + event.kind.slice(1)) as 'onEnter']
|
|
390
|
+
if (callback) {
|
|
391
|
+
try {
|
|
392
|
+
const cleanup = callback(instance(doc, event.name), types)
|
|
393
|
+
if (typeof cleanup === 'function') {
|
|
394
|
+
if (done) cleanup()
|
|
395
|
+
else cleanups.push(cleanup)
|
|
396
|
+
}
|
|
397
|
+
} catch (caught) { if (!failed) { error = caught; failed = true } }
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
if (failed) throw error
|
|
401
|
+
},
|
|
402
|
+
restore,
|
|
403
|
+
finish,
|
|
404
|
+
cancel: finish,
|
|
405
|
+
}
|
|
406
|
+
}
|