@smartsoft001-mobilems/articles-shell-angular 2.107.0 → 2.109.0

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.
@@ -1,15 +1,27 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, inject, contentChild, ChangeDetectionStrategy, Component, NgModule, Directive, effect, PLATFORM_ID, signal, computed } from '@angular/core';
3
- import { FILE_SERVICE_CONFIG } from '@smartsoft001/angular';
2
+ import { Injectable, inject, contentChild, ChangeDetectionStrategy, Component, NgModule, Directive, effect, PLATFORM_ID, signal, computed, input, viewChild } from '@angular/core';
3
+ import { FILE_SERVICE_CONFIG, ListMode } from '@smartsoft001/angular';
4
4
  import { CrudFacade, initialState as initialState$1, getReducer as getReducer$1, CrudService, CrudConfig, CrudFullConfig, CrudFullModule, CrudModule, CrudListPageBaseComponent, CrudItemPageBaseComponent } from '@smartsoft001/crud-shell-angular';
5
- import { CrudBaseService, FiltersBaseComponent, SharedConfig, SharedModule, SeoService, TranslationService } from '@smartsoft001-mobilems/angular';
5
+ import { CrudBaseService, FiltersBaseComponent, SharedConfig, SharedModule, SeoService, TranslationService, normalizeCycleId, FileUrlService, DataService, MuseumSeoService, PARENT_TITLE_TOKEN, decodeHtmlEntities, stripHtml, createCatalog } from '@smartsoft001-mobilems/angular';
6
+ import { firstValueFrom, map, catchError, of, distinctUntilChanged, switchMap } from 'rxjs';
6
7
  import { NgTemplateOutlet, CommonModule, isPlatformBrowser } from '@angular/common';
7
8
  import { createFeatureSelector } from '@ngrx/store';
8
- import { toSignal } from '@angular/core/rxjs-interop';
9
+ import { toSignal, toObservable } from '@angular/core/rxjs-interop';
9
10
  import { ActivatedRoute, Router } from '@angular/router';
10
11
  import { HttpClient } from '@angular/common/http';
11
- import { map, catchError, of } from 'rxjs';
12
+ import { TranslateService } from '@ngx-translate/core';
13
+ import { capitalize } from '@smartsoft001/utils';
12
14
 
15
+ /**
16
+ * Cards with an image first (order within each group kept), so grey
17
+ * placeholders do not open the module when the API has cards with images.
18
+ */
19
+ function withImagesFirst(cards) {
20
+ return [
21
+ ...cards.filter((card) => !!card?.image),
22
+ ...cards.filter((card) => !card?.image),
23
+ ];
24
+ }
13
25
  class ArticlesService extends CrudBaseService {
14
26
  getUrlNameForDetails() {
15
27
  return 'article';
@@ -17,6 +29,24 @@ class ArticlesService extends CrudBaseService {
17
29
  getUrlNameForList() {
18
30
  return 'Article';
19
31
  }
32
+ /** `POST api/article/:id/visit` — the visit counter of the details page. */
33
+ logVisit(id) {
34
+ return firstValueFrom(this.http.post(`${this.config.apiUrl}api/article/${id}/visit`, {}));
35
+ }
36
+ /**
37
+ * `GET article/:id/related` — objects, collections and other articles with
38
+ * images first, without the article itself. No data or an API error give
39
+ * empty groups, so the details page works without the module.
40
+ */
41
+ getRelated(id) {
42
+ return this.http
43
+ .get(`${this.config.apiUrl}article/${id}/related`)
44
+ .pipe(map((response) => ({
45
+ objects: withImagesFirst(response?.data?.objects ?? []),
46
+ collections: withImagesFirst(response?.data?.collections ?? []),
47
+ articles: withImagesFirst((response?.data?.articles ?? []).filter((article) => String(article?.id) !== String(id))),
48
+ })), catchError(() => of({ objects: [], collections: [], articles: [] })));
49
+ }
20
50
  /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ArticlesService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
21
51
  /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ArticlesService }); }
22
52
  }
@@ -314,9 +344,480 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
314
344
  type: Directive
315
345
  }], ctorParameters: () => [] });
316
346
 
