@smartsoft001-mobilems/angular 2.13.0 → 2.15.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.
package/README.md ADDED
@@ -0,0 +1,144 @@
1
+ # @smartsoft001-mobilems/angular
2
+
3
+ Shared Angular functionality for the MobileMS Framework.
4
+
5
+ ## Components
6
+
7
+ ### HeaderComponent
8
+
9
+ Base header component with TypeScript logic for managing header state and navigation. Designed to be extended by child components with custom templates.
10
+
11
+ **Features:**
12
+
13
+ - WCAG configuration toggle
14
+ - Search panel toggle
15
+ - User authentication state
16
+ - Local collection counter
17
+ - Responsive mobile detection
18
+ - Configuration-driven visibility
19
+ - Modern Angular signals for reactive state
20
+
21
+ **Usage:**
22
+
23
+ ```typescript
24
+ import { Component } from '@angular/core';
25
+ import { HeaderComponent } from '@smartsoft001-mobilems/angular';
26
+
27
+ @Component({
28
+ selector: 'app-custom-header',
29
+ standalone: true,
30
+ imports: [],
31
+ template: `
32
+ <header class="smart-bg-white smart-border-b smart-border-gray-200">
33
+ <div class="smart-container smart-mx-auto smart-px-4">
34
+ @if (isWcagVisible()) {
35
+ <div class="smart-py-4">
36
+ <button
37
+ (click)="toggleWcagConfig()"
38
+ class="smart-px-4 smart-py-2 smart-bg-blue-500 smart-text-white smart-rounded hover:smart-bg-blue-600"
39
+ >
40
+ WCAG Settings
41
+ </button>
42
+ </div>
43
+ } @if (user()) {
44
+ <div class="smart-py-4">
45
+ <span>Welcome, {{ user()?.userID }}</span>
46
+ <button
47
+ (click)="routeToUserAccount()"
48
+ class="smart-ml-4 smart-px-4 smart-py-2 smart-bg-green-500 smart-text-white smart-rounded hover:smart-bg-green-600"
49
+ >
50
+ My Account
51
+ </button>
52
+ </div>
53
+ } @else {
54
+ <button
55
+ (click)="routeToLogin()"
56
+ class="smart-px-4 smart-py-2 smart-bg-gray-500 smart-text-white smart-rounded hover:smart-bg-gray-600"
57
+ >
58
+ Login
59
+ </button>
60
+ } @if (isLocalCollectionVisible()) {
61
+ <button
62
+ (click)="routeToLocalCollection()"
63
+ class="smart-px-4 smart-py-2 smart-bg-purple-500 smart-text-white smart-rounded hover:smart-bg-purple-600"
64
+ >
65
+ My Collection ({{ localCollectionCount() }})
66
+ </button>
67
+ }
68
+ </div>
69
+ </header>
70
+ `,
71
+ })
72
+ export class CustomHeaderComponent extends HeaderComponent {
73
+ override ngOnInit() {
74
+ super.ngOnInit();
75
+ // Custom initialization logic
76
+ this.setUser({ userID: 'john.doe@example.com' });
77
+ this.setLocalCollectionCount(5);
78
+ }
79
+ }
80
+ ```
81
+
82
+ **API:**
83
+
84
+ **Signals:**
85
+
86
+ - `showWcagConfig: Signal<boolean>` - WCAG panel visibility state
87
+ - `showSearchPanel: Signal<boolean>` - Search panel visibility state
88
+ - `user: Signal<IHeaderUser | null>` - Current user state
89
+ - `localCollectionCount: Signal<number>` - Local collection items count
90
+
91
+ **Computed Signals:**
92
+
93
+ - `config: Signal<IHeaderConfig>` - Header configuration from ConfigsFacade
94
+ - `isMobile: Signal<boolean>` - Mobile device detection
95
+ - `headerConfig: Signal<IHeaderConfig['theme']>` - Theme-specific header config
96
+ - `isWcagVisible: Signal<boolean>` - WCAG panel visibility based on config and state
97
+ - `isSearchVisible: Signal<boolean>` - Search visibility based on config
98
+ - `isLocalCollectionVisible: Signal<boolean>` - Local collection button visibility
99
+ - `isLanguageSelectorVisible: Signal<boolean>` - Language selector visibility
100
+ - `isUserAccountVisible: Signal<boolean>` - User account button visibility
101
+
102
+ **Methods:**
103
+
104
+ - `toggleWcagConfig(): void` - Toggle WCAG configuration panel
105
+ - `toggleSearchPanel(): void` - Toggle search panel
106
+ - `onWcagCloseButtonEvent(closed: boolean): void` - Handle WCAG panel close event
107
+ - `onSearchPanelButtonEvent(opened: boolean): void` - Handle search panel open event
108
+ - `routeToLogin(): void` - Navigate to login page
109
+ - `routeToUserAccount(): void` - Navigate to user account page
110
+ - `routeToLocalCollection(): void` - Navigate to local collection page
111
+ - `setUser(user: IHeaderUser | null): void` - Set current user
112
+ - `setLocalCollectionCount(count: number): void` - Set local collection count
113
+ - `getUser(): IHeaderUser | null` - Get current user
114
+ - `getLocalCollectionCount(): number` - Get local collection count
115
+ - `updateConfig(): void` - Update header configuration
116
+
117
+ **Types:**
118
+
119
+ ```typescript
120
+ export interface IHeaderUser {
121
+ userID: string;
122
+ [key: string]: unknown;
123
+ }
124
+
125
+ export interface IHeaderConfig {
126
+ theme?: {
127
+ header?: {
128
+ showWcagConfig?: boolean;
129
+ showSearch?: boolean;
130
+ showLocalCollection?: boolean;
131
+ showLanguageSelector?: boolean;
132
+ showUserAccount?: boolean;
133
+ };
134
+ };
135
+ }
136
+ ```
137
+
138
+ ### FooterComponent
139
+
140
+ Base footer component with TypeScript logic for managing footer state and static pages. Designed to be extended by child components with custom templates.
141
+
142
+ ## Services
143
+
144
+ ## State Management
@@ -1,13 +1,14 @@
1
1
  import { __decorate, __metadata } from 'tslib';
