Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions apps/blog/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -312,5 +312,10 @@
"section1": "We’ll transfer your article to the CMS and schedule its publication.",
"section2": "We’ll also promote it on social media to maximize its reach."
}
},
"relatedArticles": {
"title": "Related Articles",
"previousSlide": "Previous slide",
"nextSlide": "Next slide"
}
}
5 changes: 5 additions & 0 deletions apps/blog/src/assets/i18n/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -315,5 +315,10 @@
"section1": "Teraz my zajmiemy się przeniesieniem twojego artykułu do CMS’a i zaplanujemy jego opublikowanie",
"section2": "Zajmiemy się również jego promowaniem w mediach społecznościowych, aby jak najwięcej osób mogło skorzystać z wiedzy, którą w nim zawarłeś!"
}
},
"relatedArticles": {
"title": "Powiązane artykuły",
"previousSlide": "Poprzedni slajd",
"nextSlide": "Następny slajd"
}
}
2 changes: 2 additions & 0 deletions apps/blog/src/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
@include cdk.a11y-visually-hidden();

@import '@angular/cdk/overlay-prebuilt.css';
@import 'ngx-owl-carousel-o/lib/styles/scss/owl.carousel';
@import 'ngx-owl-carousel-o/lib/styles/scss/owl.theme.default';
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,88 @@ import {
input,
OnInit,
} from '@angular/core';
import { TranslocoDirective } from '@jsverse/transloco';
import { CarouselModule, OwlOptions } from 'ngx-owl-carousel-o';

import { RelatedArticleListStore } from '@angular-love/blog/articles/data-access';
import { UiArticleCardComponent } from '@angular-love/blog/articles/ui-article-card';
import { ButtonComponent } from '@angular-love/blog/shared/ui-button';

@Component({
selector: 'al-related-articles',
template: `
@if (store.isFetchRelatedArticleListLoaded()) {
<strong class="text-3xl">Related Articles</strong>
<div class="mt-4 grid grid-cols-1 gap-6 p-2 md:grid-cols-2">
@for (article of store.relatedArticles(); track $index) {
<al-article-card [article]="article" cardType="compact" />
}
</div>
<ng-container *transloco="let t; read: 'relatedArticles'">
<h2 class="mb-4 text-3xl font-bold">{{ t('title') }}</h2>
<div class="relative lg:px-14">
<owl-carousel-o #carousel role="list" [options]="customOptions">
@for (article of store.relatedArticles(); track $index) {
<ng-template carouselSlide>
<!-- Prevents focus rings from being cut off -->
<li class="list-none py-1">
<al-article-card [article]="article" cardType="compact" />
</li>
</ng-template>
}
</owl-carousel-o>
<div
class="absolute left-0 top-1/2 hidden w-full -translate-y-1/2 justify-between lg:flex"
>
<button
al-button
type="button"
class="bg-al-pink text-white"
[attr.aria-label]="t('previousSlide')"
(click)="carousel.prev()"
size="small"
>
<span class="text-2xl">‹</span>
Copy link

Copilot AI Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using raw Unicode characters (‹ and ›) for navigation buttons may not be announced properly by screen readers. Consider using icon components with proper aria-hidden attributes or text alternatives instead.

Copilot uses AI. Check for mistakes.
</button>
<button
al-button
type="button"
class="bg-al-pink text-white"
[attr.aria-label]="t('nextSlide')"
(click)="carousel.next()"
size="small"
>
<span class="text-2xl">›</span>
</button>
</div>
</div>
</ng-container>
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [RelatedArticleListStore],
imports: [UiArticleCardComponent],
imports: [
UiArticleCardComponent,
CarouselModule,
ButtonComponent,
TranslocoDirective,
],
})
export class RelatedArticlesComponent implements OnInit {
readonly id = input.required<number>();
readonly customOptions: OwlOptions = {
loop: true,
mouseDrag: false,
touchDrag: true,
pullDrag: true,
dots: true,
margin: 24,
navSpeed: 700,
navText: ['', ''],
responsive: {
// Keep in mind these breakpoints refer to container width, not the viewport width
0: {
items: 1,
},
540: {
items: 2,
},
},
};

readonly store = inject(RelatedArticleListStore);

Expand Down
2 changes: 2 additions & 0 deletions libs/blog/shared/ui-avatar/src/lib/avatar.component.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<div
class="flex items-center justify-center overflow-hidden rounded-full border border-white bg-gray-200"
[style.width.px]="size()"
[style.height.px]="size()"
>
@if (imageSrc()) {
<img
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"highlight.js": "^11.9.0",
"hono": "^4.7.5",
"lint-staged": "^15.0.2",
"ngx-owl-carousel-o": "^20.0.1",
"ngx-skeleton-loader": "^8.1.0",
"panzoom": "^9.4.3",
"prettier-plugin-organize-attributes": "^1.0.0",
Expand Down
Loading