mirror of https://github.com/abpframework/abp
commit
09dccf67da
@ -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