diff --git a/package.json b/package.json index b2f1e7760b..f3972c23b5 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,11 @@ "@angularclass/bootloader": "1.0.1", "@angularclass/idle-preload": "1.0.4", "@ng-bootstrap/ng-bootstrap": "1.0.0-alpha.14", + "@ngrx/core": "^1.2.0", + "@ngrx/effects": "^2.0.0", + "@ngrx/router-store": "^1.2.5", + "@ngrx/store": "^2.2.1", + "@ngrx/store-devtools": "^3.2.2", "angular2-express-engine": "2.1.0-rc.1", "angular2-platform-node": "2.1.0-rc.1", "angular2-universal": "2.1.0-rc.1", diff --git a/src/app/app.component.html b/src/app/app.component.html index 83a269739f..a562e0b675 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,21 +1,4 @@ -
- -
+
diff --git a/src/app/app.component.scss b/src/app/app.component.scss index dc8b94f882..8b13789179 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -1,13 +1 @@ -@import '../styles/variables.scss'; -header nav.navbar { - border-radius: 0rem; -} - -header nav.navbar .navbar-toggler { - float: right; -} - -header nav.navbar .navbar-toggler:hover { - cursor: pointer; -} diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 5f1726afd0..15485eb6a6 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,7 +1,14 @@ -import { Component, HostListener, Input, ChangeDetectionStrategy, ViewEncapsulation, OnDestroy, OnInit } from '@angular/core'; -import { Event, NavigationEnd, Router } from '@angular/router'; - -import { TranslateService } from 'ng2-translate'; +import { + Component, + ChangeDetectionStrategy, + ViewEncapsulation, + OnDestroy, + OnInit, HostListener +} from "@angular/core"; +import { TranslateService } from "ng2-translate"; +import { HostWindowState } from "./shared/host-window.reducer"; +import { Store } from "@ngrx/store"; +import { HostWindowActions } from "./shared/host-window.actions"; @Component({ changeDetection: ChangeDetectionStrategy.Default, @@ -11,14 +18,6 @@ import { TranslateService } from 'ng2-translate'; styleUrls: ['./app.component.css'] }) export class AppComponent implements OnDestroy, OnInit { - - // TODO: move header and all related properties into its own component - - private navCollapsed: boolean; - - private routerSubscription: any; - - private translateSubscription: any; example: string; @@ -26,32 +25,25 @@ export class AppComponent implements OnDestroy, OnInit { data: any = { greeting: 'Hello', recipient: 'World' - } + }; - constructor(public translate: TranslateService, private router: Router) { + constructor( + private translate: TranslateService, + private store: Store + ) { // this language will be used as a fallback when a translation isn't found in the current language translate.setDefaultLang('en'); // the lang to use, if the lang isn't available, it will use the current loader to get them translate.use('en'); - - this.collapse(); } ngOnInit() { - this.routerSubscription = this.router.events.subscribe((event: Event) => { - if (event instanceof NavigationEnd) { - this.collapse(); - } - }); this.translateSubscription = this.translate.get('example.with.data', { greeting: 'Hello', recipient: 'DSpace' }).subscribe((translation: string) => { this.example = translation; }); } ngOnDestroy() { - if (this.routerSubscription) { - this.routerSubscription.unsubscribe(); - } if (this.translateSubscription) { this.translateSubscription.unsubscribe(); } @@ -59,23 +51,9 @@ export class AppComponent implements OnDestroy, OnInit { @HostListener('window:resize', ['$event']) private onResize(event): void { - this.collapse(); - } - - private collapse(): void { - this.navCollapsed = true; - } - - private expand(): void { - this.navCollapsed = false; - } - - public toggle(): void { - this.navCollapsed ? this.expand() : this.collapse(); - } - - public isNavBarCollaped(): boolean { - return this.navCollapsed; + this.store.dispatch( + HostWindowActions.resize(event.target.innerWidth, event.target.innerHeight) + ); } } diff --git a/src/app/app.effects.ts b/src/app/app.effects.ts new file mode 100644 index 0000000000..6f149709f8 --- /dev/null +++ b/src/app/app.effects.ts @@ -0,0 +1,6 @@ +import { EffectsModule } from "@ngrx/effects"; +import { HeaderEffects } from "./header/header.effects"; + +export const effects = [ + EffectsModule.run(HeaderEffects) +]; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 8defafd570..ead69d94f0 100755 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -5,14 +5,54 @@ import { SharedModule } from './shared/shared.module'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; +import { HeaderComponent } from './header/header.component'; +import { StoreModule } from "@ngrx/store"; +import { RouterStoreModule } from "@ngrx/router-store"; +import { StoreDevtoolsModule } from "@ngrx/store-devtools"; + +import { rootReducer } from './app.reducers'; +import { effects } from './app.effects'; @NgModule({ - declarations: [AppComponent], + declarations: [ + AppComponent, + HeaderComponent + ], imports: [ SharedModule, HomeModule, - AppRoutingModule + AppRoutingModule, + /** + * StoreModule.provideStore is imported once in the root module, accepting a reducer + * function or object map of reducer functions. If passed an object of + * reducers, combineReducers will be run creating your application + * meta-reducer. This returns all providers for an @ngrx/store + * based application. + */ + StoreModule.provideStore(rootReducer), + + /** + * @ngrx/router-store keeps router state up-to-date in the store and uses + * the store as the single source of truth for the router's state. + */ + RouterStoreModule.connectRouter(), + + /** + * Store devtools instrument the store retaining past versions of state + * and recalculating new states. This enables powerful time-travel + * debugging. + * + * To use the debugger, install the Redux Devtools extension for either + * Chrome or Firefox + * + * See: https://github.com/zalmoxisus/redux-devtools-extension + */ + StoreDevtoolsModule.instrumentOnlyWithExtension(), + + effects + ], + providers: [ ] }) export class AppModule { diff --git a/src/app/app.reducers.ts b/src/app/app.reducers.ts new file mode 100644 index 0000000000..deb75dd06a --- /dev/null +++ b/src/app/app.reducers.ts @@ -0,0 +1,20 @@ +import { combineReducers } from "@ngrx/store"; +import { routerReducer, RouterState } from "@ngrx/router-store"; +import { headerReducer, HeaderState } from './header/header.reducer'; +import { hostWindowReducer, HostWindowState } from "./shared/host-window.reducer"; + +export interface AppState { + router: RouterState; + hostWindow: HostWindowState; + header: HeaderState; +} + +export const reducers = { + router: routerReducer, + hostWindow: hostWindowReducer, + header: headerReducer +}; + +export function rootReducer(state: any, action: any) { + return combineReducers(reducers)(state, action); +} diff --git a/src/app/header/header.actions.ts b/src/app/header/header.actions.ts new file mode 100644 index 0000000000..e3f64c1019 --- /dev/null +++ b/src/app/header/header.actions.ts @@ -0,0 +1,24 @@ +import { Action } from "@ngrx/store"; + +export class HeaderActions { + static COLLAPSE = 'dspace/header/COLLAPSE'; + static collapse(): Action { + return { + type: HeaderActions.COLLAPSE + } + } + + static EXPAND = 'dspace/header/EXPAND'; + static expand(): Action { + return { + type: HeaderActions.EXPAND + } + } + + static TOGGLE = 'dspace/header/TOGGLE'; + static toggle(): Action { + return { + type: HeaderActions.TOGGLE + } + } +} diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html new file mode 100644 index 0000000000..a4b7cf117c --- /dev/null +++ b/src/app/header/header.component.html @@ -0,0 +1,18 @@ +
+ +
diff --git a/src/app/header/header.component.scss b/src/app/header/header.component.scss new file mode 100644 index 0000000000..2170e0b905 --- /dev/null +++ b/src/app/header/header.component.scss @@ -0,0 +1,13 @@ +@import '../../styles/variables.scss'; + +header nav.navbar { + border-radius: 0rem; +} + +header nav.navbar .navbar-toggler { + float: right; +} + +header nav.navbar .navbar-toggler:hover { + cursor: pointer; +} diff --git a/src/app/header/header.component.ts b/src/app/header/header.component.ts new file mode 100644 index 0000000000..69da1aea68 --- /dev/null +++ b/src/app/header/header.component.ts @@ -0,0 +1,39 @@ +import { Component, OnInit } from "@angular/core"; +import { Store } from "@ngrx/store"; +import { HeaderState } from "./header.reducer"; +import { HeaderActions } from "./header.actions"; +import { Observable } from "rxjs"; +import 'rxjs/add/operator/filter'; + +@Component({ + selector: 'ds-header', + styleUrls: ['header.component.css'], + templateUrl: 'header.component.html' +}) +export class HeaderComponent implements OnInit { + public isNavBarCollapsed: Observable; + + constructor( + private store: Store + ) { + } + + ngOnInit(): void { + this.isNavBarCollapsed = this.store.select('header') + //unwrap navCollapsed + .map(({ navCollapsed }: HeaderState) => navCollapsed); + } + + private collapse(): void { + this.store.dispatch(HeaderActions.collapse()); + } + + private expand(): void { + this.store.dispatch(HeaderActions.expand()); + } + + public toggle(): void { + this.store.dispatch(HeaderActions.toggle()); + } + +} diff --git a/src/app/header/header.effects.ts b/src/app/header/header.effects.ts new file mode 100644 index 0000000000..d9ea53adda --- /dev/null +++ b/src/app/header/header.effects.ts @@ -0,0 +1,21 @@ +import { Injectable } from "@angular/core"; +import { Effect, Actions } from '@ngrx/effects' +import { HeaderActions } from "./header.actions"; +import { HostWindowActions } from "../shared/host-window.actions"; +import { routerActions } from "@ngrx/router-store"; + +@Injectable() +export class HeaderEffects { + + constructor( + private actions$: Actions + ) { } + + @Effect() resize$ = this.actions$ + .ofType(HostWindowActions.RESIZE) + .map(() => HeaderActions.collapse()); + + @Effect() routeChange$ = this.actions$ + .ofType(routerActions.UPDATE_LOCATION) + .map(() => HeaderActions.collapse()); +} diff --git a/src/app/header/header.reducer.ts b/src/app/header/header.reducer.ts new file mode 100644 index 0000000000..af153fba24 --- /dev/null +++ b/src/app/header/header.reducer.ts @@ -0,0 +1,39 @@ +import { Action } from "@ngrx/store"; +import { HeaderActions } from "./header.actions"; + +export interface HeaderState { + navCollapsed: boolean; +} + +const initialState: HeaderState = { + navCollapsed: true +}; + +export const headerReducer = (state = initialState, action: Action): HeaderState => { + switch (action.type) { + + case HeaderActions.COLLAPSE: { + return Object.assign({}, state, { + navCollapsed: true + }); + } + + case HeaderActions.EXPAND: { + return Object.assign({}, state, { + navCollapsed: false + }); + + } + + case HeaderActions.TOGGLE: { + return Object.assign({}, state, { + navCollapsed: !state.navCollapsed + }); + + } + + default: { + return state; + } + } +}; diff --git a/src/app/shared/host-window.actions.ts b/src/app/shared/host-window.actions.ts new file mode 100644 index 0000000000..de41c69564 --- /dev/null +++ b/src/app/shared/host-window.actions.ts @@ -0,0 +1,14 @@ +import { Action } from "@ngrx/store"; + +export class HostWindowActions { + static RESIZE = 'dspace/host-window/RESIZE'; + static resize(newWidth: number, newHeight: number): Action { + return { + type: HostWindowActions.RESIZE, + payload: { + width: newWidth, + height: newHeight + } + } + } +} diff --git a/src/app/shared/host-window.reducer.ts b/src/app/shared/host-window.reducer.ts new file mode 100644 index 0000000000..4b8e1d3cb9 --- /dev/null +++ b/src/app/shared/host-window.reducer.ts @@ -0,0 +1,25 @@ +import { Action } from "@ngrx/store"; +import { HostWindowActions } from "./host-window.actions"; + +export interface HostWindowState { + width: number; + height: number; +} + +const initialState: HostWindowState = { + width: null, + height: null +}; + +export const hostWindowReducer = (state = initialState, action: Action): HostWindowState => { + switch (action.type) { + + case HostWindowActions.RESIZE: { + return Object.assign({}, state, action.payload); + } + + default: { + return state; + } + } +};