mirror of https://github.com/abpframework/abp.git
committed by
GitHub
35 changed files with 489 additions and 148 deletions
@ -0,0 +1,3 @@ |
|||||
|
# @abp/ng.account.core |
||||
|
|
||||
|
Visit the [ABP documentation](https://docs.abp.io) |
||||
@ -0,0 +1,6 @@ |
|||||
|
const jestConfig = require('../../jest.config'); |
||||
|
|
||||
|
module.exports = { |
||||
|
...jestConfig, |
||||
|
name: 'account-core', |
||||
|
}; |
||||
@ -0,0 +1,7 @@ |
|||||
|
{ |
||||
|
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json", |
||||
|
"dest": "../../dist/account-core", |
||||
|
"lib": { |
||||
|
"entryFile": "src/public-api.ts" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
{ |
||||
|
"name": "@abp/ng.account.core", |
||||
|
"version": "0.0.1", |
||||
|
"homepage": "https://abp.io", |
||||
|
"repository": { |
||||
|
"type": "git", |
||||
|
"url": "https://github.com/abpframework/abp.git" |
||||
|
}, |
||||
|
"peerDependencies": { |
||||
|
"@angular/common": ">=11.1.2", |
||||
|
"@angular/core": ">=11.1.2", |
||||
|
"@abp/ng.core": "~4.3.3", |
||||
|
"@abp/ng.theme.shared": "~4.3.3" |
||||
|
}, |
||||
|
"dependencies": { |
||||
|
"tslib": "^2.0.0" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
import { TestBed } from '@angular/core/testing'; |
||||
|
|
||||
|
import { AuthWrapperService } from './auth-wrapper.service'; |
||||
|
|
||||
|
describe('AuthWrapperService', () => { |
||||
|
let service: AuthWrapperService; |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
TestBed.configureTestingModule({}); |
||||
|
service = TestBed.inject(AuthWrapperService); |
||||
|
}); |
||||
|
|
||||
|
it('should be created', () => { |
||||
|
expect(service).toBeTruthy(); |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,16 @@ |
|||||
|
import { TestBed } from '@angular/core/testing'; |
||||
|
|
||||
|
import { TenantBoxService } from './tenant-box.service'; |
||||
|
|
||||
|
describe('TenantBoxService', () => { |
||||
|
let service: TenantBoxService; |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
TestBed.configureTestingModule({}); |
||||
|
service = TestBed.inject(TenantBoxService); |
||||
|
}); |
||||
|
|
||||
|
it('should be created', () => { |
||||
|
expect(service).toBeTruthy(); |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,6 @@ |
|||||
|
/* |
||||
|
* Public API Surface of account-core |
||||
|
*/ |
||||
|
|
||||
|
export * from './lib/auth-wrapper.service'; |
||||
|
export * from './lib/tenant-box.service'; |
||||
@ -0,0 +1,26 @@ |
|||||
|
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||
|
|
||||
|
import 'zone.js/dist/zone'; |
||||
|
import 'zone.js/dist/zone-testing'; |
||||
|
import { getTestBed } from '@angular/core/testing'; |
||||
|
import { |
||||
|
BrowserDynamicTestingModule, |
||||
|
platformBrowserDynamicTesting |
||||
|
} from '@angular/platform-browser-dynamic/testing'; |
||||
|
|
||||
|
declare const require: { |
||||
|
context(path: string, deep?: boolean, filter?: RegExp): { |
||||
|
keys(): string[]; |
||||
|
<T>(id: string): T; |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
// First, initialize the Angular testing environment.
|
||||
|
getTestBed().initTestEnvironment( |
||||
|
BrowserDynamicTestingModule, |
||||
|
platformBrowserDynamicTesting() |
||||
|
); |
||||
|
// Then we find all the tests.
|
||||
|
const context = require.context('./', true, /\.spec\.ts$/); |
||||
|
// And load the modules.
|
||||
|
context.keys().map(context); |
||||
@ -0,0 +1,20 @@ |
|||||
|
/* To learn more about this file see: https://angular.io/config/tsconfig. */ |
||||
|
{ |
||||
|
"extends": "../../tsconfig.prod.json", |
||||
|
"compilerOptions": { |
||||
|
"outDir": "../../out-tsc/lib", |
||||
|
"target": "es2015", |
||||
|
"declaration": true, |
||||
|
"inlineSources": true, |
||||
|
"types": [], |
||||
|
"lib": ["dom", "es2018"] |
||||
|
}, |
||||
|
"angularCompilerOptions": { |
||||
|
"skipTemplateCodegen": true, |
||||
|
"strictMetadataEmit": true, |
||||
|
"fullTemplateTypeCheck": true, |
||||
|
"strictInjectionParameters": true, |
||||
|
"enableResourceInlining": true |
||||
|
}, |
||||
|
"exclude": ["src/test.ts", "**/*.spec.ts"] |
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
{ |
||||
|
"extends": "./tsconfig.lib.json", |
||||
|
"angularCompilerOptions": { |
||||
|
"enableIvy": false |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
/* To learn more about this file see: https://angular.io/config/tsconfig. */ |
||||
|
{ |
||||
|
"extends": "../../tsconfig.json", |
||||
|
"compilerOptions": { |
||||
|
"outDir": "../../out-tsc/spec", |
||||
|
"types": [ |
||||
|
"jasmine" |
||||
|
] |
||||
|
}, |
||||
|
"files": [ |
||||
|
"src/test.ts" |
||||
|
], |
||||
|
"include": [ |
||||
|
"**/*.spec.ts", |
||||
|
"**/*.d.ts" |
||||
|
] |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
{ |
||||
|
"extends": "../../tslint.json", |
||||
|
"rules": { |
||||
|
"directive-selector": [ |
||||
|
true, |
||||
|
"attribute", |
||||
|
"lib", |
||||
|
"camelCase" |
||||
|
], |
||||
|
"component-selector": [ |
||||
|
true, |
||||
|
"element", |
||||
|
"lib", |
||||
|
"kebab-case" |
||||
|
] |
||||
|
} |
||||
|
} |
||||
@ -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,21 @@ |
|||||
import { Component } from '@angular/core'; |
import { AfterViewInit, Component } from '@angular/core'; |
||||
import { eLayoutType } from '@abp/ng.core'; |
import { eLayoutType, SubscriptionService } from '@abp/ng.core'; |
||||
|
import { LayoutService } from '../../services/layout.service'; |
||||
|
|
||||
@Component({ |
@Component({ |
||||
selector: 'abp-layout-account', |
selector: 'abp-layout-account', |
||||
template: ` |
templateUrl: './account-layout.component.html', |
||||
<router-outlet></router-outlet> |
providers: [LayoutService, SubscriptionService], |
||||
<abp-confirmation></abp-confirmation> |
|
||||
`,
|
|
||||
}) |
}) |
||||
export class AccountLayoutComponent { |
export class AccountLayoutComponent implements AfterViewInit { |
||||
// required for dynamic component
|
// required for dynamic component
|
||||
static type = eLayoutType.account; |
static type = eLayoutType.account; |
||||
|
|
||||
|
authWrapperKey = 'Account.AuthWrapperComponent'; |
||||
|
|
||||
|
constructor(public service: LayoutService) {} |
||||
|
|
||||
|
ngAfterViewInit() { |
||||
|
this.service.subscribeWindowSize(); |
||||
|
} |
||||
} |
} |
||||
|
|||||
@ -1,16 +1,18 @@ |
|||||
<div class="row"> |
<div class="row"> |
||||
<div class="mx-auto col col-md-5"> |
<div class="mx-auto col col-md-5"> |
||||
<ng-container *ngIf="(isMultiTenancyEnabled$ | async) && isTenantBoxVisible"> |
<ng-container *ngIf="(service.isMultiTenancyEnabled$ | async) && service.isTenantBoxVisible"> |
||||
<abp-tenant-box *abpReplaceableTemplate="{ componentKey: tenantBoxKey }"></abp-tenant-box> |
<abp-tenant-box |
||||
|
*abpReplaceableTemplate="{ componentKey: service.tenantBoxKey }" |
||||
|
></abp-tenant-box> |
||||
</ng-container> |
</ng-container> |
||||
|
|
||||
<div class="abp-account-container"> |
<div class="abp-account-container"> |
||||
<div |
<div |
||||
*ngIf="enableLocalLogin$ | async; else disableLocalLoginTemplate" |
*ngIf="service.enableLocalLogin$ | async; else disableLocalLoginTemplate" |
||||
class="card mt-3 shadow-sm rounded" |
class="card mt-3 shadow-sm rounded" |
||||
> |
> |
||||
<div class="card-body p-5"> |
<div class="card-body p-5"> |
||||
<router-outlet></router-outlet> |
<ng-content></ng-content> |
||||
</div> |
</div> |
||||
</div> |
</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 { eLayoutType, SubscriptionService } from '@abp/ng.core'; |
||||
import { collapseWithMargin, slideFromBottom } from '@abp/ng.theme.shared'; |
import { collapseWithMargin, slideFromBottom } from '@abp/ng.theme.shared'; |
||||
import { AfterViewInit, Component, OnDestroy } from '@angular/core'; |
import { AfterViewInit, Component } from '@angular/core'; |
||||
import { fromEvent } from 'rxjs'; |
import { LayoutService } from '../../services/layout.service'; |
||||
import { debounceTime } from 'rxjs/operators'; |
|
||||
import { eThemeBasicComponents } from '../../enums/components'; |
|
||||
|
|
||||
@Component({ |
@Component({ |
||||
selector: 'abp-layout-application', |
selector: 'abp-layout-application', |
||||
templateUrl: './application-layout.component.html', |
templateUrl: './application-layout.component.html', |
||||
animations: [slideFromBottom, collapseWithMargin], |
animations: [slideFromBottom, collapseWithMargin], |
||||
providers: [SubscriptionService], |
providers: [LayoutService, SubscriptionService], |
||||
}) |
}) |
||||
export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { |
export class ApplicationLayoutComponent implements AfterViewInit { |
||||
// required for dynamic component
|
// required for dynamic component
|
||||
static type = eLayoutType.application; |
static type = eLayoutType.application; |
||||
|
|
||||
isCollapsed = true; |
constructor(public service: LayoutService) {} |
||||
|
|
||||
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); |
|
||||
} |
|
||||
|
|
||||
ngAfterViewInit() { |
ngAfterViewInit() { |
||||
this.checkWindowWidth(); |
this.service.subscribeWindowSize(); |
||||
|
|
||||
const resize$ = fromEvent(window, 'resize').pipe(debounceTime(150)); |
|
||||
this.subscription.addOne(resize$, () => this.checkWindowWidth()); |
|
||||
} |
} |
||||
|
|
||||
ngOnDestroy() {} |
|
||||
} |
} |
||||
|
|||||
@ -0,0 +1,39 @@ |
|||||
|
import { ChangeDetectorRef, 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 cdRef: ChangeDetectorRef) {} |
||||
|
|
||||
|
private checkWindowWidth() { |
||||
|
const isSmallScreen = window.innerWidth < 992; |
||||
|
if (isSmallScreen && this.smallScreen === false) { |
||||
|
this.isCollapsed = false; |
||||
|
setTimeout(() => { |
||||
|
this.isCollapsed = true; |
||||
|
}, 100); |
||||
|
} |
||||
|
this.smallScreen = isSmallScreen; |
||||
|
this.cdRef.detectChanges(); |
||||
|
} |
||||
|
|
||||
|
subscribeWindowSize() { |
||||
|
this.checkWindowWidth(); |
||||
|
|
||||
|
const resize$ = fromEvent(window, 'resize').pipe(debounceTime(150)); |
||||
|
this.subscription.addOne(resize$, () => this.checkWindowWidth()); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue