From 9fe356db10b40940428804aebf7c7fac1149b12e Mon Sep 17 00:00:00 2001 From: Thomas Dickhut Date: Mon, 18 Nov 2024 14:24:19 +0100 Subject: [PATCH] Issue #534: lux-app-footer: der footer soll optional nicht sticky sein --- public_api.ts | 1 + src/app/app-routing.module.ts | 2 + src/app/app.component.html | 8 +- src/app/app.component.ts | 21 +++-- src/app/demo/abstract/dse/dse.component.ts | 2 +- .../abstract/impressum/impressum.component.ts | 2 +- .../license-hint/license-hint.component.ts | 2 +- .../baseline/baseline-example.component.scss | 5 ++ .../components-overview-routing.module.ts | 2 - .../components-overview.component.scss | 6 ++ .../icon-example/icon-example.component.html | 2 +- .../icon-overview.component.scss | 4 +- .../infinite-scrolling-example.component.html | 2 +- .../infinite-scrolling-example.component.scss | 29 +------ .../list-example/list-example.component.html | 1 + .../list-example/list-example.component.scss | 4 + ...er-detail-authentic-example.component.html | 1 + ...er-detail-authentic-example.component.scss | 32 ++++++++ .../stepper-large-example.component.scss | 14 ++++ .../configuration.component.html | 10 ++- .../example-base-structure.component.scss | 6 -- .../example-root.component.scss | 19 +---- .../example-root.component.ts | 26 +------ src/app/demo/form/form-example.component.ts | 3 +- src/app/demo/home/home.component.scss | 6 ++ src/app/demo/home/home.component.ts | 2 +- ...-components-config-parameters.interface.ts | 5 ++ .../lux-components-config.service.ts | 4 + .../lux-app-content.component.html | 2 +- .../lux-app-content.component.scss | 17 ++++ .../lux-app-content.component.ts | 33 +++++++- .../lux-app-footer-fixed.service.ts | 55 +++++++++++++ .../lux-app-footer.component.ts | 1 + .../lux-media-query-observer.service.ts | 78 ++++++++++++++++--- src/locale/messages.en.xlf | 2 +- src/locale/messages.xlf | 2 +- 36 files changed, 295 insertions(+), 116 deletions(-) create mode 100644 src/app/modules/lux-layout/lux-app-footer/lux-app-footer-fixed.service.ts diff --git a/public_api.ts b/public_api.ts index b28b8cf9..7d233659 100644 --- a/public_api.ts +++ b/public_api.ts @@ -274,6 +274,7 @@ export * from './src/app/modules/lux-layout/lux-checkbox-container-ac/lux-checkb // Services export * from './src/app/modules/lux-layout/lux-app-footer/lux-app-footer-button.service'; export * from './src/app/modules/lux-layout/lux-app-footer/lux-app-footer-link.service'; +export * from './src/app/modules/lux-layout/lux-app-footer/lux-app-footer-fixed.service'; export * from './src/app/modules/lux-layout/lux-stepper/lux-stepper-helper.service'; export * from './src/app/modules/lux-layout/lux-stepper-large/lux-stepper-large-subcomponents/lux-stepper-large-mobile-overlay/lux-stepper-large-mobile-overlay.service'; // Classes diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 09064884..b15e82a7 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -8,6 +8,7 @@ import { DseComponent } from './demo/abstract/dse/dse.component'; import { DseResolver } from './demo/abstract/dse/dse.resolver'; import { ImpressumComponent } from './demo/abstract/impressum/impressum.component'; import { ImpressumResolver } from './demo/abstract/impressum/impressum.resolver'; +import { IconOverviewComponent } from './demo/components-overview/icon-example/icon-overview/icon-overview.component'; const routes: Routes = [ { path: '', redirectTo: '/home', pathMatch: 'full' }, @@ -33,6 +34,7 @@ const routes: Routes = [ }, { path: 'form', loadChildren: () => import('./demo/form/form-example.module').then((m) => m.FormExampleModule) }, { path: 'configuration', component: ConfigurationComponent }, + { path: 'icon-overview', component: IconOverviewComponent }, { path: 'baseline', loadChildren: () => import('./demo/baseline/baseline.module').then((m) => m.BaselineModule) } ]; diff --git a/src/app/app.component.html b/src/app/app.component.html index 81ff6cd6..5b3ddea3 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -207,7 +207,7 @@

Navigation

