mirror of https://github.com/abpframework/abp.git
13 changed files with 231 additions and 67 deletions
@ -0,0 +1,62 @@ |
|||
<nav |
|||
class="navbar navbar-expand-lg navbar-dark bg-dark shadow-sm flex-column flex-md-row mb-4" |
|||
id="main-navbar" |
|||
style="min-height: 4rem" |
|||
> |
|||
<div class="container"> |
|||
<abp-logo *abpReplaceableTemplate="{ componentKey: service.logoComponentKey }"></abp-logo> |
|||
<button |
|||
class="navbar-toggler" |
|||
type="button" |
|||
[attr.aria-expanded]="!service.isCollapsed" |
|||
(click)="service.isCollapsed = !service.isCollapsed" |
|||
> |
|||
<span class="navbar-toggler-icon"></span> |
|||
</button> |
|||
<div |
|||
class="navbar-collapse" |
|||
[class.overflow-hidden]="service.smallScreen" |
|||
id="main-navbar-collapse" |
|||
> |
|||
<ng-container *ngTemplateOutlet="!service.smallScreen ? navigations : null"></ng-container> |
|||
|
|||
<div |
|||
*ngIf="service.smallScreen" |
|||
[@collapseWithMargin]="service.isCollapsed ? 'collapsed' : 'expanded'" |
|||
> |
|||
<ng-container *ngTemplateOutlet="navigations"></ng-container> |
|||
</div> |
|||
|
|||
<ng-template #navigations> |
|||
<abp-routes |
|||
*abpReplaceableTemplate="{ |
|||
componentKey: service.routesComponentKey, |
|||
inputs: { |
|||
smallScreen: { value: service.smallScreen } |
|||
} |
|||
}" |
|||
class="mx-auto" |
|||
[smallScreen]="service.smallScreen" |
|||
></abp-routes> |
|||
|
|||
<abp-nav-items |
|||
*abpReplaceableTemplate="{ |
|||
componentKey: service.navItemsComponentKey |
|||
}" |
|||
></abp-nav-items> |
|||
</ng-template> |
|||
</div> |
|||
</div> |
|||
</nav> |
|||
|
|||
<!-- [@slideFromBottom]="outlet.isActivated && outlet.activatedRoute?.routeConfig?.path" TODO: throws ExpressionChangedAfterItHasBeenCheck when animation is active. It should be fixed --> |
|||
<div class="container"> |
|||
<abp-page-alert-container></abp-page-alert-container> |
|||
<abp-auth-wrapper |
|||
*abpReplaceableTemplate="{ |
|||
componentKey: authWrapperKey |
|||
}" |
|||
> |
|||
<router-outlet #outlet="outlet"></router-outlet> |
|||
</abp-auth-wrapper> |
|||
</div> |
|||
@ -1,14 +1,22 @@ |
|||
import { Component } from '@angular/core'; |
|||
import { eLayoutType } from '@abp/ng.core'; |
|||
import { AfterViewInit, Component } from '@angular/core'; |
|||
import { eLayoutType, SubscriptionService } from '@abp/ng.core'; |
|||
|
|||
import { LayoutService } from '../../services/layout.service'; |
|||
|
|||
@Component({ |
|||
selector: 'abp-layout-account', |
|||
template: ` |
|||
<router-outlet></router-outlet> |
|||
<abp-confirmation></abp-confirmation> |
|||
`,
|
|||
templateUrl: './account-layout.component.html', |
|||
providers: [LayoutService, SubscriptionService], |
|||
}) |
|||
export class AccountLayoutComponent { |
|||
export class AccountLayoutComponent implements AfterViewInit { |
|||
// required for dynamic component
|
|||
static type = eLayoutType.account; |
|||
|
|||
authWrapperKey = 'Account.AuthWrapperComponent'; |
|||
|
|||
constructor(public service: LayoutService) {} |
|||
|
|||
ngAfterViewInit() { |
|||
this.service.subscribeWindowSize(); |
|||
} |
|||
} |
|||
|
|||
@ -1,16 +1,18 @@ |
|||
<div class="row"> |
|||
<div class="mx-auto col col-md-5"> |
|||
<ng-container *ngIf="(isMultiTenancyEnabled$ | async) && isTenantBoxVisible"> |
|||
<abp-tenant-box *abpReplaceableTemplate="{ componentKey: tenantBoxKey }"></abp-tenant-box> |
|||
<ng-container *ngIf="(service.isMultiTenancyEnabled$ | async) && service.isTenantBoxVisible"> |
|||
<abp-tenant-box |
|||
*abpReplaceableTemplate="{ componentKey: service.tenantBoxKey }" |
|||
></abp-tenant-box> |
|||
</ng-container> |
|||
|
|||
<div class="abp-account-container"> |
|||
<div |
|||
*ngIf="enableLocalLogin$ | async; else disableLocalLoginTemplate" |
|||
*ngIf="service.enableLocalLogin$ | async; else disableLocalLoginTemplate" |
|||
class="card mt-3 shadow-sm rounded" |
|||
> |
|||
<div class="card-body p-5"> |
|||
<router-outlet></router-outlet> |
|||
<ng-content></ng-content> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,25 @@ |
|||
import { ComponentFixture, TestBed } from '@angular/core/testing'; |
|||
|
|||
import { AuthWrapperComponent } from './auth-wrapper.component'; |
|||
|
|||
describe('AuthWrapperComponent', () => { |
|||
let component: AuthWrapperComponent; |
|||
let fixture: ComponentFixture<AuthWrapperComponent>; |
|||
|
|||
beforeEach(async () => { |
|||
await TestBed.configureTestingModule({ |
|||
declarations: [ AuthWrapperComponent ] |
|||
}) |
|||
.compileComponents(); |
|||
}); |
|||
|
|||
beforeEach(() => { |
|||
fixture = TestBed.createComponent(AuthWrapperComponent); |
|||
component = fixture.componentInstance; |
|||
fixture.detectChanges(); |
|||
}); |
|||
|
|||
it('should create', () => { |
|||
expect(component).toBeTruthy(); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,13 @@ |
|||
import { Component, OnInit } from '@angular/core'; |
|||
import { AuthWrapperService } from '@abp/ng.account-core'; |
|||
|
|||
@Component({ |
|||
selector: 'abp-auth-wrapper', |
|||
templateUrl: './auth-wrapper.component.html', |
|||
providers: [AuthWrapperService], |
|||
}) |
|||
export class AuthWrapperComponent implements OnInit { |
|||
constructor(public service: AuthWrapperService) {} |
|||
|
|||
ngOnInit(): void {} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
import { ComponentFixture, TestBed } from '@angular/core/testing'; |
|||
|
|||
import { TenantBoxComponent } from './tenant-box.component'; |
|||
|
|||
describe('TenantBoxComponent', () => { |
|||
let component: TenantBoxComponent; |
|||
let fixture: ComponentFixture<TenantBoxComponent>; |
|||
|
|||
beforeEach(async () => { |
|||
await TestBed.configureTestingModule({ |
|||
declarations: [ TenantBoxComponent ] |
|||
}) |
|||
.compileComponents(); |
|||
}); |
|||
|
|||
beforeEach(() => { |
|||
fixture = TestBed.createComponent(TenantBoxComponent); |
|||
component = fixture.componentInstance; |
|||
fixture.detectChanges(); |
|||
}); |
|||
|
|||
it('should create', () => { |
|||
expect(component).toBeTruthy(); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,13 @@ |
|||
import { Component, OnInit } from '@angular/core'; |
|||
import { TenantBoxService } from '@abp/ng.account-core'; |
|||
|
|||
@Component({ |
|||
selector: 'abp-tenant-box', |
|||
templateUrl: './tenant-box.component.html', |
|||
providers: [TenantBoxService], |
|||
}) |
|||
export class TenantBoxComponent implements OnInit { |
|||
constructor(public service: TenantBoxService) {} |
|||
|
|||
ngOnInit(): void {} |
|||
} |
|||
@ -1,54 +1,21 @@ |
|||
import { eLayoutType, SubscriptionService } from '@abp/ng.core'; |
|||
import { collapseWithMargin, slideFromBottom } from '@abp/ng.theme.shared'; |
|||
import { AfterViewInit, Component, OnDestroy } from '@angular/core'; |
|||
import { fromEvent } from 'rxjs'; |
|||
import { debounceTime } from 'rxjs/operators'; |
|||
import { eThemeBasicComponents } from '../../enums/components'; |
|||
import { AfterViewInit, Component } from '@angular/core'; |
|||
import { LayoutService } from '../../services/layout.service'; |
|||
|
|||
@Component({ |
|||
selector: 'abp-layout-application', |
|||
templateUrl: './application-layout.component.html', |
|||
animations: [slideFromBottom, collapseWithMargin], |
|||
providers: [SubscriptionService], |
|||
providers: [LayoutService, SubscriptionService], |
|||
}) |
|||
export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { |
|||
export class ApplicationLayoutComponent implements AfterViewInit { |
|||
// required for dynamic component
|
|||
static type = eLayoutType.application; |
|||
|
|||
isCollapsed = true; |
|||
|
|||
smallScreen: boolean; // do not set true or false
|
|||
|
|||
logoComponentKey = eThemeBasicComponents.Logo; |
|||
|
|||
routesComponentKey = eThemeBasicComponents.Routes; |
|||
|
|||
navItemsComponentKey = eThemeBasicComponents.NavItems; |
|||
|
|||
constructor(private subscription: SubscriptionService) {} |
|||
|
|||
private checkWindowWidth() { |
|||
setTimeout(() => { |
|||
if (window.innerWidth < 992) { |
|||
if (this.smallScreen === false) { |
|||
this.isCollapsed = false; |
|||
setTimeout(() => { |
|||
this.isCollapsed = true; |
|||
}, 100); |
|||
} |
|||
this.smallScreen = true; |
|||
} else { |
|||
this.smallScreen = false; |
|||
} |
|||
}, 0); |
|||
} |
|||
constructor(public service: LayoutService) {} |
|||
|
|||
ngAfterViewInit() { |
|||
this.checkWindowWidth(); |
|||
|
|||
const resize$ = fromEvent(window, 'resize').pipe(debounceTime(150)); |
|||
this.subscription.addOne(resize$, () => this.checkWindowWidth()); |
|||
this.service.subscribeWindowSize(); |
|||
} |
|||
|
|||
ngOnDestroy() {} |
|||
} |
|||
|
|||
@ -0,0 +1,43 @@ |
|||
import { Injectable } from '@angular/core'; |
|||
import { eThemeBasicComponents } from '../enums'; |
|||
import { SubscriptionService } from '@abp/ng.core'; |
|||
import { fromEvent } from 'rxjs'; |
|||
import { debounceTime } from 'rxjs/operators'; |
|||
|
|||
@Injectable() |
|||
export class LayoutService { |
|||
isCollapsed = true; |
|||
|
|||
smallScreen: boolean; // do not set true or false
|
|||
|
|||
logoComponentKey = eThemeBasicComponents.Logo; |
|||
|
|||
routesComponentKey = eThemeBasicComponents.Routes; |
|||
|
|||
navItemsComponentKey = eThemeBasicComponents.NavItems; |
|||
|
|||
constructor(private subscription: SubscriptionService) {} |
|||
|
|||
private checkWindowWidth() { |
|||
setTimeout(() => { |
|||
if (window.innerWidth < 992) { |
|||
if (this.smallScreen === false) { |
|||
this.isCollapsed = false; |
|||
setTimeout(() => { |
|||
this.isCollapsed = true; |
|||
}, 100); |
|||
} |
|||
this.smallScreen = true; |
|||
} else { |
|||
this.smallScreen = false; |
|||
} |
|||
}, 0); |
|||
} |
|||
|
|||
subscribeWindowSize() { |
|||
this.checkWindowWidth(); |
|||
|
|||
const resize$ = fromEvent(window, 'resize').pipe(debounceTime(150)); |
|||
this.subscription.addOne(resize$, () => this.checkWindowWidth()); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue