From a25d94b3d5a34c78944cdd5a0f6bd9ceae386c71 Mon Sep 17 00:00:00 2001 From: Damian Maduzia Date: Fri, 24 Oct 2025 13:19:46 +0200 Subject: [PATCH 1/3] feat: featured articles carousel poc --- apps/blog/src/styles.scss | 2 + .../src/lib/related-articles.component.ts | 34 ++++++++- .../ui-avatar/src/lib/avatar.component.html | 2 + .../ui-avatar/src/lib/avatar.component.ts | 3 + package.json | 1 + pnpm-lock.yaml | 75 ++++++++++++++----- 6 files changed, 95 insertions(+), 22 deletions(-) diff --git a/apps/blog/src/styles.scss b/apps/blog/src/styles.scss index 26b764ed3..697c9cad1 100644 --- a/apps/blog/src/styles.scss +++ b/apps/blog/src/styles.scss @@ -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'; diff --git a/libs/blog/articles/feature-related-articles/src/lib/related-articles.component.ts b/libs/blog/articles/feature-related-articles/src/lib/related-articles.component.ts index a3bf36f0a..51e00d8d8 100644 --- a/libs/blog/articles/feature-related-articles/src/lib/related-articles.component.ts +++ b/libs/blog/articles/feature-related-articles/src/lib/related-articles.component.ts @@ -5,6 +5,7 @@ import { input, OnInit, } from '@angular/core'; +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'; @@ -14,19 +15,44 @@ import { UiArticleCardComponent } from '@angular-love/blog/articles/ui-article-c template: ` @if (store.isFetchRelatedArticleListLoaded()) { Related Articles -
+ @for (article of store.relatedArticles(); track $index) { - + + + + + + } -
+ } `, changeDetection: ChangeDetectionStrategy.OnPush, providers: [RelatedArticleListStore], - imports: [UiArticleCardComponent], + imports: [UiArticleCardComponent, CarouselModule], }) export class RelatedArticlesComponent implements OnInit { readonly id = input.required(); + customOptions: OwlOptions = { + loop: true, + mouseDrag: false, + touchDrag: true, + pullDrag: false, + dots: true, + margin: 24, // Add spacing between items + navSpeed: 700, + navText: ['', ''], + responsive: { + // Keep in mind these breakpoints refer to container width, not the viewport width + 0: { + items: 1, + }, + 640: { + items: 2, + }, + }, + nav: true, + }; readonly store = inject(RelatedArticleListStore); diff --git a/libs/blog/shared/ui-avatar/src/lib/avatar.component.html b/libs/blog/shared/ui-avatar/src/lib/avatar.component.html index 838a7a121..a9febea61 100644 --- a/libs/blog/shared/ui-avatar/src/lib/avatar.component.html +++ b/libs/blog/shared/ui-avatar/src/lib/avatar.component.html @@ -1,5 +1,7 @@
@if (imageSrc()) { (); diff --git a/package.json b/package.json index ab15c0f07..6049b9e05 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 552d85adb..b47efb1b5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -73,7 +73,7 @@ importers: version: 17.2.0(@angular/core@19.2.5(rxjs@7.8.2)(zone.js@0.14.4))(rxjs@7.8.2) '@nx/angular': specifier: 20.7.1 - version: 20.7.1(693afb23b549613d019c9e7c1d8e1423) + version: 20.7.1(06d083ec7b8710b15f0ae77fdd0247b4) '@push-based/ngx-fast-svg': specifier: ^18.1.0 version: 18.1.0(@angular/common@19.2.5(@angular/core@19.2.5(rxjs@7.8.2)(zone.js@0.14.4))(rxjs@7.8.2))(@angular/core@19.2.5(rxjs@7.8.2)(zone.js@0.14.4))(rxjs@7.8.2) @@ -113,6 +113,9 @@ importers: lint-staged: specifier: ^15.0.2 version: 15.5.2 + ngx-owl-carousel-o: + specifier: ^20.0.1 + version: 20.0.1(@angular/common@19.2.5(@angular/core@19.2.5(rxjs@7.8.2)(zone.js@0.14.4))(rxjs@7.8.2))(@angular/core@19.2.5(rxjs@7.8.2)(zone.js@0.14.4))(rxjs@7.8.2) ngx-skeleton-loader: specifier: ^8.1.0 version: 8.1.0(@angular/common@19.2.5(@angular/core@19.2.5(rxjs@7.8.2)(zone.js@0.14.4))(rxjs@7.8.2))(@angular/core@19.2.5(rxjs@7.8.2)(zone.js@0.14.4)) @@ -158,7 +161,7 @@ importers: devDependencies: '@angular-devkit/build-angular': specifier: 19.2.6 - version: 19.2.6(76df311846185dc42c879194ee06e89f) + version: 19.2.6(5656755dcc4e0f6f7c6c4e844249b617) '@angular-devkit/core': specifier: 19.2.6 version: 19.2.6(chokidar@4.0.3) @@ -242,7 +245,7 @@ importers: version: 8.4.7(@types/react@18.3.21)(storybook@8.6.12(prettier@3.5.3)) '@storybook/angular': specifier: 8.4.7 - version: 8.4.7(5d83d14307f3397e6404bf89d2737cdd) + version: 8.4.7(aacb154f385fdf6941de0697b6a13681) '@storybook/core-server': specifier: 8.4.7 version: 8.4.7(storybook@8.6.12(prettier@3.5.3)) @@ -7547,6 +7550,13 @@ packages: ngraph.events@1.2.2: resolution: {integrity: sha512-JsUbEOzANskax+WSYiAPETemLWYXmixuPAlmZmhIbIj6FH/WDgEGCGnRwUQBK0GjOnVm8Ui+e5IJ+5VZ4e32eQ==} + ngx-owl-carousel-o@20.0.1: + resolution: {integrity: sha512-4mDWyN2gAWPcbFkvVc+Tw4Ha11ltDWnKGKfS4VTrebzBqucY2VuPugEmcKETpv1ytkyu6Qsg8Z6il/0L0l9aBA==} + peerDependencies: + '@angular/common': ' ^20.0.0-rc.0 || ^20.0.0' + '@angular/core': ^20.0.0-rc.0 || ^20.0.0 + rxjs: ^6.0.1 || ^7.0.0 + ngx-skeleton-loader@8.1.0: resolution: {integrity: sha512-Ap/QSjadv/Kl0Vj7BiIWLG0JZZlo0eCJhdIVJ3Ryz7R0c4BR2Eq3O01du3ftf0SDbwt5vt7342NyqhLNrQ08RA==} peerDependencies: @@ -10247,7 +10257,7 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular-devkit/build-angular@19.2.6(76df311846185dc42c879194ee06e89f)': + '@angular-devkit/build-angular@19.2.6(5656755dcc4e0f6f7c6c4e844249b617)': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.1902.6(chokidar@4.0.3) @@ -10266,7 +10276,7 @@ snapshots: '@babel/runtime': 7.26.10 '@discoveryjs/json-ext': 0.6.3 '@ngtools/webpack': 19.2.6(@angular/compiler-cli@19.2.5(@angular/compiler@19.2.5)(typescript@5.7.3))(typescript@5.7.3)(webpack@5.98.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.25.1)) - '@vitejs/plugin-basic-ssl': 1.2.0(vite@6.2.4(@types/node@18.19.31)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.87.0)(sass@1.85.0)(stylus@0.64.0)(terser@5.39.0)(yaml@2.7.1)) + '@vitejs/plugin-basic-ssl': 1.2.0(vite@6.2.4(@types/node@18.19.31)(jiti@2.4.2)(less@4.3.0)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(terser@5.39.0)(yaml@2.7.1)) ansi-colors: 4.1.3 autoprefixer: 10.4.20(postcss@8.5.2) babel-loader: 9.2.1(@babel/core@7.26.10)(webpack@5.98.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.25.1)) @@ -10440,7 +10450,7 @@ snapshots: '@babel/helper-split-export-declaration': 7.24.7 '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.10) '@inquirer/confirm': 5.1.6(@types/node@18.19.31) - '@vitejs/plugin-basic-ssl': 1.2.0(vite@6.2.4(@types/node@18.19.31)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.87.0)(sass@1.85.0)(stylus@0.64.0)(terser@5.39.0)(yaml@2.7.1)) + '@vitejs/plugin-basic-ssl': 1.2.0(vite@6.2.4(@types/node@18.19.31)(jiti@2.4.2)(less@4.3.0)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(terser@5.39.0)(yaml@2.7.1)) beasties: 0.2.0 browserslist: 4.24.5 esbuild: 0.25.1 @@ -10458,7 +10468,7 @@ snapshots: semver: 7.7.1 source-map-support: 0.5.21 typescript: 5.7.3 - vite: 6.2.4(@types/node@18.19.31)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(terser@5.39.0)(yaml@2.7.1) + vite: 6.2.4(@types/node@18.19.31)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.87.0)(sass@1.85.0)(stylus@0.64.0)(terser@5.39.0)(yaml@2.7.1) watchpack: 2.4.2 optionalDependencies: '@angular/platform-server': 19.2.5(@angular/common@19.2.5(@angular/core@19.2.5(rxjs@7.8.2)(zone.js@0.14.4))(rxjs@7.8.2))(@angular/compiler@19.2.5)(@angular/core@19.2.5(rxjs@7.8.2)(zone.js@0.14.4))(@angular/platform-browser@19.2.5(@angular/animations@19.2.5(@angular/common@19.2.5(@angular/core@19.2.5(rxjs@7.8.2)(zone.js@0.14.4))(rxjs@7.8.2))(@angular/core@19.2.5(rxjs@7.8.2)(zone.js@0.14.4)))(@angular/common@19.2.5(@angular/core@19.2.5(rxjs@7.8.2)(zone.js@0.14.4))(rxjs@7.8.2))(@angular/core@19.2.5(rxjs@7.8.2)(zone.js@0.14.4)))(rxjs@7.8.2) @@ -13634,16 +13644,16 @@ snapshots: transitivePeerDependencies: - supports-color - '@nx/angular@20.7.1(693afb23b549613d019c9e7c1d8e1423)': + '@nx/angular@20.7.1(06d083ec7b8710b15f0ae77fdd0247b4)': dependencies: - '@angular-devkit/build-angular': 19.2.6(76df311846185dc42c879194ee06e89f) + '@angular-devkit/build-angular': 19.2.6(5656755dcc4e0f6f7c6c4e844249b617) '@angular-devkit/core': 19.2.6(chokidar@4.0.3) '@angular-devkit/schematics': 19.2.6(chokidar@4.0.3) '@nx/devkit': 20.7.1(nx@20.7.1(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.12))) '@nx/eslint': 20.7.1(@babel/traverse@7.27.1)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@zkochan/js-yaml@0.0.7)(eslint@8.57.0)(nx@20.7.1(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.12))) '@nx/js': 20.7.1(@babel/traverse@7.27.1)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.12))(nx@20.7.1(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.12))) '@nx/module-federation': 20.7.1(@babel/traverse@7.27.1)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(esbuild@0.19.12)(nx@20.7.1(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) - '@nx/rspack': 20.7.1(@babel/traverse@7.27.1)(@module-federation/enhanced@0.13.1(@rspack/core@1.3.9(@swc/helpers@0.5.12))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)(webpack@5.99.8(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)))(@module-federation/node@2.7.2(@rspack/core@1.3.9(@swc/helpers@0.5.12))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)(webpack@5.99.8(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)))(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(@types/express@4.17.21)(esbuild@0.19.12)(less@4.1.3)(nx@20.7.1(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(react-dom@18.3.1(react@18.3.1))(react-refresh@0.17.0)(react@18.3.1)(typescript@5.7.3)(webpack-hot-middleware@2.26.1) + '@nx/rspack': 20.7.1(@babel/traverse@7.27.1)(@module-federation/enhanced@0.13.1(@rspack/core@1.3.9(@swc/helpers@0.5.12))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)(webpack@5.99.8(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)))(@module-federation/node@2.7.2(@rspack/core@1.3.9(@swc/helpers@0.5.12))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)(webpack@5.99.8(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)))(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(@types/express@4.17.21)(esbuild@0.19.12)(less@4.3.0)(nx@20.7.1(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(react-dom@18.3.1(react@18.3.1))(react-refresh@0.17.0)(react@18.3.1)(typescript@5.7.3)(webpack-hot-middleware@2.26.1) '@nx/web': 20.7.1(@babel/traverse@7.27.1)(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.12))(nx@20.7.1(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.12))) '@nx/webpack': 20.7.1(@babel/traverse@7.27.1)(@rspack/core@1.3.9(@swc/helpers@0.5.12))(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)(html-webpack-plugin@5.6.3(@rspack/core@1.3.9(@swc/helpers@0.5.12))(webpack@5.99.8(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)))(nx@20.7.1(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(typescript@5.7.3) '@nx/workspace': 20.7.1(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.12)) @@ -13971,7 +13981,7 @@ snapshots: - typescript - verdaccio - '@nx/rspack@20.7.1(@babel/traverse@7.27.1)(@module-federation/enhanced@0.13.1(@rspack/core@1.3.9(@swc/helpers@0.5.12))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)(webpack@5.99.8(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)))(@module-federation/node@2.7.2(@rspack/core@1.3.9(@swc/helpers@0.5.12))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)(webpack@5.99.8(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)))(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(@types/express@4.17.21)(esbuild@0.19.12)(less@4.1.3)(nx@20.7.1(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(react-dom@18.3.1(react@18.3.1))(react-refresh@0.17.0)(react@18.3.1)(typescript@5.7.3)(webpack-hot-middleware@2.26.1)': + '@nx/rspack@20.7.1(@babel/traverse@7.27.1)(@module-federation/enhanced@0.13.1(@rspack/core@1.3.9(@swc/helpers@0.5.12))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)(webpack@5.99.8(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)))(@module-federation/node@2.7.2(@rspack/core@1.3.9(@swc/helpers@0.5.12))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)(webpack@5.99.8(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)))(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(@types/express@4.17.21)(esbuild@0.19.12)(less@4.3.0)(nx@20.7.1(@swc-node/register@1.9.2(@swc/core@1.5.7(@swc/helpers@0.5.12))(@swc/types@0.1.7)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.12)))(react-dom@18.3.1(react@18.3.1))(react-refresh@0.17.0)(react@18.3.1)(typescript@5.7.3)(webpack-hot-middleware@2.26.1)': dependencies: '@module-federation/enhanced': 0.13.1(@rspack/core@1.3.9(@swc/helpers@0.5.12))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)(webpack@5.99.8(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) '@module-federation/node': 2.7.2(@rspack/core@1.3.9(@swc/helpers@0.5.12))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)(webpack@5.99.8(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) @@ -13989,7 +13999,7 @@ snapshots: enquirer: 2.3.6 express: 4.21.2 http-proxy-middleware: 3.0.5 - less-loader: 11.1.0(less@4.1.3)(webpack@5.99.8(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) + less-loader: 11.1.0(less@4.3.0)(webpack@5.99.8(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) license-webpack-plugin: 4.0.2(webpack@5.99.8(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)) loader-utils: 2.0.4 picocolors: 1.1.1 @@ -14638,10 +14648,10 @@ snapshots: memoizerific: 1.11.3 storybook: 8.6.12(prettier@3.5.3) - '@storybook/angular@8.4.7(5d83d14307f3397e6404bf89d2737cdd)': + '@storybook/angular@8.4.7(aacb154f385fdf6941de0697b6a13681)': dependencies: '@angular-devkit/architect': 0.1902.11(chokidar@4.0.3) - '@angular-devkit/build-angular': 19.2.6(76df311846185dc42c879194ee06e89f) + '@angular-devkit/build-angular': 19.2.6(5656755dcc4e0f6f7c6c4e844249b617) '@angular-devkit/core': 19.2.6(chokidar@4.0.3) '@angular/common': 19.2.5(@angular/core@19.2.5(rxjs@7.8.2)(zone.js@0.14.4))(rxjs@7.8.2) '@angular/compiler': 19.2.5 @@ -15306,9 +15316,9 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@vitejs/plugin-basic-ssl@1.2.0(vite@6.2.4(@types/node@18.19.31)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.87.0)(sass@1.85.0)(stylus@0.64.0)(terser@5.39.0)(yaml@2.7.1))': + '@vitejs/plugin-basic-ssl@1.2.0(vite@6.2.4(@types/node@18.19.31)(jiti@2.4.2)(less@4.3.0)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(terser@5.39.0)(yaml@2.7.1))': dependencies: - vite: 6.2.4(@types/node@18.19.31)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(terser@5.39.0)(yaml@2.7.1) + vite: 6.2.4(@types/node@18.19.31)(jiti@2.4.2)(less@4.3.0)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(terser@5.39.0)(yaml@2.7.1) '@webassemblyjs/ast@1.14.1': dependencies: @@ -18542,6 +18552,12 @@ snapshots: less: 4.1.3 webpack: 5.99.8(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) + less-loader@11.1.0(less@4.3.0)(webpack@5.99.8(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)): + dependencies: + klona: 2.0.6 + less: 4.3.0 + webpack: 5.99.8(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) + less-loader@12.2.0(@rspack/core@1.3.9(@swc/helpers@0.5.12))(less@4.2.2)(webpack@5.98.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.25.1)): dependencies: less: 4.2.2 @@ -19112,6 +19128,13 @@ snapshots: ngraph.events@1.2.2: {} + ngx-owl-carousel-o@20.0.1(@angular/common@19.2.5(@angular/core@19.2.5(rxjs@7.8.2)(zone.js@0.14.4))(rxjs@7.8.2))(@angular/core@19.2.5(rxjs@7.8.2)(zone.js@0.14.4))(rxjs@7.8.2): + dependencies: + '@angular/common': 19.2.5(@angular/core@19.2.5(rxjs@7.8.2)(zone.js@0.14.4))(rxjs@7.8.2) + '@angular/core': 19.2.5(rxjs@7.8.2)(zone.js@0.14.4) + rxjs: 7.8.2 + tslib: 2.8.1 + ngx-skeleton-loader@8.1.0(@angular/common@19.2.5(@angular/core@19.2.5(rxjs@7.8.2)(zone.js@0.14.4))(rxjs@7.8.2))(@angular/core@19.2.5(rxjs@7.8.2)(zone.js@0.14.4)): dependencies: '@angular/common': 19.2.5(@angular/core@19.2.5(rxjs@7.8.2)(zone.js@0.14.4))(rxjs@7.8.2) @@ -21592,7 +21615,7 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 - vite@6.2.4(@types/node@18.19.31)(jiti@2.4.2)(less@4.1.3)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(terser@5.39.0)(yaml@2.7.1): + vite@6.2.4(@types/node@18.19.31)(jiti@2.4.2)(less@4.2.2)(sass-embedded@1.87.0)(sass@1.85.0)(stylus@0.64.0)(terser@5.39.0)(yaml@2.7.1): dependencies: esbuild: 0.25.1 postcss: 8.5.3 @@ -21601,7 +21624,23 @@ snapshots: '@types/node': 18.19.31 fsevents: 2.3.3 jiti: 2.4.2 - less: 4.1.3 + less: 4.2.2 + sass: 1.85.0 + sass-embedded: 1.87.0 + stylus: 0.64.0 + terser: 5.39.0 + yaml: 2.7.1 + + vite@6.2.4(@types/node@18.19.31)(jiti@2.4.2)(less@4.3.0)(sass-embedded@1.87.0)(sass@1.87.0)(stylus@0.64.0)(terser@5.39.0)(yaml@2.7.1): + dependencies: + esbuild: 0.25.1 + postcss: 8.5.3 + rollup: 4.34.8 + optionalDependencies: + '@types/node': 18.19.31 + fsevents: 2.3.3 + jiti: 2.4.2 + less: 4.3.0 sass: 1.87.0 sass-embedded: 1.87.0 stylus: 0.64.0 From 379c811b904e53553e8081edebdeb6aff20e638f Mon Sep 17 00:00:00 2001 From: Damian Maduzia Date: Fri, 24 Oct 2025 14:04:19 +0200 Subject: [PATCH 2/3] feat: improve a11y --- apps/blog/src/assets/i18n/en.json | 5 ++ apps/blog/src/assets/i18n/pl.json | 5 ++ .../src/lib/related-articles.component.ts | 66 ++++++++++++++----- .../ui-avatar/src/lib/avatar.component.ts | 3 - 4 files changed, 60 insertions(+), 19 deletions(-) diff --git a/apps/blog/src/assets/i18n/en.json b/apps/blog/src/assets/i18n/en.json index e6570d6ea..5314270ae 100644 --- a/apps/blog/src/assets/i18n/en.json +++ b/apps/blog/src/assets/i18n/en.json @@ -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" } } diff --git a/apps/blog/src/assets/i18n/pl.json b/apps/blog/src/assets/i18n/pl.json index 61acd50bf..00aec82a9 100644 --- a/apps/blog/src/assets/i18n/pl.json +++ b/apps/blog/src/assets/i18n/pl.json @@ -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" } } diff --git a/libs/blog/articles/feature-related-articles/src/lib/related-articles.component.ts b/libs/blog/articles/feature-related-articles/src/lib/related-articles.component.ts index 51e00d8d8..6ad4c7bff 100644 --- a/libs/blog/articles/feature-related-articles/src/lib/related-articles.component.ts +++ b/libs/blog/articles/feature-related-articles/src/lib/related-articles.component.ts @@ -5,31 +5,66 @@ 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()) { - Related Articles - - @for (article of store.relatedArticles(); track $index) { - - - - - - - } - + +

{{ t('title') }}

+
+ + @for (article of store.relatedArticles(); track $index) { + + +
  • + +
  • +
    + } +
    + +
    +
    } `, changeDetection: ChangeDetectionStrategy.OnPush, providers: [RelatedArticleListStore], - imports: [UiArticleCardComponent, CarouselModule], + imports: [ + UiArticleCardComponent, + CarouselModule, + ButtonComponent, + TranslocoDirective, + ], }) export class RelatedArticlesComponent implements OnInit { readonly id = input.required(); @@ -37,9 +72,9 @@ export class RelatedArticlesComponent implements OnInit { loop: true, mouseDrag: false, touchDrag: true, - pullDrag: false, + pullDrag: true, dots: true, - margin: 24, // Add spacing between items + margin: 24, navSpeed: 700, navText: ['', ''], responsive: { @@ -47,11 +82,10 @@ export class RelatedArticlesComponent implements OnInit { 0: { items: 1, }, - 640: { + 540: { items: 2, }, }, - nav: true, }; readonly store = inject(RelatedArticleListStore); diff --git a/libs/blog/shared/ui-avatar/src/lib/avatar.component.ts b/libs/blog/shared/ui-avatar/src/lib/avatar.component.ts index c7db52d50..7f57bb0cc 100644 --- a/libs/blog/shared/ui-avatar/src/lib/avatar.component.ts +++ b/libs/blog/shared/ui-avatar/src/lib/avatar.component.ts @@ -9,9 +9,6 @@ type AvatarSize = '32' | '96'; styleUrls: ['./avatar.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgOptimizedImage], - host: { - class: 'block', - }, }) export class AvatarComponent { readonly imageSrc = input.required(); From 2897f72228085085c4809128e60344b9034e94ef Mon Sep 17 00:00:00 2001 From: Damian Maduzia Date: Fri, 24 Oct 2025 15:21:23 +0200 Subject: [PATCH 3/3] refactor: add readonly --- .../src/lib/related-articles.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/blog/articles/feature-related-articles/src/lib/related-articles.component.ts b/libs/blog/articles/feature-related-articles/src/lib/related-articles.component.ts index 6ad4c7bff..df1a97d12 100644 --- a/libs/blog/articles/feature-related-articles/src/lib/related-articles.component.ts +++ b/libs/blog/articles/feature-related-articles/src/lib/related-articles.component.ts @@ -68,7 +68,7 @@ import { ButtonComponent } from '@angular-love/blog/shared/ui-button'; }) export class RelatedArticlesComponent implements OnInit { readonly id = input.required(); - customOptions: OwlOptions = { + readonly customOptions: OwlOptions = { loop: true, mouseDrag: false, touchDrag: true,