mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 18:14:17 +00:00
57053: horizontal menu
This commit is contained in:
@@ -46,7 +46,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nav": {
|
"nav": {
|
||||||
"home": "Home",
|
"browse": {
|
||||||
|
"header": "All of DSpace"
|
||||||
|
},
|
||||||
|
"community-browse": {
|
||||||
|
"header": "By Community"
|
||||||
|
},
|
||||||
|
"statistics": {
|
||||||
|
"header": "Statistics"
|
||||||
|
},
|
||||||
"login": "Log In",
|
"login": "Log In",
|
||||||
"logout": "Log Out"
|
"logout": "Log Out"
|
||||||
},
|
},
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
<div class="outer-wrapper">
|
<div class="outer-wrapper">
|
||||||
<div class="inner-wrapper">
|
<div class="inner-wrapper">
|
||||||
<ds-header></ds-header>
|
<ds-header-navbar-wrapper></ds-header-navbar-wrapper>
|
||||||
<ds-navbar></ds-navbar>
|
|
||||||
|
|
||||||
<ds-notifications-board
|
<ds-notifications-board
|
||||||
[options]="config.notifications">
|
[options]="config.notifications">
|
||||||
|
@@ -34,6 +34,6 @@ body {
|
|||||||
margin-bottom: $content-spacing;
|
margin-bottom: $content-spacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
ds-navbar {
|
ds-header-navbar-wrapper {
|
||||||
z-index: $nav-z-index;
|
z-index: $nav-z-index;
|
||||||
}
|
}
|
||||||
|
@@ -32,6 +32,7 @@ import { NotificationsBoardComponent } from './shared/notifications/notification
|
|||||||
import { NotificationComponent } from './shared/notifications/notification/notification.component';
|
import { NotificationComponent } from './shared/notifications/notification/notification.component';
|
||||||
import { SharedModule } from './shared/shared.module';
|
import { SharedModule } from './shared/shared.module';
|
||||||
import { NavbarComponent } from './navbar/navbar.component';
|
import { NavbarComponent } from './navbar/navbar.component';
|
||||||
|
import { HeaderNavbarWrapperComponent } from './header-nav-wrapper/header-navbar-wrapper.component';
|
||||||
|
|
||||||
export function getConfig() {
|
export function getConfig() {
|
||||||
return ENV_CONFIG;
|
return ENV_CONFIG;
|
||||||
@@ -89,6 +90,7 @@ if (!ENV_CONFIG.production) {
|
|||||||
AppComponent,
|
AppComponent,
|
||||||
HeaderComponent,
|
HeaderComponent,
|
||||||
NavbarComponent,
|
NavbarComponent,
|
||||||
|
HeaderNavbarWrapperComponent,
|
||||||
FooterComponent,
|
FooterComponent,
|
||||||
PageNotFoundComponent,
|
PageNotFoundComponent,
|
||||||
NotificationComponent,
|
NotificationComponent,
|
||||||
|
@@ -0,0 +1,4 @@
|
|||||||
|
<div [ngClass]="{'open': !(isNavBarCollapsed | async)}">
|
||||||
|
<ds-header></ds-header>
|
||||||
|
<ds-navbar [isNavBarCollapsed]="isNavBarCollapsed"></ds-navbar>
|
||||||
|
</div>
|
@@ -0,0 +1,9 @@
|
|||||||
|
@import '../../styles/variables.scss';
|
||||||
|
|
||||||
|
@media screen and (max-width: map-get($grid-breakpoints, md)) {
|
||||||
|
:host.open {
|
||||||
|
background-color: $white;
|
||||||
|
top: 0;
|
||||||
|
position: sticky;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,36 @@
|
|||||||
|
import { Component, HostBinding, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
import { createSelector, Store } from '@ngrx/store';
|
||||||
|
import { AppState } from '../app.reducer';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { NavbarState } from '../navbar/navbar.reducer';
|
||||||
|
import { Subscription } from 'rxjs/Subscription';
|
||||||
|
import { hasValue } from '../shared/empty.util';
|
||||||
|
|
||||||
|
const navbarStateSelector = (state: AppState) => state.navbar;
|
||||||
|
const navCollapsedSelector = createSelector(navbarStateSelector, (navbar: NavbarState) => navbar.navCollapsed);
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-header-navbar-wrapper',
|
||||||
|
styleUrls: ['header-navbar-wrapper.component.scss'],
|
||||||
|
templateUrl: 'header-navbar-wrapper.component.html',
|
||||||
|
})
|
||||||
|
export class HeaderNavbarWrapperComponent implements OnInit, OnDestroy {
|
||||||
|
@HostBinding('class.open') isOpen = false;
|
||||||
|
private sub: Subscription;
|
||||||
|
public isNavBarCollapsed: Observable<boolean>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private store: Store<AppState>
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.isNavBarCollapsed = this.store.select(navCollapsedSelector);
|
||||||
|
this.sub = this.isNavBarCollapsed.subscribe((isCollapsed) => this.isOpen = !isCollapsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (hasValue(this.sub)) {
|
||||||
|
this.sub.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -3,8 +3,16 @@
|
|||||||
<a class="navbar-brand my-2" routerLink="/home">
|
<a class="navbar-brand my-2" routerLink="/home">
|
||||||
<img src="assets/images/dspace-logo.svg"/>
|
<img src="assets/images/dspace-logo.svg"/>
|
||||||
</a>
|
</a>
|
||||||
<nav class="navbar navbar-expand-md float-right p-0">
|
|
||||||
|
<nav class="navbar navbar-light navbar-expand-md float-right px-0">
|
||||||
<ds-auth-nav-menu></ds-auth-nav-menu>
|
<ds-auth-nav-menu></ds-auth-nav-menu>
|
||||||
|
<div class="pl-2">
|
||||||
|
<button class="navbar-toggler" type="button" (click)="toggle()"
|
||||||
|
aria-controls="collapsingNav"
|
||||||
|
aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon fa fa-bars fa-fw" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
@@ -2,4 +2,8 @@
|
|||||||
|
|
||||||
.navbar-brand img {
|
.navbar-brand img {
|
||||||
height: $header-logo-height;
|
height: $header-logo-height;
|
||||||
}
|
}
|
||||||
|
.navbar-toggler .navbar-toggler-icon {
|
||||||
|
background-image: none !important;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { NavbarToggleAction } from '../navbar/navbar.actions';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { AppState } from '../app.reducer';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-header',
|
selector: 'ds-header',
|
||||||
@@ -13,4 +16,13 @@ export class HeaderComponent {
|
|||||||
*/
|
*/
|
||||||
public isAuthenticated: Observable<boolean>;
|
public isAuthenticated: Observable<boolean>;
|
||||||
public showAuth = false;
|
public showAuth = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private store: Store<AppState>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public toggle(): void {
|
||||||
|
this.store.dispatch(new NavbarToggleAction());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,43 +1,43 @@
|
|||||||
<nav class="navbar navbar-expand-md p-0">
|
<nav [ngClass]="{'open': !(isNavBarCollapsed | async)}"
|
||||||
|
[@slideMobileNav]="!(windowService.isXsOrSm() | async) ? 'default' : ((isNavBarCollapsed | async) ? 'collapsed' : 'expanded')"
|
||||||
|
class="navbar navbar-light navbar-expand-md p-md-0 navbar-container"> <!-- TODO remove navbar-container class when https://github.com/twbs/bootstrap/issues/24726 is fixed -->
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="reset-padding">
|
<div class="reset-padding-md w-100">
|
||||||
<button class="navbar-toggler" type="button" (click)="toggle()"
|
<div id="collapsingNav">
|
||||||
aria-controls="collapsingNav"
|
<ul class="navbar-nav mr-auto shadow-none">
|
||||||
aria-expanded="false" aria-label="Toggle navigation">
|
<li ngbDropdown class="nav-item" #dropdown1="ngbDropdown"
|
||||||
<span class="navbar-toggler-icon fa fa-bars fa-fw" aria-hidden="true"></span>
|
(mouseenter)="openDropdownOnHover(dropdown1)"
|
||||||
</button>
|
(mouseleave)="closeDropdownOnHover(dropdown1)">
|
||||||
<div [ngbCollapse]="(isNavBarCollapsed | async)" class="collapse navbar-collapse"
|
|
||||||
id="collapsingNav">
|
|
||||||
<ul class="navbar-nav mr-auto">
|
|
||||||
<li ngbDropdown class="nav-item">
|
|
||||||
<a href="#" ngbDropdownToggle class="nav-link" routerLinkActive="active"
|
<a href="#" ngbDropdownToggle class="nav-link" routerLinkActive="active"
|
||||||
id="browseDropdown" (click)="$event.preventDefault();"
|
id="browseDropdown" (click)="$event.preventDefault();"
|
||||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{
|
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{
|
||||||
'nav.browse.header' | translate }}</a>
|
'nav.browse.header' | translate }}</a>
|
||||||
<div ngbDropdownMenu aria-labelledby="browseDropdown"
|
<div ngbDropdownMenu aria-labelledby="browseDropdown"
|
||||||
class="rounded-0 m-0 border-0">
|
class="rounded-bottom m-0 shadow-none border-top-0">
|
||||||
<a class="dropdown-item" href="/communitylist">Communities &
|
<a class="dropdown-item" href="/communitylist">Communities &
|
||||||
Collections</a>
|
Collections</a>
|
||||||
<a class="dropdown-item" href="/browse?type=dateissued">By Issue
|
<a class="dropdown-item" href="/browse?type=dateissued">By Issue
|
||||||
Date</a>
|
Date</a>
|
||||||
<a class="dropdown-item" href="/browse?type=author">Authors</a>
|
<a class="dropdown-item" href="/browse?type=author">By Author</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li ngbDropdown class="nav-item">
|
<li ngbDropdown class="nav-item" #dropdown2="ngbDropdown"
|
||||||
|
(mouseenter)="openDropdownOnHover(dropdown2)"
|
||||||
|
(mouseleave)="closeDropdownOnHover(dropdown2)">
|
||||||
<a href="#" ngbDropdownToggle class="nav-link" routerLinkActive="active"
|
<a href="#" ngbDropdownToggle class="nav-link" routerLinkActive="active"
|
||||||
id="scopeBrowseDropdown" (click)="$event.preventDefault();"
|
id="scopeBrowseDropdown" (click)="$event.preventDefault();"
|
||||||
data-toggle="dropdown" aria-haspopup="true"
|
data-toggle="dropdown" aria-haspopup="true"
|
||||||
aria-expanded="false">{{ 'nav.communityBrowse.header' | translate }}</a>
|
aria-expanded="false">{{ 'nav.community-browse.header' | translate }}</a>
|
||||||
<div ngbDropdownMenu aria-labelledby="scopeBrowseDropdown"
|
<div ngbDropdownMenu aria-labelledby="scopeBrowseDropdown"
|
||||||
class="rounded-0 m-0 border-0">
|
class="rounded-bottom m-0 shadow-none border-top-0">
|
||||||
<a class="dropdown-item" href="/browse?type=dateissued">By Issue
|
<a class="dropdown-item" href="/browse?type=dateissued">By Issue
|
||||||
Date</a>
|
Date</a>
|
||||||
<a class="dropdown-item" href="/browse?type=author">Authors</a>
|
<a class="dropdown-item" href="/browse?type=author">By Author</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" routerLink="/home" routerLinkActive="active">{{
|
<a class="nav-link" routerLink="/statistics" routerLinkActive="active">{{
|
||||||
'nav.statistics' | translate }}</a>
|
'nav.statistics.header' | translate }}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,18 +1,56 @@
|
|||||||
@import '../../styles/variables.scss';
|
@import '../../styles/variables.scss';
|
||||||
|
|
||||||
nav.navbar {
|
nav.navbar {
|
||||||
border-bottom: 1px $gray-400 solid;
|
border-bottom: 1px $gray-400 solid;
|
||||||
}
|
align-items: baseline;
|
||||||
|
|
||||||
nav.navbar .navbar-toggler .navbar-toggler-icon {
|
|
||||||
background-image: none !important;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
}
|
}
|
||||||
.reset-padding {
|
|
||||||
margin-left: -$spacer/2;
|
/** Mobile menu styling **/
|
||||||
margin-right: -$spacer/2;
|
@media screen and (max-width: map-get($grid-breakpoints, md)) {
|
||||||
}
|
.navbar {
|
||||||
|
width: 100%;
|
||||||
|
left: 0;
|
||||||
|
background-color: $white;
|
||||||
|
position: absolute;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 0;
|
||||||
|
&.open {
|
||||||
|
height: 100vh; //doesn't matter because wrapper is sticky
|
||||||
|
}
|
||||||
|
.dropdown-toggle {
|
||||||
|
&:after {
|
||||||
|
float: right;
|
||||||
|
margin-top: $spacer/2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.dropdown-menu {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: map-get($grid-breakpoints, md)) {
|
||||||
|
.reset-padding-md {
|
||||||
|
margin-left: -$spacer/2;
|
||||||
|
margin-right: -$spacer/2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO remove when https://github.com/twbs/bootstrap/issues/24726 is fixed */
|
||||||
|
.navbar-expand-md.navbar-container {
|
||||||
|
@media screen and (max-width: map-get($grid-breakpoints, md)) {
|
||||||
|
> .container {
|
||||||
|
padding-left: $spacer;
|
||||||
|
padding-right: $spacer;
|
||||||
|
}
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,34 +1,45 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
import { createSelector, Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
|
||||||
import { NavbarState } from './navbar.reducer';
|
|
||||||
import { NavbarToggleAction } from './navbar.actions';
|
|
||||||
import { AppState } from '../app.reducer';
|
import { AppState } from '../app.reducer';
|
||||||
|
import { slideMobileNav } from '../shared/animations/slide';
|
||||||
const navbarStateSelector = (state: AppState) => state.navbar;
|
import { HostWindowService } from '../shared/host-window.service';
|
||||||
const navCollapsedSelector = createSelector(navbarStateSelector, (navbar: NavbarState) => navbar.navCollapsed);
|
import { first } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-navbar',
|
selector: 'ds-navbar',
|
||||||
styleUrls: ['navbar.component.scss'],
|
styleUrls: ['navbar.component.scss'],
|
||||||
templateUrl: 'navbar.component.html',
|
templateUrl: 'navbar.component.html',
|
||||||
|
animations: [slideMobileNav]
|
||||||
})
|
})
|
||||||
export class NavbarComponent implements OnInit {
|
export class NavbarComponent {
|
||||||
public isNavBarCollapsed: Observable<boolean>;
|
@Input() isNavBarCollapsed: Observable<boolean>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private store: Store<AppState>,
|
private store: Store<AppState>,
|
||||||
|
protected windowService: HostWindowService
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
// set loading
|
|
||||||
this.isNavBarCollapsed = this.store.select(navCollapsedSelector);
|
openDropdownOnHover(dropdown: any): void {
|
||||||
|
this.windowService.isXsOrSm().pipe(
|
||||||
|
first()
|
||||||
|
).subscribe((isMobile) => {
|
||||||
|
if (!isMobile) {
|
||||||
|
dropdown.open();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public toggle(): void {
|
closeDropdownOnHover(dropdown: any): void {
|
||||||
this.store.dispatch(new NavbarToggleAction());
|
this.windowService.isXsOrSm().pipe(
|
||||||
|
first()
|
||||||
|
).subscribe((isMobile) => {
|
||||||
|
if (!isMobile) {
|
||||||
|
dropdown.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { animate, state, transition, trigger, style, stagger, query } from '@angular/animations';
|
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||||
|
|
||||||
export const slide = trigger('slide', [
|
export const slide = trigger('slide', [
|
||||||
|
|
||||||
@@ -8,3 +8,12 @@ export const slide = trigger('slide', [
|
|||||||
|
|
||||||
transition('expanded <=> collapsed', animate(250))
|
transition('expanded <=> collapsed', animate(250))
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
export const slideMobileNav = trigger('slideMobileNav', [
|
||||||
|
|
||||||
|
state('expanded', style({ height: '100vh' })),
|
||||||
|
|
||||||
|
state('collapsed', style({ height: 0 })),
|
||||||
|
|
||||||
|
transition('expanded <=> collapsed', animate(300))
|
||||||
|
]);
|
||||||
|
@@ -33,5 +33,6 @@ $navbar-dark-color: rgba(white, .5) !default;
|
|||||||
$navbar-light-color: rgba(black, .5) !default;
|
$navbar-light-color: rgba(black, .5) !default;
|
||||||
$navbar-dark-toggler-icon-bg: url("data%3Aimage%2Fsvg+xml%3Bcharset%3Dutf8%2C%3Csvg+viewBox%3D%270+0+30+30%27+xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3Cpath+stroke%3D%27#{$navbar-dark-color}%27+stroke-width%3D%272%27+stroke-linecap%3D%27round%27+stroke-miterlimit%3D%2710%27+d%3D%27M4+7h22M4+15h22M4+23h22%27%2F%3E%3C%2Fsvg%3E");
|
$navbar-dark-toggler-icon-bg: url("data%3Aimage%2Fsvg+xml%3Bcharset%3Dutf8%2C%3Csvg+viewBox%3D%270+0+30+30%27+xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3Cpath+stroke%3D%27#{$navbar-dark-color}%27+stroke-width%3D%272%27+stroke-linecap%3D%27round%27+stroke-miterlimit%3D%2710%27+d%3D%27M4+7h22M4+15h22M4+23h22%27%2F%3E%3C%2Fsvg%3E");
|
||||||
$navbar-light-toggler-icon-bg: url("data%3Aimage%2Fsvg+xml%3Bcharset%3Dutf8%2C%3Csvg+viewBox%3D%270+0+30+30%27+xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3Cpath+stroke%3D%27#{$navbar-light-color}%27+stroke-width%3D%272%27+stroke-linecap%3D%27round%27+stroke-miterlimit%3D%2710%27+d%3D%27M4+7h22M4+15h22M4+23h22%27%2F%3E%3C%2Fsvg%3E");
|
$navbar-light-toggler-icon-bg: url("data%3Aimage%2Fsvg+xml%3Bcharset%3Dutf8%2C%3Csvg+viewBox%3D%270+0+30+30%27+xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3Cpath+stroke%3D%27#{$navbar-light-color}%27+stroke-width%3D%272%27+stroke-linecap%3D%27round%27+stroke-miterlimit%3D%2710%27+d%3D%27M4+7h22M4+15h22M4+23h22%27%2F%3E%3C%2Fsvg%3E");
|
||||||
|
$dropdown-border-radius: 0px;
|
||||||
|
|
||||||
$enable-shadows: true !default;
|
$enable-shadows: true !default;
|
||||||
|
Reference in New Issue
Block a user