diff --git a/apps/angular/view-transition/.eslintrc.json b/apps/angular/view-transition/.eslintrc.json index 8ebcbfd..3a73702 100644 --- a/apps/angular/view-transition/.eslintrc.json +++ b/apps/angular/view-transition/.eslintrc.json @@ -7,25 +7,7 @@ "extends": [ "plugin:@nx/angular", "plugin:@angular-eslint/template/process-inline-templates" - ], - "rules": { - "@angular-eslint/directive-selector": [ - "error", - { - "type": "attribute", - "prefix": "app", - "style": "camelCase" - } - ], - "@angular-eslint/component-selector": [ - "error", - { - "type": "element", - "prefix": "app", - "style": "kebab-case" - } - ] - } + ] }, { "files": ["*.html"], diff --git a/apps/angular/view-transition/project.json b/apps/angular/view-transition/project.json index d0a27fd..338da85 100644 --- a/apps/angular/view-transition/project.json +++ b/apps/angular/view-transition/project.json @@ -15,12 +15,12 @@ "browser": "apps/angular/view-transition/src/main.ts", "polyfills": ["zone.js"], "tsConfig": "apps/angular/view-transition/tsconfig.app.json", - "inlineStyleLanguage": "scss", + "inlineStyleLanguage": "css", "assets": [ "apps/angular/view-transition/src/favicon.ico", "apps/angular/view-transition/src/assets" ], - "styles": ["apps/angular/view-transition/src/styles.scss"], + "styles": ["apps/angular/view-transition/src/styles.css"], "scripts": [] }, "configurations": { diff --git a/apps/angular/view-transition/src/app/app.component.ts b/apps/angular/view-transition/src/app/app.component.ts index 1e49189..da56c04 100644 --- a/apps/angular/view-transition/src/app/app.component.ts +++ b/apps/angular/view-transition/src/app/app.component.ts @@ -1,28 +1,13 @@ -import { Component } from '@angular/core'; -import { RouterLink, RouterOutlet } from '@angular/router'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; @Component({ standalone: true, - imports: [RouterLink, RouterOutlet], + imports: [RouterOutlet], selector: 'app-root', template: ` -
- - -
`, - host: { - class: 'flex flex-col gap-10 border p-10 h-screen', - }, - styles: [''], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class AppComponent {} diff --git a/apps/angular/view-transition/src/app/app.config.ts b/apps/angular/view-transition/src/app/app.config.ts index 790c11a..4c128f0 100644 --- a/apps/angular/view-transition/src/app/app.config.ts +++ b/apps/angular/view-transition/src/app/app.config.ts @@ -1,14 +1,17 @@ import { ApplicationConfig } from '@angular/core'; -import { provideRouter, withViewTransitions } from '@angular/router'; +import { provideRouter, withComponentInputBinding } from '@angular/router'; export const appConfig: ApplicationConfig = { providers: [ provideRouter( [ - { path: 'bar', loadComponent: () => import('./bar.component') }, - { path: 'foo', loadComponent: () => import('./foo.component') }, + { path: '', loadComponent: () => import('./blog/blog.component') }, + { + path: 'post/:id', + loadComponent: () => import('./post/post.component'), + }, ], - withViewTransitions(), + withComponentInputBinding(), ), ], }; diff --git a/apps/angular/view-transition/src/app/bar.component.ts b/apps/angular/view-transition/src/app/bar.component.ts deleted file mode 100644 index 011f14a..0000000 --- a/apps/angular/view-transition/src/app/bar.component.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Component } from '@angular/core'; -@Component({ - selector: 'app-bar', - standalone: true, - imports: [], - template: ` - bar-component - `, - host: { - class: 'block h-full bg-green-500', - }, -}) -export default class BarComponent {} diff --git a/apps/angular/view-transition/src/app/blog/blog.component.ts b/apps/angular/view-transition/src/app/blog/blog.component.ts new file mode 100644 index 0000000..6826644 --- /dev/null +++ b/apps/angular/view-transition/src/app/blog/blog.component.ts @@ -0,0 +1,24 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { posts } from '../data'; +import { ThumbnailComponent } from './thumbnail.component'; + +@Component({ + selector: 'blog', + standalone: true, + imports: [ThumbnailComponent], + template: ` +
+ Blog List +
+
+ @for (post of posts; track post.id) { + + } +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export default class BlogComponent { + posts = posts; +} diff --git a/apps/angular/view-transition/src/app/blog/thumbnail-header.component.ts b/apps/angular/view-transition/src/app/blog/thumbnail-header.component.ts new file mode 100644 index 0000000..0ee21dc --- /dev/null +++ b/apps/angular/view-transition/src/app/blog/thumbnail-header.component.ts @@ -0,0 +1,29 @@ +import { NgOptimizedImage } from '@angular/common'; +import { Component, input } from '@angular/core'; + +@Component({ + selector: 'thumbnail-header', + standalone: true, + imports: [NgOptimizedImage], + template: ` +
+ +
+ Thomas Laforge + {{ date() }} +
+
+ + `, + host: { + class: 'flex w-full px-4 py-5 gap-4 justify-between', + }, +}) +export class ThumbnailHeaderComponent { + date = input.required(); +} diff --git a/apps/angular/view-transition/src/app/blog/thumbnail.component.ts b/apps/angular/view-transition/src/app/blog/thumbnail.component.ts new file mode 100644 index 0000000..6263e97 --- /dev/null +++ b/apps/angular/view-transition/src/app/blog/thumbnail.component.ts @@ -0,0 +1,32 @@ +import { NgOptimizedImage } from '@angular/common'; +import { ChangeDetectionStrategy, Component, input } from '@angular/core'; +import { RouterLinkWithHref } from '@angular/router'; +import { Post } from '../post.model'; +import { ThumbnailHeaderComponent } from './thumbnail-header.component'; + +@Component({ + selector: 'blog-thumbnail', + standalone: true, + imports: [NgOptimizedImage, ThumbnailHeaderComponent, RouterLinkWithHref], + template: ` + + +

{{ post().title }}

+

{{ post().description }}

+ +
+ `, + host: { + class: 'w-full max-w-[600px] rounded-3xl border-none shadow-lg', + }, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ThumbnailComponent { + post = input.required(); +} diff --git a/apps/angular/view-transition/src/app/data.ts b/apps/angular/view-transition/src/app/data.ts new file mode 100644 index 0000000..52ed38e --- /dev/null +++ b/apps/angular/view-transition/src/app/data.ts @@ -0,0 +1,38 @@ +import { Post } from './post.model'; + +export const posts: Post[] = [ + { + id: '1', + title: 'Future of Change Detection in Angular with Signals', + description: + 'Learn how change detection is evolving and how signal will improve performance in the future.', + image: '/assets/signal-cd.full.webp', + date: 'May 23, 2023', + readingTime: 5, + }, + { + id: '2', + title: 'Everything you need to know about route Guard in Angular', + description: + 'Routing is a significant aspect of any SPA application, and protecting these routes is often necessary. We’ll learn all build-in guards', + image: '/assets/guard.full.webp', + date: 'Jan 18, 2023', + readingTime: 5, + }, + { + id: '3', + title: 'Create a highly customizable component', + description: 'Learn how to create highly customizable component', + image: '/assets/highly-custom.full.webp', + date: 'Nov 9, 2022', + readingTime: 5, + }, +]; + +export const fakeTextChapters = [ + 'Cyprum itidem insulam procul a continenti discretam et portuosam inter municipia crebra urbes duae faciunt claram Salamis et Paphus, altera Iovis delubris altera Veneris templo insignis. tanta autem tamque multiplici fertilitate abundat rerum omnium eadem Cyprus ut nullius externi indigens adminiculi indigenis viribus a fundamento ipso carinae ad supremos usque carbasos aedificet onerariam navem omnibusque armamentis instructam mari committat', + 'Et quoniam mirari posse quosdam peregrinos existimo haec lecturos forsitan, si contigerit, quamobrem cum oratio ad ea monstranda deflexerit quae Romae gererentur, nihil praeter seditiones narratur et tabernas et vilitates harum similis alias, summatim causas perstringam nusquam a veritate sponte propria digressurus', + 'Utque aegrum corpus quassari etiam levibus solet offensis, ita animus eius angustus et tener, quicquid increpuisset, ad salutis suae dispendium existimans factum aut cogitatum, insontium caedibus fecit victoriam luctuosam', + 'Novo denique perniciosoque exemplo idem Gallus ausus est inire flagitium grave, quod Romae cum ultimo dedecore temptasse aliquando dicitur Gallienus, et adhibitis paucis clam ferro succinctis vesperi per tabernas palabatur et conpita quaeritando Graeco sermone, cuius erat inpendio gnarus, quid de Caesare quisque sentiret. et haec confidenter agebat in urbe ubi pernoctantium luminum claritudo dierum solet imitari fulgorem. postremo agnitus saepe iamque, si prodisset, conspicuum se fore contemplans, non nisi luce palam egrediens ad agenda quae putabat seria cernebatur. et haec quidem medullitus multis gementibus agebantur.', + 'Et quoniam mirari posse quosdam peregrinos existimo haec lecturos forsitan, si contigerit, quamobrem cum oratio ad ea monstranda deflexerit quae Romae gererentur, nihil praeter seditiones narratur et tabernas et vilitates harum similis alias, summatim causas perstringam nusquam a veritate sponte propria digressurus.', +]; diff --git a/apps/angular/view-transition/src/app/foo.component.ts b/apps/angular/view-transition/src/app/foo.component.ts deleted file mode 100644 index 4f19ff3..0000000 --- a/apps/angular/view-transition/src/app/foo.component.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-foo', - standalone: true, - imports: [], - template: ` -
app-foo
- `, - host: { - class: 'block h-full bg-red-500', - }, - styles: ` - .count { - view-transition-name: count; - width: fit-content - } - `, -}) -export default class FooComponent {} diff --git a/apps/angular/view-transition/src/app/post.model.ts b/apps/angular/view-transition/src/app/post.model.ts new file mode 100644 index 0000000..232e9ef --- /dev/null +++ b/apps/angular/view-transition/src/app/post.model.ts @@ -0,0 +1,8 @@ +export interface Post { + id: string; + title: string; + description: string; + image: string; + date: string; + readingTime: number; +} diff --git a/apps/angular/view-transition/src/app/post/post-header.component.ts b/apps/angular/view-transition/src/app/post/post-header.component.ts new file mode 100644 index 0000000..db2f42d --- /dev/null +++ b/apps/angular/view-transition/src/app/post/post-header.component.ts @@ -0,0 +1,34 @@ +import { NgOptimizedImage } from '@angular/common'; +import { Component, input } from '@angular/core'; + +@Component({ + selector: 'post-header', + standalone: true, + imports: [NgOptimizedImage], + template: ` + +
+ + +
+ Thomas Laforge + {{ date() }} + + `, + host: { + class: 'flex flex-col justify-center items-center', + }, +}) +export class PostHeaderComponent { + date = input.required(); +} diff --git a/apps/angular/view-transition/src/app/post/post.component.ts b/apps/angular/view-transition/src/app/post/post.component.ts new file mode 100644 index 0000000..09bfed8 --- /dev/null +++ b/apps/angular/view-transition/src/app/post/post.component.ts @@ -0,0 +1,36 @@ +import { NgOptimizedImage } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + computed, + input, +} from '@angular/core'; +import { ThumbnailHeaderComponent } from '../blog/thumbnail-header.component'; +import { fakeTextChapters, posts } from '../data'; +import { PostHeaderComponent } from './post-header.component'; + +@Component({ + selector: 'post', + standalone: true, + imports: [ThumbnailHeaderComponent, NgOptimizedImage, PostHeaderComponent], + template: ` +
+ +

{{ post().title }}

+ + @for (chapter of fakeTextChapter; track $index) { +

{{ chapter }}

+ } +
+ `, + host: { + class: 'flex h-full justify-center', + }, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export default class PostComponent { + id = input.required(); + post = computed(() => posts.filter((p) => p.id === this.id())[0]); + + fakeTextChapter = fakeTextChapters; +} diff --git a/apps/angular/view-transition/src/assets/angular.webp b/apps/angular/view-transition/src/assets/angular.webp new file mode 100644 index 0000000..b204928 Binary files /dev/null and b/apps/angular/view-transition/src/assets/angular.webp differ diff --git a/apps/angular/view-transition/src/assets/guard.full.webp b/apps/angular/view-transition/src/assets/guard.full.webp new file mode 100644 index 0000000..2f0b4c3 Binary files /dev/null and b/apps/angular/view-transition/src/assets/guard.full.webp differ diff --git a/apps/angular/view-transition/src/assets/highly-custom.full.webp b/apps/angular/view-transition/src/assets/highly-custom.full.webp new file mode 100644 index 0000000..74c2df8 Binary files /dev/null and b/apps/angular/view-transition/src/assets/highly-custom.full.webp differ diff --git a/apps/angular/view-transition/src/assets/profil.webp b/apps/angular/view-transition/src/assets/profil.webp new file mode 100644 index 0000000..52adde8 Binary files /dev/null and b/apps/angular/view-transition/src/assets/profil.webp differ diff --git a/apps/angular/view-transition/src/assets/signal-cd.full.webp b/apps/angular/view-transition/src/assets/signal-cd.full.webp new file mode 100644 index 0000000..e483bbf Binary files /dev/null and b/apps/angular/view-transition/src/assets/signal-cd.full.webp differ diff --git a/apps/angular/view-transition/src/styles.scss b/apps/angular/view-transition/src/styles.css similarity index 70% rename from apps/angular/view-transition/src/styles.scss rename to apps/angular/view-transition/src/styles.css index a0b4da4..8e4d322 100644 --- a/apps/angular/view-transition/src/styles.scss +++ b/apps/angular/view-transition/src/styles.css @@ -2,6 +2,7 @@ @tailwind components; @tailwind utilities; + @keyframes fade-in { from { opacity: 0; @@ -27,15 +28,13 @@ } ::view-transition-old(root) { - animation: - 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out, - 300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left; + animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out, + 300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left; } ::view-transition-new(root) { - animation: - 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in, - 300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right; + animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in, + 300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right; } @keyframes rotate { @@ -46,9 +45,11 @@ transform: rotate(360deg); } } + ::view-transition-new(count) { animation: rotate 2s linear; } + ::view-transition-old(count) { animation: rotate 0.5s linear; }