347
+ /**
348
+ * Cykle artykułu z pominięciem pustych wpisów, z id znormalizowanym do
349
+ * liczby. `cycles: null` (albo brak pola) daje pustą tablicę, więc szablon
350
+ * nie renderuje wtedy wiersza tagów (MOB-1605).
351
+ */
352
+ function articleCycleTags(item) {
353
+ const cycles = item?.cycles;
354
+ if (!Array.isArray(cycles))
355
+ return [];
356
+ return cycles.flatMap((cycle) => {
357
+ const id = normalizeCycleId(cycle?.id);
358
+ const name = typeof cycle?.name === 'string' ? cycle.name.trim() : '';
359
+ return id === null || !name ? [] : [{ id, name }];
360
+ });
361
+ }
362
+ /**
363
+ * `queryParams` linku tagu — lista artykułów zawężona do cyklu, w formacie
364
+ * `?filter=` czytanym przez listę (`filter[cycles][]=<id>` w API).
365
+ */
366
+ function cycleFilterQueryParams(tag) {
367
+ return {
368
+ filter: JSON.stringify([
369
+ { name: 'cycles', data: [{ id: tag.id, value: tag.name }] },
370
+ ]),
371
+ };
372
+ }
373
+ /**
374
+ * Pastylki karty artykułu: wyłącznie `cycles[]` („Opis pól”, MOB-1823 —
375
+ * „cycles: name, kolejne w pastylkach”). Zapas `keywords[]` z MOB-1604
376
+ * zniknął: dokument go nie przewiduje, a reguła „`null`/puste → nie
377
+ * pokazujemy całej kategorii” chowa wiersz tagów, gdy artykuł nie ma cykli.
378
+ */
379
+ function articleCardTags(item) {
380
+ return articleCycleTags(item).map((tag) => ({ ...tag, source: 'cycles' }));
381
+ }
382
+ /**
383
+ * `queryParams` linku pastylki — lista zawężona do cyklu
384
+ * (`filter[cycles][]=<id>`) albo słowa kluczowego (`filter[keywords][]=<id>`).
385
+ */
386
+ function cardTagFilterQueryParams(tag) {
387
+ return {
388
+ filter: JSON.stringify([
389
+ { name: tag.source, data: [{ id: tag.id, value: tag.name }] },
390
+ ]),
391
+ };
392
+ }
393
+ /** Odstęp między pastylkami (`gap-2`). */
394
+ const TAG_GAP = 8;
395
+ /** Padding 12 + 12 i obrys 1 + 1 pastylki (`smart-px-3 smart-border`). */
396
+ const TAG_CHROME = 26;
397
+ /** Etykieta przycina się na 18rem (`smart-max-w-[18rem]`). */
398
+ const TAG_MAX_LABEL = 288;
399
+ /**
400
+ * Zachowawcza szerokość etykiety Open Sans 14 / tracking 0,5 px. Pomiar
401
+ * w Chromium 2026-10-06: 7,2–8,1 px na znak dla zwykłych słów, 13,4 dla „W”
402
+ * — wolimy przeszacować (pastylka spadnie do „+N”) niż wyjść poza blok.
403
+ */
404
+ function estimateTagWidth(label) {
405
+ let width = 0;
406
+ for (const char of label) {
407
+ if (char === ' ')
408
+ width += 4.5;
409
+ else if ('WMwm'.includes(char))
410
+ width += 13.5;
411
+ else if (char !== char.toLowerCase())
412
+ width += 11;
413
+ else if ('iljtfr.,:;!|'.includes(char))
414
+ width += 5;
415
+ else if (/[\d+#&%]/.test(char))
416
+ width += 9;
417
+ else
418
+ width += 8.2;
419
+ }
420
+ return TAG_CHROME + Math.min(TAG_MAX_LABEL, Math.ceil(width));
421
+ }
422
+ /**
423
+ * Ile pastylek zmieści się w `maxRows` wierszach o szerokości `rowWidth`
424
+ * (MOB-1604, recenzja): liczone PRZED renderem, więc w DOM nie ma linków
425
+ * ukrytych przez `overflow` (fokus nie trafia w niewidoczne elementy).
426
+ * Nadmiar zastępuje pastylka „+N” — jej szerokość jest rezerwowana
427
+ * w ostatnim wierszu.
428
+ */
429
+ function fitCardTags(tags, rowWidth, maxRows = 2) {
430
+ const place = (widths) => {
431
+ let rows = widths.length ? 1 : 0;
432
+ let used = 0;
433
+ for (const width of widths) {
434
+ const next = used === 0 ? width : used + TAG_GAP + width;
435
+ if (next <= rowWidth) {
436
+ used = next;
437
+ }
438
+ else {
439
+ rows += 1;
440
+ used = width;
441
+ }
442
+ }
443
+ return rows;
444
+ };
445
+ const widths = tags.map((tag) => estimateTagWidth(tag.name));
446
+ if (place(widths) <= maxRows)
447
+ return { visible: tags, hidden: 0 };
448
+ for (let count = tags.length - 1; count >= 0; count--) {
449
+ const more = estimateTagWidth(`+${tags.length - count}`);
450
+ if (place([...widths.slice(0, count), more]) <= maxRows) {
451
+ return { visible: tags.slice(0, count), hidden: tags.length - count };
452
+ }
453
+ }
454
+ return { visible: [], hidden: tags.length };
455
+ }
456
+ /**
457
+ * Tag key `${source}:${id}` — cycle and keyword ids are separate domains, so
458
+ * an id alone may repeat. Route ids are strings, dictionary ids numbers:
459
+ * `'7'` and `7` give the same key.
460
+ */
461
+ function articleFilterTagKey(tag) {
462
+ const id = typeof tag.id === 'string' && tag.id.trim() !== '' && !isNaN(Number(tag.id))
463
+ ? Number(tag.id)
464
+ : tag.id;
465
+ return `${tag.source ?? 'cycles'}:${String(id)}`;
466
+ }
467
+ /**
468
+ * Polish plural form of a minute count for the reading time
469
+ * (`ARTICLES.readingTime.one|few|many`).
470
+ */
471
+ function readingTimeForm(minutes) {
472
+ if (minutes === 1)
473
+ return 'one';
474
+ const lastDigit = minutes % 10;
475
+ const lastTwo = minutes % 100;
476
+ return lastDigit >= 2 && lastDigit <= 4 && (lastTwo < 12 || lastTwo > 14)
477
+ ? 'few'
478
+ : 'many';
479
+ }
480
+
481
+ /**
482
+ * Logic of an article list card (grid or list row): image with the
483
+ * placeholder, description, authors, cycle and keyword tags with their list
484
+ * links. The card keeps its template, e.g. `fitCardTags(tags(), 400)`.
485
+ */
486
+ class ArticleCardBaseComponent {
487
+ constructor() {
488
+ this.fileUrlService = inject(FileUrlService);
489
+ this.item = input(undefined, /* @ts-ignore */
490
+ ...(ngDevMode ? [{ debugName: "item" }] : /* istanbul ignore next */ []));
491
+ /** Placeholder image when the article has none. */
492
+ this.placeholderImage = '/gfx/no-image.png';
493
+ this.imageSrc = computed(() => {
494
+ const item = this.item();
495
+ return item
496
+ ? this.fileUrlService.get(item.image, 'cache', this.placeholderImage)
497
+ : '';
498
+ }, /* @ts-ignore */
499
+ ...(ngDevMode ? [{ debugName: "imageSrc" }] : /* istanbul ignore next */ []));
500
+ this.description = computed(() => this.item()?.shortDescription || '', /* @ts-ignore */
501
+ ...(ngDevMode ? [{ debugName: "description" }] : /* istanbul ignore next */ []));
502
+ /** Authors, comma separated; nameless entries are dropped. */
503
+ this.authorsText = computed(() => (this.item()?.authors ?? [])
504
+ .map((author) => author?.name?.trim())
505
+ .filter((name) => !!name)
506
+ .join(', '), /* @ts-ignore */
507
+ ...(ngDevMode ? [{ debugName: "authorsText" }] : /* istanbul ignore next */ []));
508
+ this.cycles = computed(() => articleCycleTags(this.item()), /* @ts-ignore */
509
+ ...(ngDevMode ? [{ debugName: "cycles" }] : /* istanbul ignore next */ []));
510
+ /** Card tags — cycles only; none = no tag row. */
511
+ this.tags = computed(() => articleCardTags(this.item()), /* @ts-ignore */
512
+ ...(ngDevMode ? [{ debugName: "tags" }] : /* istanbul ignore next */ []));
513
+ this.keywords = computed(() => this.item()?.keywords ?? [], /* @ts-ignore */
514
+ ...(ngDevMode ? [{ debugName: "keywords" }] : /* istanbul ignore next */ []));
515
+ }
516
+ /** `queryParams` of a tag link — the list narrowed to the cycle/keyword. */
517
+ tagQueryParams(tag) {
518
+ return cardTagFilterQueryParams(tag);
519
+ }
520
+ /** `queryParams` of a keyword link; `{}` for a nameless keyword. */
521
+ keywordQueryParams(keyword) {
522
+ const name = keyword?.name;
523
+ return name
524
+ ? cardTagFilterQueryParams({
525
+ id: keyword.id,
526
+ name,
527
+ source: 'keywords',
528
+ })
529
+ : {};
530
+ }
531
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ArticleCardBaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
532
+ /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.3", type: ArticleCardBaseComponent, isStandalone: true, inputs: { item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
533
+ }
534
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ArticleCardBaseComponent, decorators: [{
535
+ type: Directive
536
+ }], propDecorators: { item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: false }] }] } });
537
+
538
+ /** Query key of the theme tags — cycles (`filter[cycles][]=<id>`). */
539
+ const ARTICLE_CYCLES_FILTER_KEY = 'cycles';
540
+ /**
541
+ * Query key of a keyword from the route (`filter[keywords][]=<id>`) — links
542
+ * from older card tags. The tag bar has no keyword dictionary, so an active
543
+ * keyword shows as a selected tag that can be unselected.
544
+ */
545
+ const ARTICLE_KEYWORDS_FILTER_KEY = 'keywords';
546
+ /**
547
+ * Theme tag bar of an articles list (cycles + keywords). Call in an injection
548
+ * context; the dictionary is loaded once per instance.
549
+ */
550
+ function createArticleCycleTags(catalog) {
551
+ const dictionary = toSignal(inject(DataService)
552
+ .loadCycleTags()
553
+ .pipe(map((items) => items.filter((item) => !!item?.name?.trim())), catchError(() => of([]))), { initialValue: [] });
554
+ const keywordTags = computed(() => catalog
555
+ .queryItems(ARTICLE_KEYWORDS_FILTER_KEY)
556
+ .filter((item) => normalizeCycleId(item?.id) !== null && !!item?.name?.trim())
557
+ .map((item) => ({ ...item, source: 'keywords' })), /* @ts-ignore */
558
+ ...(ngDevMode ? [{ debugName: "keywordTags" }] : /* istanbul ignore next */ []));
559
+ const missingCycleTags = computed(() => {
560
+ const known = new Set(dictionary().map((item) => normalizeCycleId(item.id)));
561
+ const seen = new Set();
562
+ return catalog.queryItems(ARTICLE_CYCLES_FILTER_KEY).flatMap((item) => {
563
+ const id = normalizeCycleId(item?.id);
564
+ const name = typeof item?.name === 'string' ? item.name.trim() : '';
565
+ if (id === null || !name || known.has(id) || seen.has(id)) {
566
+ return [];
567
+ }
568
+ seen.add(id);
569
+ return [{ ...item, id, name, source: 'cycles' }];
570
+ });
571
+ }, /* @ts-ignore */
572
+ ...(ngDevMode ? [{ debugName: "missingCycleTags" }] : /* istanbul ignore next */ []));
573
+ const selectedCycleIds = computed(() => catalog
574
+ .queryItems(ARTICLE_CYCLES_FILTER_KEY)
575
+ .map((item) => normalizeCycleId(item.id))
576
+ .filter((id) => id !== null), /* @ts-ignore */
577
+ ...(ngDevMode ? [{ debugName: "selectedCycleIds" }] : /* istanbul ignore next */ []));
578
+ return {
579
+ dictionary,
580
+ keywordTags,
581
+ missingCycleTags,
582
+ barTags: computed(() => [
583
+ ...keywordTags(),
584
+ ...missingCycleTags(),
585
+ ...dictionary().map((item) => ({ ...item, source: 'cycles' })),
586
+ ]),
587
+ selectedCycleIds,
588
+ selectedKeys: computed(() => [
589
+ ...selectedCycleIds().map((id) => articleFilterTagKey({ id, source: 'cycles' })),
590
+ ...keywordTags().map((tag) => articleFilterTagKey(tag)),
591
+ ]),
592
+ toggle: (tag) => {
593
+ const key = tag.source === ARTICLE_KEYWORDS_FILTER_KEY
594
+ ? ARTICLE_KEYWORDS_FILTER_KEY
595
+ : ARTICLE_CYCLES_FILTER_KEY;
596
+ const { source: _source, ...plain } = tag;
597
+ catalog.toggleQueryItem(key, plain);
598
+ },
599
+ };
600
+ }
601
+
602
+ /**
603
+ * Base of an article details page: SEO (also during SSR, via
604
+ * `MuseumSeoService.applyForSsr`), the parent title for widget downloads,
605
+ * scroll to top and the visit counter on every loaded article, sorted
606
+ * widgets, plain title, lead, authors and cycle tags. The page provides
607
+ * `PARENT_TITLE_TOKEN` (`useFactory: () => signal('')`) and its template.
608
+ */
609
+ class ArticleDetailsBaseComponent extends ArticlesItemComponent {
610
+ constructor() {
611
+ super();
612
+ this.fileUrlService = inject(FileUrlService);
613
+ this.museumSeoService = inject(MuseumSeoService);
614
+ this.articlesService = inject(ArticlesService);
615
+ this.parentTitle = inject(PARENT_TITLE_TOKEN);
616
+ this.platformId = inject(PLATFORM_ID);
617
+ /** Translation key of the site name in the SEO title. */
618
+ this.siteNameKey = 'MODEL.mainTitle';
619
+ this.article = computed(() => this.item(), /* @ts-ignore */
620
+ ...(ngDevMode ? [{ debugName: "article" }] : /* istanbul ignore next */ []));
621
+ /** Bottom widgets by `position` (stable; without a position last). */
622
+ this.widgets = computed(() => sortByPosition(this.item()?.bottomWidgets ?? []), /* @ts-ignore */
623
+ ...(ngDevMode ? [{ debugName: "widgets" }] : /* istanbul ignore next */ []));
624
+ /** Title without markup — the API returns `<p>…</p>`. */
625
+ this.altTitle = computed(() => decodeHtmlEntities(stripHtml(this.item()?.title ?? '')).trim(), /* @ts-ignore */
626
+ ...(ngDevMode ? [{ debugName: "altTitle" }] : /* istanbul ignore next */ []));
627
+ /** `shortDescription` when it has text, else `''`. */
628
+ this.lead = computed(() => {
629
+ const text = this.item()?.shortDescription ?? '';
630
+ return stripHtml(text).trim() ? text : '';
631
+ }, /* @ts-ignore */
632
+ ...(ngDevMode ? [{ debugName: "lead" }] : /* istanbul ignore next */ []));
633
+ this.authorsText = computed(() => (this.article()?.authors ?? [])
634
+ .map((author) => author?.name?.trim())
635
+ .filter(Boolean)
636
+ .join(', '), /* @ts-ignore */
637
+ ...(ngDevMode ? [{ debugName: "authorsText" }] : /* istanbul ignore next */ []));
638
+ this.cycleTags = computed(() => articleCycleTags(this.article()), /* @ts-ignore */
639
+ ...(ngDevMode ? [{ debugName: "cycleTags" }] : /* istanbul ignore next */ []));
640
+ /** Rounded `estimatedReadingTime` in minutes, `0` when unknown. */
641
+ this.readingMinutes = computed(() => Math.round(Number(this.article()?.estimatedReadingTime) || 0), /* @ts-ignore */
642
+ ...(ngDevMode ? [{ debugName: "readingMinutes" }] : /* istanbul ignore next */ []));
643
+ this.museumSeoService.applyForSsr(() => this.fetchSsrSeo());
644
+ effect(() => {
645
+ const item = this.item();
646
+ if (!item)
647
+ return;
648
+ this.parentTitle.set(this.altTitle());
649
+ this.museumSeoService.apply(this.buildSeoMetadata(item));
650
+ if (isPlatformBrowser(this.platformId)) {
651
+ window.scrollTo({ top: 0, behavior: 'instant' });
652
+ void this.articlesService.logVisit(item.id).catch(() => undefined);
653
+ }
654
+ });
655
+ }
656
+ /** SEO of the article: `<title> - <site>`, description, image. */
657
+ buildSeoMetadata(item) {
658
+ const title = decodeHtmlEntities(stripHtml(item.title)).trim();
659
+ return {
660
+ title: `${title} - ${this.translationService.get(this.siteNameKey)}`,
661
+ description: item.shortDescription
662
+ ? decodeHtmlEntities(stripHtml(item.shortDescription)).trim()
663
+ : undefined,
664
+ image: item.image ? this.fileUrlService.get(item.image) : undefined,
665
+ imageAlt: title,
666
+ type: 'article',
667
+ };
668
+ }
669
+ async fetchSsrSeo() {
670
+ const id = this.route.snapshot.paramMap.get('id');
671
+ if (!id)
672
+ return null;
673
+ try {
674
+ const item = await this.articlesService.getById(id);
675
+ return item ? this.buildSeoMetadata(item) : null;
676
+ }
677
+ catch {
678
+ return null;
679
+ }
680
+ }
681
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ArticleDetailsBaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
682
+ /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.3", type: ArticleDetailsBaseComponent, isStandalone: true, usesInheritance: true, ngImport: i0 }); }
683
+ }
684
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ArticleDetailsBaseComponent, decorators: [{
685
+ type: Directive
686
+ }], ctorParameters: () => [] });
687
+ /**
688
+ * „Related” module of a details page — `GET article/:id/related` for the
689
+ * current article, empty groups before the first answer. Call in an
690
+ * injection context, e.g. a field of an `ArticleDetailsBaseComponent`.
691
+ */
692
+ function createArticleRelated(articleId) {
693
+ const service = inject(ArticlesService);
694
+ const empty = {
695
+ objects: [],
696
+ collections: [],
697
+ articles: [],
698
+ };
699
+ return toSignal(toObservable(articleId).pipe(distinctUntilChanged(), switchMap((id) => id !== undefined && id !== null
700
+ ? service.getRelated(id)
701
+ : of(empty)), map((related) => related ?? empty)), { initialValue: empty });
702
+ }
703
+ /** Like lodash `sortBy(items, 'position')`: stable, missing positions last. */
704
+ function sortByPosition(items) {
705
+ return [...items].sort((a, b) => {
706
+ const left = a?.position;
707
+ const right = b?.position;
708
+ if (left == null && right == null)
709
+ return 0;
710
+ if (left == null)
711
+ return 1;
712
+ if (right == null)
713
+ return -1;
714
+ return left - right;
715
+ });
716
+ }
717
+
718
+ /**
719
+ * Base of an articles list page on the shared catalog engine
720
+ * (`createCatalog`): first read, persistence, sort/size/page, query helpers
721
+ * and the read guard come from `catalog`; the page keeps its template.
722
+ *
723
+ * ```ts
724
+ * export class ListComponent extends ArticlesCatalogBaseComponent {
725
+ * constructor() {
726
+ * super({ defaults, persistKey: 'ARTICLES', listPath: '/artykuly', searchTab: 'artykuly' });
727
+ * }
728
+ * }
729
+ * ```
730
+ *
731
+ * A `#listContainer` element in the template is scrolled to after a page
732
+ * change from the bottom pagination (`bottomPageChange`).
733
+ */
734
+ class ArticlesCatalogBaseComponent extends ArticlesListComponent {
735
+ constructor(options) {
736
+ super();
737
+ this.ListMode = ListMode;
738
+ this.translateService = inject(TranslateService);
739
+ this.museumSeoService = inject(MuseumSeoService);
740
+ this.platformId = inject(PLATFORM_ID);
741
+ this.listContainer = viewChild('listContainer', /* @ts-ignore */
742
+ ...(ngDevMode ? [{ debugName: "listContainer" }] : /* istanbul ignore next */ []));
743
+ this.scrollToListAfterLoad = false;
744
+ const { seo, ...catalogOptions } = options;
745
+ this.catalog = createCatalog({ ...catalogOptions, facade: this.facade });
746
+ // After a page change from the bottom pagination scroll to the list top
747
+ // once the data has loaded.
748
+ effect(() => {
749
+ const loading = this.catalog.isLoading();
750
+ if (!loading &&
751
+ this.scrollToListAfterLoad &&
752
+ isPlatformBrowser(this.platformId)) {
753
+ this.scrollToListAfterLoad = false;
754
+ setTimeout(() => this.listContainer()?.nativeElement.scrollIntoView({
755
+ behavior: 'smooth',
756
+ block: 'start',
757
+ }));
758
+ }
759
+ });
760
+ if (seo)
761
+ this.applyListSeo(seo);
762
+ }
763
+ /** The list root element (`#listContainer`), e.g. to move focus. */
764
+ get listContainerElement() {
765
+ return this.listContainer()?.nativeElement;
766
+ }
767
+ onSortChange(sort) {
768
+ this.catalog.setSort(sort);
769
+ }
770
+ /** Sort by key (`title-asc`, `random`). */
771
+ sortChange(key) {
772
+ this.catalog.setSortKey(key);
773
+ }
774
+ onLimitChange(limit) {
775
+ this.catalog.setLimit(limit);
776
+ }
777
+ pageChange(page) {
778
+ this.catalog.setPage(page);
779
+ }
780
+ bottomPageChange(page) {
781
+ this.scrollToListAfterLoad = true;
782
+ this.pageChange(page);
783
+ }
784
+ onListModeChange(mode) {
785
+ this.catalog.setListMode(mode);
786
+ }
787
+ onFilterBarSearch(phrase) {
788
+ this.catalog.search(phrase);
789
+ }
790
+ retryLoad() {
791
+ this.catalog.retry();
792
+ }
793
+ clearAllFilters() {
794
+ this.catalog.clearAll();
795
+ }
796
+ /**
797
+ * Full list meta (title, description, canonical, og), not in a search tab —
798
+ * the canonical url of the list must not point at a phrase url.
799
+ */
800
+ applyListSeo(seo) {
801
+ if (this.catalog.searchEnabled())
802
+ return;
803
+ const t = (key) => capitalize(this.translateService.instant(key));
804
+ this.museumSeoService.apply({
805
+ title: `${t(seo.titleKey)} - ${t(seo.siteNameKey ?? 'MODEL.mainTitle')}`,
806
+ description: t(seo.descriptionKey),
807
+ type: 'website',
808
+ url: seo.url,
809
+ });
810
+ }
811
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ArticlesCatalogBaseComponent, deps: "invalid", target: i0.ɵɵFactoryTarget.Directive }); }
812
+ /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.1.3", type: ArticlesCatalogBaseComponent, isStandalone: true, viewQueries: [{ propertyName: "listContainer", first: true, predicate: ["listContainer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0 }); }
813
+ }
814
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ArticlesCatalogBaseComponent, decorators: [{
815
+ type: Directive
816
+ }], ctorParameters: () => [{ type: undefined }], propDecorators: { listContainer: [{ type: i0.ViewChild, args: ['listContainer', { isSignal: true }] }] } });
817
+
317
818
  /**
318
819
  * Generated bundle index. Do not edit.
319
820
  */
