From 8e9050e6cd5d2ac4c023b96e1e27ccb0436ce76e Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Thu, 7 May 2020 11:07:09 +0300 Subject: [PATCH 1/4] feat(theme-basic): create logo component --- .../src/lib/components/logo/logo.component.ts | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 npm/ng-packs/packages/theme-basic/src/lib/components/logo/logo.component.ts diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/logo/logo.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/logo/logo.component.ts new file mode 100644 index 0000000000..6f8365c3e3 --- /dev/null +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/logo/logo.component.ts @@ -0,0 +1,29 @@ +import { Config, ConfigState } from '@abp/ng.core'; +import { Component } from '@angular/core'; +import { Store } from '@ngxs/store'; + +@Component({ + selector: 'abp-logo', + template: ` + + + + + + {{ appInfo.name }} + + `, +}) +export class LogoComponent { + get appInfo(): Config.Application { + return this.store.selectSnapshot(ConfigState.getApplicationInfo); + } + + constructor(private store: Store) {} +} From 99fb9b6e59a990c46ec1d54cedbec9a5fe45ee23 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Thu, 7 May 2020 11:07:23 +0300 Subject: [PATCH 2/4] feat(theme-basic): create nav-items component --- .../nav-items/nav-items.component.html | 80 +++++++++++ .../nav-items/nav-items.component.ts | 125 ++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.html create mode 100644 npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.ts diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.html b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.html new file mode 100644 index 0000000000..94d7954570 --- /dev/null +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.html @@ -0,0 +1,80 @@ + + + + + + + + + diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.ts new file mode 100644 index 0000000000..7bae923989 --- /dev/null +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.ts @@ -0,0 +1,125 @@ +import { + Component, + AfterViewInit, + TrackByFunction, + TemplateRef, + ViewChild, + OnDestroy, + Input, +} from '@angular/core'; +import { + ABP, + takeUntilDestroy, + SetLanguage, + AuthService, + ConfigState, + ApplicationConfiguration, + SessionState, +} from '@abp/ng.core'; +import { LayoutState } from '../../states/layout.state'; +import { Store, Select } from '@ngxs/store'; +import { eNavigationElementNames } from '../../enums/navigation-element-names'; +import { AddNavigationElement } from '../../actions/layout.actions'; +import { map, filter } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { Layout } from '../../models/layout'; +import { Navigate, RouterState } from '@ngxs/router-plugin'; +import snq from 'snq'; +import compare from 'just-compare'; + +@Component({ + selector: 'abp-nav-items', + templateUrl: 'nav-items.component.html', +}) +export class NavItemsComponent implements AfterViewInit, OnDestroy { + @Select(LayoutState.getNavigationElements) + navElements$: Observable; + + @Select(ConfigState.getOne('currentUser')) + currentUser$: Observable; + + @Select(ConfigState.getDeep('localization.languages')) + languages$: Observable; + + @ViewChild('currentUser', { static: false, read: TemplateRef }) + currentUserRef: TemplateRef; + + @ViewChild('language', { static: false, read: TemplateRef }) + languageRef: TemplateRef; + + @Input() + smallScreen: boolean; + + rightPartElements: TemplateRef[] = []; + + trackByFn: TrackByFunction = (_, element) => element; + + get defaultLanguage$(): Observable { + return this.languages$.pipe( + map( + languages => + snq( + () => languages.find(lang => lang.cultureName === this.selectedLangCulture).displayName, + ), + '', + ), + ); + } + + get dropdownLanguages$(): Observable { + return this.languages$.pipe( + map( + languages => + snq(() => languages.filter(lang => lang.cultureName !== this.selectedLangCulture)), + [], + ), + ); + } + + get selectedLangCulture(): string { + return this.store.selectSnapshot(SessionState.getLanguage); + } + + constructor(private store: Store, private authService: AuthService) {} + + ngAfterViewInit() { + const navigations = this.store + .selectSnapshot(LayoutState.getNavigationElements) + .map(({ name }) => name); + + if (navigations.indexOf(eNavigationElementNames.Language) < 0) { + this.store.dispatch( + new AddNavigationElement([ + { element: this.languageRef, order: 4, name: eNavigationElementNames.Language }, + { element: this.currentUserRef, order: 5, name: eNavigationElementNames.User }, + ]), + ); + } + + this.navElements$ + .pipe( + map(elements => elements.map(({ element }) => element)), + filter(elements => !compare(elements, this.rightPartElements)), + takeUntilDestroy(this), + ) + .subscribe(elements => { + setTimeout(() => (this.rightPartElements = elements), 0); + }); + } + + ngOnDestroy() {} + + onChangeLang(cultureName: string) { + this.store.dispatch(new SetLanguage(cultureName)); + } + + logout() { + this.authService.logout().subscribe(() => { + this.store.dispatch( + new Navigate(['/'], null, { + state: { redirectUrl: this.store.selectSnapshot(RouterState).state.url }, + }), + ); + }); + } +} From 5bb96ddc5bffbf8076277f4021af6ab6641d3dbc Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Thu, 7 May 2020 11:07:46 +0300 Subject: [PATCH 3/4] feat(theme-basic): create routes component --- .../application-layout.component.html | 219 ++---------------- .../application-layout.component.ts | 160 +------------ .../theme-basic/src/lib/components/index.ts | 3 + .../components/routes/routes.component.html | 111 +++++++++ .../lib/components/routes/routes.component.ts | 51 ++++ .../theme-basic/src/lib/enums/components.ts | 3 + .../theme-basic/src/lib/theme-basic.module.ts | 13 +- 7 files changed, 202 insertions(+), 358 deletions(-) create mode 100644 npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html create mode 100644 npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.ts diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html b/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html index 438c315c16..1faaafaa58 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html @@ -3,16 +3,8 @@ id="main-navbar" style="min-height: 4rem;" > - @@ -160,81 +45,3 @@ > - - - {{ appInfo.name }} - - - - - - - - - diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.ts index 75ab177fb4..e996d2f786 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.ts @@ -1,34 +1,10 @@ -import { - ABP, - ApplicationConfiguration, - AuthService, - Config, - ConfigState, - eLayoutType, - SessionState, - SetLanguage, - takeUntilDestroy, -} from '@abp/ng.core'; +import { eLayoutType, takeUntilDestroy } from '@abp/ng.core'; import { collapseWithMargin, slideFromBottom } from '@abp/ng.theme.shared'; -import { - AfterViewInit, - Component, - OnDestroy, - Renderer2, - TemplateRef, - TrackByFunction, - ViewChild, -} from '@angular/core'; -import { Navigate, RouterState } from '@ngxs/router-plugin'; -import { Select, Store } from '@ngxs/store'; -import compare from 'just-compare'; -import { fromEvent, Observable } from 'rxjs'; -import { debounceTime, filter, map } from 'rxjs/operators'; -import snq from 'snq'; -import { AddNavigationElement } from '../../actions'; -import { Layout } from '../../models/layout'; -import { LayoutState } from '../../states'; -import { eNavigationElementNames } from '../../enums/navigation-element-names'; +import { AfterViewInit, Component, OnDestroy } from '@angular/core'; +import { Store } from '@ngxs/store'; +import { fromEvent } from 'rxjs'; +import { debounceTime } from 'rxjs/operators'; +import { eThemeBasicComponents } from '../../enums/components'; @Component({ selector: 'abp-layout-application', @@ -39,75 +15,19 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { // required for dynamic component static type = eLayoutType.application; - @Select(ConfigState.getOne('routes')) - routes$: Observable; - - @Select(ConfigState.getOne('currentUser')) - currentUser$: Observable; - - @Select(ConfigState.getDeep('localization.languages')) - languages$: Observable; - - @Select(LayoutState.getNavigationElements) - navElements$: Observable; - - @ViewChild('currentUser', { static: false, read: TemplateRef }) - currentUserRef: TemplateRef; - - @ViewChild('language', { static: false, read: TemplateRef }) - languageRef: TemplateRef; - isDropdownChildDynamic: boolean; isCollapsed = true; smallScreen: boolean; // do not set true or false - get appInfo(): Config.Application { - return this.store.selectSnapshot(ConfigState.getApplicationInfo); - } - - get visibleRoutes$(): Observable { - return this.routes$.pipe(map(routes => getVisibleRoutes(routes))); - } - - get defaultLanguage$(): Observable { - return this.languages$.pipe( - map( - languages => - snq( - () => languages.find(lang => lang.cultureName === this.selectedLangCulture).displayName, - ), - '', - ), - ); - } - - get dropdownLanguages$(): Observable { - return this.languages$.pipe( - map( - languages => - snq(() => languages.filter(lang => lang.cultureName !== this.selectedLangCulture)), - [], - ), - ); - } - - get selectedLangCulture(): string { - return this.store.selectSnapshot(SessionState.getLanguage); - } - - rightPartElements: TemplateRef[] = []; + logoComponentKey = eThemeBasicComponents.Logo; - trackByFn: TrackByFunction = (_, item) => item.name; + routesComponentKey = eThemeBasicComponents.Routes; - trackElementByFn: TrackByFunction = (_, element) => element; + navItemsComponentKey = eThemeBasicComponents.NavItems; - constructor( - private store: Store, - private renderer: Renderer2, - private authService: AuthService, - ) {} + constructor(private store: Store) {} private checkWindowWidth() { setTimeout(() => { @@ -128,29 +48,6 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { } ngAfterViewInit() { - const navigations = this.store - .selectSnapshot(LayoutState.getNavigationElements) - .map(({ name }) => name); - - if (navigations.indexOf(eNavigationElementNames.Language) < 0) { - this.store.dispatch( - new AddNavigationElement([ - { element: this.languageRef, order: 4, name: eNavigationElementNames.Language }, - { element: this.currentUserRef, order: 5, name: eNavigationElementNames.User }, - ]), - ); - } - - this.navElements$ - .pipe( - map(elements => elements.map(({ element }) => element)), - filter(elements => !compare(elements, this.rightPartElements)), - takeUntilDestroy(this), - ) - .subscribe(elements => { - setTimeout(() => (this.rightPartElements = elements), 0); - }); - this.checkWindowWidth(); fromEvent(window, 'resize') @@ -161,41 +58,4 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { } ngOnDestroy() {} - - onChangeLang(cultureName: string) { - this.store.dispatch(new SetLanguage(cultureName)); - } - - logout() { - this.authService.logout().subscribe(() => { - this.store.dispatch( - new Navigate(['/'], null, { - state: { redirectUrl: this.store.selectSnapshot(RouterState).state.url }, - }), - ); - }); - } - - openChange(event: boolean, childrenContainer: HTMLDivElement) { - if (!event) { - Object.keys(childrenContainer.style) - .filter(key => Number.isInteger(+key)) - .forEach(key => { - this.renderer.removeStyle(childrenContainer, childrenContainer.style[key]); - }); - this.renderer.removeStyle(childrenContainer, 'left'); - } - } -} - -function getVisibleRoutes(routes: ABP.FullRoute[]) { - return routes.reduce((acc, val) => { - if (val.invisible) return acc; - - if (val.children && val.children.length) { - val.children = getVisibleRoutes(val.children); - } - - return [...acc, val]; - }, []); } diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/index.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/index.ts index cd1e7e8757..06b6d5339c 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/index.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/index.ts @@ -1,4 +1,7 @@ export * from './account-layout/account-layout.component'; export * from './application-layout/application-layout.component'; export * from './empty-layout/empty-layout.component'; +export * from './logo/logo.component'; +export * from './nav-items/nav-items.component'; +export * from './routes/routes.component'; export * from './validation-error/validation-error.component'; diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html b/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html new file mode 100644 index 0000000000..226e19c39e --- /dev/null +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html @@ -0,0 +1,111 @@ + diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.ts new file mode 100644 index 0000000000..22b30426fa --- /dev/null +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.ts @@ -0,0 +1,51 @@ +import { Component, OnInit, TrackByFunction, Input, Renderer2 } from '@angular/core'; +import { Observable } from 'rxjs'; +import { ABP, ConfigState } from '@abp/ng.core'; +import { map } from 'rxjs/operators'; +import { Select } from '@ngxs/store'; + +@Component({ + selector: 'abp-routes', + templateUrl: 'routes.component.html', +}) +export class RoutesComponent { + @Select(ConfigState.getOne('routes')) + routes$: Observable; + + @Input() + smallScreen: boolean; + + @Input() + isDropdownChildDynamic: boolean; + + get visibleRoutes$(): Observable { + return this.routes$.pipe(map(routes => getVisibleRoutes(routes))); + } + + trackByFn: TrackByFunction = (_, item) => item.name; + + constructor(private renderer: Renderer2) {} + + openChange(event: boolean, childrenContainer: HTMLDivElement) { + if (!event) { + Object.keys(childrenContainer.style) + .filter(key => Number.isInteger(+key)) + .forEach(key => { + this.renderer.removeStyle(childrenContainer, childrenContainer.style[key]); + }); + this.renderer.removeStyle(childrenContainer, 'left'); + } + } +} + +function getVisibleRoutes(routes: ABP.FullRoute[]) { + return routes.reduce((acc, val) => { + if (val.invisible) return acc; + + if (val.children && val.children.length) { + val.children = getVisibleRoutes(val.children); + } + + return [...acc, val]; + }, []); +} diff --git a/npm/ng-packs/packages/theme-basic/src/lib/enums/components.ts b/npm/ng-packs/packages/theme-basic/src/lib/enums/components.ts index e773e17464..ab7aa86942 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/enums/components.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/enums/components.ts @@ -2,4 +2,7 @@ export const enum eThemeBasicComponents { ApplicationLayout = 'Theme.ApplicationLayoutComponent', AccountLayout = 'Theme.AccountLayoutComponent', EmptyLayout = 'Theme.EmptyLayoutComponent', + Logo = 'Theme.LogoComponent', + Routes = 'Theme.RoutesComponent', + NavItems = 'Theme.NavItemsComponent', } diff --git a/npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts b/npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts index 827983b15f..f7625b7cf9 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts @@ -10,11 +10,20 @@ import { EmptyLayoutComponent } from './components/empty-layout/empty-layout.com import { LayoutState } from './states/layout.state'; import { ValidationErrorComponent } from './components/validation-error/validation-error.component'; import { InitialService } from './services/initial.service'; +import { LogoComponent } from './components/logo/logo.component'; +import { RoutesComponent } from './components/routes/routes.component'; +import { NavItemsComponent } from './components/nav-items/nav-items.component'; export const LAYOUTS = [ApplicationLayoutComponent, AccountLayoutComponent, EmptyLayoutComponent]; @NgModule({ - declarations: [...LAYOUTS, ValidationErrorComponent], + declarations: [ + ...LAYOUTS, + ValidationErrorComponent, + LogoComponent, + NavItemsComponent, + RoutesComponent, + ], imports: [ CoreModule, ThemeSharedModule, @@ -38,7 +47,7 @@ export const LAYOUTS = [ApplicationLayoutComponent, AccountLayoutComponent, Empt errorTemplate: ValidationErrorComponent, }), ], - exports: [...LAYOUTS], + exports: [...LAYOUTS, LogoComponent, ValidationErrorComponent], entryComponents: [...LAYOUTS, ValidationErrorComponent], }) export class ThemeBasicModule { From d288ab381df423d7b4f6e31265ac7fc813420606 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Thu, 7 May 2020 11:54:23 +0300 Subject: [PATCH 4/4] fix(theme-basic): small screen dropdown problem --- .../src/lib/components/nav-items/nav-items.component.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.html b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.html index 94d7954570..cb86773db5 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.html +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.html @@ -2,10 +2,11 @@ - +