@thescaffold/ngx-apps-blog 0.2.31 → 0.2.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,7 +14,7 @@ Run `ng build blog` to build the project. The build artifacts will be stored in
14
14
 
15
15
  ## Publishing
16
16
 
17
- After building your library with `ng build blog`, go to the dist folder `cd dist/blog` and run `npm publish`.
17
+ After building your library with `ng build blog`, go to the dist folder `cd dist/blog` and run `pnpm publish`.
18
18
 
19
19
  ## Running unit tests
20
20
 
@@ -0,0 +1,190 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, HostListener, Component, NgModule } from '@angular/core';
3
+ import * as i1 from '@angular/router';
4
+ import { ActivatedRoute, RouterModule } from '@angular/router';
5
+ import * as i4 from '@thescaffold/ngx-core';
6
+ import { BaseDirective, MediaService, SharedModule } from '@thescaffold/ngx-core';
7
+ import * as i2 from '@thescaffold/ngx-ui';
8
+ import { LayoutsModule, ElementsModule } from '@thescaffold/ngx-ui';
9
+ import { switchMap } from 'rxjs';
10
+ import * as i1$1 from '@angular/common';
11
+
12
+ class ArticleComponent extends BaseDirective {
13
+ constructor() {
14
+ super(...arguments);
15
+ this.route = inject(ActivatedRoute);
16
+ this.mediaService = inject(MediaService);
17
+ this.article$ = this.route.params.pipe(switchMap((params) => {
18
+ this.category = params['category'];
19
+ this.title = params['article'];
20
+ const name = this.category
21
+ ? `${this.category}/${this.title}`
22
+ : `${this.title}`;
23
+ return this.mediaService.context('blog').article(name);
24
+ }));
25
+ }
26
+ /**
27
+ * Intercepts clicks on anchor tags within the documentation content
28
+ * and routes internal links via Angular Router instead of triggering
29
+ * a full page reload.
30
+ */
31
+ onContentClick(event) {
32
+ const target = event.target;
33
+ const anchor = target.closest('a');
34
+ if (!anchor)
35
+ return;
36
+ const href = anchor.getAttribute('href');
37
+ if (!href)
38
+ return;
39
+ // Check if it's an internal link (starts with /, ./, or ../)
40
+ if (href.startsWith('/') ||
41
+ href.startsWith('./') ||
42
+ href.startsWith('../')) {
43
+ event.preventDefault();
44
+ event.stopPropagation();
45
+ // Handle fragment links
46
+ const [path, fragment] = href.split('#');
47
+ const extras = {};
48
+ if (fragment) {
49
+ extras.fragment = fragment;
50
+ }
51
+ this.router.navigate([path || './'], extras);
52
+ }
53
+ else if (href.startsWith('#')) {
54
+ // Same-page fragment navigation
55
+ event.preventDefault();
56
+ event.stopPropagation();
57
+ this.router.navigate([], {
58
+ fragment: href.substring(1),
59
+ relativeTo: this.route,
60
+ });
61
+ }
62
+ // External links (http://, https://, etc.) will navigate normally
63
+ }
64
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: ArticleComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
65
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.4", type: ArticleComponent, isStandalone: false, selector: "x-article", host: { listeners: { "click": "onContentClick($event)" } }, usesInheritance: true, ngImport: i0, template: `
66
+ @if (article$ | async; as article) {
67
+ <article class="x-article">
68
+ <header class="x-article-header">
69
+ <nav class="x-article-breadcrumb" aria-label="breadcrumb">
70
+ @if (title) {
71
+ <ol class="breadcrumb mb-0">
72
+ <li class="breadcrumb-item">
73
+ <a
74
+ [routerLink]="'blog.root' | xRoute"
75
+ class="x-article-breadcrumb-link"
76
+ >
77
+ <i class="fa-solid fa-home me-1 opacity-75"></i>
78
+ {{ 'apps.blog.component.article.home.title' | xTranslate }}
79
+ </a>
80
+ </li>
81
+ @if (category) {
82
+ <li class="breadcrumb-item x-article-breadcrumb-category">
83
+ {{ category }}
84
+ </li>
85
+ }
86
+ @if (title) {
87
+ <li
88
+ class="breadcrumb-item active x-article-breadcrumb-title"
89
+ aria-current="page"
90
+ >
91
+ {{ title }}
92
+ </li>
93
+ }
94
+ </ol>
95
+ }
96
+ </nav>
97
+ </header>
98
+ <div class="x-article-content" [innerHTML]="article"></div>
99
+ </article>
100
+ } @else {
101
+ <x-element-blank></x-element-blank>
102
+ }
103
+
104
+ <!-- misc -->
105
+ <ng-template #loader>
106
+ <x-element-loader></x-element-loader>
107
+ </ng-template>
108
+ `, isInline: true, styles: [":host{display:block}.x-article{max-width:800px;margin:0 auto;padding:1.5rem 1rem 3rem}.x-article-header{margin-bottom:2rem;padding-bottom:1rem;border-bottom:1px solid var(--bs-border-color-translucent)}.x-article-breadcrumb{font-size:.875rem}.x-article-breadcrumb .breadcrumb{background:transparent;padding:0}.x-article-breadcrumb-link{color:var(--bs-secondary-color);text-decoration:none;transition:color .15s ease}.x-article-breadcrumb-link:hover{color:var(--bs-primary)}.x-article-breadcrumb-category{color:var(--bs-secondary-color);text-transform:capitalize}.x-article-breadcrumb-title{color:var(--bs-body-color);font-weight:500;text-transform:capitalize;max-width:200px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.x-article-content{line-height:1.75;color:var(--bs-body-color)}:host ::ng-deep .x-article-content h1{font-size:2.25rem;font-weight:700;margin-top:0;margin-bottom:1.5rem;line-height:1.3;color:var(--bs-heading-color, var(--bs-body-color))}:host ::ng-deep .x-article-content h2{font-size:1.75rem;font-weight:600;margin-top:2.5rem;margin-bottom:1rem;padding-bottom:.5rem;border-bottom:1px solid var(--bs-border-color-translucent);color:var(--bs-heading-color, var(--bs-body-color))}:host ::ng-deep .x-article-content h3{font-size:1.375rem;font-weight:600;margin-top:2rem;margin-bottom:.75rem;color:var(--bs-heading-color, var(--bs-body-color))}:host ::ng-deep .x-article-content h4{font-size:1.125rem;font-weight:600;margin-top:1.5rem;margin-bottom:.5rem;color:var(--bs-heading-color, var(--bs-body-color))}:host ::ng-deep .x-article-content h5,:host ::ng-deep .x-article-content h6{font-size:1rem;font-weight:600;margin-top:1.25rem;margin-bottom:.5rem;color:var(--bs-heading-color, var(--bs-body-color))}:host ::ng-deep .x-article-content p{margin-bottom:1.25rem}:host ::ng-deep .x-article-content a{color:var(--bs-primary);text-decoration:none;border-bottom:1px solid transparent;transition:border-color .15s ease}:host ::ng-deep .x-article-content a:hover{border-bottom-color:var(--bs-primary)}:host ::ng-deep .x-article-content ul,:host ::ng-deep .x-article-content ol{margin-bottom:1.25rem;padding-left:1.5rem}:host ::ng-deep .x-article-content li{margin-bottom:.5rem}:host ::ng-deep .x-article-content li>ul,:host ::ng-deep .x-article-content li>ol{margin-top:.5rem;margin-bottom:0}:host ::ng-deep .x-article-content code{font-family:SF Mono,Monaco,Cascadia Code,Roboto Mono,Consolas,monospace;font-size:.875em;padding:.2em .4em;background-color:var(--bs-tertiary-bg);border-radius:.25rem;color:var(--bs-code-color, #d63384)}:host ::ng-deep .x-article-content pre{background-color:var(--bs-tertiary-bg);border:1px solid var(--bs-border-color);border-radius:.5rem;padding:1rem 1.25rem;margin-bottom:1.5rem;overflow-x:auto;font-size:.875rem;line-height:1.6}:host ::ng-deep .x-article-content pre code{background:transparent;padding:0;border-radius:0;color:inherit;font-size:inherit}:host ::ng-deep .x-article-content blockquote{margin:1.5rem 0;padding:1rem 1.5rem;border-left:4px solid var(--bs-primary);background-color:var(--bs-tertiary-bg);border-radius:0 .5rem .5rem 0;font-style:italic;color:var(--bs-secondary-color)}:host ::ng-deep .x-article-content blockquote p:last-child{margin-bottom:0}:host ::ng-deep .x-article-content table{width:100%;margin-bottom:1.5rem;border-collapse:collapse;font-size:.9375rem}:host ::ng-deep .x-article-content th,:host ::ng-deep .x-article-content td{padding:.75rem 1rem;border:1px solid var(--bs-border-color);text-align:left}:host ::ng-deep .x-article-content th{background-color:var(--bs-tertiary-bg);font-weight:600}:host ::ng-deep .x-article-content tr:hover{background-color:var(--bs-tertiary-bg)}:host ::ng-deep .x-article-content img{max-width:100%;height:auto;border-radius:.5rem;margin:1rem 0;box-shadow:0 2px 8px #0000001a}:host ::ng-deep .x-article-content hr{margin:2rem 0;border:none;border-top:1px solid var(--bs-border-color)}:host ::ng-deep .x-article-content dl{margin-bottom:1.25rem}:host ::ng-deep .x-article-content dt{font-weight:600;margin-top:1rem}:host ::ng-deep .x-article-content dd{margin-left:1rem;margin-bottom:.5rem;color:var(--bs-secondary-color)}:host ::ng-deep .x-article-content iframe,:host ::ng-deep .x-article-content video{max-width:100%;border-radius:.5rem;margin:1rem 0}@media(max-width:575.98px){.x-article{padding:1rem .75rem 2rem}.x-article-header{margin-bottom:1.5rem}.x-article-breadcrumb{font-size:.8125rem}.x-article-breadcrumb-title{max-width:120px}:host ::ng-deep .x-article-content h1{font-size:1.75rem}:host ::ng-deep .x-article-content h2{font-size:1.5rem;margin-top:2rem}:host ::ng-deep .x-article-content h3{font-size:1.25rem}:host ::ng-deep .x-article-content pre{padding:.75rem 1rem;font-size:.8125rem;border-radius:.375rem}:host ::ng-deep .x-article-content blockquote{padding:.75rem 1rem;margin:1rem 0}:host ::ng-deep .x-article-content table{font-size:.875rem;display:block;overflow-x:auto;white-space:nowrap}:host ::ng-deep .x-article-content th,:host ::ng-deep .x-article-content td{padding:.5rem .75rem}}@media(min-width:576px)and (max-width:991.98px){.x-article{padding:1.5rem 1.25rem 2.5rem;max-width:720px}:host ::ng-deep .x-article-content h1{font-size:2rem}}@media(min-width:992px){.x-article{padding:2rem 1.5rem 4rem}}@media print{.x-article{max-width:100%;padding:0}.x-article-header{border-bottom:1px solid #ccc}:host ::ng-deep .x-article-content pre{white-space:pre-wrap;word-wrap:break-word}}\n"], dependencies: [{ kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: i2.BlankComponent, selector: "x-element-blank", inputs: ["image", "icon", "type"] }, { kind: "component", type: i2.LoaderComponent, selector: "x-element-loader", inputs: ["data"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.RoutePipe, name: "xRoute" }, { kind: "pipe", type: i4.TranslatePipe, name: "xTranslate" }] }); }
109
+ }
110
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: ArticleComponent, decorators: [{
111
+ type: Component,
112
+ args: [{ standalone: false, selector: 'x-article', template: `
113
+ @if (article$ | async; as article) {
114
+ <article class="x-article">
115
+ <header class="x-article-header">
116
+ <nav class="x-article-breadcrumb" aria-label="breadcrumb">
117
+ @if (title) {
118
+ <ol class="breadcrumb mb-0">
119
+ <li class="breadcrumb-item">
120
+ <a
121
+ [routerLink]="'blog.root' | xRoute"
122
+ class="x-article-breadcrumb-link"
123
+ >
124
+ <i class="fa-solid fa-home me-1 opacity-75"></i>
125
+ {{ 'apps.blog.component.article.home.title' | xTranslate }}
126
+ </a>
127
+ </li>
128
+ @if (category) {
129
+ <li class="breadcrumb-item x-article-breadcrumb-category">
130
+ {{ category }}
131
+ </li>
132
+ }
133
+ @if (title) {
134
+ <li
135
+ class="breadcrumb-item active x-article-breadcrumb-title"
136
+ aria-current="page"
137
+ >
138
+ {{ title }}
139
+ </li>
140
+ }
141
+ </ol>
142
+ }
143
+ </nav>
144
+ </header>
145
+ <div class="x-article-content" [innerHTML]="article"></div>
146
+ </article>
147
+ } @else {
148
+ <x-element-blank></x-element-blank>
149
+ }
150
+
151
+ <!-- misc -->
152
+ <ng-template #loader>
153
+ <x-element-loader></x-element-loader>
154
+ </ng-template>
155
+ `, styles: [":host{display:block}.x-article{max-width:800px;margin:0 auto;padding:1.5rem 1rem 3rem}.x-article-header{margin-bottom:2rem;padding-bottom:1rem;border-bottom:1px solid var(--bs-border-color-translucent)}.x-article-breadcrumb{font-size:.875rem}.x-article-breadcrumb .breadcrumb{background:transparent;padding:0}.x-article-breadcrumb-link{color:var(--bs-secondary-color);text-decoration:none;transition:color .15s ease}.x-article-breadcrumb-link:hover{color:var(--bs-primary)}.x-article-breadcrumb-category{color:var(--bs-secondary-color);text-transform:capitalize}.x-article-breadcrumb-title{color:var(--bs-body-color);font-weight:500;text-transform:capitalize;max-width:200px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.x-article-content{line-height:1.75;color:var(--bs-body-color)}:host ::ng-deep .x-article-content h1{font-size:2.25rem;font-weight:700;margin-top:0;margin-bottom:1.5rem;line-height:1.3;color:var(--bs-heading-color, var(--bs-body-color))}:host ::ng-deep .x-article-content h2{font-size:1.75rem;font-weight:600;margin-top:2.5rem;margin-bottom:1rem;padding-bottom:.5rem;border-bottom:1px solid var(--bs-border-color-translucent);color:var(--bs-heading-color, var(--bs-body-color))}:host ::ng-deep .x-article-content h3{font-size:1.375rem;font-weight:600;margin-top:2rem;margin-bottom:.75rem;color:var(--bs-heading-color, var(--bs-body-color))}:host ::ng-deep .x-article-content h4{font-size:1.125rem;font-weight:600;margin-top:1.5rem;margin-bottom:.5rem;color:var(--bs-heading-color, var(--bs-body-color))}:host ::ng-deep .x-article-content h5,:host ::ng-deep .x-article-content h6{font-size:1rem;font-weight:600;margin-top:1.25rem;margin-bottom:.5rem;color:var(--bs-heading-color, var(--bs-body-color))}:host ::ng-deep .x-article-content p{margin-bottom:1.25rem}:host ::ng-deep .x-article-content a{color:var(--bs-primary);text-decoration:none;border-bottom:1px solid transparent;transition:border-color .15s ease}:host ::ng-deep .x-article-content a:hover{border-bottom-color:var(--bs-primary)}:host ::ng-deep .x-article-content ul,:host ::ng-deep .x-article-content ol{margin-bottom:1.25rem;padding-left:1.5rem}:host ::ng-deep .x-article-content li{margin-bottom:.5rem}:host ::ng-deep .x-article-content li>ul,:host ::ng-deep .x-article-content li>ol{margin-top:.5rem;margin-bottom:0}:host ::ng-deep .x-article-content code{font-family:SF Mono,Monaco,Cascadia Code,Roboto Mono,Consolas,monospace;font-size:.875em;padding:.2em .4em;background-color:var(--bs-tertiary-bg);border-radius:.25rem;color:var(--bs-code-color, #d63384)}:host ::ng-deep .x-article-content pre{background-color:var(--bs-tertiary-bg);border:1px solid var(--bs-border-color);border-radius:.5rem;padding:1rem 1.25rem;margin-bottom:1.5rem;overflow-x:auto;font-size:.875rem;line-height:1.6}:host ::ng-deep .x-article-content pre code{background:transparent;padding:0;border-radius:0;color:inherit;font-size:inherit}:host ::ng-deep .x-article-content blockquote{margin:1.5rem 0;padding:1rem 1.5rem;border-left:4px solid var(--bs-primary);background-color:var(--bs-tertiary-bg);border-radius:0 .5rem .5rem 0;font-style:italic;color:var(--bs-secondary-color)}:host ::ng-deep .x-article-content blockquote p:last-child{margin-bottom:0}:host ::ng-deep .x-article-content table{width:100%;margin-bottom:1.5rem;border-collapse:collapse;font-size:.9375rem}:host ::ng-deep .x-article-content th,:host ::ng-deep .x-article-content td{padding:.75rem 1rem;border:1px solid var(--bs-border-color);text-align:left}:host ::ng-deep .x-article-content th{background-color:var(--bs-tertiary-bg);font-weight:600}:host ::ng-deep .x-article-content tr:hover{background-color:var(--bs-tertiary-bg)}:host ::ng-deep .x-article-content img{max-width:100%;height:auto;border-radius:.5rem;margin:1rem 0;box-shadow:0 2px 8px #0000001a}:host ::ng-deep .x-article-content hr{margin:2rem 0;border:none;border-top:1px solid var(--bs-border-color)}:host ::ng-deep .x-article-content dl{margin-bottom:1.25rem}:host ::ng-deep .x-article-content dt{font-weight:600;margin-top:1rem}:host ::ng-deep .x-article-content dd{margin-left:1rem;margin-bottom:.5rem;color:var(--bs-secondary-color)}:host ::ng-deep .x-article-content iframe,:host ::ng-deep .x-article-content video{max-width:100%;border-radius:.5rem;margin:1rem 0}@media(max-width:575.98px){.x-article{padding:1rem .75rem 2rem}.x-article-header{margin-bottom:1.5rem}.x-article-breadcrumb{font-size:.8125rem}.x-article-breadcrumb-title{max-width:120px}:host ::ng-deep .x-article-content h1{font-size:1.75rem}:host ::ng-deep .x-article-content h2{font-size:1.5rem;margin-top:2rem}:host ::ng-deep .x-article-content h3{font-size:1.25rem}:host ::ng-deep .x-article-content pre{padding:.75rem 1rem;font-size:.8125rem;border-radius:.375rem}:host ::ng-deep .x-article-content blockquote{padding:.75rem 1rem;margin:1rem 0}:host ::ng-deep .x-article-content table{font-size:.875rem;display:block;overflow-x:auto;white-space:nowrap}:host ::ng-deep .x-article-content th,:host ::ng-deep .x-article-content td{padding:.5rem .75rem}}@media(min-width:576px)and (max-width:991.98px){.x-article{padding:1.5rem 1.25rem 2.5rem;max-width:720px}:host ::ng-deep .x-article-content h1{font-size:2rem}}@media(min-width:992px){.x-article{padding:2rem 1.5rem 4rem}}@media print{.x-article{max-width:100%;padding:0}.x-article-header{border-bottom:1px solid #ccc}:host ::ng-deep .x-article-content pre{white-space:pre-wrap;word-wrap:break-word}}\n"] }]
156
+ }], propDecorators: { onContentClick: [{
157
+ type: HostListener,
158
+ args: ['click', ['$event']]
159
+ }] } });
160
+
161
+ class ArticleModule {
162
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: ArticleModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
163
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.4", ngImport: i0, type: ArticleModule, declarations: [ArticleComponent], imports: [SharedModule, i1.RouterModule, LayoutsModule,
164
+ ElementsModule] }); }
165
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: ArticleModule, imports: [SharedModule,
166
+ RouterModule.forChild([
167
+ { path: ':category/:article', component: ArticleComponent },
168
+ { path: ':article', component: ArticleComponent },
169
+ ]),
170
+ LayoutsModule,
171
+ ElementsModule] }); }
172
+ }
173
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: ArticleModule, decorators: [{
174
+ type: NgModule,
175
+ args: [{
176
+ declarations: [ArticleComponent],
177
+ imports: [
178
+ SharedModule,
179
+ RouterModule.forChild([
180
+ { path: ':category/:article', component: ArticleComponent },
181
+ { path: ':article', component: ArticleComponent },
182
+ ]),
183
+ LayoutsModule,
184
+ ElementsModule,
185
+ ],
186
+ }]
187
+ }] });
188
+
189
+ export { ArticleModule };
190
+ //# sourceMappingURL=thescaffold-ngx-apps-blog-article.module-BjQ6ipkg.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thescaffold-ngx-apps-blog-article.module-BjQ6ipkg.mjs","sources":["../../../projects/blog/src/lib/pages/article/article.component.ts","../../../projects/blog/src/lib/pages/article/article.module.ts"],"sourcesContent":["import { Component, HostListener, inject } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\nimport { BaseDirective, MediaService } from '@thescaffold/ngx-core';\nimport { Observable, switchMap } from 'rxjs';\n\n@Component({\n standalone: false,\n selector: 'x-article',\n template: `\n @if (article$ | async; as article) {\n <article class=\"x-article\">\n <header class=\"x-article-header\">\n <nav class=\"x-article-breadcrumb\" aria-label=\"breadcrumb\">\n @if (title) {\n <ol class=\"breadcrumb mb-0\">\n <li class=\"breadcrumb-item\">\n <a\n [routerLink]=\"'blog.root' | xRoute\"\n class=\"x-article-breadcrumb-link\"\n >\n <i class=\"fa-solid fa-home me-1 opacity-75\"></i>\n {{ 'apps.blog.component.article.home.title' | xTranslate }}\n </a>\n </li>\n @if (category) {\n <li class=\"breadcrumb-item x-article-breadcrumb-category\">\n {{ category }}\n </li>\n }\n @if (title) {\n <li\n class=\"breadcrumb-item active x-article-breadcrumb-title\"\n aria-current=\"page\"\n >\n {{ title }}\n </li>\n }\n </ol>\n }\n </nav>\n </header>\n <div class=\"x-article-content\" [innerHTML]=\"article\"></div>\n </article>\n } @else {\n <x-element-blank></x-element-blank>\n }\n\n <!-- misc -->\n <ng-template #loader>\n <x-element-loader></x-element-loader>\n </ng-template>\n `,\n styles: [\n `\n :host {\n display: block;\n }\n\n .x-article {\n max-width: 800px;\n margin: 0 auto;\n padding: 1.5rem 1rem 3rem;\n }\n\n .x-article-header {\n margin-bottom: 2rem;\n padding-bottom: 1rem;\n border-bottom: 1px solid var(--bs-border-color-translucent);\n }\n\n .x-article-breadcrumb {\n font-size: 0.875rem;\n }\n\n .x-article-breadcrumb .breadcrumb {\n background: transparent;\n padding: 0;\n }\n\n .x-article-breadcrumb-link {\n color: var(--bs-secondary-color);\n text-decoration: none;\n transition: color 0.15s ease;\n }\n\n .x-article-breadcrumb-link:hover {\n color: var(--bs-primary);\n }\n\n .x-article-breadcrumb-category {\n color: var(--bs-secondary-color);\n text-transform: capitalize;\n }\n\n .x-article-breadcrumb-title {\n color: var(--bs-body-color);\n font-weight: 500;\n text-transform: capitalize;\n max-width: 200px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n /* Article content typography */\n .x-article-content {\n line-height: 1.75;\n color: var(--bs-body-color);\n }\n\n /* Headings */\n :host ::ng-deep .x-article-content h1 {\n font-size: 2.25rem;\n font-weight: 700;\n margin-top: 0;\n margin-bottom: 1.5rem;\n line-height: 1.3;\n color: var(--bs-heading-color, var(--bs-body-color));\n }\n\n :host ::ng-deep .x-article-content h2 {\n font-size: 1.75rem;\n font-weight: 600;\n margin-top: 2.5rem;\n margin-bottom: 1rem;\n padding-bottom: 0.5rem;\n border-bottom: 1px solid var(--bs-border-color-translucent);\n color: var(--bs-heading-color, var(--bs-body-color));\n }\n\n :host ::ng-deep .x-article-content h3 {\n font-size: 1.375rem;\n font-weight: 600;\n margin-top: 2rem;\n margin-bottom: 0.75rem;\n color: var(--bs-heading-color, var(--bs-body-color));\n }\n\n :host ::ng-deep .x-article-content h4 {\n font-size: 1.125rem;\n font-weight: 600;\n margin-top: 1.5rem;\n margin-bottom: 0.5rem;\n color: var(--bs-heading-color, var(--bs-body-color));\n }\n\n :host ::ng-deep .x-article-content h5,\n :host ::ng-deep .x-article-content h6 {\n font-size: 1rem;\n font-weight: 600;\n margin-top: 1.25rem;\n margin-bottom: 0.5rem;\n color: var(--bs-heading-color, var(--bs-body-color));\n }\n\n /* Paragraphs */\n :host ::ng-deep .x-article-content p {\n margin-bottom: 1.25rem;\n }\n\n /* Links */\n :host ::ng-deep .x-article-content a {\n color: var(--bs-primary);\n text-decoration: none;\n border-bottom: 1px solid transparent;\n transition: border-color 0.15s ease;\n }\n\n :host ::ng-deep .x-article-content a:hover {\n border-bottom-color: var(--bs-primary);\n }\n\n /* Lists */\n :host ::ng-deep .x-article-content ul,\n :host ::ng-deep .x-article-content ol {\n margin-bottom: 1.25rem;\n padding-left: 1.5rem;\n }\n\n :host ::ng-deep .x-article-content li {\n margin-bottom: 0.5rem;\n }\n\n :host ::ng-deep .x-article-content li > ul,\n :host ::ng-deep .x-article-content li > ol {\n margin-top: 0.5rem;\n margin-bottom: 0;\n }\n\n /* Code */\n :host ::ng-deep .x-article-content code {\n font-family:\n 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, monospace;\n font-size: 0.875em;\n padding: 0.2em 0.4em;\n background-color: var(--bs-tertiary-bg);\n border-radius: 0.25rem;\n color: var(--bs-code-color, #d63384);\n }\n\n :host ::ng-deep .x-article-content pre {\n background-color: var(--bs-tertiary-bg);\n border: 1px solid var(--bs-border-color);\n border-radius: 0.5rem;\n padding: 1rem 1.25rem;\n margin-bottom: 1.5rem;\n overflow-x: auto;\n font-size: 0.875rem;\n line-height: 1.6;\n }\n\n :host ::ng-deep .x-article-content pre code {\n background: transparent;\n padding: 0;\n border-radius: 0;\n color: inherit;\n font-size: inherit;\n }\n\n /* Blockquotes */\n :host ::ng-deep .x-article-content blockquote {\n margin: 1.5rem 0;\n padding: 1rem 1.5rem;\n border-left: 4px solid var(--bs-primary);\n background-color: var(--bs-tertiary-bg);\n border-radius: 0 0.5rem 0.5rem 0;\n font-style: italic;\n color: var(--bs-secondary-color);\n }\n\n :host ::ng-deep .x-article-content blockquote p:last-child {\n margin-bottom: 0;\n }\n\n /* Tables */\n :host ::ng-deep .x-article-content table {\n width: 100%;\n margin-bottom: 1.5rem;\n border-collapse: collapse;\n font-size: 0.9375rem;\n }\n\n :host ::ng-deep .x-article-content th,\n :host ::ng-deep .x-article-content td {\n padding: 0.75rem 1rem;\n border: 1px solid var(--bs-border-color);\n text-align: left;\n }\n\n :host ::ng-deep .x-article-content th {\n background-color: var(--bs-tertiary-bg);\n font-weight: 600;\n }\n\n :host ::ng-deep .x-article-content tr:hover {\n background-color: var(--bs-tertiary-bg);\n }\n\n /* Images */\n :host ::ng-deep .x-article-content img {\n max-width: 100%;\n height: auto;\n border-radius: 0.5rem;\n margin: 1rem 0;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n }\n\n /* Horizontal rule */\n :host ::ng-deep .x-article-content hr {\n margin: 2rem 0;\n border: none;\n border-top: 1px solid var(--bs-border-color);\n }\n\n /* Definition lists */\n :host ::ng-deep .x-article-content dl {\n margin-bottom: 1.25rem;\n }\n\n :host ::ng-deep .x-article-content dt {\n font-weight: 600;\n margin-top: 1rem;\n }\n\n :host ::ng-deep .x-article-content dd {\n margin-left: 1rem;\n margin-bottom: 0.5rem;\n color: var(--bs-secondary-color);\n }\n\n /* Video embeds */\n :host ::ng-deep .x-article-content iframe,\n :host ::ng-deep .x-article-content video {\n max-width: 100%;\n border-radius: 0.5rem;\n margin: 1rem 0;\n }\n\n /* Mobile styles - phones */\n @media (max-width: 575.98px) {\n .x-article {\n padding: 1rem 0.75rem 2rem;\n }\n\n .x-article-header {\n margin-bottom: 1.5rem;\n }\n\n .x-article-breadcrumb {\n font-size: 0.8125rem;\n }\n\n .x-article-breadcrumb-title {\n max-width: 120px;\n }\n\n :host ::ng-deep .x-article-content h1 {\n font-size: 1.75rem;\n }\n\n :host ::ng-deep .x-article-content h2 {\n font-size: 1.5rem;\n margin-top: 2rem;\n }\n\n :host ::ng-deep .x-article-content h3 {\n font-size: 1.25rem;\n }\n\n :host ::ng-deep .x-article-content pre {\n padding: 0.75rem 1rem;\n font-size: 0.8125rem;\n border-radius: 0.375rem;\n }\n\n :host ::ng-deep .x-article-content blockquote {\n padding: 0.75rem 1rem;\n margin: 1rem 0;\n }\n\n :host ::ng-deep .x-article-content table {\n font-size: 0.875rem;\n display: block;\n overflow-x: auto;\n white-space: nowrap;\n }\n\n :host ::ng-deep .x-article-content th,\n :host ::ng-deep .x-article-content td {\n padding: 0.5rem 0.75rem;\n }\n }\n\n /* Tablet styles */\n @media (min-width: 576px) and (max-width: 991.98px) {\n .x-article {\n padding: 1.5rem 1.25rem 2.5rem;\n max-width: 720px;\n }\n\n :host ::ng-deep .x-article-content h1 {\n font-size: 2rem;\n }\n }\n\n /* Large screens */\n @media (min-width: 992px) {\n .x-article {\n padding: 2rem 1.5rem 4rem;\n }\n }\n\n /* Print styles */\n @media print {\n .x-article {\n max-width: 100%;\n padding: 0;\n }\n\n .x-article-header {\n border-bottom: 1px solid #ccc;\n }\n\n :host ::ng-deep .x-article-content pre {\n white-space: pre-wrap;\n word-wrap: break-word;\n }\n }\n `,\n ],\n})\nexport class ArticleComponent extends BaseDirective {\n private readonly route = inject(ActivatedRoute);\n private readonly mediaService = inject(MediaService);\n\n category?: string;\n title?: string;\n article$: Observable<any> = this.route.params.pipe(\n switchMap((params) => {\n this.category = params['category'];\n this.title = params['article'];\n const name = this.category\n ? `${this.category}/${this.title}`\n : `${this.title}`;\n return this.mediaService.context('blog').article(name);\n }),\n );\n\n /**\n * Intercepts clicks on anchor tags within the documentation content\n * and routes internal links via Angular Router instead of triggering\n * a full page reload.\n */\n @HostListener('click', ['$event'])\n onContentClick(event: MouseEvent): void {\n const target = event.target as HTMLElement;\n const anchor = target.closest('a');\n\n if (!anchor) return;\n\n const href = anchor.getAttribute('href');\n if (!href) return;\n\n // Check if it's an internal link (starts with /, ./, or ../)\n if (\n href.startsWith('/') ||\n href.startsWith('./') ||\n href.startsWith('../')\n ) {\n event.preventDefault();\n event.stopPropagation();\n\n // Handle fragment links\n const [path, fragment] = href.split('#');\n const extras: any = {};\n if (fragment) {\n extras.fragment = fragment;\n }\n\n this.router.navigate([path || './'], extras);\n } else if (href.startsWith('#')) {\n // Same-page fragment navigation\n event.preventDefault();\n event.stopPropagation();\n this.router.navigate([], {\n fragment: href.substring(1),\n relativeTo: this.route,\n });\n }\n // External links (http://, https://, etc.) will navigate normally\n }\n}\n","import { NgModule } from '@angular/core';\nimport { RouterModule } from '@angular/router';\nimport { SharedModule } from '@thescaffold/ngx-core';\nimport { ElementsModule, LayoutsModule } from '@thescaffold/ngx-ui';\nimport { ArticleComponent } from './article.component';\n\n@NgModule({\n declarations: [ArticleComponent],\n imports: [\n SharedModule,\n RouterModule.forChild([\n { path: ':category/:article', component: ArticleComponent },\n { path: ':article', component: ArticleComponent },\n ]),\n LayoutsModule,\n ElementsModule,\n ],\n})\nexport class ArticleModule {}\n"],"names":["i3"],"mappings":";;;;;;;;;;;AAuYM,MAAO,gBAAiB,SAAQ,aAAa,CAAA;AAlYnD,IAAA,WAAA,GAAA;;AAmYmB,QAAA,IAAA,CAAA,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC;AAC9B,QAAA,IAAA,CAAA,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;AAIpD,QAAA,IAAA,CAAA,QAAQ,GAAoB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAChD,SAAS,CAAC,CAAC,MAAM,KAAI;AACnB,YAAA,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC;AAClC,YAAA,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC;AAC9B,YAAA,MAAM,IAAI,GAAG,IAAI,CAAC;kBACd,GAAG,IAAI,CAAC,QAAQ,CAAA,CAAA,EAAI,IAAI,CAAC,KAAK,CAAA;AAChC,kBAAE,CAAA,EAAG,IAAI,CAAC,KAAK,EAAE;AACnB,YAAA,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;QACxD,CAAC,CAAC,CACH;AA6CF,IAAA;AA3CC;;;;AAIG;AAEH,IAAA,cAAc,CAAC,KAAiB,EAAA;AAC9B,QAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAAqB;QAC1C,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC;AAElC,QAAA,IAAI,CAAC,MAAM;YAAE;QAEb,MAAM,IAAI,GAAG,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC;AACxC,QAAA,IAAI,CAAC,IAAI;YAAE;;AAGX,QAAA,IACE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;AACpB,YAAA,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;AACrB,YAAA,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EACtB;YACA,KAAK,CAAC,cAAc,EAAE;YACtB,KAAK,CAAC,eAAe,EAAE;;AAGvB,YAAA,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;YACxC,MAAM,MAAM,GAAQ,EAAE;YACtB,IAAI,QAAQ,EAAE;AACZ,gBAAA,MAAM,CAAC,QAAQ,GAAG,QAAQ;YAC5B;AAEA,YAAA,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,MAAM,CAAC;QAC9C;AAAO,aAAA,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE;;YAE/B,KAAK,CAAC,cAAc,EAAE;YACtB,KAAK,CAAC,eAAe,EAAE;AACvB,YAAA,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE;AACvB,gBAAA,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;gBAC3B,UAAU,EAAE,IAAI,CAAC,KAAK;AACvB,aAAA,CAAC;QACJ;;IAEF;8GA3DW,gBAAgB,EAAA,IAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAAhB,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,gBAAgB,EAAA,YAAA,EAAA,KAAA,EAAA,QAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,OAAA,EAAA,wBAAA,EAAA,EAAA,EAAA,eAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EA/XjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,k0KAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,UAAA,EAAA,QAAA,EAAA,cAAA,EAAA,MAAA,EAAA,CAAA,QAAA,EAAA,aAAA,EAAA,UAAA,EAAA,qBAAA,EAAA,OAAA,EAAA,MAAA,EAAA,YAAA,EAAA,kBAAA,EAAA,oBAAA,EAAA,YAAA,EAAA,YAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,cAAA,EAAA,QAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,eAAA,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,CAAA,MAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,MAAA,EAAA,IAAA,EAAAA,IAAA,CAAA,SAAA,EAAA,IAAA,EAAA,OAAA,EAAA,EAAA,EAAA,IAAA,EAAA,MAAA,EAAA,IAAA,EAAA,EAAA,CAAA,SAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,MAAA,EAAA,IAAA,EAAA,EAAA,CAAA,aAAA,EAAA,IAAA,EAAA,YAAA,EAAA,CAAA,EAAA,CAAA,CAAA;;2FAoVU,gBAAgB,EAAA,UAAA,EAAA,CAAA;kBAlY5B,SAAS;iCACI,KAAK,EAAA,QAAA,EACP,WAAW,EAAA,QAAA,EACX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CT,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,k0KAAA,CAAA,EAAA;;sBA0WA,YAAY;uBAAC,OAAO,EAAE,CAAC,QAAQ,CAAC;;;MC3YtB,aAAa,CAAA;8GAAb,aAAa,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,QAAA,EAAA,CAAA,CAAA;AAAb,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,mBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,aAAa,EAAA,YAAA,EAAA,CAXT,gBAAgB,CAAA,EAAA,OAAA,EAAA,CAE7B,YAAY,mBAKZ,aAAa;YACb,cAAc,CAAA,EAAA,CAAA,CAAA;AAGL,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,mBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,aAAa,YATtB,YAAY;YACZ,YAAY,CAAC,QAAQ,CAAC;AACpB,gBAAA,EAAE,IAAI,EAAE,oBAAoB,EAAE,SAAS,EAAE,gBAAgB,EAAE;AAC3D,gBAAA,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,gBAAgB,EAAE;aAClD,CAAC;YACF,aAAa;YACb,cAAc,CAAA,EAAA,CAAA,CAAA;;2FAGL,aAAa,EAAA,UAAA,EAAA,CAAA;kBAZzB,QAAQ;AAAC,YAAA,IAAA,EAAA,CAAA;oBACR,YAAY,EAAE,CAAC,gBAAgB,CAAC;AAChC,oBAAA,OAAO,EAAE;wBACP,YAAY;wBACZ,YAAY,CAAC,QAAQ,CAAC;AACpB,4BAAA,EAAE,IAAI,EAAE,oBAAoB,EAAE,SAAS,EAAE,gBAAgB,EAAE;AAC3D,4BAAA,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,gBAAgB,EAAE;yBAClD,CAAC;wBACF,aAAa;wBACb,cAAc;AACf,qBAAA;AACF,iBAAA;;;;;"}