luxLabel="Iconsuche" luxAriaLabel="Lux-Icons suchen" luxTagId="navItem4" - [luxSelected]="url.endsWith('components-overview/example/icon-overview')" + [luxSelected]="url.endsWith('icon-overview')" (luxClicked)="goToIconSearch()" > Navigation > - - + + + + diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 21e4f16e..ba883e13 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,9 +1,7 @@ import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { DomSanitizer } from '@angular/platform-browser'; import { NavigationEnd, Router } from '@angular/router'; import { Subscription } from 'rxjs'; import { ComponentsOverviewNavigationService } from './demo/components-overview/components-overview-navigation.service'; -import { LuxAppFooterButtonService } from './modules/lux-layout/lux-app-footer/lux-app-footer-button.service'; import { LuxAppFooterLinkInfo } from './modules/lux-layout/lux-app-footer/lux-app-footer-link-info'; import { LuxAppFooterLinkService } from './modules/lux-layout/lux-app-footer/lux-app-footer-link.service'; import { LuxSideNavComponent } from './modules/lux-layout/lux-app-header/lux-app-header-subcomponents/lux-side-nav/lux-side-nav.component'; @@ -12,8 +10,9 @@ import { LuxThemeService } from './modules/lux-theme/lux-theme.service'; import { LuxAppService } from './modules/lux-util/lux-app.service'; import { LuxConsoleService } from './modules/lux-util/lux-console.service'; import { LuxMediaQueryObserverService } from './modules/lux-util/lux-media-query-observer.service'; -import {TenantLogoExampleHeaderService} from "./demo/components-overview/tenant-logo-example/tenant-logo-example-header.service"; -import {TenantLogoExampleConfigData} from "./demo/components-overview/tenant-logo-example/tenant-logo-example-config/tenant-logo-example-config-data"; +import { TenantLogoExampleHeaderService } from "./demo/components-overview/tenant-logo-example/tenant-logo-example-header.service"; +import { TenantLogoExampleConfigData } from "./demo/components-overview/tenant-logo-example/tenant-logo-example-config/tenant-logo-example-config-data"; +import { LuxAppFooterFixedService } from './modules/lux-layout/lux-app-footer/lux-app-footer-fixed.service'; @Component({ selector: 'app-root', @@ -41,26 +40,24 @@ export class AppComponent implements OnInit, OnDestroy { constructor( public router: Router, private linkService: LuxAppFooterLinkService, - private buttonService: LuxAppFooterButtonService, private snackbarService: LuxSnackbarService, public navigationService: ComponentsOverviewNavigationService, - private sanitizer: DomSanitizer, private themeService: LuxThemeService, private elementRef: ElementRef, private appService: LuxAppService, private mediaQueryService: LuxMediaQueryObserverService, public componentsOverviewService: ComponentsOverviewNavigationService, - public tenantLogoHeaderService: TenantLogoExampleHeaderService + public tenantLogoHeaderService: TenantLogoExampleHeaderService, + public fixedFooterService: LuxAppFooterFixedService ) { themeService.loadTheme(); this.themeName = themeService.getTheme().name; router.initialNavigation(); this.appService.appEl = elementRef.nativeElement; - this.mobileView = mediaQueryService.activeMediaQuery === 'xs' || mediaQueryService.activeMediaQuery === 'sd'; - - this.subscriptions.push(this.mediaQueryService.getMediaQueryChangedAsObservable().subscribe((query) => { - this.mobileView = query === 'xs' || query === 'sd'; + this.mobileView = mediaQueryService.isHandset(); + this.subscriptions.push(this.mediaQueryService.getMediaQueryChangedAsObservable().subscribe(() => { + this.mobileView = this.mediaQueryService.isHandset(); })); this.components = componentsOverviewService.filteredComponents.length; @@ -130,7 +127,7 @@ export class AppComponent implements OnInit, OnDestroy { this.router.navigate(['baseline']); } goToIconSearch() { - this.router.navigate(['components-overview/example/icon-overview']); + this.router.navigate(['icon-overview']); } goToHomepage() { diff --git a/src/app/demo/abstract/dse/dse.component.ts b/src/app/demo/abstract/dse/dse.component.ts index 4b692644..1cfbbdf8 100644 --- a/src/app/demo/abstract/dse/dse.component.ts +++ b/src/app/demo/abstract/dse/dse.component.ts @@ -4,7 +4,7 @@ import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'lux-dse', templateUrl: './dse.component.html', - styles: [':host { display: flex; justify-content: center}'], + styles: [':host { display: flex; align-items: start; justify-content: center; flex: 1 1 auto;}'], changeDetection: ChangeDetectionStrategy.OnPush }) export class DseComponent implements OnInit { diff --git a/src/app/demo/abstract/impressum/impressum.component.ts b/src/app/demo/abstract/impressum/impressum.component.ts index 2bb47d09..6556dfac 100644 --- a/src/app/demo/abstract/impressum/impressum.component.ts +++ b/src/app/demo/abstract/impressum/impressum.component.ts @@ -4,7 +4,7 @@ import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'lux-impressum', templateUrl: './impressum.component.html', - styles: [':host { display: flex; justify-content: center}'], + styles: [':host { display: flex; align-items: start; justify-content: center; flex: 1 1 auto;}'], changeDetection: ChangeDetectionStrategy.OnPush }) export class ImpressumComponent implements OnInit { diff --git a/src/app/demo/base/license-hint/license-hint.component.ts b/src/app/demo/base/license-hint/license-hint.component.ts index c2ace818..d16bfa42 100644 --- a/src/app/demo/base/license-hint/license-hint.component.ts +++ b/src/app/demo/base/license-hint/license-hint.component.ts @@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; @Component({ selector: 'app-license-hint', - styles: [':host { display: flex; justify-content: center}'], + styles: [':host { display: flex; align-items: start; justify-content: center; flex: 1 1 auto;}'], templateUrl: './license-hint.component.html', changeDetection: ChangeDetectionStrategy.OnPush }) diff --git a/src/app/demo/baseline/baseline-example.component.scss b/src/app/demo/baseline/baseline-example.component.scss index e69de29b..36e195eb 100644 --- a/src/app/demo/baseline/baseline-example.component.scss +++ b/src/app/demo/baseline/baseline-example.component.scss @@ -0,0 +1,5 @@ +:host { + display: flex; + flex: 1 1 auto; + flex-direction: column; +} \ No newline at end of file diff --git a/src/app/demo/components-overview/components-overview-routing.module.ts b/src/app/demo/components-overview/components-overview-routing.module.ts index ca7f7726..b85b4d83 100644 --- a/src/app/demo/components-overview/components-overview-routing.module.ts +++ b/src/app/demo/components-overview/components-overview-routing.module.ts @@ -59,7 +59,6 @@ import { TextareaAuthenticExampleComponent } from './textarea-authentic-example/ import { FileInputAuthenticExampleComponent } from './file-example/file-input-authentic-example/file-input-authentic-example.component'; import { ChipAuthenticExampleComponent } from './chip-authentic-example/chip-authentic-example.component'; import { SliderAuthenticExampleComponent } from './slider-authentic-example/slider-authentic-example.component'; -import { IconOverviewComponent } from './icon-example/icon-overview/icon-overview.component'; import { CheckboxContainerAcExampleComponent } from './checkbox-container-ac-example/checkbox-container-ac-example.component'; import { TextboxExampleComponent } from './textbox-example/textbox-example.component'; import { TenantLogoExampleComponent } from './tenant-logo-example/tenant-logo-example.component'; @@ -126,7 +125,6 @@ const routes: Routes = [ { path: 'input-ac', component: InputAuthenticExampleComponent }, { path: 'checkbox-ac', component: CheckboxAuthenticExampleComponent }, { path: 'tile-overview', component: OverviewExampleComponent }, - { path: 'icon-overview', component: IconOverviewComponent }, { path: 'master-detail-ac', component: MasterDetailAuthenticExampleComponent }, { path: 'checkbox-container-ac', component: CheckboxContainerAcExampleComponent }, { path: 'textbox', component: TextboxExampleComponent }, diff --git a/src/app/demo/components-overview/components-overview.component.scss b/src/app/demo/components-overview/components-overview.component.scss index 85796c3a..051cee71 100644 --- a/src/app/demo/components-overview/components-overview.component.scss +++ b/src/app/demo/components-overview/components-overview.component.scss @@ -1,3 +1,9 @@ +:host{ + display: flex; + flex-direction: column; + flex: 1 1 auto; +} + lux-card.lux-card-mobile ::ng-deep mat-card { margin-left: 0 !important; margin-right: 0 !important; diff --git a/src/app/demo/components-overview/icon-example/icon-example.component.html b/src/app/demo/components-overview/icon-example/icon-example.component.html index 5740db5b..ab09c62f 100644 --- a/src/app/demo/components-overview/icon-example/icon-example.component.html +++ b/src/app/demo/components-overview/icon-example/icon-example.component.html @@ -77,7 +77,7 @@ diff --git a/src/app/demo/components-overview/icon-example/icon-overview/icon-overview.component.scss b/src/app/demo/components-overview/icon-example/icon-overview/icon-overview.component.scss index 1ceb7ab0..0b46e8b2 100644 --- a/src/app/demo/components-overview/icon-example/icon-overview/icon-overview.component.scss +++ b/src/app/demo/components-overview/icon-example/icon-overview/icon-overview.component.scss @@ -1,8 +1,6 @@ .main-container { width: 100%; max-height: 100%; - // margin: 0 auto; - // max-width: 1200px; box-sizing: border-box; } @@ -32,7 +30,7 @@ .left-container, .right-container { - max-height: 100%; + max-height: 100vh; overflow: auto; lux-card ::ng-deep { diff --git a/src/app/demo/components-overview/infinite-scrolling-example/infinite-scrolling-example.component.html b/src/app/demo/components-overview/infinite-scrolling-example/infinite-scrolling-example.component.html index 64ed8ee2..a05e6008 100644 --- a/src/app/demo/components-overview/infinite-scrolling-example/infinite-scrolling-example.component.html +++ b/src/app/demo/components-overview/infinite-scrolling-example/infinite-scrolling-example.component.html @@ -5,7 +5,7 @@ .mat-mdc-card > .lux-card-content-container { - overflow-y: auto; - - > mat-card-content { - height: 100%; - overflow-y: hidden !important; - - > .lux-card-content { - height: 100%; - - > lux-card-content { - height: 100%; - } - } - } - } - } -} - -example-base-content { - max-height: 100%; - height: 100%; -} +.infinite-list { + overflow: auto; + max-height: calc(100vh - 350px); +} \ No newline at end of file diff --git a/src/app/demo/components-overview/list-example/list-example.component.html b/src/app/demo/components-overview/list-example/list-example.component.html index 7e1d47f6..36d0ea93 100644 --- a/src/app/demo/components-overview/list-example/list-example.component.html +++ b/src/app/demo/components-overview/list-example/list-example.component.html @@ -1,6 +1,7 @@ Beispiel für den Master-Detail Allgemein [luxDisabled]="!currentConfig.labelConfiguration!.allUppercase" [luxNoBottomLabel]="true" > - + Allgemein [luxNoBottomLabel]="true" > +
+

Footer

+ + + + +
+

Ripples

* { max-width: 100%; } } lux-card ::ng-deep > mat-card { - height: 100%; width: 100%; - max-height: 100%; margin: 0 !important; } @@ -33,8 +30,6 @@ lux-card ::ng-deep mat-card-content .lux-card-content { } .example-base-options { - max-height: 100%; - ::ng-deep { lux-card-actions { width: 100%; @@ -51,7 +46,6 @@ lux-card ::ng-deep mat-card-content .lux-card-content { ::ng-deep { .example-content-centered { width: 100%; - height: 100%; display: flex; align-items: center; justify-content: center; diff --git a/src/app/demo/example-base/example-base-root/example-root.component.scss b/src/app/demo/example-base/example-base-root/example-root.component.scss index 575c9de3..37d09d70 100644 --- a/src/app/demo/example-base/example-base-root/example-root.component.scss +++ b/src/app/demo/example-base/example-base-root/example-root.component.scss @@ -1,15 +1,11 @@ :host { display: flex; + flex: 1 1 auto; width: 100%; - height: 100%; - max-height: calc(100% - 32px); } .example-base-structure { position: relative; - height: 100%; - min-height: 100%; - max-height: 100%; width: 100%; min-width: 100%; @@ -18,7 +14,6 @@ border-right: 1px solid; min-width: 253px; padding: 16px 8px; - height: 100% !important; lux-panel { padding: 0 0 16px 0; @@ -59,21 +54,9 @@ position: relative; display: flex; flex: 0 0 calc(100% - 302px); - max-height: 100%; - height: 100%; router-outlet + ::ng-deep * { width: 100%; } } - - .example-base-options { - max-height: 100%; - } - - &.example-mobile { - ::ng-deep .example-base-options { - max-height: unset; - } - } } diff --git a/src/app/demo/example-base/example-base-root/example-root.component.ts b/src/app/demo/example-base/example-base-root/example-root.component.ts index 628849b7..42fd639d 100644 --- a/src/app/demo/example-base/example-base-root/example-root.component.ts +++ b/src/app/demo/example-base/example-base-root/example-root.component.ts @@ -1,17 +1,17 @@ -import { AfterViewInit, Component, ElementRef, OnDestroy, ViewChild } from '@angular/core'; +import { Component, ElementRef, OnDestroy, ViewChild } from '@angular/core'; import { ComponentsOverviewNavigationService } from '../../components-overview/components-overview-navigation.service'; import { LuxMediaQueryObserverService } from '../../../modules/lux-util/lux-media-query-observer.service'; import { NavigationEnd, Router } from '@angular/router'; import { Subscription } from 'rxjs'; +import { LuxUtil } from '../../../modules/lux-util/lux-util'; @Component({ selector: 'example-root', templateUrl: './example-root.component.html', styleUrls: ['./example-root.component.scss'] }) -export class ExampleRootComponent implements AfterViewInit, OnDestroy { +export class ExampleRootComponent implements OnDestroy { private routerSubscription: Subscription; - private blockScrolling = false; private subscription: Subscription; desktopView: boolean; @@ -25,11 +25,7 @@ export class ExampleRootComponent implements AfterViewInit, OnDestroy { ) { this.routerSubscription = this.router.events.subscribe((event: any) => { if (event instanceof NavigationEnd) { - if (this.blockScrolling) { - this.blockScrolling = false; - } else { - this.findSelectedExampleEntry(); - } + LuxUtil.goToTop(); } }); @@ -40,10 +36,6 @@ export class ExampleRootComponent implements AfterViewInit, OnDestroy { }); } - ngAfterViewInit() { - this.findSelectedExampleEntry(); - } - ngOnDestroy() { if (this.routerSubscription) { this.routerSubscription.unsubscribe(); @@ -58,15 +50,5 @@ export class ExampleRootComponent implements AfterViewInit, OnDestroy { */ onComponentClick(component: any) { component.onclick(); - this.blockScrolling = true; - } - - private findSelectedExampleEntry() { - setTimeout(() => { - const activeItem = document.querySelector('.example-component-list-item-active'); - if (activeItem) { - activeItem.scrollIntoView({ block: 'start', behavior: 'smooth' }); - } - }); } } diff --git a/src/app/demo/form/form-example.component.ts b/src/app/demo/form/form-example.component.ts index 3cf353cc..46f17b02 100644 --- a/src/app/demo/form/form-example.component.ts +++ b/src/app/demo/form/form-example.component.ts @@ -14,7 +14,8 @@ import { Router } from '@angular/router'; @Component({ selector: 'app-form-example', - templateUrl: './form-example.component.html' + templateUrl: './form-example.component.html', + styles: [':host { display: flex; flex-direction: column; flex: 1 1 auto;}'] }) export class FormExampleComponent implements IUnsavedDataCheck, OnInit, AfterViewInit, OnDestroy { @ViewChild(FormCommonComponent) formCommon!: FormCommonComponent; diff --git a/src/app/demo/home/home.component.scss b/src/app/demo/home/home.component.scss index ea4988fa..e959a0e8 100644 --- a/src/app/demo/home/home.component.scss +++ b/src/app/demo/home/home.component.scss @@ -1,3 +1,9 @@ +:host{ + display: flex; + flex-direction: column; + flex: 1 1 auto; +} + .page-content { margin: 32px 24px; diff --git a/src/app/demo/home/home.component.ts b/src/app/demo/home/home.component.ts index dd192519..7334e237 100644 --- a/src/app/demo/home/home.component.ts +++ b/src/app/demo/home/home.component.ts @@ -24,7 +24,7 @@ export class HomeComponent { this.router.navigate(['/baseline']); break; case 'Iconsearch': - this.router.navigate(['/components-overview/example/icon-overview']); + this.router.navigate(['/icon-overview']); break; } } diff --git a/src/app/modules/lux-components-config/lux-components-config-parameters.interface.ts b/src/app/modules/lux-components-config/lux-components-config-parameters.interface.ts index c1abe9f8..a3cfc703 100644 --- a/src/app/modules/lux-components-config/lux-components-config-parameters.interface.ts +++ b/src/app/modules/lux-components-config/lux-components-config-parameters.interface.ts @@ -72,4 +72,9 @@ export interface LuxComponentsConfigParameters { centeredView?: boolean; centeredWidth?: string; }; + + appFooter?: { + fixedDesktop?: boolean; + fixedMobile?: boolean; + }; } diff --git a/src/app/modules/lux-components-config/lux-components-config.service.ts b/src/app/modules/lux-components-config/lux-components-config.service.ts index 0c082789..d1934ede 100644 --- a/src/app/modules/lux-components-config/lux-components-config.service.ts +++ b/src/app/modules/lux-components-config/lux-components-config.service.ts @@ -32,6 +32,10 @@ export class LuxComponentsConfigService { viewConfiguration: { centeredView: false, centeredWidth: '1500px' + }, + appFooter: { + fixedDesktop: true, + fixedMobile: true } }; diff --git a/src/app/modules/lux-layout/lux-app-content/lux-app-content.component.html b/src/app/modules/lux-layout/lux-app-content/lux-app-content.component.html index 2f01dae0..c1b106ad 100644 --- a/src/app/modules/lux-layout/lux-app-content/lux-app-content.component.html +++ b/src/app/modules/lux-layout/lux-app-content/lux-app-content.component.html @@ -1,5 +1,5 @@
diff --git a/src/app/modules/lux-layout/lux-app-content/lux-app-content.component.scss b/src/app/modules/lux-layout/lux-app-content/lux-app-content.component.scss index f3e368bd..57382b13 100644 --- a/src/app/modules/lux-layout/lux-app-content/lux-app-content.component.scss +++ b/src/app/modules/lux-layout/lux-app-content/lux-app-content.component.scss @@ -1,3 +1,20 @@ +:host.lux-app-footer-no-fixed { + display: flex; + flex: 1 1 auto; + flex-direction: column; + + .lux-app-content-container { + display: flex; + flex: 1 1 auto; + flex-direction: column; + position: unset; + top: unset; + left: unset; + right: unset; + bottom: unset; + } +} + :host { position: relative; flex: 1 1 100%; diff --git a/src/app/modules/lux-layout/lux-app-content/lux-app-content.component.ts b/src/app/modules/lux-layout/lux-app-content/lux-app-content.component.ts index 401c6f2e..ed156929 100644 --- a/src/app/modules/lux-layout/lux-app-content/lux-app-content.component.ts +++ b/src/app/modules/lux-layout/lux-app-content/lux-app-content.component.ts @@ -1,20 +1,47 @@ -import { Component, ElementRef, HostListener, Input } from "@angular/core"; +import { Component, ElementRef, HostBinding, HostListener, Input, OnDestroy } from "@angular/core"; import { LuxAppService } from "../../lux-util/lux-app.service"; +import { LuxAppFooterFixedService } from "../lux-app-footer/lux-app-footer-fixed.service"; +import { Subscription } from "rxjs"; +import { LuxThemeService } from "../../lux-theme/lux-theme.service"; @Component({ selector: 'lux-app-content', templateUrl: './lux-app-content.component.html', styleUrls: ['./lux-app-content.component.scss'] }) -export class LuxAppContentComponent { +export class LuxAppContentComponent implements OnDestroy { @Input() luxAriaRoleMainLabel = $localize `:@@luxc.app_content.ariarolemain:Inhaltsbereich`; @HostListener('window:resize') windowResize() { this.appService.onResize(); } - constructor(private elementRef: ElementRef, private appService: LuxAppService) { + @HostBinding('class.lux-app-footer-no-fixed') get getNoStickModeClass() { + return !this.fixedMode; + } + + fixedMode: boolean; + themeName: string; + subscriptions: Subscription[] = []; + + constructor(private elementRef: ElementRef, private appService: LuxAppService, private footerService: LuxAppFooterFixedService, public themeService: LuxThemeService) { this.appService.appContentEl = elementRef.nativeElement; + + this.fixedMode = this.footerService.fixedMode; + this.subscriptions.push(this.footerService.fixedModeAsObservable.subscribe((fixedMode) => { + this.fixedMode = fixedMode; + })); + + this.themeName = this.themeService.getTheme().name; + this.subscriptions.push(this.themeService.getThemeAsObservable().subscribe((theme) => { + this.themeName = theme.name; + })); + } + + ngOnDestroy(): void { + this.subscriptions.forEach(sub => { + sub.unsubscribe(); + }); } } diff --git a/src/app/modules/lux-layout/lux-app-footer/lux-app-footer-fixed.service.ts b/src/app/modules/lux-layout/lux-app-footer/lux-app-footer-fixed.service.ts new file mode 100644 index 00000000..84a8bda3 --- /dev/null +++ b/src/app/modules/lux-layout/lux-app-footer/lux-app-footer-fixed.service.ts @@ -0,0 +1,55 @@ +import { Injectable, OnDestroy } from '@angular/core'; +import { BehaviorSubject, Observable, Subscription } from "rxjs"; +import { LuxComponentsConfigService } from '../../lux-components-config/lux-components-config.service'; +import { LuxMediaQueryObserverService } from '../../lux-util/lux-media-query-observer.service'; + +@Injectable({ + providedIn: 'root' +}) +export class LuxAppFooterFixedService implements OnDestroy { + + private fixedModeSubject = new BehaviorSubject(true); + + subscriptions: Subscription[] = []; + + constructor(private configService: LuxComponentsConfigService, private mediaQueryService: LuxMediaQueryObserverService) { + this.updateFixedMode(); + + this.subscriptions.push(this.configService.config.subscribe(() => { + this.updateFixedMode(); + })); + + this.subscriptions.push(this.mediaQueryService.getMediaQueryChangedAsObservable(...LuxMediaQueryObserverService.BREAKPOINTS_ALL).subscribe(() => { + this.updateFixedMode(); + })); + } + + ngOnDestroy() { + this.subscriptions.forEach(sub => { + sub.unsubscribe(); + }); + } + + get fixedMode(): boolean { + return this.fixedModeSubject.getValue(); + } + + get fixedModeAsObservable(): Observable { + return this.fixedModeSubject.asObservable(); + } + + private updateFixedMode() { + const fixedDesktopConfig = !!this.configService.currentConfig.appFooter?.fixedDesktop; + const fixedMobileConfig = !!this.configService.currentConfig.appFooter?.fixedMobile; + const isMobile = this.mediaQueryService.isHandset(); + const isDesktop = !isMobile; + + const isFixedDesktop = (fixedDesktopConfig && isDesktop); + const isFixedMobile = (fixedMobileConfig && isMobile); + const newFixedMode = isFixedDesktop || isFixedMobile; + + if (this.fixedMode !== newFixedMode) { + this.fixedModeSubject.next(newFixedMode); + } + } +} diff --git a/src/app/modules/lux-layout/lux-app-footer/lux-app-footer.component.ts b/src/app/modules/lux-layout/lux-app-footer/lux-app-footer.component.ts index 489e6978..ede80d15 100644 --- a/src/app/modules/lux-layout/lux-app-footer/lux-app-footer.component.ts +++ b/src/app/modules/lux-layout/lux-app-footer/lux-app-footer.component.ts @@ -40,6 +40,7 @@ export class LuxAppFooterComponent implements OnInit, AfterViewInit, OnDestroy { } ngOnInit() { + this.desktopView = this.mediaObserver.isSM() || this.mediaObserver.isMD() || this.mediaObserver.isLG() || this.mediaObserver.isXL(); this.subscriptions.push( this.mediaObserver.getMediaQueryChangedAsObservable().subscribe(() => { this.desktopView = this.mediaObserver.isSM() || this.mediaObserver.isMD() || this.mediaObserver.isLG() || this.mediaObserver.isXL(); diff --git a/src/app/modules/lux-util/lux-media-query-observer.service.ts b/src/app/modules/lux-util/lux-media-query-observer.service.ts index 16d2073d..ec65caa5 100644 --- a/src/app/modules/lux-util/lux-media-query-observer.service.ts +++ b/src/app/modules/lux-util/lux-media-query-observer.service.ts @@ -1,13 +1,18 @@ import { Injectable, OnDestroy } from '@angular/core'; import { LuxConsoleService } from './lux-console.service'; -import { BehaviorSubject, Observable, Subscription } from 'rxjs'; +import { BehaviorSubject, filter, Observable, Subscription } from 'rxjs'; import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout'; @Injectable({ providedIn: "root" }) export class LuxMediaQueryObserverService implements OnDestroy { + + public static BREAKPOINTS_DEFAULT: string[] = ['xs', 'sm', 'md', 'lg', 'xl']; + public static BREAKPOINTS_ALL: string[] = ['xs', 'sm', 'md', 'lg', 'xl', 'Handset', 'Tablet', 'Web', 'HandsetPortrait', 'TabletPortrait', 'WebPortrait', 'HandsetLandscape', 'TabletLandscape', 'WebLandscape']; + protected _mediaQueryChanged: BehaviorSubject = new BehaviorSubject(''); + protected _mediaQueryAllChanged: BehaviorSubject = new BehaviorSubject(''); protected _subscriptions: Subscription[] = []; constructor(private breakpointObserver: BreakpointObserver, private logger: LuxConsoleService) { @@ -16,6 +21,15 @@ export class LuxMediaQueryObserverService implements OnDestroy { this.addQuerySubscription(Breakpoints.Medium, 'md'); this.addQuerySubscription(Breakpoints.Large, 'lg'); this.addQuerySubscription(Breakpoints.XLarge, 'xl'); + this.addQuerySubscription(Breakpoints.Handset, 'Handset'); + this.addQuerySubscription(Breakpoints.Tablet, 'Tablet'); + this.addQuerySubscription(Breakpoints.Web, 'Web'); + this.addQuerySubscription(Breakpoints.HandsetPortrait, 'HandsetPortrait'); + this.addQuerySubscription(Breakpoints.TabletPortrait, 'TabletPortrait'); + this.addQuerySubscription(Breakpoints.WebPortrait, 'WebPortrait'); + this.addQuerySubscription(Breakpoints.HandsetLandscape, 'HandsetLandscape'); + this.addQuerySubscription(Breakpoints.TabletLandscape, 'TabletLandscape'); + this.addQuerySubscription(Breakpoints.WebLandscape, 'WebLandscape'); } public get activeMediaQuery() { @@ -27,8 +41,11 @@ export class LuxMediaQueryObserverService implements OnDestroy { this._mediaQueryChanged.complete(); } - public getMediaQueryChangedAsObservable(): Observable { - return this._mediaQueryChanged.asObservable(); + public getMediaQueryChangedAsObservable(...breakpoints: string[]): Observable { + if (breakpoints.length === 0) { + return this._mediaQueryChanged.asObservable(); + } + return this._mediaQueryAllChanged.asObservable().pipe(filter((value: string) => breakpoints.includes(value))); } public isSmaller(query: string): boolean { @@ -65,31 +82,70 @@ export class LuxMediaQueryObserverService implements OnDestroy { } public isXS(): boolean { - return this.activeMediaQuery === 'xs'; + return this.breakpointObserver.isMatched(Breakpoints.XSmall); } public isSM(): boolean { - return this.activeMediaQuery === 'sm'; + return this.breakpointObserver.isMatched(Breakpoints.Small); } public isMD(): boolean { - return this.activeMediaQuery === 'md'; + return this.breakpointObserver.isMatched(Breakpoints.Medium); } public isLG(): boolean { - return this.activeMediaQuery === 'lg'; + return this.breakpointObserver.isMatched(Breakpoints.Large); } public isXL(): boolean { - return this.activeMediaQuery === 'xl'; + return this.breakpointObserver.isMatched(Breakpoints.XLarge); + } + + public isHandset(): boolean { + return this.breakpointObserver.isMatched(Breakpoints.Handset); + } + + public isTablet(): boolean { + return this.breakpointObserver.isMatched(Breakpoints.Tablet); + } + + public isWeb(): boolean { + return this.breakpointObserver.isMatched(Breakpoints.Web); + } + + public isHandsetPortrait(): boolean { + return this.breakpointObserver.isMatched(Breakpoints.HandsetPortrait); + } + + public isTabletPortrait(): boolean { + return this.breakpointObserver.isMatched(Breakpoints.TabletPortrait); + } + + public isWebPortrait(): boolean { + return this.breakpointObserver.isMatched(Breakpoints.WebPortrait); + } + + public isHandsetLandscape(): boolean { + return this.breakpointObserver.isMatched(Breakpoints.HandsetLandscape); + } + + public isTabletLandscape(): boolean { + return this.breakpointObserver.isMatched(Breakpoints.TabletLandscape); + } + + public isWebLandscape(): boolean { + return this.breakpointObserver.isMatched(Breakpoints.WebLandscape); } - private addQuerySubscription(breakpoint: any, breakpointString: string) { + private addQuerySubscription(breakpoint: string, breakpointString: string) { this._subscriptions.push( this.breakpointObserver.observe([breakpoint]).subscribe((state: BreakpointState) => { if (state.matches) { - this._mediaQueryChanged.next(breakpointString); - this.logger.log(`MediaQuery [${this.activeMediaQuery}] activated.`); + if (LuxMediaQueryObserverService.BREAKPOINTS_DEFAULT.includes(breakpointString)) { + this._mediaQueryChanged.next(breakpointString); + } + this._mediaQueryAllChanged.next(breakpointString); + this.logger.log(`MediaQuery [${breakpointString}] activated.`); } }) ); diff --git a/src/locale/messages.en.xlf b/src/locale/messages.en.xlf index 7e13d43f..00528a75 100644 --- a/src/locale/messages.en.xlf +++ b/src/locale/messages.en.xlf @@ -805,7 +805,7 @@ Main content src/app/modules/lux-layout/lux-app-content/lux-app-content.component.ts - 10 + 13 diff --git a/src/locale/messages.xlf b/src/locale/messages.xlf index 539c575e..3d69628d 100644 --- a/src/locale/messages.xlf +++ b/src/locale/messages.xlf @@ -709,7 +709,7 @@ Inhaltsbereich src/app/modules/lux-layout/lux-app-content/lux-app-content.component.ts - 10 + 13