Skip to content

Commit 096713e

Browse files
committed
Fix race condition when cleaning up polyfilled type attributes
1 parent 7137dcc commit 096713e

4 files changed

Lines changed: 34 additions & 21 deletions

File tree

.changeset/tasty-eggs-sing.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@vtbag/utensil-drawer': patch
3+
---
4+
5+
Fixes a race condition where the wrong polyfill classes were removed when a transition was skipped.

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ Utensil Drawer: Pick the tools you need to craft the view transitions you want!
1212
The @vtbag website can be found at https://vtbag.dev/
1313

1414
## !!! News !!!
15-
16-
> `mayStartViewTransition` can now also handle scoped view transitions on supporting browsers. Usage: add `scope: someElement` to the extension object.
15+
Fixed a race condition that led to removal of the wrong polyfill classes during cleanup.
1716

1817
For details see https://vtbag.dev/tools/utensil-drawer/
1918

2019
## What happened before?
2120

21+
> `mayStartViewTransition` can now also handle scoped view transitions on supporting browsers. Usage: add `scope: someElement` to the extension object.
2222
2323
> Do many of the elements you want to automatically add view-transition-names to fall outside the viewport? The declarative-names script now supports a new pseudo-class that you can add at the end of selectors. By using `:in-viewport`, only the elements overlapping with the current viewport will be named!
2424

src/may-start-view-transition.ts

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getTypeAttributes, polyfilledTypes, root } from './polyfilled-types.js';
1+
import { polyfilledTypes, root } from './polyfilled-types.js';
22
import {
33
createViewTransitionProxy,
44
SwitchableViewTransition,
@@ -135,22 +135,37 @@ export function mayStartViewTransition(
135135
if (!scopeData.currentViewTransition || collisionBehavior === 'skipOld') {
136136
scopeData.keepLast = !!scopeData.currentViewTransition && !!scopeData.open;
137137
scopeData.open = requestAnimationFrame(() => close(scopeData));
138-
scopeData.currentViewTransition = polyfilledTypes(
139-
scope,
140-
(scopeData.currentViewTransition = surrogate
141-
? createViewTransitionSurrogate(unchainUpdates)
142-
: scope.startViewTransition!(unchainUpdates)),
143-
useTypesPolyfill === 'always' || (useTypesPolyfill !== 'never' && nativeSupport === 'partial')
144-
);
138+
scopeData.currentViewTransition = surrogate
139+
? createViewTransitionSurrogate(unchainUpdates)
140+
: scope.startViewTransition!(unchainUpdates);
141+
if (
142+
useTypesPolyfill === 'always' ||
143+
(useTypesPolyfill !== 'never' && nativeSupport === 'partial')
144+
)
145+
scopeData.currentViewTransition = polyfilledTypes(scope, scopeData.currentViewTransition);
146+
147+
const localTransition = scopeData.currentViewTransition;
145148
if (catchErrors) {
146149
const error = (e: any) =>
147150
(catchErrors as any) !== 'suppress' && (console.error(e), undefined);
148151
scopeData.currentViewTransition.updateCallbackDone.catch(error);
149152
scopeData.currentViewTransition.ready.catch(error);
150153
}
154+
151155
scopeData.currentViewTransition.finished.finally(() => {
152-
getTypeAttributes()?.forEach((t) => root(scope).classList.remove(t));
153-
scopeData.currentViewTransition = undefined;
156+
if (scopeData.currentViewTransition !== localTransition) {
157+
[...(localTransition.types ?? [])].forEach(
158+
(t) =>
159+
scopeData.currentViewTransition?.types.has(t) ||
160+
root(scope).classList.remove(`vtbag-vtt-${t}`)
161+
);
162+
} else {
163+
[...(localTransition.types ?? [])].forEach((t) =>
164+
root(scope).classList.remove(`vtbag-vtt-${t}`)
165+
);
166+
root(scope).classList.remove('vtbag-vtt-0');
167+
scopeData.currentViewTransition = undefined;
168+
}
154169
scopeData.chained
155170
.splice(0, scopeData.chained.length)
156171
.forEach(({ update, extensions, proxy }) => {

src/polyfilled-types.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,17 @@
1-
let typeAttributes: Set<string>;
2-
export const getTypeAttributes = () => typeAttributes;
31

42
export function root(scope: Document | HTMLElement): HTMLElement {
53
return 'documentElement' in scope ? scope.documentElement : scope;
64
}
75

86
export function polyfilledTypes(
97
scope: Document | Element,
10-
viewTransition: ViewTransition,
11-
proxied: boolean
8+
viewTransition: ViewTransition
129
): ViewTransition {
13-
if (!proxied) return viewTransition;
14-
//@ts-ignore
1510
const classList = ('documentElement' in scope ? scope.documentElement : scope).classList;
1611
let types = undefined;
1712

1813
const typeAttr = 'vtbag-vtt-0'; // for :active-view-transition, postcss-active-view-transition-type
1914
classList.add(typeAttr);
20-
typeAttributes = new Set<string>([typeAttr]);
2115

2216
return new Proxy(viewTransition, {
2317
get(target, prop: keyof ViewTransition) {
@@ -28,7 +22,6 @@ export function polyfilledTypes(
2822
return (value: string) => {
2923
typesTarget.add(value);
3024
const typeAttr = 'vtbag-vtt-' + value;
31-
typeAttributes.add(typeAttr);
3225
classList.add(typeAttr);
3326
};
3427
} else if (typesProp === 'delete') {
@@ -42,7 +35,7 @@ export function polyfilledTypes(
4235
typesTarget.clear();
4336
};
4437
} else if (typesProp === 'has') {
45-
return (value: string) => typeAttributes.has(value);
38+
return (value: string) => classList.contains('vtbag-vtt-' + value);
4639
} else if (typesProp === Symbol.iterator) {
4740
return () => typesTarget[Symbol.iterator]();
4841
}

0 commit comments

Comments
 (0)