diff --git a/npm/ng-packs/packages/theme-basic/src/lib/actions/index.ts b/npm/ng-packs/packages/theme-basic/src/lib/actions/index.ts deleted file mode 100644 index 2e29080de8..0000000000 --- a/npm/ng-packs/packages/theme-basic/src/lib/actions/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './layout.actions'; diff --git a/npm/ng-packs/packages/theme-basic/src/lib/actions/layout.actions.ts b/npm/ng-packs/packages/theme-basic/src/lib/actions/layout.actions.ts deleted file mode 100644 index 3786ea82b7..0000000000 --- a/npm/ng-packs/packages/theme-basic/src/lib/actions/layout.actions.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Layout } from '../models/layout'; - -export class AddNavigationElement { - static readonly type = '[Layout] Add Navigation Element'; - constructor(public payload: Layout.NavigationElement | Layout.NavigationElement[]) {} -} - -export class RemoveNavigationElementByName { - static readonly type = '[Layout] Remove Navigation ElementByName'; - constructor(public name: string) {} -} 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 c4acbec880..bb34ec33f6 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 @@ -34,12 +34,8 @@ 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 06b6d5339c..6c4de6cf21 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 @@ -2,6 +2,8 @@ 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/current-user.component'; +export * from './nav-items/languages.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/nav-items/current-user.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/current-user.component.ts new file mode 100644 index 0000000000..70238d3538 --- /dev/null +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/current-user.component.ts @@ -0,0 +1,67 @@ +import { ApplicationConfiguration, AuthService, ConfigState } from '@abp/ng.core'; +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { Select } from '@ngxs/store'; +import { Observable } from 'rxjs'; + +@Component({ + selector: 'abp-current-user', + // tslint:disable-next-line: component-max-inline-declarations + template: ` + + {{ + 'AbpAccount::Login' | abpLocalization + }} + + + `, +}) +export class CurrentUserComponent implements OnInit { + @Select(ConfigState.getOne('currentUser')) + currentUser$: Observable; + + get smallScreen(): boolean { + return window.innerWidth < 992; + } + + constructor(private authService: AuthService, private router: Router) {} + + ngOnInit() {} + + logout() { + this.authService.logout().subscribe(() => { + this.router.navigate(['/'], { state: { redirectUrl: this.router.url } }); + }); + } +} diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/languages.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/languages.component.ts new file mode 100644 index 0000000000..115e9e563e --- /dev/null +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/languages.component.ts @@ -0,0 +1,88 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { Store, Select } from '@ngxs/store'; +import { SetLanguage, ConfigState, ApplicationConfiguration, SessionState } from '@abp/ng.core'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import snq from 'snq'; + +@Component({ + selector: 'abp-languages', + // tslint:disable-next-line: component-max-inline-declarations + template: ` + + `, +}) +export class LanguagesComponent implements OnInit { + get smallScreen(): boolean { + return window.innerWidth < 992; + } + + @Select(ConfigState.getDeep('localization.languages')) + languages$: Observable; + + 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) {} + + ngOnInit() {} + + onChangeLang(cultureName: string) { + this.store.dispatch(new SetLanguage(cultureName)); + } +} 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 cb86773db5..955ccb8878 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 @@ -1,81 +1,16 @@ - - - - + - + 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 index 6819807cd7..7a5ee9b37a 100644 --- 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 @@ -1,125 +1,12 @@ -import { - ABP, - ApplicationConfiguration, - AuthService, - ConfigState, - SessionState, - SetLanguage, - takeUntilDestroy, -} from '@abp/ng.core'; -import { - AfterViewInit, - Component, - Input, - OnDestroy, - 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 { Observable } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import snq from 'snq'; -import { AddNavigationElement } from '../../actions/layout.actions'; -import { eNavigationElementNames } from '../../enums/navigation-element-names'; -import { Layout } from '../../models/layout'; -import { LayoutState } from '../../states/layout.state'; +import { NavItem, NavItemsService } from '@abp/ng.theme.shared'; +import { Component, Input, TrackByFunction } from '@angular/core'; @Component({ selector: 'abp-nav-items', templateUrl: 'nav-items.component.html', }) -export class NavItemsComponent implements AfterViewInit, OnDestroy { - @Select(LayoutState.getNavigationElements) - navElements$: Observable; +export class NavItemsComponent { + trackByFn: TrackByFunction = (_, element) => element.id; - @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.name; - - 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 }, - }), - ); - }); - } + constructor(public readonly navItems: NavItemsService) {} } 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 ab7aa86942..e62f551851 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 @@ -5,4 +5,6 @@ export const enum eThemeBasicComponents { Logo = 'Theme.LogoComponent', Routes = 'Theme.RoutesComponent', NavItems = 'Theme.NavItemsComponent', + CurrentUser = 'Theme.CurrentUserComponent', + Languages = 'Theme.LanguagesComponent', } diff --git a/npm/ng-packs/packages/theme-basic/src/lib/enums/index.ts b/npm/ng-packs/packages/theme-basic/src/lib/enums/index.ts index 51f9e1ead1..07635cbbc8 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/enums/index.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/enums/index.ts @@ -1,2 +1 @@ export * from './components'; -export * from './navigation-element-names'; diff --git a/npm/ng-packs/packages/theme-basic/src/lib/enums/navigation-element-names.ts b/npm/ng-packs/packages/theme-basic/src/lib/enums/navigation-element-names.ts deleted file mode 100644 index 12aa3f0941..0000000000 --- a/npm/ng-packs/packages/theme-basic/src/lib/enums/navigation-element-names.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const enum eNavigationElementNames { - Language = 'LanguageRef', - User = 'CurrentUserRef', -} diff --git a/npm/ng-packs/packages/theme-basic/src/lib/providers/index.ts b/npm/ng-packs/packages/theme-basic/src/lib/providers/index.ts index 607efd62a5..8102d94f62 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/providers/index.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/providers/index.ts @@ -1 +1,2 @@ +export * from './nav-item.provider'; export * from './styles.provider'; diff --git a/npm/ng-packs/packages/theme-basic/src/lib/providers/nav-item.provider.ts b/npm/ng-packs/packages/theme-basic/src/lib/providers/nav-item.provider.ts new file mode 100644 index 0000000000..f5eca00f04 --- /dev/null +++ b/npm/ng-packs/packages/theme-basic/src/lib/providers/nav-item.provider.ts @@ -0,0 +1,31 @@ +import { NavItemsService } from '@abp/ng.theme.shared'; +import { APP_INITIALIZER } from '@angular/core'; +import { CurrentUserComponent } from '../components/nav-items/current-user.component'; +import { LanguagesComponent } from '../components/nav-items/languages.component'; +import { eThemeBasicComponents } from '../enums/components'; + +export const BASIC_THEME_NAV_ITEM_PROVIDERS = [ + { + provide: APP_INITIALIZER, + useFactory: configureNavItems, + deps: [NavItemsService], + multi: true, + }, +]; + +export function configureNavItems(navItems: NavItemsService) { + return () => { + navItems.addItems([ + { + id: eThemeBasicComponents.Languages, + order: 100, + component: LanguagesComponent, + }, + { + id: eThemeBasicComponents.CurrentUser, + order: 101, + component: CurrentUserComponent, + }, + ]); + }; +} diff --git a/npm/ng-packs/packages/theme-basic/src/lib/services/index.ts b/npm/ng-packs/packages/theme-basic/src/lib/services/index.ts deleted file mode 100644 index 455d7798f5..0000000000 --- a/npm/ng-packs/packages/theme-basic/src/lib/services/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './layout-state.service'; diff --git a/npm/ng-packs/packages/theme-basic/src/lib/services/layout-state.service.ts b/npm/ng-packs/packages/theme-basic/src/lib/services/layout-state.service.ts deleted file mode 100644 index 415cef61d3..0000000000 --- a/npm/ng-packs/packages/theme-basic/src/lib/services/layout-state.service.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Store } from '@ngxs/store'; -import { LayoutState } from '../states/layout.state'; -import { AddNavigationElement, RemoveNavigationElementByName } from '../actions'; -import { Layout } from '../models/layout'; - -@Injectable({ providedIn: 'root' }) -export class LayoutStateService { - constructor(private store: Store) {} - - getNavigationElements() { - return this.store.selectSnapshot(LayoutState.getNavigationElements); - } - - dispatchAddNavigationElement(...args: ConstructorParameters) { - return this.store.dispatch(new AddNavigationElement(...args)); - } - - dispatchRemoveNavigationElementByName( - ...args: ConstructorParameters - ) { - return this.store.dispatch(new RemoveNavigationElementByName(...args)); - } -} diff --git a/npm/ng-packs/packages/theme-basic/src/lib/states/index.ts b/npm/ng-packs/packages/theme-basic/src/lib/states/index.ts deleted file mode 100644 index 1e257fc64c..0000000000 --- a/npm/ng-packs/packages/theme-basic/src/lib/states/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './layout.state'; diff --git a/npm/ng-packs/packages/theme-basic/src/lib/states/layout.state.ts b/npm/ng-packs/packages/theme-basic/src/lib/states/layout.state.ts deleted file mode 100644 index 93f4352d69..0000000000 --- a/npm/ng-packs/packages/theme-basic/src/lib/states/layout.state.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Action, Selector, State, StateContext } from '@ngxs/store'; -import snq from 'snq'; -import { AddNavigationElement, RemoveNavigationElementByName } from '../actions/layout.actions'; -import { Layout } from '../models/layout'; - -@State({ - name: 'LayoutState', - defaults: { navigationElements: [] } as Layout.State, -}) -@Injectable() -export class LayoutState { - @Selector() - static getNavigationElements({ navigationElements }: Layout.State): Layout.NavigationElement[] { - return navigationElements; - } - - @Action(AddNavigationElement) - layoutAddAction( - { getState, patchState }: StateContext, - { payload = [] }: AddNavigationElement, - ) { - let { navigationElements } = getState(); - - if (!Array.isArray(payload)) { - payload = [payload]; - } - - if (navigationElements.length) { - payload = snq( - () => - (payload as Layout.NavigationElement[]).filter( - ({ name }) => navigationElements.findIndex(nav => nav.name === name) < 0, - ), - [], - ); - } - - if (!payload.length) return; - - navigationElements = [...navigationElements, ...payload] - .map(element => ({ ...element, order: element.order || 99 })) - .sort((a, b) => a.order - b.order); - - return patchState({ - navigationElements, - }); - } - - @Action(RemoveNavigationElementByName) - layoutRemoveAction( - { getState, patchState }: StateContext, - { name }: RemoveNavigationElementByName, - ) { - let { navigationElements } = getState(); - - const index = navigationElements.findIndex(element => element.name === name); - - if (index > -1) { - navigationElements = navigationElements.splice(index, 1); - } - - return patchState({ - navigationElements, - }); - } -} diff --git a/npm/ng-packs/packages/theme-basic/src/lib/tests/layout-state.service.spec.ts b/npm/ng-packs/packages/theme-basic/src/lib/tests/layout-state.service.spec.ts deleted file mode 100644 index de4f489aaa..0000000000 --- a/npm/ng-packs/packages/theme-basic/src/lib/tests/layout-state.service.spec.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator/jest'; -import { Store } from '@ngxs/store'; -import * as LayoutActions from '../actions'; -import { LayoutStateService } from '../services/layout-state.service'; -import { LayoutState } from '../states/layout.state'; -describe('LayoutStateService', () => { - let service: LayoutStateService; - let spectator: SpectatorService; - let store: SpyObject; - - const createService = createServiceFactory({ service: LayoutStateService, mocks: [Store] }); - beforeEach(() => { - spectator = createService(); - service = spectator.service; - store = spectator.get(Store); - }); - - test('should have the all LayoutState static methods', () => { - const reg = /(?<=static )(.*)(?=\()/gm; - LayoutState.toString() - .match(reg) - .forEach(fnName => { - expect(service[fnName]).toBeTruthy(); - - const spy = jest.spyOn(store, 'selectSnapshot'); - spy.mockClear(); - - const isDynamicSelector = LayoutState[fnName].name !== 'memoized'; - - if (isDynamicSelector) { - LayoutState[fnName] = jest.fn((...args) => args); - service[fnName]('test', 0, {}); - expect(LayoutState[fnName]).toHaveBeenCalledWith('test', 0, {}); - } else { - service[fnName](); - expect(spy).toHaveBeenCalledWith(LayoutState[fnName]); - } - }); - }); - - test('should have a dispatch method for every LayoutState action', () => { - const reg = /(?<=dispatch)(\w+)(?=\()/gm; - LayoutStateService.toString() - .match(reg) - .forEach(fnName => { - expect(LayoutActions[fnName]).toBeTruthy(); - - const spy = jest.spyOn(store, 'dispatch'); - spy.mockClear(); - - const params = Array.from(new Array(LayoutActions[fnName].length)); - - service[`dispatch${fnName}`](...params); - expect(spy).toHaveBeenCalledWith(new LayoutActions[fnName](...params)); - }); - }); -}); 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 5c4d67515e..f599124e83 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 @@ -3,7 +3,6 @@ import { ThemeSharedModule } from '@abp/ng.theme.shared'; import { ModuleWithProviders, NgModule } from '@angular/core'; import { NgbCollapseModule, NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; import { NgxValidateCoreModule } from '@ngx-validate/core'; -import { NgxsModule } from '@ngxs/store'; import { AccountLayoutComponent } from './components/account-layout/account-layout.component'; import { ApplicationLayoutComponent } from './components/application-layout/application-layout.component'; import { EmptyLayoutComponent } from './components/empty-layout/empty-layout.component'; @@ -11,8 +10,10 @@ import { LogoComponent } from './components/logo/logo.component'; import { NavItemsComponent } from './components/nav-items/nav-items.component'; import { RoutesComponent } from './components/routes/routes.component'; import { ValidationErrorComponent } from './components/validation-error/validation-error.component'; +import { CurrentUserComponent } from './components/nav-items/current-user.component'; +import { LanguagesComponent } from './components/nav-items/languages.component'; import { BASIC_THEME_STYLES_PROVIDERS } from './providers/styles.provider'; -import { LayoutState } from './states/layout.state'; +import { BASIC_THEME_NAV_ITEM_PROVIDERS } from './providers/nav-item.provider'; export const LAYOUTS = [ApplicationLayoutComponent, AccountLayoutComponent, EmptyLayoutComponent]; @@ -23,6 +24,17 @@ export const LAYOUTS = [ApplicationLayoutComponent, AccountLayoutComponent, Empt LogoComponent, NavItemsComponent, RoutesComponent, + CurrentUserComponent, + LanguagesComponent, + ], + exports: [ + ...LAYOUTS, + ValidationErrorComponent, + LogoComponent, + NavItemsComponent, + RoutesComponent, + CurrentUserComponent, + LanguagesComponent, ], imports: [ CoreModule, @@ -30,7 +42,6 @@ export const LAYOUTS = [ApplicationLayoutComponent, AccountLayoutComponent, Empt NgbCollapseModule, NgbDropdownModule, NgxValidateCoreModule, - NgxsModule.forFeature([LayoutState]), NgxValidateCoreModule.forRoot({ targetSelector: '.form-group', blueprints: { @@ -52,20 +63,13 @@ export const LAYOUTS = [ApplicationLayoutComponent, AccountLayoutComponent, Empt errorTemplate: ValidationErrorComponent, }), ], - exports: [ - ...LAYOUTS, - ValidationErrorComponent, - LogoComponent, - NavItemsComponent, - RoutesComponent, - ], - entryComponents: [...LAYOUTS, ValidationErrorComponent], + entryComponents: [...LAYOUTS, ValidationErrorComponent, CurrentUserComponent, LanguagesComponent], }) export class ThemeBasicModule { static forRoot(): ModuleWithProviders { return { ngModule: ThemeBasicModule, - providers: [BASIC_THEME_STYLES_PROVIDERS], + providers: [BASIC_THEME_NAV_ITEM_PROVIDERS, BASIC_THEME_STYLES_PROVIDERS], }; } } diff --git a/npm/ng-packs/packages/theme-basic/src/public-api.ts b/npm/ng-packs/packages/theme-basic/src/public-api.ts index 680c00ebbb..9b5870914b 100644 --- a/npm/ng-packs/packages/theme-basic/src/public-api.ts +++ b/npm/ng-packs/packages/theme-basic/src/public-api.ts @@ -2,11 +2,8 @@ * Public API Surface of theme-basic */ -export * from './lib/actions'; export * from './lib/components'; export * from './lib/enums'; export * from './lib/models'; export * from './lib/providers'; -export * from './lib/services'; -export * from './lib/states'; export * from './lib/theme-basic.module'; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/models/index.ts b/npm/ng-packs/packages/theme-shared/src/lib/models/index.ts index 628ce6d80d..d1e2e71c5c 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/models/index.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/models/index.ts @@ -2,3 +2,4 @@ export * from './common'; export * from './confirmation'; export * from './statistics'; export * from './toaster'; +export * from './nav-item'; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/models/nav-item.ts b/npm/ng-packs/packages/theme-shared/src/lib/models/nav-item.ts new file mode 100644 index 0000000000..ea3d5c45a2 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/models/nav-item.ts @@ -0,0 +1,10 @@ +import { Type } from '@angular/core'; + +export interface NavItem { + id: string | number; + component?: Type; + html?: string; + action?: () => void; + order?: number; + requiredPolicy?: string; +} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/services/index.ts b/npm/ng-packs/packages/theme-shared/src/lib/services/index.ts index c363a56894..29a2ca9668 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/services/index.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/services/index.ts @@ -1,3 +1,4 @@ export * from './confirmation.service'; export * from './modal.service'; export * from './toaster.service'; +export * from './nav-items.service'; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/services/nav-items.service.ts b/npm/ng-packs/packages/theme-shared/src/lib/services/nav-items.service.ts new file mode 100644 index 0000000000..95403837fd --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/services/nav-items.service.ts @@ -0,0 +1,49 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { NavItem } from '../models/nav-item'; + +@Injectable({ providedIn: 'root' }) +export class NavItemsService { + private _items$ = new BehaviorSubject([]); + + get items(): NavItem[] { + return this._items$.value; + } + + get items$(): Observable { + return this._items$.asObservable(); + } + + addItems(items: NavItem[]) { + this._items$.next([...this.items, ...items].sort(sortItems)); + } + + removeItem(id: string | number) { + const index = this.items.findIndex(item => item.id === id); + + if (index > -1) { + this._items$.next([...this.items.slice(0, index), ...this.items.slice(index + 1)]); + } + } + + patchItem(id: string | number, item: Partial>) { + const index = this.items.findIndex(i => i.id === id); + + if (index > -1) { + this._items$.next( + [ + ...this.items.slice(0, index), + { ...this.items[index], ...item }, + ...this.items.slice(index + 1), + ].sort(sortItems), + ); + } + } +} + +function sortItems(a: NavItem, b: NavItem) { + if (!a.order) return 1; + if (!b.order) return -1; + + return a.order < b.order ? -1 : 1; +} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/utils/index.ts b/npm/ng-packs/packages/theme-shared/src/lib/utils/index.ts index f1d694dded..9f290b5ca3 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/utils/index.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/utils/index.ts @@ -1,4 +1,3 @@ export * from './date-parser-formatter'; -export * from './nav-items'; export * from './validation-utils'; export * from './widget-utils'; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/utils/nav-items.ts b/npm/ng-packs/packages/theme-shared/src/lib/utils/nav-items.ts deleted file mode 100644 index b6b002a151..0000000000 --- a/npm/ng-packs/packages/theme-shared/src/lib/utils/nav-items.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Type } from '@angular/core'; -import { ReplaySubject } from 'rxjs'; - -export interface NavItem { - component?: Type; - html?: string; - action?: () => void; - order?: number; - permission?: string; -} - -const navItems: NavItem[] = []; -const navItems$ = new ReplaySubject(1); - -export function addNavItem(item: NavItem) { - navItems.push(item); - navItems$.next(navItems.sort((a, b) => (a.order ? a.order - b.order : 1))); -} - -export function getNavItems() { - return navItems$.asObservable(); -}