Skip to content

Commit 5b1667c

Browse files
authored
feat(components, pages): extend skeleton cards (#270)
* feat(about): add skeleton card loader to profile picture * refactor(photo-album): improve skeleton card mobile positioning * refactor(photo-album): remove dead code * feat(talks): add skeleton cards to talks images * feat(pages): add skeleton card to profile picture on homepage * style(pages): organize rules * fix(pages): fix positioning for safari * refactor(talks): remove dead code
1 parent 3e2278b commit 5b1667c

File tree

4 files changed

+129
-56
lines changed

4 files changed

+129
-56
lines changed

src/app/components/photo-album/photo-album.component.ts

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,10 @@
11
import { NgIf, NgOptimizedImage, NgStyle } from '@angular/common';
2-
import {
3-
Component,
4-
DestroyRef,
5-
inject,
6-
Input,
7-
OnInit,
8-
signal,
9-
WritableSignal,
10-
} from '@angular/core';
11-
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
12-
13-
import { Observable, debounceTime } from 'rxjs';
2+
import { Component, Input, signal, WritableSignal } from '@angular/core';
143

154
import { SkeletonCardComponent } from '@components/skeleton-card/skeleton-card.component';
165
import { flickr } from '@constants/flickr';
176
import { ReplaceBrokenImageDirective } from '@directives/replace-broken-image.directive';
187
import { PhotosetListItem } from '@models/flickr';
19-
import { ScreenSizeService } from '@services/screen-size.service';
208

219
@Component({
2210
selector: 'app-photo-album',
@@ -35,7 +23,7 @@ import { ScreenSizeService } from '@services/screen-size.service';
3523
class="rounded-md absolute min-w-full h-full"
3624
height="100%"
3725
maxWidth="100%"
38-
[width]="isSmallScreen ? '' : '736px'"
26+
width=""
3927
/>
4028
<a
4129
[href]="flickr.albumUrl + '/' + photo.id"
@@ -44,7 +32,7 @@ import { ScreenSizeService } from '@services/screen-size.service';
4432
rel="noopener"
4533
>
4634
<img
47-
[ngSrc]="
35+
[src]="
4836
flickr.albumPhotoUrl +
4937
'/' +
5038
photo.server +
@@ -59,8 +47,6 @@ import { ScreenSizeService } from '@services/screen-size.service';
5947
alt=""
6048
appReplaceBrokenImage
6149
class="w-full h-full rounded-md object-cover"
62-
height="491"
63-
width="736"
6450
/>
6551
<div
6652
class="absolute top-0 left-0 right-0 bottom-0 flex flex-col justify-end p-4"
@@ -79,27 +65,12 @@ import { ScreenSizeService } from '@services/screen-size.service';
7965
</div>
8066
`,
8167
})
82-
export class PhotoAlbumComponent implements OnInit {
68+
export class PhotoAlbumComponent {
8369
@Input() public photo!: PhotosetListItem;
8470

8571
public flickr = flickr;
86-
public isSmallScreen: boolean = false;
87-
public screenWidth$!: Observable<number>;
8872
public showSkeleton: WritableSignal<boolean> = signal(true);
8973

90-
private destroyRef = inject(DestroyRef);
91-
private screenSizeService = inject(ScreenSizeService);
92-
93-
public ngOnInit(): void {
94-
const smallScreenSize = 768;
95-
this.screenWidth$ = this.screenSizeService.screenWidth;
96-
this.screenWidth$
97-
.pipe(debounceTime(300), takeUntilDestroyed(this.destroyRef))
98-
.subscribe((width) => {
99-
this.isSmallScreen = width < smallScreenSize;
100-
});
101-
}
102-
10374
public onLoad(): void {
10475
this.showSkeleton.set(false);
10576
}

src/app/pages/about/index.page.ts

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { AsyncPipe, NgIf, NgOptimizedImage } from '@angular/common';
2-
import { Component, inject } from '@angular/core';
2+
import { Component, inject, signal, WritableSignal } from '@angular/core';
33
import { MetaDefinition } from '@angular/platform-browser';
44

55
import { MarkdownComponent, injectContent } from '@analogjs/content';
66
import { RouteMeta } from '@analogjs/router';
77

8+
import { SkeletonCardComponent } from '@components/skeleton-card/skeleton-card.component';
89
import { siteName } from '@constants/site-name';
910
import { url } from '@constants/site-url';
1011
import { MetadataService } from '@services/metadata.service';
@@ -38,20 +39,39 @@ export const metaTagList: MetaDefinition[] = [
3839
@Component({
3940
selector: 'app-about-index',
4041
standalone: true,
41-
imports: [AsyncPipe, NgIf, NgOptimizedImage, MarkdownComponent],
42+
imports: [
43+
AsyncPipe,
44+
NgIf,
45+
NgOptimizedImage,
46+
MarkdownComponent,
47+
SkeletonCardComponent,
48+
],
4249
template: `
4350
<h1 class="sr-only">About</h1>
4451
<div class="md:max-w md:mx-auto md:flex md:justify-center">
4552
<div class="md:w-[48rem] p-4">
4653
<div class="flex-1">
47-
<img
48-
ngSrc="${url}/images/self/me.jpg"
49-
class="rounded max-h-[32rem] mx-auto"
50-
alt="Me in Norway"
51-
priority
52-
height="816"
53-
width="384"
54-
/>
54+
<div
55+
class="relative mx-auto [@media(min-width:430px)]:max-h-[32rem] [@media(min-width:430px)]:w-96"
56+
>
57+
<app-skeleton-card
58+
*ngIf="showSkeleton()"
59+
class="rounded-md absolute min-w-full h-full"
60+
height="100%"
61+
maxWidth="100%"
62+
width="100%"
63+
/>
64+
<img
65+
[ngStyle]="{
66+
visibility: showSkeleton() ? 'hidden' : 'visible'
67+
}"
68+
(load)="onLoad()"
69+
src="${url}/images/self/me.jpg"
70+
class="rounded-md"
71+
alt="Me in Norway"
72+
priority
73+
/>
74+
</div>
5575
<div *ngIf="about$ | async as about">
5676
<analog-markdown
5777
class="prose dark:prose-invert prose-code:before:hidden prose-code:after:hidden"
@@ -73,8 +93,11 @@ export const metaTagList: MetaDefinition[] = [
7393
`,
7494
})
7595
export default class IndexPageComponent {
96+
public showSkeleton: WritableSignal<boolean> = signal(true);
7697
public version = version;
98+
7799
private metadataService = inject(MetadataService);
100+
78101
readonly about$ = injectContent<{ content: string }>({
79102
customFilename: 'about/about',
80103
});
@@ -83,4 +106,8 @@ export default class IndexPageComponent {
83106
this.metadataService.setPageURLMetaTitle(pageTitle.title);
84107
this.metadataService.updateTags(metaTagList);
85108
}
109+
110+
public onLoad(): void {
111+
this.showSkeleton.set(false);
112+
}
86113
}

src/app/pages/index.page.ts

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1-
import { NgFor, NgIf } from '@angular/common';
2-
import { Component, OnInit, inject } from '@angular/core';
1+
import { NgFor, NgIf, NgStyle } from '@angular/common';
2+
import {
3+
Component,
4+
OnInit,
5+
WritableSignal,
6+
inject,
7+
signal,
8+
} from '@angular/core';
39
import { MetaDefinition } from '@angular/platform-browser';
410
import { RouterLink } from '@angular/router';
511

@@ -8,6 +14,7 @@ import { RouteMeta } from '@analogjs/router';
814

915
import { BlogCardComponent } from '@components/blog-card/blog-card.component';
1016
import { RecentPhotoAlbumsComponent } from '@components/recent-photo-albums/recent-photo-albums.component';
17+
import { SkeletonCardComponent } from '@components/skeleton-card/skeleton-card.component';
1118
import { siteName } from '@constants/site-name';
1219
import { BlogPost } from '@models/post';
1320
import { MetadataService } from '@services/metadata.service';
@@ -50,8 +57,10 @@ export const metaTagList: MetaDefinition[] = [
5057
BlogCardComponent,
5158
NgIf,
5259
NgFor,
60+
NgStyle,
5361
RecentPhotoAlbumsComponent,
5462
RouterLink,
63+
SkeletonCardComponent,
5564
],
5665
template: `
5766
<div class="md:max-w md:mx-auto md:flex md:justify-center">
@@ -61,11 +70,26 @@ export const metaTagList: MetaDefinition[] = [
6170
<div
6271
class="flex flex-col sm:flex-row justify-evenly items-center pb-4 sm:flex-nowrap"
6372
>
64-
<img
65-
src="images/self/me-sq.jpg"
66-
class="rounded-xl max-h-32 sm:max-h-36"
67-
alt="Me in Norway"
68-
/>
73+
<div
74+
class="relative flex justify-center rounded-xl max-h-32 max-w-32 !w-fit"
75+
>
76+
<app-skeleton-card
77+
*ngIf="showSkeleton()"
78+
class="rounded-md absolute min-w-full h-full"
79+
height="100%"
80+
maxWidth="100%"
81+
width="100%"
82+
/>
83+
<img
84+
[ngStyle]="{
85+
visibility: showSkeleton() ? 'hidden' : 'visible'
86+
}"
87+
(load)="onLoad()"
88+
src="images/self/me-sq.jpg"
89+
class="rounded-xl"
90+
alt="Me in Norway"
91+
/>
92+
</div>
6993
<div class="pt-4 sm:pl-4 sm:pt-0">
7094
My name is Elanna Grossman. I am a full-stack developer, primarily
7195
focused on Angular and .NET. I most recently worked at
@@ -106,11 +130,16 @@ export default class HomeComponent implements OnInit {
106130
.filter((post) => post.attributes.published)
107131
.sort(sortByUpdatedOrOriginalDate)
108132
.slice(0, 3);
133+
public showSkeleton: WritableSignal<boolean> = signal(true);
109134

110135
private metadataService = inject(MetadataService);
111136

112137
ngOnInit(): void {
113138
this.metadataService.setPageURLMetaTitle(pageTitle.title);
114139
this.metadataService.updateTags(metaTagList);
115140
}
141+
142+
public onLoad(): void {
143+
this.showSkeleton.set(false);
144+
}
116145
}

src/app/pages/talks/index.page.ts

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
1-
import { JsonPipe, NgFor, NgIf } from '@angular/common';
2-
import { Component, inject } from '@angular/core';
1+
import { NgFor, NgIf, NgStyle } from '@angular/common';
2+
import {
3+
Component,
4+
DestroyRef,
5+
inject,
6+
OnInit,
7+
signal,
8+
WritableSignal,
9+
} from '@angular/core';
10+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
311
import { MetaDefinition } from '@angular/platform-browser';
412

513
import { RouteMeta } from '@analogjs/router';
14+
import { debounceTime, Observable } from 'rxjs';
615

16+
import { SkeletonCardComponent } from '@components/skeleton-card/skeleton-card.component';
17+
import { smallBreakpointSize } from '@constants/breakpoint-size';
718
import { siteName } from '@constants/site-name';
8-
import { MetadataService } from '@services/metadata.service';
919
import { talks } from '@constants/talks';
20+
import { MetadataService } from '@services/metadata.service';
21+
import { ScreenSizeService } from '@services/screen-size.service';
1022

1123
export const pageTitle = {
1224
title: `Talks | ${siteName}`,
@@ -36,7 +48,7 @@ export const metaTagList: MetaDefinition[] = [
3648
@Component({
3749
selector: 'app-talks-index',
3850
standalone: true,
39-
imports: [JsonPipe, NgFor, NgIf],
51+
imports: [NgFor, NgIf, NgStyle, SkeletonCardComponent],
4052
template: `
4153
<div class="md:max-w md:mx-auto md:flex md:flex-col md:items-center">
4254
<div class="md:w-[48rem] p-4">
@@ -65,12 +77,23 @@ export const metaTagList: MetaDefinition[] = [
6577
</div>
6678
<div
6779
*ngIf="talk?.imageLink"
68-
class="sm:w-80 sm:min-w-[20rem] sm:h-52"
80+
class="relative sm:w-80 sm:min-w-[20rem] sm:h-52"
6981
>
82+
<app-skeleton-card
83+
*ngIf="showSkeleton()"
84+
class="rounded-md absolute min-w-full h-full"
85+
height="100%"
86+
maxWidth="100%"
87+
[width]="isSmallScreen ? '' : '320px'"
88+
/>
7089
<ng-container *ngIf="i === 0; else nonPriority">
7190
<img
7291
[src]="talk.imageLink"
7392
[alt]="talk.title || 'Talk Cover Image'"
93+
[ngStyle]="{
94+
visibility: showSkeleton() ? 'hidden' : 'visible'
95+
}"
96+
(load)="onLoad()"
7497
appReplaceBrokenImage
7598
class="sm:max-w-xs rounded-md sm:w-full sm:h-full sm:object-cover sm:object-center"
7699
priority
@@ -80,6 +103,10 @@ export const metaTagList: MetaDefinition[] = [
80103
<img
81104
[src]="talk.imageLink"
82105
[alt]="talk.title || 'Talk Cover Image'"
106+
[ngStyle]="{
107+
visibility: showSkeleton() ? 'hidden' : 'visible'
108+
}"
109+
(load)="onLoad()"
83110
appReplaceBrokenImage
84111
class="sm:max-w-xs rounded-md sm:w-full sm:h-full sm:object-cover sm:object-center"
85112
loading="lazy"
@@ -94,13 +121,32 @@ export const metaTagList: MetaDefinition[] = [
94121
</div>
95122
`,
96123
})
97-
export default class IndexPageComponent {
124+
export default class IndexPageComponent implements OnInit {
125+
public isSmallScreen: boolean = false;
126+
public screenWidth$!: Observable<number>;
127+
public showSkeleton: WritableSignal<boolean> = signal(true);
128+
129+
private destroyRef = inject(DestroyRef);
98130
private metadataService = inject(MetadataService);
131+
private screenSizeService = inject(ScreenSizeService);
99132

100133
public talks = talks;
101134

102135
constructor() {
103136
this.metadataService.setPageURLMetaTitle(pageTitle.title);
104137
this.metadataService.updateTags(metaTagList);
105138
}
139+
140+
public ngOnInit(): void {
141+
this.screenWidth$ = this.screenSizeService.screenWidth;
142+
this.screenWidth$
143+
.pipe(debounceTime(300), takeUntilDestroyed(this.destroyRef))
144+
.subscribe((width) => {
145+
this.isSmallScreen = width < smallBreakpointSize;
146+
});
147+
}
148+
149+
public onLoad(): void {
150+
this.showSkeleton.set(false);
151+
}
106152
}

0 commit comments

Comments
 (0)