@bgub/fig-reconciler 0.0.1 → 0.1.0-alpha.1
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/act.d.ts +4 -0
- package/dist/act.js +2 -0
- package/dist/commit-coordinator.d.ts +21 -0
- package/dist/commit-coordinator.js +1 -0
- package/dist/devtools.d.ts +3 -4
- package/dist/devtools.js +1 -22
- package/dist/fiber-traversal-BHyIAyG9.js +251 -0
- package/dist/fiber-traversal-BHyIAyG9.js.map +1 -0
- package/dist/index.d.ts +20 -34
- package/dist/index.js +296 -863
- package/dist/index.js.map +1 -1
- package/dist/refresh.js.map +1 -1
- package/dist/scheduler-WVeIVb_n.js +209 -0
- package/dist/scheduler-WVeIVb_n.js.map +1 -0
- package/dist/view-transitions.d.ts +31 -0
- package/dist/view-transitions.js +404 -0
- package/dist/view-transitions.js.map +1 -0
- package/package.json +18 -3
- package/dist/devtools.js.map +0 -1
package/dist/refresh.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refresh.js","names":[],"sources":["../src/refresh.ts"],"sourcesContent":["import { setRefreshHandlerState } from \"./refresh-
|
|
1
|
+
{"version":3,"file":"refresh.js","names":[],"sources":["../src/refresh.ts"],"sourcesContent":["import { setRefreshHandlerState } from \"./refresh-internal.ts\";\n\ndeclare const __FIG_DEV__: boolean | undefined;\n\nconst __DEV__ = typeof __FIG_DEV__ === \"boolean\" ? __FIG_DEV__ : false;\n\n// A family groups every version of a component across hot edits; `current` is\n// the latest implementation. The handler is module-global (one refresh runtime\n// per app) so module-level reconcile helpers can consult it. In production no\n// handler is ever set, so all of this collapses to identity/equality paths.\nexport interface RefreshFamily {\n current: unknown;\n}\n\nexport interface RefreshUpdate {\n staleFamilies: Set<RefreshFamily>;\n updatedFamilies: Set<RefreshFamily>;\n}\n\nexport function setRefreshHandler(\n handler: ((type: unknown) => RefreshFamily | undefined) | null,\n): void {\n if (__DEV__) {\n setRefreshHandlerState(handler);\n }\n}\n"],"mappings":";AAmBA,SAAgB,kBACd,SACM,CAIR"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
const priorityTimeouts = {
|
|
2
|
+
[1]: -1,
|
|
3
|
+
[2]: 250,
|
|
4
|
+
[3]: 5e3,
|
|
5
|
+
[4]: 1e4,
|
|
6
|
+
[5]: 1073741823
|
|
7
|
+
};
|
|
8
|
+
const frameInterval = 5;
|
|
9
|
+
function compare(a, b) {
|
|
10
|
+
return a.expirationTime - b.expirationTime || a.id - b.id;
|
|
11
|
+
}
|
|
12
|
+
var MinHeap = class {
|
|
13
|
+
items = [];
|
|
14
|
+
push(value) {
|
|
15
|
+
this.items.push(value);
|
|
16
|
+
this.siftUp(this.items.length - 1);
|
|
17
|
+
}
|
|
18
|
+
peek() {
|
|
19
|
+
return this.items[0] ?? null;
|
|
20
|
+
}
|
|
21
|
+
pop() {
|
|
22
|
+
const first = this.items[0];
|
|
23
|
+
const last = this.items.pop();
|
|
24
|
+
if (first === void 0 || last === void 0) return null;
|
|
25
|
+
if (first !== last) {
|
|
26
|
+
this.items[0] = last;
|
|
27
|
+
this.siftDown(0);
|
|
28
|
+
}
|
|
29
|
+
return first;
|
|
30
|
+
}
|
|
31
|
+
siftUp(index) {
|
|
32
|
+
const value = this.items[index];
|
|
33
|
+
while (index > 0) {
|
|
34
|
+
const parentIndex = index - 1 >>> 1;
|
|
35
|
+
const parent = this.items[parentIndex];
|
|
36
|
+
if (compare(parent, value) <= 0) return;
|
|
37
|
+
this.items[parentIndex] = value;
|
|
38
|
+
this.items[index] = parent;
|
|
39
|
+
index = parentIndex;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
siftDown(index) {
|
|
43
|
+
const length = this.items.length;
|
|
44
|
+
const value = this.items[index];
|
|
45
|
+
while (index < length) {
|
|
46
|
+
const leftIndex = index * 2 + 1;
|
|
47
|
+
const rightIndex = leftIndex + 1;
|
|
48
|
+
let smallest = index;
|
|
49
|
+
if (leftIndex < length && compare(this.items[leftIndex], this.items[smallest]) < 0) smallest = leftIndex;
|
|
50
|
+
if (rightIndex < length && compare(this.items[rightIndex], this.items[smallest]) < 0) smallest = rightIndex;
|
|
51
|
+
if (smallest === index) return;
|
|
52
|
+
this.items[index] = this.items[smallest];
|
|
53
|
+
this.items[smallest] = value;
|
|
54
|
+
index = smallest;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const taskQueue = new MinHeap();
|
|
59
|
+
let taskId = 1;
|
|
60
|
+
let messageLoopRunning = false;
|
|
61
|
+
let needsPaint = false;
|
|
62
|
+
let startTime = -1;
|
|
63
|
+
let actQueue = null;
|
|
64
|
+
let actScopeDepth = 0;
|
|
65
|
+
let flushingActQueue = false;
|
|
66
|
+
const actFlushLimit = 1e3;
|
|
67
|
+
function now() {
|
|
68
|
+
return globalThis.performance?.now?.() ?? Date.now();
|
|
69
|
+
}
|
|
70
|
+
function shouldYieldToHost() {
|
|
71
|
+
return needsPaint || now() - startTime >= frameInterval;
|
|
72
|
+
}
|
|
73
|
+
function requestPaint() {
|
|
74
|
+
needsPaint = true;
|
|
75
|
+
}
|
|
76
|
+
function scheduleCallback(priority, callback) {
|
|
77
|
+
const task = {
|
|
78
|
+
id: taskId++,
|
|
79
|
+
callback,
|
|
80
|
+
expirationTime: now() + priorityTimeouts[priority]
|
|
81
|
+
};
|
|
82
|
+
if (actQueue !== null) actQueue.push(task);
|
|
83
|
+
else {
|
|
84
|
+
taskQueue.push(task);
|
|
85
|
+
requestHostCallback();
|
|
86
|
+
}
|
|
87
|
+
return { cancel() {
|
|
88
|
+
task.callback = null;
|
|
89
|
+
} };
|
|
90
|
+
}
|
|
91
|
+
async function act(callback) {
|
|
92
|
+
const previousActQueue = actQueue;
|
|
93
|
+
const previousActScopeDepth = actScopeDepth;
|
|
94
|
+
const queue = previousActQueue ?? [];
|
|
95
|
+
actQueue = queue;
|
|
96
|
+
actScopeDepth = previousActScopeDepth + 1;
|
|
97
|
+
try {
|
|
98
|
+
const result = await callback();
|
|
99
|
+
actScopeDepth = previousActScopeDepth;
|
|
100
|
+
if (previousActScopeDepth === 0) await flushActQueueUntilSettled(queue);
|
|
101
|
+
return result;
|
|
102
|
+
} finally {
|
|
103
|
+
actScopeDepth = previousActScopeDepth;
|
|
104
|
+
actQueue = previousActQueue;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async function flushActQueueUntilSettled(queue) {
|
|
108
|
+
for (let flushes = 0; flushes < actFlushLimit; flushes += 1) {
|
|
109
|
+
flushActQueue(queue);
|
|
110
|
+
await Promise.resolve();
|
|
111
|
+
if (hasActWork(queue)) continue;
|
|
112
|
+
await waitForActMacrotask();
|
|
113
|
+
if (!hasActWork(queue)) {
|
|
114
|
+
queue.length = 0;
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
throw new Error("act() exceeded the scheduled work flush limit.");
|
|
119
|
+
}
|
|
120
|
+
function flushActQueue(queue) {
|
|
121
|
+
if (flushingActQueue) return;
|
|
122
|
+
flushingActQueue = true;
|
|
123
|
+
try {
|
|
124
|
+
let task = takeNextActTask(queue);
|
|
125
|
+
while (task !== null) {
|
|
126
|
+
const callback = task.callback;
|
|
127
|
+
if (callback !== null) {
|
|
128
|
+
task.callback = null;
|
|
129
|
+
needsPaint = false;
|
|
130
|
+
startTime = now();
|
|
131
|
+
const continuation = callback();
|
|
132
|
+
if (typeof continuation === "function") {
|
|
133
|
+
task.callback = continuation;
|
|
134
|
+
queue.push(task);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
task = takeNextActTask(queue);
|
|
138
|
+
}
|
|
139
|
+
} finally {
|
|
140
|
+
flushingActQueue = false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function hasActWork(queue) {
|
|
144
|
+
return queue.some((task) => task.callback !== null);
|
|
145
|
+
}
|
|
146
|
+
function takeNextActTask(queue) {
|
|
147
|
+
let nextIndex = -1;
|
|
148
|
+
for (let index = 0; index < queue.length; index += 1) {
|
|
149
|
+
const task = queue[index];
|
|
150
|
+
if (task.callback === null) continue;
|
|
151
|
+
if (nextIndex === -1 || compare(task, queue[nextIndex]) < 0) nextIndex = index;
|
|
152
|
+
}
|
|
153
|
+
if (nextIndex === -1) {
|
|
154
|
+
queue.length = 0;
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
const [task] = queue.splice(nextIndex, 1);
|
|
158
|
+
return task;
|
|
159
|
+
}
|
|
160
|
+
function waitForActMacrotask() {
|
|
161
|
+
return new Promise((resolve) => {
|
|
162
|
+
if (typeof setImmediate === "function") setImmediate(resolve);
|
|
163
|
+
else setTimeout(resolve, 0);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
function requestHostCallback() {
|
|
167
|
+
if (messageLoopRunning) return;
|
|
168
|
+
messageLoopRunning = true;
|
|
169
|
+
scheduleHostCallback();
|
|
170
|
+
}
|
|
171
|
+
function performWorkUntilDeadline() {
|
|
172
|
+
needsPaint = false;
|
|
173
|
+
startTime = now();
|
|
174
|
+
let hasMoreWork = false;
|
|
175
|
+
try {
|
|
176
|
+
hasMoreWork = flushWork(startTime);
|
|
177
|
+
} finally {
|
|
178
|
+
if (hasMoreWork) scheduleHostCallback();
|
|
179
|
+
else messageLoopRunning = false;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
let channel = null;
|
|
183
|
+
const scheduleHostCallback = typeof setImmediate === "function" ? () => void setImmediate(performWorkUntilDeadline) : typeof MessageChannel === "function" ? () => {
|
|
184
|
+
if (channel === null) {
|
|
185
|
+
channel = new MessageChannel();
|
|
186
|
+
channel.port1.onmessage = () => performWorkUntilDeadline();
|
|
187
|
+
}
|
|
188
|
+
channel.port2.postMessage(null);
|
|
189
|
+
} : () => void setTimeout(performWorkUntilDeadline, 0);
|
|
190
|
+
function flushWork(currentTime) {
|
|
191
|
+
let task = taskQueue.peek();
|
|
192
|
+
while (task !== null) {
|
|
193
|
+
if (task.expirationTime > currentTime && shouldYieldToHost()) break;
|
|
194
|
+
const callback = task.callback;
|
|
195
|
+
if (callback === null) taskQueue.pop();
|
|
196
|
+
else {
|
|
197
|
+
task.callback = null;
|
|
198
|
+
const continuation = callback();
|
|
199
|
+
if (typeof continuation === "function") task.callback = continuation;
|
|
200
|
+
else if (task === taskQueue.peek()) taskQueue.pop();
|
|
201
|
+
}
|
|
202
|
+
task = taskQueue.peek();
|
|
203
|
+
}
|
|
204
|
+
return task !== null;
|
|
205
|
+
}
|
|
206
|
+
//#endregion
|
|
207
|
+
export { shouldYieldToHost as a, scheduleCallback as i, now as n, requestPaint as r, act as t };
|
|
208
|
+
|
|
209
|
+
//# sourceMappingURL=scheduler-WVeIVb_n.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler-WVeIVb_n.js","names":[],"sources":["../src/scheduler.ts"],"sourcesContent":["// Fig's cooperative task scheduler: a macrotask-hopping work loop with five\n// priority tiers (mapped from lanes in lanes.ts) and starvation timeouts.\n// Internal to fig-reconciler — the reconciler is its only consumer, so it is\n// deliberately not a published package.\n\nexport type PriorityLevel = 1 | 2 | 3 | 4 | 5;\nexport type SchedulerCallback = () => SchedulerCallback | undefined;\n\nexport interface ScheduledTask {\n cancel(): void;\n}\n\nexport const ImmediatePriority = 1;\nexport const UserBlockingPriority = 2;\nexport const NormalPriority = 3;\nexport const LowPriority = 4;\nexport const IdlePriority = 5;\n\n// Starvation timeouts: once a task has waited this long it runs even past\n// frame-budget yields (see flushWork's expiration check).\nconst priorityTimeouts: Record<PriorityLevel, number> = {\n [ImmediatePriority]: -1,\n [UserBlockingPriority]: 250,\n [NormalPriority]: 5_000,\n [LowPriority]: 10_000,\n [IdlePriority]: 1_073_741_823,\n};\n\nconst frameInterval = 5;\n\ninterface Task {\n id: number;\n callback: SchedulerCallback | null;\n expirationTime: number;\n}\n\nfunction compare(a: Task, b: Task): number {\n return a.expirationTime - b.expirationTime || a.id - b.id;\n}\n\nclass MinHeap {\n readonly items: Task[] = [];\n\n push(value: Task): void {\n this.items.push(value);\n this.siftUp(this.items.length - 1);\n }\n\n peek(): Task | null {\n return this.items[0] ?? null;\n }\n\n pop(): Task | null {\n const first = this.items[0];\n const last = this.items.pop();\n\n if (first === undefined || last === undefined) return null;\n\n if (first !== last) {\n this.items[0] = last;\n this.siftDown(0);\n }\n\n return first;\n }\n\n private siftUp(index: number): void {\n const value = this.items[index];\n\n while (index > 0) {\n const parentIndex = (index - 1) >>> 1;\n const parent = this.items[parentIndex];\n if (compare(parent, value) <= 0) return;\n\n this.items[parentIndex] = value;\n this.items[index] = parent;\n index = parentIndex;\n }\n }\n\n private siftDown(index: number): void {\n const length = this.items.length;\n const value = this.items[index];\n\n while (index < length) {\n const leftIndex = index * 2 + 1;\n const rightIndex = leftIndex + 1;\n let smallest = index;\n\n if (\n leftIndex < length &&\n compare(this.items[leftIndex], this.items[smallest]) < 0\n ) {\n smallest = leftIndex;\n }\n\n if (\n rightIndex < length &&\n compare(this.items[rightIndex], this.items[smallest]) < 0\n ) {\n smallest = rightIndex;\n }\n\n if (smallest === index) return;\n\n this.items[index] = this.items[smallest];\n this.items[smallest] = value;\n index = smallest;\n }\n }\n}\n\nconst taskQueue = new MinHeap();\nlet taskId = 1;\nlet messageLoopRunning = false;\nlet needsPaint = false;\nlet startTime = -1;\nlet actQueue: Task[] | null = null;\nlet actScopeDepth = 0;\nlet flushingActQueue = false;\n\nconst actFlushLimit = 1_000;\n\nexport function now(): number {\n return globalThis.performance?.now?.() ?? Date.now();\n}\n\nexport function shouldYieldToHost(): boolean {\n return needsPaint || now() - startTime >= frameInterval;\n}\n\n// Renderers call this after committing host mutations: the next\n// shouldYieldToHost() returns true, so the work loop hands the thread back\n// and the host can paint before further scheduled work runs.\nexport function requestPaint(): void {\n needsPaint = true;\n}\n\nexport function scheduleCallback(\n priority: PriorityLevel,\n callback: SchedulerCallback,\n): ScheduledTask {\n const task: Task = {\n id: taskId++,\n callback,\n expirationTime: now() + priorityTimeouts[priority],\n };\n\n if (actQueue !== null) {\n actQueue.push(task);\n } else {\n taskQueue.push(task);\n requestHostCallback();\n }\n\n return {\n cancel() {\n task.callback = null;\n },\n };\n}\n\nexport async function act<T>(\n callback: () => T | PromiseLike<T>,\n): Promise<Awaited<T>> {\n const previousActQueue = actQueue;\n const previousActScopeDepth = actScopeDepth;\n const queue = previousActQueue ?? [];\n\n actQueue = queue;\n actScopeDepth = previousActScopeDepth + 1;\n\n try {\n const result = await callback();\n\n actScopeDepth = previousActScopeDepth;\n if (previousActScopeDepth === 0) {\n await flushActQueueUntilSettled(queue);\n }\n\n return result;\n } finally {\n actScopeDepth = previousActScopeDepth;\n actQueue = previousActQueue;\n }\n}\n\nasync function flushActQueueUntilSettled(queue: Task[]): Promise<void> {\n for (let flushes = 0; flushes < actFlushLimit; flushes += 1) {\n flushActQueue(queue);\n await Promise.resolve();\n\n if (hasActWork(queue)) continue;\n\n await waitForActMacrotask();\n if (!hasActWork(queue)) {\n queue.length = 0;\n return;\n }\n }\n\n throw new Error(\"act() exceeded the scheduled work flush limit.\");\n}\n\nfunction flushActQueue(queue: Task[]): void {\n if (flushingActQueue) return;\n\n flushingActQueue = true;\n try {\n let task = takeNextActTask(queue);\n while (task !== null) {\n const callback = task.callback;\n\n if (callback !== null) {\n task.callback = null;\n needsPaint = false;\n startTime = now();\n const continuation = callback();\n if (typeof continuation === \"function\") {\n task.callback = continuation;\n queue.push(task);\n }\n }\n\n task = takeNextActTask(queue);\n }\n } finally {\n flushingActQueue = false;\n }\n}\n\nfunction hasActWork(queue: Task[]): boolean {\n return queue.some((task) => task.callback !== null);\n}\n\nfunction takeNextActTask(queue: Task[]): Task | null {\n let nextIndex = -1;\n for (let index = 0; index < queue.length; index += 1) {\n const task = queue[index];\n if (task.callback === null) continue;\n\n if (nextIndex === -1 || compare(task, queue[nextIndex]) < 0) {\n nextIndex = index;\n }\n }\n\n if (nextIndex === -1) {\n queue.length = 0;\n return null;\n }\n\n const [task] = queue.splice(nextIndex, 1);\n return task;\n}\n\nfunction waitForActMacrotask(): Promise<void> {\n return new Promise((resolve) => {\n if (typeof setImmediate === \"function\") {\n void setImmediate(resolve);\n } else {\n setTimeout(resolve, 0);\n }\n });\n}\n\nfunction requestHostCallback(): void {\n if (messageLoopRunning) return;\n messageLoopRunning = true;\n scheduleHostCallback();\n}\n\nfunction performWorkUntilDeadline(): void {\n needsPaint = false;\n startTime = now();\n\n let hasMoreWork = false;\n try {\n hasMoreWork = flushWork(startTime);\n } finally {\n if (hasMoreWork) scheduleHostCallback();\n else messageLoopRunning = false;\n }\n}\n\ndeclare const setImmediate: ((callback: () => void) => unknown) | undefined;\n\nlet channel: MessageChannel | null = null;\n\n// Host-callback preference, matching React's scheduler (facebook/react#20756):\n// - setImmediate first (Node, old IE): unlike a MessagePort with a message\n// handler it never refs an idle event loop — importing the scheduler cannot\n// keep a Node process alive — and it fires earlier in the loop's turn.\n// Caveat shared with React: a page-level setImmediate polyfill would win\n// this check in a browser; real browsers never reach it natively.\n// - MessageChannel in browsers, where setImmediate does not exist: preferred\n// over setTimeout because nested setTimeout(0) is clamped to 4ms+, wasting\n// most of a frame per work-loop hop. Created on the first post so module\n// evaluation allocates nothing.\n// - setTimeout as the last resort for hosts with neither.\nconst scheduleHostCallback: () => void =\n typeof setImmediate === \"function\"\n ? () => void setImmediate(performWorkUntilDeadline)\n : typeof MessageChannel === \"function\"\n ? () => {\n if (channel === null) {\n channel = new MessageChannel();\n channel.port1.onmessage = () => performWorkUntilDeadline();\n }\n channel.port2.postMessage(null);\n }\n : () => void setTimeout(performWorkUntilDeadline, 0);\n\nfunction flushWork(currentTime: number): boolean {\n let task = taskQueue.peek();\n while (task !== null) {\n if (task.expirationTime > currentTime && shouldYieldToHost()) break;\n\n const callback = task.callback;\n if (callback === null) {\n taskQueue.pop();\n } else {\n task.callback = null;\n const continuation = callback();\n\n if (typeof continuation === \"function\") {\n task.callback = continuation;\n } else if (task === taskQueue.peek()) {\n // The callback may have scheduled work that sorts ahead of this task,\n // so only pop when this task is still at the top of the heap; a stale\n // entry is skipped via its null callback when it surfaces later.\n taskQueue.pop();\n }\n }\n\n task = taskQueue.peek();\n }\n\n return task !== null;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ReconcilerCommitCoordinator, ReconcilerCommitResult } from "./commit-coordinator.js";
|
|
2
|
+
import { Props, ViewTransitionSurface } from "@bgub/fig";
|
|
3
|
+
//#region src/view-transitions.d.ts
|
|
4
|
+
type ViewTransitionCommitResult = ReconcilerCommitResult;
|
|
5
|
+
interface ViewTransitionSurfaceMeasurement {
|
|
6
|
+
absolutelyPositioned: boolean;
|
|
7
|
+
height: number;
|
|
8
|
+
inViewport: boolean;
|
|
9
|
+
width: number;
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
}
|
|
13
|
+
interface ViewTransitionMutationResult {
|
|
14
|
+
canceledNames: string[];
|
|
15
|
+
cancelRootSnapshot: boolean;
|
|
16
|
+
}
|
|
17
|
+
interface ViewTransitionSurfaceSnapshots {
|
|
18
|
+
readonly old: boolean;
|
|
19
|
+
readonly new: boolean;
|
|
20
|
+
}
|
|
21
|
+
interface ViewTransitionHostConfig<Container, Instance> {
|
|
22
|
+
commit(this: void, container: Container, types: readonly string[], prepareSnapshot: () => void, mutate: () => ViewTransitionMutationResult, ready: (active: boolean) => void, finished: () => void): ViewTransitionCommitResult;
|
|
23
|
+
apply(this: void, instance: Instance, name: string, className: string | null): void;
|
|
24
|
+
restore(this: void, instance: Instance, props: Props): void;
|
|
25
|
+
measure?(this: void, instance: Instance): ViewTransitionSurfaceMeasurement | null;
|
|
26
|
+
createSurface?(this: void, instance: Instance, name: string, snapshots: ViewTransitionSurfaceSnapshots): ViewTransitionSurface;
|
|
27
|
+
suspend?(this: void, container: Container, onFinished: () => void): boolean;
|
|
28
|
+
}
|
|
29
|
+
declare function createViewTransitionCommitCoordinator<Container, Instance>(host: ViewTransitionHostConfig<Container, Instance>): ReconcilerCommitCoordinator<Container, Instance>;
|
|
30
|
+
//#endregion
|
|
31
|
+
export { ViewTransitionCommitResult, ViewTransitionHostConfig, ViewTransitionMutationResult, ViewTransitionSurfaceMeasurement, ViewTransitionSurfaceSnapshots, createViewTransitionCommitCoordinator };
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
import { A as transitionTypeHooks, a as IdleLane, g as includesSomeLane, i as DeferredLane, r as AllTransitionLanes, s as RetryLanes, t as walkFiberForest, v as laneToIndex } from "./fiber-traversal-BHyIAyG9.js";
|
|
2
|
+
//#region src/transition-types.ts
|
|
3
|
+
const activeTransitions = [];
|
|
4
|
+
const rootTransitionTypes = /* @__PURE__ */ new WeakMap();
|
|
5
|
+
function getCurrentTransitionTypes(lane) {
|
|
6
|
+
const transition = activeTransitions[laneToIndex(lane)];
|
|
7
|
+
return transition === void 0 || transition === null || transition.types.size === 0 ? null : transition.types;
|
|
8
|
+
}
|
|
9
|
+
function getRootTransitionTypes(root, renderLanes) {
|
|
10
|
+
const types = /* @__PURE__ */ new Set();
|
|
11
|
+
const typesByLane = rootTransitionTypes.get(root);
|
|
12
|
+
if (typesByLane === void 0) return [];
|
|
13
|
+
for (const [lane, laneTypes] of typesByLane) {
|
|
14
|
+
if (!includesSomeLane(renderLanes, lane)) continue;
|
|
15
|
+
for (const type of laneTypes) types.add(type);
|
|
16
|
+
}
|
|
17
|
+
return [...types];
|
|
18
|
+
}
|
|
19
|
+
function retainTransitionTypes(lane, types) {
|
|
20
|
+
const index = laneToIndex(lane);
|
|
21
|
+
let transition = activeTransitions[index];
|
|
22
|
+
if (transition === void 0 || transition === null) {
|
|
23
|
+
transition = {
|
|
24
|
+
scopes: 0,
|
|
25
|
+
types: /* @__PURE__ */ new Set()
|
|
26
|
+
};
|
|
27
|
+
activeTransitions[index] = transition;
|
|
28
|
+
}
|
|
29
|
+
transition.scopes += 1;
|
|
30
|
+
if (types !== void 0) for (const type of types) transition.types.add(type);
|
|
31
|
+
return () => {
|
|
32
|
+
transition.scopes -= 1;
|
|
33
|
+
if (transition.scopes === 0) activeTransitions[index] = null;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function recordRootTransitionTypes(root, lane) {
|
|
37
|
+
const types = getCurrentTransitionTypes(lane);
|
|
38
|
+
if (types === null) return;
|
|
39
|
+
let typesByLane = rootTransitionTypes.get(root);
|
|
40
|
+
if (typesByLane === void 0) {
|
|
41
|
+
typesByLane = /* @__PURE__ */ new Map();
|
|
42
|
+
rootTransitionTypes.set(root, typesByLane);
|
|
43
|
+
}
|
|
44
|
+
let pendingTypes = typesByLane.get(lane);
|
|
45
|
+
if (pendingTypes === void 0) {
|
|
46
|
+
pendingTypes = /* @__PURE__ */ new Set();
|
|
47
|
+
typesByLane.set(lane, pendingTypes);
|
|
48
|
+
}
|
|
49
|
+
for (const type of types) pendingTypes.add(type);
|
|
50
|
+
}
|
|
51
|
+
function completeRootTransitionTypes(root, remainingLanes) {
|
|
52
|
+
const typesByLane = rootTransitionTypes.get(root);
|
|
53
|
+
if (typesByLane === void 0) return;
|
|
54
|
+
for (const lane of typesByLane.keys()) if (!includesSomeLane(remainingLanes, lane)) typesByLane.delete(lane);
|
|
55
|
+
if (typesByLane.size === 0) rootTransitionTypes.delete(root);
|
|
56
|
+
}
|
|
57
|
+
transitionTypeHooks.retain = retainTransitionTypes;
|
|
58
|
+
transitionTypeHooks.record = recordRootTransitionTypes;
|
|
59
|
+
transitionTypeHooks.complete = completeRootTransitionTypes;
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/view-transitions.ts
|
|
62
|
+
const ViewTransitionEligibleLanes = AllTransitionLanes | RetryLanes | DeferredLane | IdleLane;
|
|
63
|
+
function createViewTransitionCommitCoordinator(host) {
|
|
64
|
+
let autoNameCounter = 0;
|
|
65
|
+
function isEligible(root) {
|
|
66
|
+
return !root.clearContainerBeforeCommit && root.renderLanes !== 0 && (root.renderLanes & ~ViewTransitionEligibleLanes) === 0;
|
|
67
|
+
}
|
|
68
|
+
function preparePlan(root, finishedWork) {
|
|
69
|
+
if (!isEligible(root)) return null;
|
|
70
|
+
if ((finishedWork.subtreeFlags & 4096) === 0 && !root.needsCommitDeletions) return null;
|
|
71
|
+
const plan = {
|
|
72
|
+
newSurfaces: [],
|
|
73
|
+
oldSurfaces: [],
|
|
74
|
+
rootAffected: false,
|
|
75
|
+
types: getRootTransitionTypes(root, root.renderLanes)
|
|
76
|
+
};
|
|
77
|
+
const collection = {
|
|
78
|
+
changedBoundaries: null,
|
|
79
|
+
exitsByName: /* @__PURE__ */ new Map(),
|
|
80
|
+
plan
|
|
81
|
+
};
|
|
82
|
+
if (root.needsCommitDeletions) collectDeletedViewTransitions(root, finishedWork, collection);
|
|
83
|
+
collection.changedBoundaries = attributeQueuedHostUpdates(root, plan);
|
|
84
|
+
collectFinishedViewTransitions(finishedWork.child, false, false, false, collection);
|
|
85
|
+
if (plan.oldSurfaces.length === 0 && plan.newSurfaces.length === 0) return null;
|
|
86
|
+
return plan;
|
|
87
|
+
}
|
|
88
|
+
function collectDeletedViewTransitions(root, node, collection) {
|
|
89
|
+
for (const cursor of root.commitIndex) {
|
|
90
|
+
if (cursor.deletions === null) continue;
|
|
91
|
+
for (const deletion of cursor.deletions) collectDeletedViewTransitionFiber(deletion, collection, true);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function collectDeletedViewTransitionFiber(cursor, collection, collectExit) {
|
|
95
|
+
if (cursor.tag === 8 || isHiddenBoundary(cursor)) return;
|
|
96
|
+
if (cursor.tag === 11) {
|
|
97
|
+
const name = explicitViewTransitionName(cursor);
|
|
98
|
+
if (name !== null) collection.exitsByName.set(name, cursor);
|
|
99
|
+
if (collectExit) collectViewTransitionSurfaces(cursor, "exit", collection.plan.oldSurfaces, "committed");
|
|
100
|
+
for (let child = cursor.child; child !== null; child = child.sibling) collectDeletedViewTransitionFiber(child, collection, false);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if ((cursor.subtreeFlags & 4096) === 0) return;
|
|
104
|
+
for (let child = cursor.child; child !== null; child = child.sibling) collectDeletedViewTransitionFiber(child, collection, collectExit);
|
|
105
|
+
}
|
|
106
|
+
function attributeQueuedHostUpdates(root, plan) {
|
|
107
|
+
let changed = null;
|
|
108
|
+
for (const entry of root.commitIndex) {
|
|
109
|
+
if ((entry.flags & 10) === 0) continue;
|
|
110
|
+
let sawPortal = false;
|
|
111
|
+
let boundary = null;
|
|
112
|
+
for (let parent = entry.return; parent !== null; parent = parent.return) {
|
|
113
|
+
if (parent.tag === 11) {
|
|
114
|
+
boundary = parent;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
if (parent.tag === 8) sawPortal = true;
|
|
118
|
+
}
|
|
119
|
+
if (boundary === null) plan.rootAffected = true;
|
|
120
|
+
else if (!sawPortal) (changed ??= /* @__PURE__ */ new Set()).add(boundary);
|
|
121
|
+
}
|
|
122
|
+
return changed;
|
|
123
|
+
}
|
|
124
|
+
function collectFinishedViewTransitions(node, placed, insideBoundary, ancestorLayoutChanged, collection) {
|
|
125
|
+
const { changedBoundaries, exitsByName, plan } = collection;
|
|
126
|
+
let layoutChanged = ancestorLayoutChanged;
|
|
127
|
+
for (let cursor = node; !layoutChanged && cursor !== null; cursor = cursor.sibling) if ((cursor.flags & 1) !== 0 && (cursor.alternate !== null || (cursor.flags & 16) !== 0)) layoutChanged = true;
|
|
128
|
+
for (let cursor = node; cursor !== null; cursor = cursor.sibling) {
|
|
129
|
+
const cursorPlaced = placed || (cursor.flags & 1) !== 0;
|
|
130
|
+
if (!(cursor.tag === 11 || (cursor.subtreeFlags & 4096) !== 0)) {
|
|
131
|
+
if (!insideBoundary && ((cursor.flags | cursor.subtreeFlags) & 175) !== 0) plan.rootAffected = true;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (isStablyHiddenBoundary(cursor)) continue;
|
|
135
|
+
if (cursor.tag === 11) {
|
|
136
|
+
if (!cursorPlaced && ((cursor.flags | cursor.subtreeFlags) & 4) !== 0) continue;
|
|
137
|
+
if (cursorPlaced) {
|
|
138
|
+
const name = viewTransitionName(cursor);
|
|
139
|
+
const pairedExit = exitsByName.get(name);
|
|
140
|
+
if (pairedExit !== void 0) {
|
|
141
|
+
if (!insideBoundary) plan.rootAffected = true;
|
|
142
|
+
collectViewTransitionPair(plan, pairedExit, cursor);
|
|
143
|
+
exitsByName.delete(name);
|
|
144
|
+
} else if (cursor.alternate !== null) {
|
|
145
|
+
collectViewTransitionSurfaces(cursor.alternate, "update", plan.oldSurfaces, "committed", false);
|
|
146
|
+
collectViewTransitionSurfaces(cursor, "update", plan.newSurfaces, "finished", false);
|
|
147
|
+
} else {
|
|
148
|
+
if (!insideBoundary) plan.rootAffected = true;
|
|
149
|
+
collectViewTransitionSurfaces(cursor, "enter", plan.newSurfaces, "finished");
|
|
150
|
+
}
|
|
151
|
+
collectAppearingPairViewTransitions(cursor.child, collection);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
const current = cursor.alternate ?? cursor;
|
|
155
|
+
const contentChanged = viewTransitionChangedOutsideNested(cursor, changedBoundaries);
|
|
156
|
+
if (contentChanged || layoutChanged) {
|
|
157
|
+
collectViewTransitionSurfaces(current, "update", plan.oldSurfaces, "committed", contentChanged);
|
|
158
|
+
collectViewTransitionSurfaces(cursor, "update", plan.newSurfaces, "finished", contentChanged);
|
|
159
|
+
}
|
|
160
|
+
collectFinishedViewTransitions(cursor.child, cursorPlaced, true, contentChanged || layoutChanged, collection);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (cursor.tag !== 8) {
|
|
164
|
+
if (!insideBoundary && (cursor.flags & 175) !== 0) plan.rootAffected = true;
|
|
165
|
+
collectFinishedViewTransitions(cursor.child, cursorPlaced, insideBoundary, layoutChanged || (cursor.flags & 175) !== 0, collection);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function collectAppearingPairViewTransitions(node, collection) {
|
|
170
|
+
const { exitsByName, plan } = collection;
|
|
171
|
+
if (exitsByName.size === 0) return;
|
|
172
|
+
walkFiberForest(node, (cursor) => {
|
|
173
|
+
if (exitsByName.size === 0) return false;
|
|
174
|
+
if (cursor.tag === 8 || isStablyHiddenBoundary(cursor)) return false;
|
|
175
|
+
if (cursor.tag === 11) {
|
|
176
|
+
const name = explicitViewTransitionName(cursor);
|
|
177
|
+
if (name !== null) {
|
|
178
|
+
const pairedExit = exitsByName.get(name);
|
|
179
|
+
if (pairedExit !== void 0) {
|
|
180
|
+
collectViewTransitionPair(plan, pairedExit, cursor);
|
|
181
|
+
exitsByName.delete(name);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return true;
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
function collectViewTransitionPair(plan, oldBoundary, newBoundary) {
|
|
189
|
+
removeViewTransitionSurfaces(plan.oldSurfaces, oldBoundary);
|
|
190
|
+
collectViewTransitionSurfaces(oldBoundary, "share", plan.oldSurfaces, "committed");
|
|
191
|
+
collectViewTransitionSurfaces(newBoundary, "share", plan.newSurfaces, "finished");
|
|
192
|
+
}
|
|
193
|
+
function isHiddenBoundary(node) {
|
|
194
|
+
return node.tag === 10 && node.props.mode === "hidden";
|
|
195
|
+
}
|
|
196
|
+
function isStablyHiddenBoundary(node) {
|
|
197
|
+
if (!isHiddenBoundary(node)) return false;
|
|
198
|
+
const current = node.alternate;
|
|
199
|
+
return current === null || (current.memoizedProps ?? current.props).mode === "hidden";
|
|
200
|
+
}
|
|
201
|
+
function viewTransitionChangedOutsideNested(boundary, changedBoundaries) {
|
|
202
|
+
if ((boundary.flags & 175) !== 0) return true;
|
|
203
|
+
if (changedBoundaries?.has(boundary) === true) return true;
|
|
204
|
+
return subtreeChangedOutsideNested(boundary.child);
|
|
205
|
+
}
|
|
206
|
+
function subtreeChangedOutsideNested(node) {
|
|
207
|
+
for (let cursor = node; cursor !== null; cursor = cursor.sibling) {
|
|
208
|
+
if (cursor.tag === 8 || cursor.tag === 11) continue;
|
|
209
|
+
if ((cursor.flags & 175) !== 0) return true;
|
|
210
|
+
if ((cursor.subtreeFlags & 175) === 0) continue;
|
|
211
|
+
if (subtreeChangedOutsideNested(cursor.child)) return true;
|
|
212
|
+
}
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
function removeViewTransitionSurfaces(surfaces, boundary) {
|
|
216
|
+
for (let index = surfaces.length - 1; index >= 0; index -= 1) if (surfaces[index].boundary === boundary) surfaces.splice(index, 1);
|
|
217
|
+
}
|
|
218
|
+
function collectViewTransitionSurfaces(boundary, phase, surfaces, propsSource, mustAnimate = true) {
|
|
219
|
+
const className = viewTransitionClass(boundary.props, phase);
|
|
220
|
+
if (className === "none") return;
|
|
221
|
+
const name = viewTransitionName(boundary);
|
|
222
|
+
let index = 0;
|
|
223
|
+
walkFiberForest(boundary.child, (cursor) => {
|
|
224
|
+
if (cursor.tag === 8 || cursor.tag === 11) return false;
|
|
225
|
+
if (cursor.tag !== 1) return true;
|
|
226
|
+
if ((cursor.flags & 8192) === 0) {
|
|
227
|
+
surfaces.push({
|
|
228
|
+
boundary,
|
|
229
|
+
className,
|
|
230
|
+
instance: cursor.stateNode,
|
|
231
|
+
measurement: null,
|
|
232
|
+
mustAnimate,
|
|
233
|
+
name: index === 0 ? name : `${name}_${index}`,
|
|
234
|
+
phase,
|
|
235
|
+
props: viewTransitionSurfaceProps(cursor, propsSource),
|
|
236
|
+
skipped: false
|
|
237
|
+
});
|
|
238
|
+
index += 1;
|
|
239
|
+
}
|
|
240
|
+
return false;
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
function viewTransitionSurfaceProps(node, source) {
|
|
244
|
+
if (source === "committed") return node.committedProps ?? node.memoizedProps ?? node.props;
|
|
245
|
+
return node.memoizedProps ?? node.props;
|
|
246
|
+
}
|
|
247
|
+
function viewTransitionName(node) {
|
|
248
|
+
const props = node.props;
|
|
249
|
+
if (props.name !== void 0 && props.name !== "auto") return props.name;
|
|
250
|
+
const state = node.stateNode;
|
|
251
|
+
state.autoName ??= `fig-vt-${autoNameCounter++}`;
|
|
252
|
+
return state.autoName;
|
|
253
|
+
}
|
|
254
|
+
function explicitViewTransitionName(node) {
|
|
255
|
+
const name = node.props.name;
|
|
256
|
+
return name === void 0 || name === "auto" ? null : name;
|
|
257
|
+
}
|
|
258
|
+
function viewTransitionClass(props, phase) {
|
|
259
|
+
const viewTransitionProps = props;
|
|
260
|
+
const phaseClass = viewTransitionProps[phase];
|
|
261
|
+
const className = phaseClass === void 0 ? viewTransitionProps.default : phaseClass;
|
|
262
|
+
return className === void 0 || className === "auto" ? null : className;
|
|
263
|
+
}
|
|
264
|
+
function applyOldViewTransitionSurfaces(plan) {
|
|
265
|
+
const measure = host.measure;
|
|
266
|
+
for (const surface of plan.oldSurfaces) {
|
|
267
|
+
surface.measurement = measure?.(surface.instance) ?? null;
|
|
268
|
+
if (surface.phase === "exit" && surface.measurement !== null && !surface.measurement.inViewport) {
|
|
269
|
+
surface.skipped = true;
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
host.apply(surface.instance, surface.name, surface.className);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
function resolveViewTransitionPlan(plan) {
|
|
276
|
+
const result = {
|
|
277
|
+
canceledNames: [],
|
|
278
|
+
cancelRootSnapshot: false
|
|
279
|
+
};
|
|
280
|
+
const measure = host.measure;
|
|
281
|
+
const oldByName = /* @__PURE__ */ new Map();
|
|
282
|
+
for (const surface of plan.oldSurfaces) if (!surface.skipped) oldByName.set(surface.name, surface);
|
|
283
|
+
let rootAffected = plan.rootAffected;
|
|
284
|
+
const newNames = /* @__PURE__ */ new Set();
|
|
285
|
+
for (const surface of plan.newSurfaces) {
|
|
286
|
+
newNames.add(surface.name);
|
|
287
|
+
const measurement = measure?.(surface.instance) ?? null;
|
|
288
|
+
if (surface.phase === "enter") {
|
|
289
|
+
if (measurement !== null && !measurement.inViewport) {
|
|
290
|
+
surface.skipped = true;
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
host.apply(surface.instance, surface.name, surface.className);
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
if (surface.phase === "update") {
|
|
297
|
+
const oldSurface = oldByName.get(surface.name);
|
|
298
|
+
const before = oldSurface?.measurement ?? null;
|
|
299
|
+
if (before !== null && measurement !== null) {
|
|
300
|
+
const moved = before.x !== measurement.x || before.y !== measurement.y || before.width !== measurement.width || before.height !== measurement.height;
|
|
301
|
+
if (!before.inViewport && !measurement.inViewport || !surface.mustAnimate && !moved) {
|
|
302
|
+
surface.skipped = true;
|
|
303
|
+
host.restore(surface.instance, surface.props);
|
|
304
|
+
if (oldSurface !== void 0) {
|
|
305
|
+
oldSurface.skipped = true;
|
|
306
|
+
result.canceledNames.push(surface.name);
|
|
307
|
+
}
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
if ((before.width !== measurement.width || before.height !== measurement.height) && !measurement.absolutelyPositioned) rootAffected = true;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
host.apply(surface.instance, surface.name, surface.className);
|
|
314
|
+
}
|
|
315
|
+
for (const [name, surface] of oldByName) if (surface.phase === "update" && !newNames.has(name)) rootAffected = true;
|
|
316
|
+
result.cancelRootSnapshot = !rootAffected;
|
|
317
|
+
return result;
|
|
318
|
+
}
|
|
319
|
+
function restoreViewTransitionSurfaces(plan) {
|
|
320
|
+
const propsByInstance = /* @__PURE__ */ new Map();
|
|
321
|
+
for (const surface of plan.oldSurfaces) propsByInstance.set(surface.instance, surface.props);
|
|
322
|
+
for (const surface of plan.newSurfaces) propsByInstance.set(surface.instance, surface.props);
|
|
323
|
+
for (const [instance, props] of propsByInstance) host.restore(instance, props);
|
|
324
|
+
}
|
|
325
|
+
function participatingSurfaceNames(surfaces) {
|
|
326
|
+
const names = /* @__PURE__ */ new Set();
|
|
327
|
+
for (const surface of surfaces) if (!surface.skipped) names.add(surface.name);
|
|
328
|
+
return names;
|
|
329
|
+
}
|
|
330
|
+
function dispatchViewTransitionCallbacks(plan, signal) {
|
|
331
|
+
const groups = /* @__PURE__ */ new Map();
|
|
332
|
+
const collect = (surface) => {
|
|
333
|
+
if (surface.skipped) return;
|
|
334
|
+
const callback = surface.boundary.props.onTransition;
|
|
335
|
+
if (callback === void 0) return;
|
|
336
|
+
const group = groups.get(surface.boundary);
|
|
337
|
+
if (group === void 0) groups.set(surface.boundary, {
|
|
338
|
+
callback,
|
|
339
|
+
surfaces: [surface]
|
|
340
|
+
});
|
|
341
|
+
else group.surfaces.push(surface);
|
|
342
|
+
};
|
|
343
|
+
for (const surface of plan.newSurfaces) collect(surface);
|
|
344
|
+
for (const surface of plan.oldSurfaces) if (surface.phase === "exit") collect(surface);
|
|
345
|
+
if (groups.size === 0) return;
|
|
346
|
+
const oldNames = participatingSurfaceNames(plan.oldSurfaces);
|
|
347
|
+
const newNames = participatingSurfaceNames(plan.newSurfaces);
|
|
348
|
+
for (const { callback, surfaces: group } of groups.values()) {
|
|
349
|
+
const surfaces = group.map((surface) => {
|
|
350
|
+
const snapshots = {
|
|
351
|
+
old: oldNames.has(surface.name),
|
|
352
|
+
new: newNames.has(surface.name)
|
|
353
|
+
};
|
|
354
|
+
return host.createSurface?.(surface.instance, surface.name, snapshots) ?? { name: surface.name };
|
|
355
|
+
});
|
|
356
|
+
callback({
|
|
357
|
+
phase: group[0].phase,
|
|
358
|
+
surfaces,
|
|
359
|
+
types: plan.types
|
|
360
|
+
}, signal);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return {
|
|
364
|
+
name: "view-transitions",
|
|
365
|
+
viewTransitions: true,
|
|
366
|
+
suspend(rootIdentity, onReady) {
|
|
367
|
+
const root = rootIdentity;
|
|
368
|
+
return isEligible(root) && host.suspend?.(root.container, onReady) === true;
|
|
369
|
+
},
|
|
370
|
+
commit(context) {
|
|
371
|
+
const root = context.root;
|
|
372
|
+
const finishedWork = context.finishedWork;
|
|
373
|
+
const plan = preparePlan(root, finishedWork);
|
|
374
|
+
if (plan === null) return false;
|
|
375
|
+
let didRunMutation = false;
|
|
376
|
+
let didFinish = false;
|
|
377
|
+
let controller = null;
|
|
378
|
+
return host.commit(context.container, plan.types, () => applyOldViewTransitionSurfaces(plan), () => {
|
|
379
|
+
didRunMutation = true;
|
|
380
|
+
return context.runMutation(() => resolveViewTransitionPlan(plan)) ?? {
|
|
381
|
+
canceledNames: [],
|
|
382
|
+
cancelRootSnapshot: false
|
|
383
|
+
};
|
|
384
|
+
}, (active) => {
|
|
385
|
+
try {
|
|
386
|
+
restoreViewTransitionSurfaces(plan);
|
|
387
|
+
} finally {
|
|
388
|
+
if (didRunMutation) context.captureFinished();
|
|
389
|
+
}
|
|
390
|
+
if (active && !didFinish && controller === null) {
|
|
391
|
+
controller = new AbortController();
|
|
392
|
+
dispatchViewTransitionCallbacks(plan, controller.signal);
|
|
393
|
+
}
|
|
394
|
+
}, () => {
|
|
395
|
+
didFinish = true;
|
|
396
|
+
controller?.abort();
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
//#endregion
|
|
402
|
+
export { createViewTransitionCommitCoordinator };
|
|
403
|
+
|
|
404
|
+
//# sourceMappingURL=view-transitions.js.map
|