2
2
  import * as i0 from '@angular/core';
3
- import { Injectable, inject, PLATFORM_ID, signal, effect, computed, Component, Directive, contentChild, HostListener, Inject, ElementRef, input, APP_INITIALIZER, NgModule } from '@angular/core';
3
+ import { Injectable, inject, PLATFORM_ID, signal, effect, computed, input, output, HostListener, Component, ApplicationRef, EnvironmentInjector, createComponent, Directive, contentChild, Inject, ElementRef, APP_INITIALIZER, NgModule } from '@angular/core';
4
4
  import { Meta, Title, DomSanitizer } from '@angular/platform-browser';
5
5
  import { StyleService as StyleService$1, ReduxAction, AppBaseComponent, PageBaseComponent, BaseComponent } from '@smartsoft001/angular';
6
6
  import { HttpClient } from '@angular/common/http';
7
7
  import * as moment from 'moment';
8
- import { lastValueFrom, combineLatest, firstValueFrom, of, throwError } from 'rxjs';
8
+ import { lastValueFrom, combineLatest, firstValueFrom, Subject, of, throwError } from 'rxjs';
9
9
  import { map, filter, catchError } from 'rxjs/operators';
10
- import { isPlatformBrowser, DOCUMENT } from '@angular/common';
10
+ import * as i1 from '@angular/common';
11
+ import { isPlatformBrowser, CommonModule, DOCUMENT } from '@angular/common';
11
12
  import { Router, ActivatedRoute, NavigationEnd, RouterOutlet, RouterModule } from '@angular/router';
12
13
  import { TranslateService } from '@ngx-translate/core';
13
14
  import { CrudService, CrudFacade } from '@smartsoft001/crud-shell-angular';
@@ -555,6 +556,164 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.0", ngImpor
555
556
  }]
556
557
  }], ctorParameters: () => [] });
557
558
 
