mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
57053: horizontal menu
This commit is contained in:
@@ -46,7 +46,15 @@
|
||||
}
|
||||
},
|
||||
"nav": {
|
||||
"home": "Home",
|
||||
"browse": {
|
||||
"header": "All of DSpace"
|
||||
},
|
||||
"community-browse": {
|
||||
"header": "By Community"
|
||||
},
|
||||
"statistics": {
|
||||
"header": "Statistics"
|
||||
},
|
||||
"login": "Log In",
|
||||
"logout": "Log Out"
|
||||
},
|
||||
|
@@ -1,7 +1,6 @@
|
||||
<div class="outer-wrapper">
|
||||
<div class="inner-wrapper">
|
||||
<ds-header></ds-header>
|
||||
<ds-navbar></ds-navbar>
|
||||
<ds-header-navbar-wrapper></ds-header-navbar-wrapper>
|
||||
|
||||
<ds-notifications-board
|
||||
[options]="config.notifications">
|
||||
|
@@ -34,6 +34,6 @@ body {
|
||||
margin-bottom: $content-spacing;
|
||||
}
|
||||
|
||||
ds-navbar {
|
||||
ds-header-navbar-wrapper {
|
||||
z-index: $nav-z-index;
|
||||
}
|
||||
|
@@ -32,6 +32,7 @@ import { NotificationsBoardComponent } from './shared/notifications/notification
|
||||
import { NotificationComponent } from './shared/notifications/notification/notification.component';
|
||||
import { SharedModule } from './shared/shared.module';
|
||||
import { NavbarComponent } from './navbar/navbar.component';
|
||||
import { HeaderNavbarWrapperComponent } from './header-nav-wrapper/header-navbar-wrapper.component';
|
||||
|
||||
export function getConfig() {
|
||||
return ENV_CONFIG;
|
||||
@@ -89,6 +90,7 @@ if (!ENV_CONFIG.production) {
|
||||
AppComponent,
|
||||
HeaderComponent,
|
||||
NavbarComponent,
|
||||
HeaderNavbarWrapperComponent,
|
||||
FooterComponent,
|
||||
PageNotFoundComponent,
|
||||
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">
|
||||
<img src="assets/images/dspace-logo.svg"/>
|
||||
</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>
|
||||
<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>
|
||||
</div>
|
||||
</header>
|
||||
|
@@ -3,3 +3,7 @@
|
||||
.navbar-brand img {
|
||||
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 { Observable } from 'rxjs/Observable';
|
||||
import { NavbarToggleAction } from '../navbar/navbar.actions';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppState } from '../app.reducer';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-header',
|
||||
@@ -13,4 +16,13 @@ export class HeaderComponent {
|
||||
*/
|
||||
public isAuthenticated: Observable<boolean>;
|
||||
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="reset-padding">
|
||||
<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 [ngbCollapse]="(isNavBarCollapsed | async)" class="collapse navbar-collapse"
|
||||
id="collapsingNav">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li ngbDropdown class="nav-item">
|
||||
<div class="reset-padding-md w-100">
|
||||
<div id="collapsingNav">
|
||||
<ul class="navbar-nav mr-auto shadow-none">
|
||||
<li ngbDropdown class="nav-item" #dropdown1="ngbDropdown"
|
||||
(mouseenter)="openDropdownOnHover(dropdown1)"
|
||||
(mouseleave)="closeDropdownOnHover(dropdown1)">
|
||||
<a href="#" ngbDropdownToggle class="nav-link" routerLinkActive="active"
|
||||
id="browseDropdown" (click)="$event.preventDefault();"
|
||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{
|
||||
'nav.browse.header' | translate }}</a>
|
||||
<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 &
|
||||
Collections</a>
|
||||
<a class="dropdown-item" href="/browse?type=dateissued">By Issue
|
||||
Date</a>
|
||||
<a class="dropdown-item" href="/browse?type=author">Authors</a>
|
||||
<a class="dropdown-item" href="/browse?type=author">By Author</a>
|
||||
</div>
|
||||
</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"
|
||||
id="scopeBrowseDropdown" (click)="$event.preventDefault();"
|
||||
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"
|
||||
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
|
||||
Date</a>
|
||||
<a class="dropdown-item" href="/browse?type=author">Authors</a>
|
||||
<a class="dropdown-item" href="/browse?type=author">By Author</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" routerLink="/home" routerLinkActive="active">{{
|
||||
'nav.statistics' | translate }}</a>
|
||||
<a class="nav-link" routerLink="/statistics" routerLinkActive="active">{{
|
||||
'nav.statistics.header' | translate }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@@ -1,18 +1,56 @@
|
||||
@import '../../styles/variables.scss';
|
||||
|
||||
nav.navbar {
|
||||
border-bottom: 1px $gray-400 solid;
|
||||
}
|
||||
|
||||
nav.navbar .navbar-toggler .navbar-toggler-icon {
|
||||
background-image: none !important;
|
||||
line-height: 1.5;
|
||||
border-bottom: 1px $gray-400 solid;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
min-width: 100%;
|
||||
min-width: 100%;
|
||||
}
|
||||
.reset-padding {
|
||||
margin-left: -$spacer/2;
|
||||
margin-right: -$spacer/2;
|
||||
|
||||
/** Mobile menu styling **/
|
||||
@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 { createSelector, Store } from '@ngrx/store';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
import { NavbarState } from './navbar.reducer';
|
||||
import { NavbarToggleAction } from './navbar.actions';
|
||||
import { AppState } from '../app.reducer';
|
||||
|
||||
const navbarStateSelector = (state: AppState) => state.navbar;
|
||||
const navCollapsedSelector = createSelector(navbarStateSelector, (navbar: NavbarState) => navbar.navCollapsed);
|
||||
import { slideMobileNav } from '../shared/animations/slide';
|
||||
import { HostWindowService } from '../shared/host-window.service';
|
||||
import { first } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-navbar',
|
||||
styleUrls: ['navbar.component.scss'],
|
||||
templateUrl: 'navbar.component.html',
|
||||
animations: [slideMobileNav]
|
||||
})
|
||||
export class NavbarComponent implements OnInit {
|
||||
public isNavBarCollapsed: Observable<boolean>;
|
||||
export class NavbarComponent {
|
||||
@Input() isNavBarCollapsed: Observable<boolean>;
|
||||
|
||||
constructor(
|
||||
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 {
|
||||
this.store.dispatch(new NavbarToggleAction());
|
||||
closeDropdownOnHover(dropdown: any): void {
|
||||
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', [
|
||||
|
||||
@@ -8,3 +8,12 @@ export const slide = trigger('slide', [
|
||||
|
||||
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-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");
|
||||
$dropdown-border-radius: 0px;
|
||||
|
||||
$enable-shadows: true !default;
|
||||
|
Reference in New Issue
Block a user