Skip to content

Commit d64f934

Browse files
committed
feat: introduce view transition directive
1 parent 1dd6046 commit d64f934

File tree

10 files changed

+102
-73
lines changed

10 files changed

+102
-73
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
export function patchViewTransitionAPI(_global: any, api: _ZonePrivate) {
3+
4+
}
5+
6+
Zone.__load_patch('viewTransition', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
7+
patchViewTransitionAPI(global, api);
8+
});

src/app/app.component.css

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ main {
2525
}
2626

2727
.header__logo:after {
28-
content: 'beta';
28+
content: "beta";
2929
background: #f57375;
3030
color: #fae9bc;
3131
padding: 3px 5px;
@@ -50,6 +50,8 @@ mat-toolbar {
5050
display: flex;
5151
justify-content: flex-start;
5252
z-index: 10;
53+
view-transition-name: mat-toolbar;
54+
contain: layout;
5355
}
5456

5557
mat-toolbar a {

src/app/shared/card/card.component.html

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
<mat-card class="mat-elevation-z4" *ngIf="package" [ngStyle]="{
2-
'view-transition-name': 'view-' + package.rev,
3-
'contain': 'layout'
4-
}">
1+
<mat-card
2+
class="mat-elevation-z4"
3+
*ngIf="package"
4+
[id]="id()"
5+
appViewTransition
6+
#viewTransitionRef
7+
>
58
<span
69
[ngClass]="{ 'is-schematic': package.computedMetadata['schematics'] }"
710
></span>

src/app/shared/card/card.component.spec.ts

Lines changed: 0 additions & 23 deletions
This file was deleted.

src/app/shared/card/card.component.ts

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
1-
import { DOCUMENT } from '@angular/common';
2-
import { Component, Inject, Input, Renderer2 } from '@angular/core';
1+
import { Component, Input, Renderer2, ViewChild, signal } from '@angular/core';
32
import { Router } from '@angular/router';
43
import { DeeplinkService } from 'src/app/shared/deeplink.service';
5-
import {
6-
CSSStyleDeclarationWithViewTransitionAPI,
7-
DocumentWithViewTransitionAPI,
8-
PackageType,
9-
} from 'src/typings';
4+
import { PackageType } from 'src/typings';
5+
import { ViewTransitionDirective } from '../view-transition.directive';
106

117
@Component({
128
selector: 'app-card',
@@ -18,13 +14,26 @@ export class CardComponent {
1814

1915
@Input() isFullMode = true;
2016

17+
@ViewChild(ViewTransitionDirective)
18+
viewTransitionRef!: ViewTransitionDirective;
19+
20+
id = signal('');
21+
2122
constructor(
2223
private renderer: Renderer2,
2324
private deeplink: DeeplinkService,
24-
private router: Router,
25-
@Inject(DOCUMENT) private document: Document
25+
26+
private router: Router
2627
) {}
2728

29+
ngOnChanges() {
30+
this.id.set('card-' + this.package!.rev + (this.isFullMode ? '-full' : ''));
31+
}
32+
33+
ngAfterContentInit() {
34+
debugger;
35+
}
36+
2837
onAvatarImageError(avatarImage: HTMLImageElement, avatarIcon: HTMLElement) {
2938
this.renderer.setStyle(avatarImage, 'display', 'none');
3039
this.renderer.setStyle(avatarIcon, 'display', 'block');
@@ -78,28 +87,17 @@ export class CardComponent {
7887
}
7988

8089
async navigateTo(pkg: PackageType, event: Event) {
81-
82-
this.#startViewTransition(
83-
() => {
84-
this.router.navigate([`pkg`, pkg.name], {
85-
state: {
86-
pkg,
87-
query: this.deeplink.getState(),
88-
},
89-
});
90-
}
91-
);
90+
await this.viewTransitionRef.startViewTransition(async () => {
91+
await this.onViewTransition(pkg);
92+
});
9293
}
9394

94-
#startViewTransition(onStart: () => void, onFinish: () => void = () => {}) {
95-
if (!(this.document as DocumentWithViewTransitionAPI).startViewTransition) {
96-
console.warn('View transition API is not available in this browser.');
97-
return onStart();
98-
}
99-
100-
const transition = (
101-
this.document as DocumentWithViewTransitionAPI
102-
).startViewTransition(onStart);
103-
transition.finished.finally(onFinish);
95+
async onViewTransition(pkg: PackageType) {
96+
return await this.router.navigate([`pkg`, pkg.name], {
97+
state: {
98+
pkg,
99+
query: this.deeplink.getState(),
100+
},
101+
});
104102
}
105103
}

src/app/shared/shared.module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { MatChipsModule } from '@angular/material/chips';
1010
import { RouterModule } from '@angular/router';
1111
import { MatButtonModule } from '@angular/material/button';
1212
import { InfiniteScrollDirective } from './infinite-scroll.directive';
13+
import { ViewTransitionDirective } from './view-transition.directive';
1314

1415
const MAT_MODULES = [
1516
MatCardModule,
@@ -21,7 +22,7 @@ const MAT_MODULES = [
2122

2223
@NgModule({
2324
imports: [CommonModule, RouterModule, ...MAT_MODULES],
24-
declarations: [NotFoundComponent, CardComponent, HumanDatePipe, InfiniteScrollDirective],
25+
declarations: [NotFoundComponent, CardComponent, HumanDatePipe, InfiniteScrollDirective, ViewTransitionDirective],
2526
exports: [NotFoundComponent, CardComponent, HumanDatePipe, InfiniteScrollDirective]
2627
})
2728
export class SharedModule {}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { DOCUMENT } from '@angular/common';
2+
import {
3+
Directive,
4+
ElementRef,
5+
Inject,
6+
NgZone,
7+
Renderer2,
8+
} from '@angular/core';
9+
import { DocumentWithViewTransitionAPI } from 'src/typings';
10+
11+
@Directive({
12+
selector: '[appViewTransition]',
13+
})
14+
export class ViewTransitionDirective {
15+
constructor(
16+
private zone: NgZone,
17+
@Inject(DOCUMENT) private document: Document,
18+
private element: ElementRef<HTMLElement>,
19+
private renderer: Renderer2
20+
) {}
21+
22+
ngOnInit() {
23+
this.renderer.setStyle(this.element.nativeElement, 'contain', 'layout');
24+
this.setElementTransitionName(this.element.nativeElement.id);
25+
}
26+
27+
setElementTransitionName(name: string) {
28+
this.renderer.setStyle(
29+
this.element.nativeElement,
30+
'viewTransitionName',
31+
name
32+
);
33+
}
34+
35+
async startViewTransition(callback: () => Promise<void>) {
36+
if (!(this.document as DocumentWithViewTransitionAPI).startViewTransition) {
37+
console.warn('View transition API is not available in this browser.');
38+
return await callback();
39+
}
40+
41+
this.setElementTransitionName(this.element.nativeElement.id + '-full');
42+
43+
(this.document as DocumentWithViewTransitionAPI).startViewTransition(() => {
44+
// TODO: patch zone.js to support view transition api callbacks
45+
this.zone.run(async () => await callback());
46+
this.setElementTransitionName(this.element.nativeElement.id);
47+
});
48+
}
49+
}

src/polyfills.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
* Zone JS is required by default for Angular itself.
4747
*/
4848
import 'zone.js'; // Included with Angular CLI.
49-
49+
import './__patch__/zone.js/lib/browser/browser.ts';
5050

5151
/***************************************************************************************************
5252
* APPLICATION IMPORTS

src/styles.scss

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,14 @@ body {
4545

4646
// View Transition API
4747

48-
mat-toolbar {
49-
view-transition-name: mat-toolbar;
50-
contain: layout;
51-
}
52-
5348
.package-details-readme {
5449
view-transition-name: package-details-readme;
5550
contain: layout;
5651
}
57-
::view-transition-old(package-details-readme) {
52+
html::view-transition-old(root) {
5853
animation: 300ms cubic-bezier(0.68, -0.55, 0.265, 1.55) both slide-up;
5954
}
60-
::view-transition-new(package-details-readme) {
55+
html::view-transition-new(root) {
6156
animation: 300ms cubic-bezier(0.68, -0.55, 0.265, 1.55) both slide-down;
6257
}
6358

@@ -66,11 +61,6 @@ mat-toolbar {
6661
contain: layout;
6762
}
6863

69-
::view-transition-old(root),
70-
::view-transition-new(root) {
71-
animation-duration: 300ms;
72-
}
73-
7464
@keyframes slide-down {
7565
from {
7666
transform: translateY(60px);

tsconfig.app.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
},
88
"files": [
99
"src/main.ts",
10-
"src/polyfills.ts"
10+
"src/polyfills.ts",
11+
"src/__patch__/zone.js/lib/browser/browser.ts"
1112
],
1213
"include": [
1314
"src/**/*.d.ts"

0 commit comments

Comments
 (0)