320
821
 
321
- export { ArticlesFacade, ArticlesItemComponent, ArticlesListComponent, ArticlesModule, ArticlesPreviewComponent, ArticlesService, ArticlesSharedModule, ComponentsModule, FiltersComponent, ListComponent, SERVICES, getArticlesState, getReducer, initialState };
822
+ export { ARTICLE_CYCLES_FILTER_KEY, ARTICLE_KEYWORDS_FILTER_KEY, ArticleCardBaseComponent, ArticleDetailsBaseComponent, ArticlesCatalogBaseComponent, ArticlesFacade, ArticlesItemComponent, ArticlesListComponent, ArticlesModule, ArticlesPreviewComponent, ArticlesService, ArticlesSharedModule, ComponentsModule, FiltersComponent, ListComponent, SERVICES, articleCardTags, articleCycleTags, articleFilterTagKey, cardTagFilterQueryParams, createArticleCycleTags, createArticleRelated, cycleFilterQueryParams, estimateTagWidth, fitCardTags, getArticlesState, getReducer, initialState, readingTimeForm, sortByPosition, withImagesFirst };
322
823
  //# sourceMappingURL=smartsoft001-mobilems-articles-shell-angular.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"smartsoft001-mobilems-articles-shell-angular.mjs","sources":["../../../../../../packages/articles/shell/angular/src/lib/services/articles/articles.service.ts","../../../../../../packages/articles/shell/angular/src/lib/+state/articles.facade.ts","../../../../../../packages/articles/shell/angular/src/lib/+state/articles.reducer.ts","../../../../../../packages/articles/shell/angular/src/lib/components/list/list.component.ts","../../../../../../packages/articles/shell/angular/src/lib/components/list/list.component.html","../../../../../../packages/articles/shell/angular/src/lib/components/filters/filters.component.ts","../../../../../../packages/articles/shell/angular/src/lib/components/filters/filters.component.html","../../../../../../packages/articles/shell/angular/src/lib/components/components.module.ts","../../../../../../packages/articles/shell/angular/src/lib/services/index.ts","../../../../../../packages/articles/shell/angular/src/lib/articles.module.ts","../../../../../../packages/articles/shell/angular/src/lib/articles-shared.module.ts","../../../../../../packages/articles/shell/angular/src/lib/+state/articles.selectors.ts","../../../../../../packages/articles/shell/angular/src/lib/pages/list/list.component.ts","../../../../../../packages/articles/shell/angular/src/lib/pages/item/item.component.ts","../../../../../../packages/articles/shell/angular/src/lib/pages/preview/preview.component.ts","../../../../../../packages/articles/shell/angular/src/smartsoft001-mobilems-articles-shell-angular.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\n\nimport { CrudBaseService } from '@smartsoft001-mobilems/angular';\nimport { Article } from '@smartsoft001-mobilems/articles-domain';\n\n@Injectable()\nexport class ArticlesService extends CrudBaseService<Article> {\n getUrlNameForDetails(): string {\n return 'article';\n }\n\n getUrlNameForList(): string {\n return 'Article';\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { CrudFacade } from '@smartsoft001/crud-shell-angular';\n\nimport { Article } from '@smartsoft001-mobilems/articles-domain';\n\nimport { ArticlesService } from '../services/articles/articles.service';\n\n@Injectable()\nexport class ArticlesFacade extends CrudFacade<Article> {\n protected service = inject(ArticlesService);\n\n async downloadPdf(item: { title: string; id: string }): Promise<void> {\n await this.service.downloadPdf(item.title, item.id);\n }\n}\n","import { Action } from '@ngrx/store';\nimport {\n State as SmartState,\n initialState as smartInitialState,\n getReducer as smartGetReducer,\n} from '@smartsoft001/crud-shell-angular';\n\nimport { Article } from '@smartsoft001-mobilems/articles-domain';\n\n// import {ArticlesFacade} from \"./articles.facade\";\n\nexport type State = SmartState<Article>;\n\nexport const initialState = {\n ...smartInitialState,\n};\n\nconst articlesReducer = (state = initialState, action: any) => {\n switch (action.type) {\n //case ArticlesFacade.name + ' test':\n // return {\n // ...state,\n // loaded: false,\n // }\n\n default:\n return smartGetReducer('articles')(state, action);\n }\n};\n\nexport function getReducer() {\n return (state: any, action: Action) => {\n return articlesReducer(state, action);\n };\n}\n","import { NgTemplateOutlet } from '@angular/common';\nimport {\n Component,\n TemplateRef,\n contentChild,\n inject,\n ChangeDetectionStrategy,\n} from '@angular/core';\n\nimport { ArticlesFacade } from '../../+state/articles.facade';\n\n@Component({\n selector: 'smart-mobilems-articles-list',\n templateUrl: './list.component.html',\n changeDetection: ChangeDetectionStrategy.Eager,\n imports: [NgTemplateOutlet],\n})\nexport class ListComponent {\n private facade = inject(ArticlesFacade);\n\n list = this.facade.list;\n loading = this.facade.loading;\n\n itemTpl = contentChild<TemplateRef<any>>('itemTpl');\n loadingTpl = contentChild<TemplateRef<any>>('loadingTpl');\n}\n","@if (list(); as listData) {\n @if (listData && itemTpl()) {\n @for (item of listData; track item) {\n <ng-container\n *ngTemplateOutlet=\"itemTpl(); context: { $implicit: item }\"\n ></ng-container>\n }\n }\n}\n\n@if (loading()) {\n @if (loadingTpl()) {\n <ng-container *ngTemplateOutlet=\"loadingTpl()\"></ng-container>\n }\n}\n","import { NgTemplateOutlet } from '@angular/common';\nimport { Component, ChangeDetectionStrategy } from '@angular/core';\n\nimport { FiltersBaseComponent } from '@smartsoft001-mobilems/angular';\nimport { Article } from '@smartsoft001-mobilems/articles-domain';\n\n@Component({\n selector: 'smart-mobilems-articles-filters',\n templateUrl: './filters.component.html',\n changeDetection: ChangeDetectionStrategy.Eager,\n imports: [NgTemplateOutlet],\n})\nexport class FiltersComponent extends FiltersBaseComponent<Article> {}\n","<ng-container\n *ngTemplateOutlet=\"contentTpl(); context: { $implicit: context }\"\n></ng-container>\n","import { CommonModule } from '@angular/common';\nimport { NgModule } from '@angular/core';\n\nimport { FiltersComponent } from './filters/filters.component';\nimport { ListComponent } from './list/list.component';\n\nconst COMPONENTS = [FiltersComponent, ListComponent];\n\n@NgModule({\n imports: [CommonModule, COMPONENTS],\n exports: [...COMPONENTS],\n})\nexport class ComponentsModule {}\n","import { CrudService } from '@smartsoft001/crud-shell-angular';\n\nimport { ArticlesService } from './articles/articles.service';\n\nexport * from './articles/articles.service';\n\nexport const SERVICES = [\n ArticlesService,\n {\n provide: CrudService,\n useClass: ArticlesService,\n },\n];\n","import { ModuleWithProviders, NgModule } from '@angular/core';\nimport { FILE_SERVICE_CONFIG, IFileServiceConfig } from '@smartsoft001/angular';\nimport {\n CrudConfig,\n CrudFullConfig,\n CrudFullModule,\n ICrudModuleOptionsWithoutRoutng,\n} from '@smartsoft001/crud-shell-angular';\nimport { IEntity } from '@smartsoft001/domain-core';\n\nimport { SharedConfig } from '@smartsoft001-mobilems/angular';\nimport { SharedModule } from '@smartsoft001-mobilems/angular';\n\nimport { ArticlesFacade } from './+state/articles.facade';\nimport { getReducer } from './+state/articles.reducer';\nimport { ComponentsModule } from './components';\nimport { SERVICES } from './services';\n\n@NgModule({\n declarations: [],\n providers: [...SERVICES, ArticlesFacade],\n imports: [SharedModule, ComponentsModule, CrudFullModule],\n exports: [ComponentsModule],\n})\nexport class ArticlesModule {\n static forFeature<T extends IEntity<string>>(\n options: ICrudModuleOptionsWithoutRoutng<T>,\n ): ModuleWithProviders<ArticlesModule> {\n if (!options.config.reducerFactory)\n options.config.reducerFactory = getReducer;\n return {\n ngModule: ArticlesModule,\n providers: [\n {\n provide: SharedConfig,\n useValue: { apiUrl: options.config.apiUrl } as SharedConfig,\n },\n { provide: CrudConfig, useValue: options.config },\n { provide: CrudFullConfig, useValue: options.config },\n {\n provide: FILE_SERVICE_CONFIG,\n useValue: { apiUrl: options.config.apiUrl } as IFileServiceConfig,\n },\n ],\n };\n }\n}\n","import { ModuleWithProviders, NgModule } from '@angular/core';\nimport { FILE_SERVICE_CONFIG, IFileServiceConfig } from '@smartsoft001/angular';\nimport {\n CrudConfig,\n CrudFullConfig,\n CrudModule,\n ICrudModuleOptionsWithoutRoutng,\n} from '@smartsoft001/crud-shell-angular';\nimport { IEntity } from '@smartsoft001/domain-core';\n\nimport { SharedConfig } from '@smartsoft001-mobilems/angular';\nimport { SharedModule } from '@smartsoft001-mobilems/angular';\n\nimport { ArticlesFacade } from './+state/articles.facade';\nimport { getReducer } from './+state/articles.reducer';\nimport { ComponentsModule } from './components';\nimport { SERVICES } from './services';\n\n@NgModule({\n declarations: [],\n providers: [...SERVICES, ArticlesFacade],\n imports: [SharedModule, ComponentsModule, CrudModule],\n exports: [ComponentsModule],\n})\nexport class ArticlesSharedModule {\n static forFeature<T extends IEntity<string>>(\n options: ICrudModuleOptionsWithoutRoutng<T>,\n ): ModuleWithProviders<ArticlesSharedModule> {\n if (!options.config.reducerFactory)\n options.config.reducerFactory = getReducer;\n return {\n ngModule: ArticlesSharedModule,\n providers: [\n {\n provide: SharedConfig,\n useValue: { apiUrl: options.config.apiUrl } as SharedConfig,\n },\n { provide: CrudConfig, useValue: options.config },\n { provide: CrudFullConfig, useValue: options.config },\n {\n provide: FILE_SERVICE_CONFIG,\n useValue: { apiUrl: options.config.apiUrl } as IFileServiceConfig,\n },\n ],\n };\n }\n}\n","import {\n createFeatureSelector,\n //createSelector\n} from '@ngrx/store';\n\nimport { State } from './articles.reducer';\n\nexport const getArticlesState = createFeatureSelector<State>('articles');\n\n//export const getArticlesTest = createSelector(\n// getArticlesState,\n// (state) => state.test\n//);\n","import { inject, Directive, Signal } from '@angular/core';\nimport { CrudListPageBaseComponent } from '@smartsoft001/crud-shell-angular';\n\nimport { Article } from '@smartsoft001-mobilems/articles-domain';\n\nimport { ArticlesFacade } from '../../+state';\n\n@Directive()\nexport abstract class ArticlesListComponent extends CrudListPageBaseComponent<Article> {\n protected readonly facade = inject(ArticlesFacade);\n\n list = this.facade.list as Signal<Article[]>;\n loading = this.facade.loading;\n filter = this.facade.filter;\n}\n","import { Directive, effect, inject } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { ActivatedRoute } from '@angular/router';\nimport { CrudItemPageBaseComponent } from '@smartsoft001/crud-shell-angular';\n\nimport { SeoService, TranslationService } from '@smartsoft001-mobilems/angular';\nimport { Article } from '@smartsoft001-mobilems/articles-domain';\n\nimport { ArticlesFacade } from '../../+state';\n\n@Directive()\nexport abstract class ArticlesItemComponent extends CrudItemPageBaseComponent<Article> {\n protected readonly route = inject(ActivatedRoute);\n protected readonly seoService = inject(SeoService);\n protected readonly translationService = inject(TranslationService);\n protected readonly params = toSignal(this.route.params);\n\n override facade = inject(ArticlesFacade);\n\n readonly loading = this.facade.loading;\n readonly item = this.facade.selected;\n\n protected constructor() {\n super();\n\n effect(() => {\n const params = this.params();\n\n const id = params['id'];\n if (id) {\n this.facade.select(id);\n }\n });\n }\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport { HttpClient } from '@angular/common/http';\nimport {\n computed,\n Directive,\n effect,\n inject,\n PLATFORM_ID,\n signal,\n} from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { catchError, map, of } from 'rxjs';\n\nimport { Article } from '@smartsoft001-mobilems/articles-domain';\nimport { ArticleWidget } from '@smartsoft001-mobilems/models';\n\n/** Query params the CMS appends when it opens `/article-preview`. */\nexport interface ArticlePreviewQueryParams {\n apiHost?: string;\n dataUrl?: string;\n pathUrl?: string;\n pageUrl?: string;\n mediaUrl?: string;\n templateName?: string;\n viewType?: string;\n}\n\n/**\n * CMS preview of an unpublished article.\n *\n * The CMS opens `/article-preview?dataUrl=...` in the editor's browser and the\n * page fetches `dataUrl` with the CMS session cookie, so the route must be\n * rendered on the client (`RenderMode.Client`). A `viewType=thematic-path`\n * preview is forwarded to `thematicPathRoute`. Subclass with the app's own\n * template; the subclass owns only the view.\n */\n@Directive()\nexport abstract class ArticlesPreviewComponent {\n protected readonly http = inject(HttpClient);\n protected readonly route = inject(ActivatedRoute);\n protected readonly router = inject(Router);\n protected readonly platformId = inject(PLATFORM_ID);\n protected readonly queryParams = toSignal(this.route.queryParams);\n\n /** Route a `thematic-path` preview is forwarded to; override per app. */\n protected readonly thematicPathRoute: string = '/sciezki-tematyczne';\n\n readonly loading = signal(true);\n readonly noQueryParams = signal(false);\n readonly noLogin = signal(false);\n readonly item = signal<Article | null>(null);\n\n readonly plainTitle = computed(() =>\n (this.item()?.title ?? '').replace(/<[^>]*>/g, ''),\n );\n\n readonly widgets = computed<ArticleWidget[]>(() => {\n const widgets = this.item()?.bottomWidgets;\n if (!widgets) return [];\n\n return [...widgets].sort((a, b) => a.position - b.position);\n });\n\n readonly authorsText = computed(\n () =>\n this.item()\n ?.authors?.map((author) => author.name)\n .filter(Boolean)\n .join(', ') ?? '',\n );\n\n protected constructor() {\n effect(() => {\n const params = (this.queryParams() ?? {}) as ArticlePreviewQueryParams;\n\n if (params.viewType === 'thematic-path' && params.pathUrl) {\n this.forwardThematicPath(params);\n return;\n }\n\n if (params.dataUrl) {\n this.fetchArticle(params.dataUrl);\n } else {\n this.noQueryParams.set(true);\n this.loading.set(false);\n }\n });\n\n effect(() => {\n if (this.item() && isPlatformBrowser(this.platformId)) {\n window.scrollTo({ top: 0, behavior: 'instant' });\n }\n });\n }\n\n private forwardThematicPath(params: ArticlePreviewQueryParams): void {\n const id = params.pathUrl?.split('/').pop();\n this.router.navigate([this.thematicPathRoute, id], {\n queryParams: {\n pathUrl: params.pathUrl,\n pageUrl: params.pageUrl,\n mediaUrl: params.mediaUrl,\n },\n });\n }\n\n private fetchArticle(dataUrl: string): void {\n this.loading.set(true);\n this.noLogin.set(false);\n\n this.http\n .get<{ data?: Article | null }>(dataUrl, { withCredentials: true })\n .pipe(\n map((response) => response?.data ?? null),\n catchError((error) => {\n console.error('Error fetching preview data:', error);\n this.noLogin.set(true);\n return of(null);\n }),\n )\n .subscribe((article) => {\n this.item.set(article);\n this.loading.set(false);\n });\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["smartInitialState","smartGetReducer"],"mappings":"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"}