559
+ /**
560
+ * Container component for modal overlay and content.
561
+ * Handles backdrop clicks and ESC key press.
562
+ */
563
+ class ModalContainerComponent {
564
+ constructor() {
565
+ /**
566
+ * Template to render inside the modal content area.
567
+ */
568
+ this.contentTemplate = input.required(...(ngDevMode ? [{ debugName: "contentTemplate" }] : []));
569
+ /**
570
+ * Emits when backdrop is clicked or ESC key is pressed.
571
+ */
572
+ this.backdropClick = output();
573
+ }
574
+ /**
575
+ * Listens for ESC key press on document level.
576
+ */
577
+ onEscapeKey() {
578
+ this.backdropClick.emit();
579
+ }
580
+ /**
581
+ * Handles backdrop click event.
582
+ * @param _event Mouse click event
583
+ */
584
+ onBackdropClick(_event) {
585
+ this.backdropClick.emit();
586
+ }
587
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: ModalContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
588
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.1.0", type: ModalContainerComponent, isStandalone: true, selector: "smart-mobilems-modal-container", inputs: { contentTemplate: { classPropertyName: "contentTemplate", publicName: "contentTemplate", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { backdropClick: "backdropClick" }, host: { listeners: { "document:keydown.escape": "onEscapeKey()" } }, ngImport: i0, template: `
589
+ <div
590
+ class="smart-fixed smart-inset-0 smart-bg-black smart-bg-opacity-50 smart-flex smart-items-center smart-justify-center smart-z-50"
591
+ (click)="onBackdropClick($event)"
592
+ >
593
+ <div
594
+ class="smart-bg-white smart-rounded-lg smart-shadow-xl smart-max-w-2xl smart-w-full smart-m-4 smart-max-h-[90vh] smart-overflow-auto"
595
+ (click)="$event.stopPropagation()"
596
+ >
597
+ <div class="smart-p-6">
598
+ <ng-container *ngTemplateOutlet="contentTemplate()"></ng-container>
599
+ </div>
600
+ </div>
601
+ </div>
602
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] }); }
603
+ }
604
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: ModalContainerComponent, decorators: [{
605
+ type: Component,
606
+ args: [{
607
+ selector: 'smart-mobilems-modal-container',
608
+ standalone: true,
609
+ imports: [CommonModule],
610
+ template: `
611
+ <div
612
+ class="smart-fixed smart-inset-0 smart-bg-black smart-bg-opacity-50 smart-flex smart-items-center smart-justify-center smart-z-50"
613
+ (click)="onBackdropClick($event)"
614
+ >
615
+ <div
616
+ class="smart-bg-white smart-rounded-lg smart-shadow-xl smart-max-w-2xl smart-w-full smart-m-4 smart-max-h-[90vh] smart-overflow-auto"
617
+ (click)="$event.stopPropagation()"
618
+ >
619
+ <div class="smart-p-6">
620
+ <ng-container *ngTemplateOutlet="contentTemplate()"></ng-container>
621
+ </div>
622
+ </div>
623
+ </div>
624
+ `,
625
+ }]
626
+ }], propDecorators: { onEscapeKey: [{
627
+ type: HostListener,
628
+ args: ['document:keydown.escape']
629
+ }] } });
630
+
631
+ /**
632
+ * Reference to an opened modal.
633
+ * Provides methods to close the modal and observe its lifecycle.
634
+ */
635
+ class ModalRef {
636
+ constructor(closeCallback) {
637
+ this.closeCallback = closeCallback;
638
+ this._afterClosed = new Subject();
639
+ /**
640
+ * Observable that emits when the modal has been closed.
641
+ * Completes after emission.
642
+ */
643
+ this.afterClosed$ = this._afterClosed.asObservable();
644
+ }
645
+ /**
646
+ * Closes the modal and triggers cleanup.
647
+ * Safe to call multiple times.
648
+ */
649
+ close() {
650
+ this.closeCallback();
651
+ this._afterClosed.next();
652
+ this._afterClosed.complete();
653
+ }
654
+ }
655
+
656
+ /**
657
+ * Service for managing modal dialogs.
658
+ * Provides methods to open, close, and manage modal instances.
659
+ */
660
+ class ModalService {
661
+ constructor() {
662
+ this.appRef = inject(ApplicationRef);
663
+ this.injector = inject(EnvironmentInjector);
664
+ this.document = inject(DOCUMENT);
665
+ this.activeModals = [];
666
+ }
667
+ /**
668
+ * Opens a modal with the provided template.
669
+ * @param template TemplateRef to render inside the modal
670
+ * @returns ModalRef instance for controlling the modal
671
+ */
672
+ open(template) {
673
+ const componentRef = createComponent(ModalContainerComponent, {
674
+ environmentInjector: this.injector,
675
+ });
676
+ componentRef.setInput('contentTemplate', template);
677
+ const modalRef = new ModalRef(() => this.closeModal(componentRef));
678
+ componentRef.instance.backdropClick.subscribe(() => {
679
+ modalRef.close();
680
+ });
681
+ this.appRef.attachView(componentRef.hostView);
682
+ const domElem = componentRef.hostView.rootNodes[0];
683
+ this.document.body.appendChild(domElem);
684
+ componentRef.changeDetectorRef.detectChanges();
685
+ this.activeModals.push(componentRef);
686
+ return modalRef;
687
+ }
688
+ /**
689
+ * Closes a specific modal instance.
690
+ * @param componentRef Component reference to close
691
+ */
692
+ closeModal(componentRef) {
693
+ const index = this.activeModals.indexOf(componentRef);
694
+ if (index > -1) {
695
+ this.activeModals.splice(index, 1);
696
+ }
697
+ this.appRef.detachView(componentRef.hostView);
698
+ componentRef.destroy();
699
+ }
700
+ /**
701
+ * Closes all currently open modals.
702
+ */
703
+ closeAll() {
704
+ const modals = [...this.activeModals];
705
+ modals.forEach((componentRef) => this.closeModal(componentRef));
706
+ }
707
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: ModalService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
708
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: ModalService, providedIn: 'root' }); }
709
+ }
710
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: ModalService, decorators: [{
711
+ type: Injectable,
712
+ args: [{
713
+ providedIn: 'root',
714
+ }]
715
+ }] });
716
+
558
717
  class SeoService {
559
718
  constructor() {
560
719
  this.title = inject(Title);
@@ -892,6 +1051,7 @@ const SERVICES = [
892
1051
  MetaService,
893
1052
  SeoService,
894
1053
  StyleService,
1054
+ ModalService,
895
1055
  ];
896
1056
 
897
1057
  class ConfigsFacade {
@@ -1369,6 +1529,89 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.0", ngImpor
1369
1529
  args: [{ selector: 'smart-mobilems-footer', standalone: true, imports: [], template: `` }]
1370
1530
  }] });
