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: `
+ 0"
+ class="dropdown"
+ ngbDropdown
+ #languageDropdown="ngbDropdown"
+ display="static"
+ >
+
+
+
+ `,
+})
+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 @@
-
-
- 0" class="nav-item">
-
-
-
+
+
-
-
-
- {{
- 'AbpAccount::Login' | abpLocalization
- }}
+
+
-
-
+
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();
-}