1
+ {"version":3,"file":"smartsoft001-mobilems-articles-shell-angular.mjs","sources":["../../../../../../packages/articles/shell/angular/src/lib/services/articles/articles.service.ts","../../../../../../packages/articles/shell/angular/src/lib/+state/articles.facade.ts","../../../../../../packages/articles/shell/angular/src/lib/+state/articles.reducer.ts","../../../../../../packages/articles/shell/angular/src/lib/components/list/list.component.ts","../../../../../../packages/articles/shell/angular/src/lib/components/list/list.component.html","../../../../../../packages/articles/shell/angular/src/lib/components/filters/filters.component.ts","../../../../../../packages/articles/shell/angular/src/lib/components/filters/filters.component.html","../../../../../../packages/articles/shell/angular/src/lib/components/components.module.ts","../../../../../../packages/articles/shell/angular/src/lib/services/index.ts","../../../../../../packages/articles/shell/angular/src/lib/articles.module.ts","../../../../../../packages/articles/shell/angular/src/lib/articles-shared.module.ts","../../../../../../packages/articles/shell/angular/src/lib/+state/articles.selectors.ts","../../../../../../packages/articles/shell/angular/src/lib/pages/list/list.component.ts","../../../../../../packages/articles/shell/angular/src/lib/pages/item/item.component.ts","../../../../../../packages/articles/shell/angular/src/lib/pages/preview/preview.component.ts","../../../../../../packages/articles/shell/angular/src/lib/tools/article-tags.ts","../../../../../../packages/articles/shell/angular/src/lib/catalog/article-card-base.component.ts","../../../../../../packages/articles/shell/angular/src/lib/catalog/article-cycle-tags.ts","../../../../../../packages/articles/shell/angular/src/lib/catalog/article-details-base.component.ts","../../../../../../packages/articles/shell/angular/src/lib/catalog/articles-catalog-base.component.ts","../../../../../../packages/articles/shell/angular/src/smartsoft001-mobilems-articles-shell-angular.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport { catchError, firstValueFrom, map, Observable, of } from 'rxjs';\n\nimport { CrudBaseService } from '@smartsoft001-mobilems/angular';\nimport { Article } from '@smartsoft001-mobilems/articles-domain';\n\n/** Cards of `GET article/:id/related`, grouped as the API returns them. */\nexport interface IArticleRelated<\n O extends IArticleRelatedCard = IArticleRelatedCard,\n C extends IArticleRelatedCard = IArticleRelatedCard,\n A extends IArticleRelatedCard = IArticleRelatedCard,\n> {\n objects: O[];\n collections: C[];\n articles: A[];\n}\n\n/** The fields the related module relies on; the API sends full list cards. */\nexport interface IArticleRelatedCard {\n id?: string | number | null;\n image?: unknown;\n}\n\n/**\n * Cards with an image first (order within each group kept), so grey\n * placeholders do not open the module when the API has cards with images.\n */\nexport function withImagesFirst<T extends { image?: unknown }>(\n cards: readonly T[],\n): T[] {\n return [\n ...cards.filter((card) => !!card?.image),\n ...cards.filter((card) => !card?.image),\n ];\n}\n\n@Injectable()\nexport class ArticlesService extends CrudBaseService<Article> {\n getUrlNameForDetails(): string {\n return 'article';\n }\n\n getUrlNameForList(): string {\n return 'Article';\n }\n\n /** `POST api/article/:id/visit` — the visit counter of the details page. */\n logVisit(id: string | number): Promise<void> {\n return firstValueFrom(\n this.http.post<void>(`${this.config.apiUrl}api/article/${id}/visit`, {}),\n );\n }\n\n /**\n * `GET article/:id/related` — objects, collections and other articles with\n * images first, without the article itself. No data or an API error give\n * empty groups, so the details page works without the module.\n */\n getRelated<\n O extends IArticleRelatedCard = IArticleRelatedCard,\n C extends IArticleRelatedCard = IArticleRelatedCard,\n A extends IArticleRelatedCard = IArticleRelatedCard,\n >(id: string | number): Observable<IArticleRelated<O, C, A>> {\n return this.http\n .get<{\n data?: {\n objects?: O[] | null;\n collections?: C[] | null;\n articles?: A[] | null;\n } | null;\n }>(`${this.config.apiUrl}article/${id}/related`)\n .pipe(\n map((response) => ({\n objects: withImagesFirst(response?.data?.objects ?? []),\n collections: withImagesFirst(response?.data?.collections ?? []),\n articles: withImagesFirst(\n (response?.data?.articles ?? []).filter(\n (article) => String(article?.id) !== String(id),\n ),\n ),\n })),\n catchError(() => of({ objects: [], collections: [], articles: [] })),\n );\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { CrudFacade } from '@smartsoft001/crud-shell-angular';\n\nimport { Article } from '@smartsoft001-mobilems/articles-domain';\n\nimport { ArticlesService } from '../services/articles/articles.service';\n\n@Injectable()\nexport class ArticlesFacade extends CrudFacade<Article> {\n protected service = inject(ArticlesService);\n\n async downloadPdf(item: { title: string; id: string }): Promise<void> {\n await this.service.downloadPdf(item.title, item.id);\n }\n}\n","import { Action } from '@ngrx/store';\nimport {\n State as SmartState,\n initialState as smartInitialState,\n getReducer as smartGetReducer,\n} from '@smartsoft001/crud-shell-angular';\n\nimport { Article } from '@smartsoft001-mobilems/articles-domain';\n\n// import {ArticlesFacade} from \"./articles.facade\";\n\nexport type State = SmartState<Article>;\n\nexport const initialState = {\n ...smartInitialState,\n};\n\nconst articlesReducer = (state = initialState, action: any) => {\n switch (action.type) {\n //case ArticlesFacade.name + ' test':\n // return {\n // ...state,\n // loaded: false,\n // }\n\n default:\n return smartGetReducer('articles')(state, action);\n }\n};\n\nexport function getReducer() {\n return (state: any, action: Action) => {\n return articlesReducer(state, action);\n };\n}\n","import { NgTemplateOutlet } from '@angular/common';\nimport {\n Component,\n TemplateRef,\n contentChild,\n inject,\n ChangeDetectionStrategy,\n} from '@angular/core';\n\nimport { ArticlesFacade } from '../../+state/articles.facade';\n\n@Component({\n selector: 'smart-mobilems-articles-list',\n templateUrl: './list.component.html',\n changeDetection: ChangeDetectionStrategy.Eager,\n imports: [NgTemplateOutlet],\n})\nexport class ListComponent {\n private facade = inject(ArticlesFacade);\n\n list = this.facade.list;\n loading = this.facade.loading;\n\n itemTpl = contentChild<TemplateRef<any>>('itemTpl');\n loadingTpl = contentChild<TemplateRef<any>>('loadingTpl');\n}\n","@if (list(); as listData) {\n @if (listData && itemTpl()) {\n @for (item of listData; track item) {\n <ng-container\n *ngTemplateOutlet=\"itemTpl(); context: { $implicit: item }\"\n ></ng-container>\n }\n }\n}\n\n@if (loading()) {\n @if (loadingTpl()) {\n <ng-container *ngTemplateOutlet=\"loadingTpl()\"></ng-container>\n }\n}\n","import { NgTemplateOutlet } from '@angular/common';\nimport { Component, ChangeDetectionStrategy } from '@angular/core';\n\nimport { FiltersBaseComponent } from '@smartsoft001-mobilems/angular';\nimport { Article } from '@smartsoft001-mobilems/articles-domain';\n\n@Component({\n selector: 'smart-mobilems-articles-filters',\n templateUrl: './filters.component.html',\n changeDetection: ChangeDetectionStrategy.Eager,\n imports: [NgTemplateOutlet],\n})\nexport class FiltersComponent extends FiltersBaseComponent<Article> {}\n","<ng-container\n *ngTemplateOutlet=\"contentTpl(); context: { $implicit: context }\"\n></ng-container>\n","import { CommonModule } from '@angular/common';\nimport { NgModule } from '@angular/core';\n\nimport { FiltersComponent } from './filters/filters.component';\nimport { ListComponent } from './list/list.component';\n\nconst COMPONENTS = [FiltersComponent, ListComponent];\n\n@NgModule({\n imports: [CommonModule, COMPONENTS],\n exports: [...COMPONENTS],\n})\nexport class ComponentsModule {}\n","import { CrudService } from '@smartsoft001/crud-shell-angular';\n\nimport { ArticlesService } from './articles/articles.service';\n\nexport * from './articles/articles.service';\n\nexport const SERVICES = [\n ArticlesService,\n {\n provide: CrudService,\n useClass: ArticlesService,\n },\n];\n","import { ModuleWithProviders, NgModule } from '@angular/core';\nimport { FILE_SERVICE_CONFIG, IFileServiceConfig } from '@smartsoft001/angular';\nimport {\n CrudConfig,\n CrudFullConfig,\n CrudFullModule,\n ICrudModuleOptionsWithoutRoutng,\n} from '@smartsoft001/crud-shell-angular';\nimport { IEntity } from '@smartsoft001/domain-core';\n\nimport { SharedConfig } from '@smartsoft001-mobilems/angular';\nimport { SharedModule } from '@smartsoft001-mobilems/angular';\n\nimport { ArticlesFacade } from './+state/articles.facade';\nimport { getReducer } from './+state/articles.reducer';\nimport { ComponentsModule } from './components';\nimport { SERVICES } from './services';\n\n@NgModule({\n declarations: [],\n providers: [...SERVICES, ArticlesFacade],\n imports: [SharedModule, ComponentsModule, CrudFullModule],\n exports: [ComponentsModule],\n})\nexport class ArticlesModule {\n static forFeature<T extends IEntity<string>>(\n options: ICrudModuleOptionsWithoutRoutng<T>,\n ): ModuleWithProviders<ArticlesModule> {\n if (!options.config.reducerFactory)\n options.config.reducerFactory = getReducer;\n return {\n ngModule: ArticlesModule,\n providers: [\n {\n provide: SharedConfig,\n useValue: { apiUrl: options.config.apiUrl } as SharedConfig,\n },\n { provide: CrudConfig, useValue: options.config },\n { provide: CrudFullConfig, useValue: options.config },\n {\n provide: FILE_SERVICE_CONFIG,\n useValue: { apiUrl: options.config.apiUrl } as IFileServiceConfig,\n },\n ],\n };\n }\n}\n","import { ModuleWithProviders, NgModule } from '@angular/core';\nimport { FILE_SERVICE_CONFIG, IFileServiceConfig } from '@smartsoft001/angular';\nimport {\n CrudConfig,\n CrudFullConfig,\n CrudModule,\n ICrudModuleOptionsWithoutRoutng,\n} from '@smartsoft001/crud-shell-angular';\nimport { IEntity } from '@smartsoft001/domain-core';\n\nimport { SharedConfig } from '@smartsoft001-mobilems/angular';\nimport { SharedModule } from '@smartsoft001-mobilems/angular';\n\nimport { ArticlesFacade } from './+state/articles.facade';\nimport { getReducer } from './+state/articles.reducer';\nimport { ComponentsModule } from './components';\nimport { SERVICES } from './services';\n\n@NgModule({\n declarations: [],\n providers: [...SERVICES, ArticlesFacade],\n imports: [SharedModule, ComponentsModule, CrudModule],\n exports: [ComponentsModule],\n})\nexport class ArticlesSharedModule {\n static forFeature<T extends IEntity<string>>(\n options: ICrudModuleOptionsWithoutRoutng<T>,\n ): ModuleWithProviders<ArticlesSharedModule> {\n if (!options.config.reducerFactory)\n options.config.reducerFactory = getReducer;\n return {\n ngModule: ArticlesSharedModule,\n providers: [\n {\n provide: SharedConfig,\n useValue: { apiUrl: options.config.apiUrl } as SharedConfig,\n },\n { provide: CrudConfig, useValue: options.config },\n { provide: CrudFullConfig, useValue: options.config },\n {\n provide: FILE_SERVICE_CONFIG,\n useValue: { apiUrl: options.config.apiUrl } as IFileServiceConfig,\n },\n ],\n };\n }\n}\n","import {\n createFeatureSelector,\n //createSelector\n} from '@ngrx/store';\n\nimport { State } from './articles.reducer';\n\nexport const getArticlesState = createFeatureSelector<State>('articles');\n\n//export const getArticlesTest = createSelector(\n// getArticlesState,\n// (state) => state.test\n//);\n","import { inject, Directive, Signal } from '@angular/core';\nimport { CrudListPageBaseComponent } from '@smartsoft001/crud-shell-angular';\n\nimport { Article } from '@smartsoft001-mobilems/articles-domain';\n\nimport { ArticlesFacade } from '../../+state';\n\n@Directive()\nexport abstract class ArticlesListComponent extends CrudListPageBaseComponent<Article> {\n protected readonly facade = inject(ArticlesFacade);\n\n list = this.facade.list as Signal<Article[]>;\n loading = this.facade.loading;\n filter = this.facade.filter;\n}\n","import { Directive, effect, inject } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { ActivatedRoute } from '@angular/router';\nimport { CrudItemPageBaseComponent } from '@smartsoft001/crud-shell-angular';\n\nimport { SeoService, TranslationService } from '@smartsoft001-mobilems/angular';\nimport { Article } from '@smartsoft001-mobilems/articles-domain';\n\nimport { ArticlesFacade } from '../../+state';\n\n@Directive()\nexport abstract class ArticlesItemComponent extends CrudItemPageBaseComponent<Article> {\n protected readonly route = inject(ActivatedRoute);\n protected readonly seoService = inject(SeoService);\n protected readonly translationService = inject(TranslationService);\n protected readonly params = toSignal(this.route.params);\n\n override facade = inject(ArticlesFacade);\n\n readonly loading = this.facade.loading;\n readonly item = this.facade.selected;\n\n protected constructor() {\n super();\n\n effect(() => {\n const params = this.params();\n\n const id = params['id'];\n if (id) {\n this.facade.select(id);\n }\n });\n }\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport { HttpClient } from '@angular/common/http';\nimport {\n computed,\n Directive,\n effect,\n inject,\n PLATFORM_ID,\n signal,\n} from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { catchError, map, of } from 'rxjs';\n\nimport { Article } from '@smartsoft001-mobilems/articles-domain';\nimport { ArticleWidget } from '@smartsoft001-mobilems/models';\n\n/** Query params the CMS appends when it opens `/article-preview`. */\nexport interface ArticlePreviewQueryParams {\n apiHost?: string;\n dataUrl?: string;\n pathUrl?: string;\n pageUrl?: string;\n mediaUrl?: string;\n templateName?: string;\n viewType?: string;\n}\n\n/**\n * CMS preview of an unpublished article.\n *\n * The CMS opens `/article-preview?dataUrl=...` in the editor's browser and the\n * page fetches `dataUrl` with the CMS session cookie, so the route must be\n * rendered on the client (`RenderMode.Client`). A `viewType=thematic-path`\n * preview is forwarded to `thematicPathRoute`. Subclass with the app's own\n * template; the subclass owns only the view.\n */\n@Directive()\nexport abstract class ArticlesPreviewComponent {\n protected readonly http = inject(HttpClient);\n protected readonly route = inject(ActivatedRoute);\n protected readonly router = inject(Router);\n protected readonly platformId = inject(PLATFORM_ID);\n protected readonly queryParams = toSignal(this.route.queryParams);\n\n /** Route a `thematic-path` preview is forwarded to; override per app. */\n protected readonly thematicPathRoute: string = '/sciezki-tematyczne';\n\n readonly loading = signal(true);\n readonly noQueryParams = signal(false);\n readonly noLogin = signal(false);\n readonly item = signal<Article | null>(null);\n\n readonly plainTitle = computed(() =>\n (this.item()?.title ?? '').replace(/<[^>]*>/g, ''),\n );\n\n readonly widgets = computed<ArticleWidget[]>(() => {\n const widgets = this.item()?.bottomWidgets;\n if (!widgets) return [];\n\n return [...widgets].sort((a, b) => a.position - b.position);\n });\n\n readonly authorsText = computed(\n () =>\n this.item()\n ?.authors?.map((author) => author.name)\n .filter(Boolean)\n .join(', ') ?? '',\n );\n\n protected constructor() {\n effect(() => {\n const params = (this.queryParams() ?? {}) as ArticlePreviewQueryParams;\n\n if (params.viewType === 'thematic-path' && params.pathUrl) {\n this.forwardThematicPath(params);\n return;\n }\n\n if (params.dataUrl) {\n this.fetchArticle(params.dataUrl);\n } else {\n this.noQueryParams.set(true);\n this.loading.set(false);\n }\n });\n\n effect(() => {\n if (this.item() && isPlatformBrowser(this.platformId)) {\n window.scrollTo({ top: 0, behavior: 'instant' });\n }\n });\n }\n\n private forwardThematicPath(params: ArticlePreviewQueryParams): void {\n const id = params.pathUrl?.split('/').pop();\n this.router.navigate([this.thematicPathRoute, id], {\n queryParams: {\n pathUrl: params.pathUrl,\n pageUrl: params.pageUrl,\n mediaUrl: params.mediaUrl,\n },\n });\n }\n\n private fetchArticle(dataUrl: string): void {\n this.loading.set(true);\n this.noLogin.set(false);\n\n this.http\n .get<{ data?: Article | null }>(dataUrl, { withCredentials: true })\n .pipe(\n map((response) => response?.data ?? null),\n catchError((error) => {\n console.error('Error fetching preview data:', error);\n this.noLogin.set(true);\n return of(null);\n }),\n )\n .subscribe((article) => {\n this.item.set(article);\n this.loading.set(false);\n });\n }\n}\n","import {\n IDictionaryEntry,\n normalizeCycleId,\n} from '@smartsoft001-mobilems/angular';\n\n/** Cykl artykułu (`cycles[]` z API) — tag tematyczny listy „Niezwykłe Historie”. */\nexport interface IArticleCycleTag {\n id: number;\n name: string;\n}\n\n/**\n * Cykle artykułu z pominięciem pustych wpisów, z id znormalizowanym do\n * liczby. `cycles: null` (albo brak pola) daje pustą tablicę, więc szablon\n * nie renderuje wtedy wiersza tagów (MOB-1605).\n */\nexport function articleCycleTags(\n item: { cycles?: unknown } | null | undefined,\n): IArticleCycleTag[] {\n const cycles = item?.cycles;\n if (!Array.isArray(cycles)) return [];\n\n return cycles.flatMap((cycle: { id?: unknown; name?: unknown } | null) => {\n const id = normalizeCycleId(cycle?.id);\n const name = typeof cycle?.name === 'string' ? cycle.name.trim() : '';\n return id === null || !name ? [] : [{ id, name }];\n });\n}\n\n/**\n * `queryParams` linku tagu — lista artykułów zawężona do cyklu, w formacie\n * `?filter=` czytanym przez listę (`filter[cycles][]=<id>` w API).\n */\nexport function cycleFilterQueryParams(tag: IArticleCycleTag): {\n filter: string;\n} {\n return {\n filter: JSON.stringify([\n { name: 'cycles', data: [{ id: tag.id, value: tag.name }] },\n ]),\n };\n}\n\n/** Źródło pastylki karty: cykl (reguła projektu) albo słowo kluczowe. */\nexport type ArticleCardTagSource = 'cycles' | 'keywords';\n\n/** Pastylka karty artykułu z polem filtra, do którego prowadzi jej link. */\nexport interface IArticleCardTag extends IArticleCycleTag {\n source: ArticleCardTagSource;\n}\n\n/**\n * Pastylki karty artykułu: wyłącznie `cycles[]` („Opis pól”, MOB-1823 —\n * „cycles: name, kolejne w pastylkach”). Zapas `keywords[]` z MOB-1604\n * zniknął: dokument go nie przewiduje, a reguła „`null`/puste → nie\n * pokazujemy całej kategorii” chowa wiersz tagów, gdy artykuł nie ma cykli.\n */\nexport function articleCardTags(\n item: { cycles?: unknown } | null | undefined,\n): IArticleCardTag[] {\n return articleCycleTags(item).map((tag) => ({ ...tag, source: 'cycles' }));\n}\n\n/**\n * `queryParams` linku pastylki — lista zawężona do cyklu\n * (`filter[cycles][]=<id>`) albo słowa kluczowego (`filter[keywords][]=<id>`).\n */\nexport function cardTagFilterQueryParams(tag: IArticleCardTag): {\n filter: string;\n} {\n return {\n filter: JSON.stringify([\n { name: tag.source, data: [{ id: tag.id, value: tag.name }] },\n ]),\n };\n}\n\n/** Pastylki karty po dopasowaniu do miejsca: widoczne i liczba pominiętych. */\nexport interface IFittedCardTags {\n visible: IArticleCardTag[];\n hidden: number;\n}\n\n/** Odstęp między pastylkami (`gap-2`). */\nconst TAG_GAP = 8;\n/** Padding 12 + 12 i obrys 1 + 1 pastylki (`smart-px-3 smart-border`). */\nconst TAG_CHROME = 26;\n/** Etykieta przycina się na 18rem (`smart-max-w-[18rem]`). */\nconst TAG_MAX_LABEL = 288;\n\n/**\n * Zachowawcza szerokość etykiety Open Sans 14 / tracking 0,5 px. Pomiar\n * w Chromium 2026-10-06: 7,2–8,1 px na znak dla zwykłych słów, 13,4 dla „W”\n * — wolimy przeszacować (pastylka spadnie do „+N”) niż wyjść poza blok.\n */\nexport function estimateTagWidth(label: string): number {\n let width = 0;\n for (const char of label) {\n if (char === ' ') width += 4.5;\n else if ('WMwm'.includes(char)) width += 13.5;\n else if (char !== char.toLowerCase()) width += 11;\n else if ('iljtfr.,:;!|'.includes(char)) width += 5;\n else if (/[\\d+#&%]/.test(char)) width += 9;\n else width += 8.2;\n }\n return TAG_CHROME + Math.min(TAG_MAX_LABEL, Math.ceil(width));\n}\n\n/**\n * Ile pastylek zmieści się w `maxRows` wierszach o szerokości `rowWidth`\n * (MOB-1604, recenzja): liczone PRZED renderem, więc w DOM nie ma linków\n * ukrytych przez `overflow` (fokus nie trafia w niewidoczne elementy).\n * Nadmiar zastępuje pastylka „+N” — jej szerokość jest rezerwowana\n * w ostatnim wierszu.\n */\nexport function fitCardTags(\n tags: IArticleCardTag[],\n rowWidth: number,\n maxRows = 2,\n): IFittedCardTags {\n const place = (widths: number[]): number => {\n let rows = widths.length ? 1 : 0;\n let used = 0;\n for (const width of widths) {\n const next = used === 0 ? width : used + TAG_GAP + width;\n if (next <= rowWidth) {\n used = next;\n } else {\n rows += 1;\n used = width;\n }\n }\n return rows;\n };\n\n const widths = tags.map((tag) => estimateTagWidth(tag.name));\n if (place(widths) <= maxRows) return { visible: tags, hidden: 0 };\n\n for (let count = tags.length - 1; count >= 0; count--) {\n const more = estimateTagWidth(`+${tags.length - count}`);\n if (place([...widths.slice(0, count), more]) <= maxRows) {\n return { visible: tags.slice(0, count), hidden: tags.length - count };\n }\n }\n return { visible: [], hidden: tags.length };\n}\n\n/** A tag of the list's tag bar with the filter field it toggles. */\nexport type IArticleFilterTag = IDictionaryEntry & {\n source?: ArticleCardTagSource;\n};\n\n/**\n * Tag key `${source}:${id}` — cycle and keyword ids are separate domains, so\n * an id alone may repeat. Route ids are strings, dictionary ids numbers:\n * `'7'` and `7` give the same key.\n */\nexport function articleFilterTagKey(tag: {\n id?: unknown;\n source?: ArticleCardTagSource;\n}): string {\n const id =\n typeof tag.id === 'string' && tag.id.trim() !== '' && !isNaN(Number(tag.id))\n ? Number(tag.id)\n : tag.id;\n return `${tag.source ?? 'cycles'}:${String(id)}`;\n}\n\n/**\n * Polish plural form of a minute count for the reading time\n * (`ARTICLES.readingTime.one|few|many`).\n */\nexport function readingTimeForm(minutes: number): 'one' | 'few' | 'many' {\n if (minutes === 1) return 'one';\n const lastDigit = minutes % 10;\n const lastTwo = minutes % 100;\n return lastDigit >= 2 && lastDigit <= 4 && (lastTwo < 12 || lastTwo > 14)\n ? 'few'\n : 'many';\n}\n","import { computed, Directive, inject, input } from '@angular/core';\n\nimport { FileUrlService } from '@smartsoft001-mobilems/angular';\nimport { Article, ArticleKeyword } from '@smartsoft001-mobilems/articles-domain';\n\nimport {\n articleCardTags,\n articleCycleTags,\n cardTagFilterQueryParams,\n IArticleCardTag,\n IArticleCycleTag,\n} from '../tools/article-tags';\n\n/** A list card article: the API list item with its slug, authors and cycles. */\nexport type IArticleCardItem = Article & {\n slug?: string;\n authors?: Array<{ id?: number; name?: string | null }> | null;\n cycles?: unknown;\n};\n\n/**\n * Logic of an article list card (grid or list row): image with the\n * placeholder, description, authors, cycle and keyword tags with their list\n * links. The card keeps its template, e.g. `fitCardTags(tags(), 400)`.\n */\n@Directive()\nexport abstract class ArticleCardBaseComponent {\n protected readonly fileUrlService = inject(FileUrlService);\n\n readonly item = input<IArticleCardItem | undefined>(undefined);\n\n /** Placeholder image when the article has none. */\n protected readonly placeholderImage: string = '/gfx/no-image.png';\n\n readonly imageSrc = computed(() => {\n const item = this.item();\n return item\n ? this.fileUrlService.get(item.image, 'cache', this.placeholderImage)\n : '';\n });\n\n readonly description = computed(() => this.item()?.shortDescription || '');\n\n /** Authors, comma separated; nameless entries are dropped. */\n readonly authorsText = computed(() =>\n (this.item()?.authors ?? [])\n .map((author) => author?.name?.trim())\n .filter((name): name is string => !!name)\n .join(', '),\n );\n\n readonly cycles = computed<IArticleCycleTag[]>(() =>\n articleCycleTags(this.item()),\n );\n\n /** Card tags — cycles only; none = no tag row. */\n readonly tags = computed<IArticleCardTag[]>(() =>\n articleCardTags(this.item()),\n );\n\n readonly keywords = computed<ArticleKeyword[]>(\n () => this.item()?.keywords ?? [],\n );\n\n /** `queryParams` of a tag link — the list narrowed to the cycle/keyword. */\n tagQueryParams(tag: IArticleCardTag): { filter: string } {\n return cardTagFilterQueryParams(tag);\n }\n\n /** `queryParams` of a keyword link; `{}` for a nameless keyword. */\n keywordQueryParams(keyword: ArticleKeyword): { filter?: string } {\n const name = keyword?.name;\n return name\n ? cardTagFilterQueryParams({\n id: keyword.id as unknown as number,\n name,\n source: 'keywords',\n })\n : {};\n }\n}\n","import { computed, inject, Signal } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { catchError, map, of } from 'rxjs';\n\nimport {\n DataService,\n ICatalog,\n IDictionaryEntry,\n normalizeCycleId,\n} from '@smartsoft001-mobilems/angular';\n\nimport { articleFilterTagKey, IArticleFilterTag } from '../tools/article-tags';\n\n/** Query key of the theme tags — cycles (`filter[cycles][]=<id>`). */\nexport const ARTICLE_CYCLES_FILTER_KEY = 'cycles';\n\n/**\n * Query key of a keyword from the route (`filter[keywords][]=<id>`) — links\n * from older card tags. The tag bar has no keyword dictionary, so an active\n * keyword shows as a selected tag that can be unselected.\n */\nexport const ARTICLE_KEYWORDS_FILTER_KEY = 'keywords';\n\nexport interface IArticleCycleTags {\n /** The whole `Cycle` dictionary, named entries only; `[]` on an API error. */\n readonly dictionary: Signal<IDictionaryEntry[]>;\n /** Active keywords as selected tags. */\n readonly keywordTags: Signal<IArticleFilterTag[]>;\n /** Active cycles the dictionary did not return (deep link, API error). */\n readonly missingCycleTags: Signal<IArticleFilterTag[]>;\n /** Tag bar: active keywords, active cycles outside the dictionary, dictionary. */\n readonly barTags: Signal<IArticleFilterTag[]>;\n readonly selectedCycleIds: Signal<number[]>;\n /** Selection of the bar by `articleFilterTagKey`. */\n readonly selectedKeys: Signal<string[]>;\n /** Toggles the tag in its own field (`cycles` or `keywords`). */\n toggle(tag: IArticleFilterTag): void;\n}\n\n/**\n * Theme tag bar of an articles list (cycles + keywords). Call in an injection\n * context; the dictionary is loaded once per instance.\n */\nexport function createArticleCycleTags(catalog: ICatalog): IArticleCycleTags {\n const dictionary = toSignal(\n inject(DataService)\n .loadCycleTags()\n .pipe(\n map((items) => items.filter((item) => !!item?.name?.trim())),\n catchError(() => of([] as IDictionaryEntry[])),\n ),\n { initialValue: [] as IDictionaryEntry[] },\n );\n\n const keywordTags = computed<IArticleFilterTag[]>(() =>\n catalog\n .queryItems(ARTICLE_KEYWORDS_FILTER_KEY)\n .filter(\n (item) => normalizeCycleId(item?.id) !== null && !!item?.name?.trim(),\n )\n .map((item) => ({ ...item, source: 'keywords' as const })),\n );\n\n const missingCycleTags = computed<IArticleFilterTag[]>(() => {\n const known = new Set(\n dictionary().map((item) => normalizeCycleId(item.id)),\n );\n const seen = new Set<number>();\n return catalog.queryItems(ARTICLE_CYCLES_FILTER_KEY).flatMap((item) => {\n const id = normalizeCycleId(item?.id);\n const name = typeof item?.name === 'string' ? item.name.trim() : '';\n if (id === null || !name || known.has(id) || seen.has(id)) {\n return [];\n }\n seen.add(id);\n return [{ ...item, id, name, source: 'cycles' as const }];\n });\n });\n\n const selectedCycleIds = computed<number[]>(() =>\n catalog\n .queryItems(ARTICLE_CYCLES_FILTER_KEY)\n .map((item) => normalizeCycleId(item.id))\n .filter((id): id is number => id !== null),\n );\n\n return {\n dictionary,\n keywordTags,\n missingCycleTags,\n barTags: computed(() => [\n ...keywordTags(),\n ...missingCycleTags(),\n ...dictionary().map((item) => ({ ...item, source: 'cycles' as const })),\n ]),\n selectedCycleIds,\n selectedKeys: computed(() => [\n ...selectedCycleIds().map((id) =>\n articleFilterTagKey({ id, source: 'cycles' }),\n ),\n ...keywordTags().map((tag) => articleFilterTagKey(tag)),\n ]),\n toggle: (tag) => {\n const key =\n tag.source === ARTICLE_KEYWORDS_FILTER_KEY\n ? ARTICLE_KEYWORDS_FILTER_KEY\n : ARTICLE_CYCLES_FILTER_KEY;\n const { source: _source, ...plain } = tag;\n catalog.toggleQueryItem(key, plain as IDictionaryEntry);\n },\n };\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n computed,\n Directive,\n effect,\n inject,\n PLATFORM_ID,\n Signal,\n} from '@angular/core';\nimport { toObservable, toSignal } from '@angular/core/rxjs-interop';\nimport { distinctUntilChanged, map, of, switchMap } from 'rxjs';\n\nimport {\n decodeHtmlEntities,\n FileUrlService,\n ISeoMetadata,\n MuseumSeoService,\n PARENT_TITLE_TOKEN,\n stripHtml,\n} from '@smartsoft001-mobilems/angular';\nimport { Article } from '@smartsoft001-mobilems/articles-domain';\nimport { ArticleWidget } from '@smartsoft001-mobilems/models';\n\nimport { ArticlesItemComponent } from '../pages/item/item.component';\nimport {\n ArticlesService,\n IArticleRelated,\n IArticleRelatedCard,\n} from '../services/articles/articles.service';\nimport { articleCycleTags, IArticleCycleTag } from '../tools/article-tags';\n\n/** Article fields of `GET article/:id` the framework model does not declare. */\nexport type IArticleDetails = Article & {\n authors?: Array<{ id?: number; name?: string | null }> | null;\n cycles?: Array<{ id: number; name: string }> | null;\n estimatedReadingTime?: number | null;\n};\n\n/**\n * Base of an article details page: SEO (also during SSR, via\n * `MuseumSeoService.applyForSsr`), the parent title for widget downloads,\n * scroll to top and the visit counter on every loaded article, sorted\n * widgets, plain title, lead, authors and cycle tags. The page provides\n * `PARENT_TITLE_TOKEN` (`useFactory: () => signal('')`) and its template.\n */\n@Directive()\nexport abstract class ArticleDetailsBaseComponent extends ArticlesItemComponent {\n protected readonly fileUrlService = inject(FileUrlService);\n protected readonly museumSeoService = inject(MuseumSeoService);\n protected readonly articlesService = inject(ArticlesService);\n private readonly parentTitle = inject(PARENT_TITLE_TOKEN);\n private readonly platformId = inject(PLATFORM_ID);\n\n /** Translation key of the site name in the SEO title. */\n protected siteNameKey = 'MODEL.mainTitle';\n\n protected readonly article = computed(\n () => this.item() as IArticleDetails | undefined,\n );\n\n /** Bottom widgets by `position` (stable; without a position last). */\n readonly widgets = computed<ArticleWidget[]>(() =>\n sortByPosition(this.item()?.bottomWidgets ?? []),\n );\n\n /** Title without markup — the API returns `<p>…</p>`. */\n readonly altTitle = computed<string>(() =>\n decodeHtmlEntities(stripHtml(this.item()?.title ?? '')).trim(),\n );\n\n /** `shortDescription` when it has text, else `''`. */\n readonly lead = computed<string>(() => {\n const text = this.item()?.shortDescription ?? '';\n return stripHtml(text).trim() ? text : '';\n });\n\n readonly authorsText = computed<string>(() =>\n (this.article()?.authors ?? [])\n .map((author) => author?.name?.trim())\n .filter(Boolean)\n .join(', '),\n );\n\n readonly cycleTags = computed<IArticleCycleTag[]>(() =>\n articleCycleTags(this.article()),\n );\n\n /** Rounded `estimatedReadingTime` in minutes, `0` when unknown. */\n readonly readingMinutes = computed<number>(() =>\n Math.round(Number(this.article()?.estimatedReadingTime) || 0),\n );\n\n protected constructor() {\n super();\n\n this.museumSeoService.applyForSsr(() => this.fetchSsrSeo());\n\n effect(() => {\n const item = this.item();\n if (!item) return;\n\n this.parentTitle.set(this.altTitle());\n this.museumSeoService.apply(this.buildSeoMetadata(item));\n\n if (isPlatformBrowser(this.platformId)) {\n window.scrollTo({ top: 0, behavior: 'instant' });\n void this.articlesService.logVisit(item.id).catch(() => undefined);\n }\n });\n }\n\n /** SEO of the article: `<title> - <site>`, description, image. */\n protected buildSeoMetadata(item: Article): ISeoMetadata {\n const title = decodeHtmlEntities(stripHtml(item.title)).trim();\n return {\n title: `${title} - ${this.translationService.get(this.siteNameKey)}`,\n description: item.shortDescription\n ? decodeHtmlEntities(stripHtml(item.shortDescription)).trim()\n : undefined,\n image: item.image ? this.fileUrlService.get(item.image) : undefined,\n imageAlt: title,\n type: 'article',\n };\n }\n\n private async fetchSsrSeo(): Promise<ISeoMetadata | null> {\n const id = this.route.snapshot.paramMap.get('id');\n if (!id) return null;\n try {\n const item = await this.articlesService.getById(id);\n return item ? this.buildSeoMetadata(item) : null;\n } catch {\n return null;\n }\n }\n}\n\n/**\n * „Related” module of a details page — `GET article/:id/related` for the\n * current article, empty groups before the first answer. Call in an\n * injection context, e.g. a field of an `ArticleDetailsBaseComponent`.\n */\nexport function createArticleRelated<\n O extends IArticleRelatedCard = IArticleRelatedCard,\n C extends IArticleRelatedCard = IArticleRelatedCard,\n A extends IArticleRelatedCard = IArticleRelatedCard,\n>(\n articleId: Signal<string | number | null | undefined>,\n): Signal<IArticleRelated<O, C, A>> {\n const service = inject(ArticlesService);\n const empty: IArticleRelated<O, C, A> = {\n objects: [],\n collections: [],\n articles: [],\n };\n return toSignal(\n toObservable(articleId).pipe(\n distinctUntilChanged(),\n switchMap((id) =>\n id !== undefined && id !== null\n ? service.getRelated<O, C, A>(id)\n : of(empty),\n ),\n map((related) => related ?? empty),\n ),\n { initialValue: empty },\n );\n}\n\n/** Like lodash `sortBy(items, 'position')`: stable, missing positions last. */\nexport function sortByPosition<T extends { position?: number | null }>(\n items: readonly T[],\n): T[] {\n return [...items].sort((a, b) => {\n const left = a?.position;\n const right = b?.position;\n if (left == null && right == null) return 0;\n if (left == null) return 1;\n if (right == null) return -1;\n return left - right;\n });\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n Directive,\n effect,\n ElementRef,\n inject,\n PLATFORM_ID,\n viewChild,\n} from '@angular/core';\nimport { TranslateService } from '@ngx-translate/core';\nimport { ListMode } from '@smartsoft001/angular';\nimport { capitalize } from '@smartsoft001/utils';\n\nimport {\n createCatalog,\n ICatalog,\n ICatalogOptions,\n ICatalogSort,\n MuseumSeoService,\n} from '@smartsoft001-mobilems/angular';\n\nimport { ArticlesListComponent } from '../pages/list/list.component';\n\n/** List SEO: `<title> - <site>`, description and canonical url. */\nexport interface IArticlesCatalogSeo {\n /** Translation key of the page title, e.g. `PAGE_TITLES.articles`. */\n titleKey: string;\n /** Translation key of the description, e.g. `ARTICLES.metaDescription`. */\n descriptionKey: string;\n /** Translation key of the site name. Default `MODEL.mainTitle`. */\n siteNameKey?: string;\n /** Canonical url of the list, e.g. `/artykuly`. */\n url: string;\n}\n\nexport interface IArticlesCatalogOptions extends Omit<\n ICatalogOptions,\n 'facade'\n> {\n /** Applied on creation, skipped in a search tab. */\n seo?: IArticlesCatalogSeo;\n}\n\n/**\n * Base of an articles list page on the shared catalog engine\n * (`createCatalog`): first read, persistence, sort/size/page, query helpers\n * and the read guard come from `catalog`; the page keeps its template.\n *\n * ```ts\n * export class ListComponent extends ArticlesCatalogBaseComponent {\n * constructor() {\n * super({ defaults, persistKey: 'ARTICLES', listPath: '/artykuly', searchTab: 'artykuly' });\n * }\n * }\n * ```\n *\n * A `#listContainer` element in the template is scrolled to after a page\n * change from the bottom pagination (`bottomPageChange`).\n */\n@Directive()\nexport abstract class ArticlesCatalogBaseComponent extends ArticlesListComponent {\n readonly ListMode = ListMode;\n protected readonly catalog: ICatalog;\n protected readonly translateService = inject(TranslateService);\n private readonly museumSeoService = inject(MuseumSeoService);\n private readonly platformId = inject(PLATFORM_ID);\n private readonly listContainer =\n viewChild<ElementRef<HTMLElement>>('listContainer');\n private scrollToListAfterLoad = false;\n\n protected constructor(options: IArticlesCatalogOptions) {\n super();\n const { seo, ...catalogOptions } = options;\n this.catalog = createCatalog({ ...catalogOptions, facade: this.facade });\n\n // After a page change from the bottom pagination scroll to the list top\n // once the data has loaded.\n effect(() => {\n const loading = this.catalog.isLoading();\n if (\n !loading &&\n this.scrollToListAfterLoad &&\n isPlatformBrowser(this.platformId)\n ) {\n this.scrollToListAfterLoad = false;\n setTimeout(() =>\n this.listContainer()?.nativeElement.scrollIntoView({\n behavior: 'smooth',\n block: 'start',\n }),\n );\n }\n });\n\n if (seo) this.applyListSeo(seo);\n }\n\n /** The list root element (`#listContainer`), e.g. to move focus. */\n protected get listContainerElement(): HTMLElement | undefined {\n return this.listContainer()?.nativeElement;\n }\n\n onSortChange(sort: ICatalogSort): void {\n this.catalog.setSort(sort);\n }\n\n /** Sort by key (`title-asc`, `random`). */\n sortChange(key: string): void {\n this.catalog.setSortKey(key);\n }\n\n onLimitChange(limit: number | string): void {\n this.catalog.setLimit(limit);\n }\n\n pageChange(page: number): void {\n this.catalog.setPage(page);\n }\n\n bottomPageChange(page: number): void {\n this.scrollToListAfterLoad = true;\n this.pageChange(page);\n }\n\n onListModeChange(mode: ListMode): void {\n this.catalog.setListMode(mode);\n }\n\n onFilterBarSearch(phrase: string): void {\n this.catalog.search(phrase);\n }\n\n retryLoad(): void {\n this.catalog.retry();\n }\n\n clearAllFilters(): void {\n this.catalog.clearAll();\n }\n\n /**\n * Full list meta (title, description, canonical, og), not in a search tab —\n * the canonical url of the list must not point at a phrase url.\n */\n protected applyListSeo(seo: IArticlesCatalogSeo): void {\n if (this.catalog.searchEnabled()) return;\n\n const t = (key: string) => capitalize(this.translateService.instant(key));\n this.museumSeoService.apply({\n title: `${t(seo.titleKey)} - ${t(seo.siteNameKey ?? 'MODEL.mainTitle')}`,\n description: t(seo.descriptionKey),\n type: 'website',\n url: seo.url,\n });\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["smartInitialState","smartGetReducer"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartsoft001-mobilems/articles-shell-angular",
3
- "version": "2.107.0",
3
+ "version": "2.109.0",
4
4
  "description": "This library was generated with [Nx](https://nx.dev).",
5
5
  "repository": {
6
6
  "type": "git",
@@ -4,16 +4,19 @@ import * as _smartsoft001_crud_shell_angular from '@smartsoft001/crud-shell-angu
4
4
  import { ICrudModuleOptionsWithoutRoutng, CrudFacade, State as State$1, CrudListPageBaseComponent, CrudItemPageBaseComponent, CrudService } from '@smartsoft001/crud-shell-angular';
5
5
  import { IEntity } from '@smartsoft001/domain-core';
6
6
  import * as i1$1 from '@smartsoft001-mobilems/angular';
7
- import { FiltersBaseComponent, CrudBaseService, SeoService, TranslationService } from '@smartsoft001-mobilems/angular';
7
+ import { FiltersBaseComponent, CrudBaseService, SeoService, TranslationService, IDictionaryEntry, FileUrlService, ICatalog, MuseumSeoService, ISeoMetadata, ICatalogOptions, ICatalogSort } from '@smartsoft001-mobilems/angular';
8
8
  import * as i1 from '@angular/common';
9
- import { Article } from '@smartsoft001-mobilems/articles-domain';
9
+ import { Article, ArticleKeyword } from '@smartsoft001-mobilems/articles-domain';
10
10
  import * as dist_packages_articles_domain_src from 'dist/packages/articles/domain/src';
11
+ import { Observable } from 'rxjs';
11
12
  import * as _angular_router from '@angular/router';
12
13
  import { ActivatedRoute, Router } from '@angular/router';
13
14
  import { HttpClient } from '@angular/common/http';
14
15
  import { ArticleWidget } from '@smartsoft001-mobilems/models';
15
16
  import * as _ngrx_store from '@ngrx/store';
16
17
  import { Action } from '@ngrx/store';
18
+ import { TranslateService } from '@ngx-translate/core';
19
+ import { ListMode } from '@smartsoft001/angular';
17
20
 
18
21
  declare class FiltersComponent extends FiltersBaseComponent<Article> {
19
22
  static ɵfac: i0.ɵɵFactoryDeclaration<FiltersComponent, never>;
@@ -50,9 +53,35 @@ declare class ArticlesSharedModule {
50
53
  static ɵinj: i0.ɵɵInjectorDeclaration<ArticlesSharedModule>;
51
54
  }
52
55
 
56
+ /** Cards of `GET article/:id/related`, grouped as the API returns them. */
57
+ interface IArticleRelated<O extends IArticleRelatedCard = IArticleRelatedCard, C extends IArticleRelatedCard = IArticleRelatedCard, A extends IArticleRelatedCard = IArticleRelatedCard> {
58
+ objects: O[];
59
+ collections: C[];
60
+ articles: A[];
61
+ }
62
+ /** The fields the related module relies on; the API sends full list cards. */
63
+ interface IArticleRelatedCard {
64
+ id?: string | number | null;
65
+ image?: unknown;
66
+ }
67
+ /**
68
+ * Cards with an image first (order within each group kept), so grey
69
+ * placeholders do not open the module when the API has cards with images.
70
+ */
71
+ declare function withImagesFirst<T extends {
72
+ image?: unknown;
73
+ }>(cards: readonly T[]): T[];
53
74
  declare class ArticlesService extends CrudBaseService<Article> {
54
75
  getUrlNameForDetails(): string;
55
76
  getUrlNameForList(): string;
77
+ /** `POST api/article/:id/visit` — the visit counter of the details page. */
78
+ logVisit(id: string | number): Promise<void>;
79
+ /**
80
+ * `GET article/:id/related` — objects, collections and other articles with
81
+ * images first, without the article itself. No data or an API error give
82
+ * empty groups, so the details page works without the module.
83
+ */
84
+ getRelated<O extends IArticleRelatedCard = IArticleRelatedCard, C extends IArticleRelatedCard = IArticleRelatedCard, A extends IArticleRelatedCard = IArticleRelatedCard>(id: string | number): Observable<IArticleRelated<O, C, A>>;
56
85
  static ɵfac: i0.ɵɵFactoryDeclaration<ArticlesService, never>;
57
86
  static ɵprov: i0.ɵɵInjectableDeclaration<ArticlesService>;
58
87
  }
@@ -177,5 +206,270 @@ declare const SERVICES: (typeof ArticlesService | {
177
206
  useClass: typeof ArticlesService;
178
207
  })[];
179
208
 
180
- export { ArticlesFacade, ArticlesItemComponent, ArticlesListComponent, ArticlesModule, ArticlesPreviewComponent, ArticlesService, ArticlesSharedModule, ComponentsModule, FiltersComponent, ListComponent, SERVICES, getArticlesState, getReducer, initialState };
181
- export type { ArticlePreviewQueryParams, State };
209
+ /** Cykl artykułu (`cycles[]` z API) — tag tematyczny listy „Niezwykłe Historie”. */
210
+ interface IArticleCycleTag {
211
+ id: number;
212
+ name: string;
213
+ }
214
+ /**
215
+ * Cykle artykułu z pominięciem pustych wpisów, z id znormalizowanym do
216
+ * liczby. `cycles: null` (albo brak pola) daje pustą tablicę, więc szablon
217
+ * nie renderuje wtedy wiersza tagów (MOB-1605).
218
+ */
219
+ declare function articleCycleTags(item: {
220
+ cycles?: unknown;
221
+ } | null | undefined): IArticleCycleTag[];
222
+ /**
223
+ * `queryParams` linku tagu — lista artykułów zawężona do cyklu, w formacie
224
+ * `?filter=` czytanym przez listę (`filter[cycles][]=<id>` w API).
225
+ */
226
+ declare function cycleFilterQueryParams(tag: IArticleCycleTag): {
227
+ filter: string;
228
+ };
229
+ /** Źródło pastylki karty: cykl (reguła projektu) albo słowo kluczowe. */
230
+ type ArticleCardTagSource = 'cycles' | 'keywords';
231
+ /** Pastylka karty artykułu z polem filtra, do którego prowadzi jej link. */
232
+ interface IArticleCardTag extends IArticleCycleTag {
233
+ source: ArticleCardTagSource;
234
+ }
235
+ /**
236
+ * Pastylki karty artykułu: wyłącznie `cycles[]` („Opis pól”, MOB-1823 —
237
+ * „cycles: name, kolejne w pastylkach”). Zapas `keywords[]` z MOB-1604
238
+ * zniknął: dokument go nie przewiduje, a reguła „`null`/puste → nie
239
+ * pokazujemy całej kategorii” chowa wiersz tagów, gdy artykuł nie ma cykli.
240
+ */
241
+ declare function articleCardTags(item: {
242
+ cycles?: unknown;
243
+ } | null | undefined): IArticleCardTag[];
244
+ /**
245
+ * `queryParams` linku pastylki — lista zawężona do cyklu
246
+ * (`filter[cycles][]=<id>`) albo słowa kluczowego (`filter[keywords][]=<id>`).
247
+ */
248
+ declare function cardTagFilterQueryParams(tag: IArticleCardTag): {
249
+ filter: string;
250
+ };
251
+ /** Pastylki karty po dopasowaniu do miejsca: widoczne i liczba pominiętych. */
252
+ interface IFittedCardTags {
253
+ visible: IArticleCardTag[];
254
+ hidden: number;
255
+ }
256
+ /**
257
+ * Zachowawcza szerokość etykiety Open Sans 14 / tracking 0,5 px. Pomiar
258
+ * w Chromium 2026-10-06: 7,2–8,1 px na znak dla zwykłych słów, 13,4 dla „W”
259
+ * — wolimy przeszacować (pastylka spadnie do „+N”) niż wyjść poza blok.
260
+ */
261
+ declare function estimateTagWidth(label: string): number;
262
+ /**
263
+ * Ile pastylek zmieści się w `maxRows` wierszach o szerokości `rowWidth`
264
+ * (MOB-1604, recenzja): liczone PRZED renderem, więc w DOM nie ma linków
265
+ * ukrytych przez `overflow` (fokus nie trafia w niewidoczne elementy).
266
+ * Nadmiar zastępuje pastylka „+N” — jej szerokość jest rezerwowana
267
+ * w ostatnim wierszu.
268
+ */
269
+ declare function fitCardTags(tags: IArticleCardTag[], rowWidth: number, maxRows?: number): IFittedCardTags;
270
+ /** A tag of the list's tag bar with the filter field it toggles. */
271
+ type IArticleFilterTag = IDictionaryEntry & {
272
+ source?: ArticleCardTagSource;
273
+ };
274
+ /**
275
+ * Tag key `${source}:${id}` — cycle and keyword ids are separate domains, so
276
+ * an id alone may repeat. Route ids are strings, dictionary ids numbers:
277
+ * `'7'` and `7` give the same key.
278
+ */
279
+ declare function articleFilterTagKey(tag: {
280
+ id?: unknown;
281
+ source?: ArticleCardTagSource;
282
+ }): string;
283
+ /**
284
+ * Polish plural form of a minute count for the reading time
285
+ * (`ARTICLES.readingTime.one|few|many`).
286
+ */
287
+ declare function readingTimeForm(minutes: number): 'one' | 'few' | 'many';
288
+
289
+ /** A list card article: the API list item with its slug, authors and cycles. */
290
+ type IArticleCardItem = Article & {
291
+ slug?: string;
292
+ authors?: Array<{
293
+ id?: number;
294
+ name?: string | null;
295
+ }> | null;
296
+ cycles?: unknown;
297
+ };
298
+ /**
299
+ * Logic of an article list card (grid or list row): image with the
300
+ * placeholder, description, authors, cycle and keyword tags with their list
301
+ * links. The card keeps its template, e.g. `fitCardTags(tags(), 400)`.
302
+ */
303
+ declare abstract class ArticleCardBaseComponent {
304
+ protected readonly fileUrlService: FileUrlService;
305
+ readonly item: i0.InputSignal<IArticleCardItem>;
306
+ /** Placeholder image when the article has none. */
307
+ protected readonly placeholderImage: string;
308
+ readonly imageSrc: i0.Signal<string>;
309
+ readonly description: i0.Signal<string>;
310
+ /** Authors, comma separated; nameless entries are dropped. */
311
+ readonly authorsText: i0.Signal<string>;
312
+ readonly cycles: i0.Signal<IArticleCycleTag[]>;
313
+ /** Card tags — cycles only; none = no tag row. */
314
+ readonly tags: i0.Signal<IArticleCardTag[]>;
315
+ readonly keywords: i0.Signal<ArticleKeyword[]>;
316
+ /** `queryParams` of a tag link — the list narrowed to the cycle/keyword. */
317
+ tagQueryParams(tag: IArticleCardTag): {
318
+ filter: string;
319
+ };
320
+ /** `queryParams` of a keyword link; `{}` for a nameless keyword. */
321
+ keywordQueryParams(keyword: ArticleKeyword): {
322
+ filter?: string;
323
+ };
324
+ static ɵfac: i0.ɵɵFactoryDeclaration<ArticleCardBaseComponent, never>;
325
+ static ɵdir: i0.ɵɵDirectiveDeclaration<ArticleCardBaseComponent, never, never, { "item": { "alias": "item"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
326
+ }
327
+
328
+ /** Query key of the theme tags — cycles (`filter[cycles][]=<id>`). */
329
+ declare const ARTICLE_CYCLES_FILTER_KEY = "cycles";
330
+ /**
331
+ * Query key of a keyword from the route (`filter[keywords][]=<id>`) — links
332
+ * from older card tags. The tag bar has no keyword dictionary, so an active
333
+ * keyword shows as a selected tag that can be unselected.
334
+ */
335
+ declare const ARTICLE_KEYWORDS_FILTER_KEY = "keywords";
336
+ interface IArticleCycleTags {
337
+ /** The whole `Cycle` dictionary, named entries only; `[]` on an API error. */
338
+ readonly dictionary: Signal<IDictionaryEntry[]>;
339
+ /** Active keywords as selected tags. */
340
+ readonly keywordTags: Signal<IArticleFilterTag[]>;
341
+ /** Active cycles the dictionary did not return (deep link, API error). */
342
+ readonly missingCycleTags: Signal<IArticleFilterTag[]>;
343
+ /** Tag bar: active keywords, active cycles outside the dictionary, dictionary. */
344
+ readonly barTags: Signal<IArticleFilterTag[]>;
345
+ readonly selectedCycleIds: Signal<number[]>;
346
+ /** Selection of the bar by `articleFilterTagKey`. */
347
+ readonly selectedKeys: Signal<string[]>;
348
+ /** Toggles the tag in its own field (`cycles` or `keywords`). */
349
+ toggle(tag: IArticleFilterTag): void;
350
+ }
351
+ /**
352
+ * Theme tag bar of an articles list (cycles + keywords). Call in an injection
353
+ * context; the dictionary is loaded once per instance.
354
+ */
355
+ declare function createArticleCycleTags(catalog: ICatalog): IArticleCycleTags;
356
+
357
+ /** Article fields of `GET article/:id` the framework model does not declare. */
358
+ type IArticleDetails = Article & {
359
+ authors?: Array<{
360
+ id?: number;
361
+ name?: string | null;
362
+ }> | null;
363
+ cycles?: Array<{
364
+ id: number;
365
+ name: string;
366
+ }> | null;
367
+ estimatedReadingTime?: number | null;
368
+ };
369
+ /**
370
+ * Base of an article details page: SEO (also during SSR, via
371
+ * `MuseumSeoService.applyForSsr`), the parent title for widget downloads,
372
+ * scroll to top and the visit counter on every loaded article, sorted
373
+ * widgets, plain title, lead, authors and cycle tags. The page provides
374
+ * `PARENT_TITLE_TOKEN` (`useFactory: () => signal('')`) and its template.
375
+ */
376
+ declare abstract class ArticleDetailsBaseComponent extends ArticlesItemComponent {
377
+ protected readonly fileUrlService: FileUrlService;
378
+ protected readonly museumSeoService: MuseumSeoService;
379
+ protected readonly articlesService: ArticlesService;
380
+ private readonly parentTitle;
381
+ private readonly platformId;
382
+ /** Translation key of the site name in the SEO title. */
383
+ protected siteNameKey: string;
384
+ protected readonly article: Signal<IArticleDetails>;
385
+ /** Bottom widgets by `position` (stable; without a position last). */
386
+ readonly widgets: Signal<ArticleWidget[]>;
387
+ /** Title without markup — the API returns `<p>…</p>`. */
388
+ readonly altTitle: Signal<string>;
389
+ /** `shortDescription` when it has text, else `''`. */
390
+ readonly lead: Signal<string>;
391
+ readonly authorsText: Signal<string>;
392
+ readonly cycleTags: Signal<IArticleCycleTag[]>;
393
+ /** Rounded `estimatedReadingTime` in minutes, `0` when unknown. */
394
+ readonly readingMinutes: Signal<number>;
395
+ protected constructor();
396
+ /** SEO of the article: `<title> - <site>`, description, image. */
397
+ protected buildSeoMetadata(item: Article): ISeoMetadata;
398
+ private fetchSsrSeo;
399
+ static ɵfac: i0.ɵɵFactoryDeclaration<ArticleDetailsBaseComponent, never>;
400
+ static ɵdir: i0.ɵɵDirectiveDeclaration<ArticleDetailsBaseComponent, never, never, {}, {}, never, never, true, never>;
401
+ }
402
+ /**
403
+ * „Related” module of a details page — `GET article/:id/related` for the
404
+ * current article, empty groups before the first answer. Call in an
405
+ * injection context, e.g. a field of an `ArticleDetailsBaseComponent`.
406
+ */
407
+ declare function createArticleRelated<O extends IArticleRelatedCard = IArticleRelatedCard, C extends IArticleRelatedCard = IArticleRelatedCard, A extends IArticleRelatedCard = IArticleRelatedCard>(articleId: Signal<string | number | null | undefined>): Signal<IArticleRelated<O, C, A>>;
408
+ /** Like lodash `sortBy(items, 'position')`: stable, missing positions last. */
409
+ declare function sortByPosition<T extends {
410
+ position?: number | null;
411
+ }>(items: readonly T[]): T[];
412
+
413
+ /** List SEO: `<title> - <site>`, description and canonical url. */
414
+ interface IArticlesCatalogSeo {
415
+ /** Translation key of the page title, e.g. `PAGE_TITLES.articles`. */
416
+ titleKey: string;
417
+ /** Translation key of the description, e.g. `ARTICLES.metaDescription`. */
418
+ descriptionKey: string;
419
+ /** Translation key of the site name. Default `MODEL.mainTitle`. */
420
+ siteNameKey?: string;
421
+ /** Canonical url of the list, e.g. `/artykuly`. */
422
+ url: string;
423
+ }
424
+ interface IArticlesCatalogOptions extends Omit<ICatalogOptions, 'facade'> {
425
+ /** Applied on creation, skipped in a search tab. */
426
+ seo?: IArticlesCatalogSeo;
427
+ }
428
+ /**
429
+ * Base of an articles list page on the shared catalog engine
430
+ * (`createCatalog`): first read, persistence, sort/size/page, query helpers
431
+ * and the read guard come from `catalog`; the page keeps its template.
432
+ *
433
+ * ```ts
434
+ * export class ListComponent extends ArticlesCatalogBaseComponent {
435
+ * constructor() {
436
+ * super({ defaults, persistKey: 'ARTICLES', listPath: '/artykuly', searchTab: 'artykuly' });
437
+ * }
438
+ * }
439
+ * ```
440
+ *
441
+ * A `#listContainer` element in the template is scrolled to after a page
442
+ * change from the bottom pagination (`bottomPageChange`).
443
+ */
444
+ declare abstract class ArticlesCatalogBaseComponent extends ArticlesListComponent {
445
+ readonly ListMode: typeof ListMode;
446
+ protected readonly catalog: ICatalog;
447
+ protected readonly translateService: TranslateService;
448
+ private readonly museumSeoService;
449
+ private readonly platformId;
450
+ private readonly listContainer;
451
+ private scrollToListAfterLoad;
452
+ protected constructor(options: IArticlesCatalogOptions);
453
+ /** The list root element (`#listContainer`), e.g. to move focus. */
454
+ protected get listContainerElement(): HTMLElement | undefined;
455
+ onSortChange(sort: ICatalogSort): void;
456
+ /** Sort by key (`title-asc`, `random`). */
457
+ sortChange(key: string): void;
458
+ onLimitChange(limit: number | string): void;
459
+ pageChange(page: number): void;
460
+ bottomPageChange(page: number): void;
461
+ onListModeChange(mode: ListMode): void;
462
+ onFilterBarSearch(phrase: string): void;
463
+ retryLoad(): void;
464
+ clearAllFilters(): void;
465
+ /**
466
+ * Full list meta (title, description, canonical, og), not in a search tab —
467
+ * the canonical url of the list must not point at a phrase url.
468
+ */
469
+ protected applyListSeo(seo: IArticlesCatalogSeo): void;
470
+ static ɵfac: i0.ɵɵFactoryDeclaration<ArticlesCatalogBaseComponent, never>;
471
+ static ɵdir: i0.ɵɵDirectiveDeclaration<ArticlesCatalogBaseComponent, never, never, {}, {}, never, never, true, never>;
472
+ }
473
+
474
+ export { ARTICLE_CYCLES_FILTER_KEY, ARTICLE_KEYWORDS_FILTER_KEY, ArticleCardBaseComponent, ArticleDetailsBaseComponent, ArticlesCatalogBaseComponent, ArticlesFacade, ArticlesItemComponent, ArticlesListComponent, ArticlesModule, ArticlesPreviewComponent, ArticlesService, ArticlesSharedModule, ComponentsModule, FiltersComponent, ListComponent, SERVICES, articleCardTags, articleCycleTags, articleFilterTagKey, cardTagFilterQueryParams, createArticleCycleTags, createArticleRelated, cycleFilterQueryParams, estimateTagWidth, fitCardTags, getArticlesState, getReducer, initialState, readingTimeForm, sortByPosition, withImagesFirst };
475
+ export type { ArticleCardTagSource, ArticlePreviewQueryParams, IArticleCardItem, IArticleCardTag, IArticleCycleTag, IArticleCycleTags, IArticleDetails, IArticleFilterTag, IArticleRelated, IArticleRelatedCard, IArticlesCatalogOptions, IArticlesCatalogSeo, IFittedCardTags, State };