1371
1531
 
1532
+ class HeaderComponent {
1533
+ constructor() {
1534
+ this.configsFacade = inject(ConfigsFacade);
1535
+ this.router = inject(Router);
1536
+ this.platformId = inject(PLATFORM_ID);
1537
+ this.config = computed(() => this.configsFacade.data, ...(ngDevMode ? [{ debugName: "config" }] : []));
1538
+ this.isMobile = computed(() => {
1539
+ if (!isPlatformBrowser(this.platformId)) {
1540
+ return false;
1541
+ }
1542
+ return typeof window !== 'undefined' && window.innerWidth <= 640;
1543
+ }, ...(ngDevMode ? [{ debugName: "isMobile" }] : []));
1544
+ this.showWcagConfig = signal(false, ...(ngDevMode ? [{ debugName: "showWcagConfig" }] : []));
1545
+ this.showSearchPanel = signal(false, ...(ngDevMode ? [{ debugName: "showSearchPanel" }] : []));
1546
+ this.user = signal(null, ...(ngDevMode ? [{ debugName: "user" }] : []));
1547
+ this.localCollectionCount = signal(0, ...(ngDevMode ? [{ debugName: "localCollectionCount" }] : []));
1548
+ this.headerConfig = computed(() => {
1549
+ const config = this.config();
1550
+ return config?.theme ?? {};
1551
+ }, ...(ngDevMode ? [{ debugName: "headerConfig" }] : []));
1552
+ this.isWcagVisible = computed(() => {
1553
+ return (this.showWcagConfig() &&
1554
+ this.headerConfig()?.header?.showWcagConfig !== false);
1555
+ }, ...(ngDevMode ? [{ debugName: "isWcagVisible" }] : []));
1556
+ this.isSearchVisible = computed(() => {
1557
+ return this.headerConfig()?.header?.showSearch !== false;
1558
+ }, ...(ngDevMode ? [{ debugName: "isSearchVisible" }] : []));
1559
+ this.isLocalCollectionVisible = computed(() => {
1560
+ return this.headerConfig()?.header?.showLocalCollection !== false;
1561
+ }, ...(ngDevMode ? [{ debugName: "isLocalCollectionVisible" }] : []));
1562
+ this.isLanguageSelectorVisible = computed(() => {
1563
+ return this.headerConfig()?.header?.showLanguageSelector !== false;
1564
+ }, ...(ngDevMode ? [{ debugName: "isLanguageSelectorVisible" }] : []));
1565
+ this.isUserAccountVisible = computed(() => {
1566
+ return this.headerConfig()?.header?.showUserAccount !== false;
1567
+ }, ...(ngDevMode ? [{ debugName: "isUserAccountVisible" }] : []));
1568
+ }
1569
+ toggleWcagConfig() {
1570
+ this.showWcagConfig.update((value) => !value);
1571
+ }
1572
+ toggleSearchPanel() {
1573
+ this.showSearchPanel.update((value) => !value);
1574
+ }
1575
+ onWcagCloseButtonEvent(closed) {
1576
+ this.showWcagConfig.set(!closed);
1577
+ }
1578
+ onSearchPanelButtonEvent(opened) {
1579
+ this.showSearchPanel.set(opened);
1580
+ }
1581
+ routeToLogin() {
1582
+ this.router.navigate(['/login']);
1583
+ }
1584
+ routeToUserAccount() {
1585
+ this.router.navigate(['/profile']);
1586
+ }
1587
+ routeToLocalCollection() {
1588
+ this.router.navigate(['/my-collection']);
1589
+ }
1590
+ setUser(user) {
1591
+ this.user.set(user);
1592
+ }
1593
+ setLocalCollectionCount(count) {
1594
+ this.localCollectionCount.set(count);
1595
+ }
1596
+ getUser() {
1597
+ return this.user();
1598
+ }
1599
+ getLocalCollectionCount() {
1600
+ return this.localCollectionCount();
1601
+ }
1602
+ updateConfig() {
1603
+ const current = this.config();
1604
+ if (!current)
1605
+ return;
1606
+ }
1607
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: HeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1608
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.0", type: HeaderComponent, isStandalone: true, selector: "smart-mobilems-header", ngImport: i0, template: ``, isInline: true }); }
1609
+ }
1610
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: HeaderComponent, decorators: [{
1611
+ type: Component,
1612
+ args: [{ selector: 'smart-mobilems-header', standalone: true, imports: [], template: `` }]
1613
+ }] });
1614
+
1372
1615
  const COMPONENTS = [AppComponent];
1373
1616
 
1374
1617
  /**
@@ -1618,5 +1861,5 @@ const unauthorizedGuard = () => {
1618
1861
  * Generated bundle index. Do not edit.
1619
1862
  */
1620
1863
 
1621
- export { AppComponent, AuthStorageService, COMPONENTS, ConfigsFacade, ConfigsService, ContrastService, CrudBaseService, DIRECTIVES, DictionaryService, FileUrlService, FiltersBaseComponent, FiltersContext, FooterComponent, GameType, GlobalService, HoverDirective, ImageBox, ListMode, MetaService, PageComponent, SERVICES, ScrollTopComponent, ScrollableDirective, SeoService, SettingsService, SharedConfig, SharedModule, StyleService, TRANSLATE_DATA_PL, TranslationService, WcagService, authenticationGuard, environment, setTranslationsAndLang, unauthorizedGuard };
1864
+ export { AppComponent, AuthStorageService, COMPONENTS, ConfigsFacade, ConfigsService, ContrastService, CrudBaseService, DIRECTIVES, DictionaryService, FileUrlService, FiltersBaseComponent, FiltersContext, FooterComponent, GameType, GlobalService, HeaderComponent, HoverDirective, ImageBox, ListMode, MetaService, ModalContainerComponent, ModalRef, ModalService, PageComponent, SERVICES, ScrollTopComponent, ScrollableDirective, SeoService, SettingsService, SharedConfig, SharedModule, StyleService, TRANSLATE_DATA_PL, TranslationService, WcagService, authenticationGuard, environment, setTranslationsAndLang, unauthorizedGuard };
1622
1865
  //# sourceMappingURL=smartsoft001-mobilems-angular.mjs.map