mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
57053: progress menu bar
This commit is contained in:
@@ -10,52 +10,81 @@ import { type } from '../../shared/ngrx/type';
|
|||||||
* literal types and runs a simple check to guarantee all
|
* literal types and runs a simple check to guarantee all
|
||||||
* action types in the application are unique.
|
* action types in the application are unique.
|
||||||
*/
|
*/
|
||||||
export const AdminSidebarSectionActionTypes = {
|
export const AdminSidebarActionTypes = {
|
||||||
COLLAPSE: type('dspace/admin-sidebar-section/COLLAPSE'),
|
SECTION_COLLAPSE: type('dspace/admin-sidebar/SECTION_COLLAPSE'),
|
||||||
EXPAND: type('dspace/admin-sidebar-sectio/EXPAND'),
|
SECTION_EXPAND: type('dspace/admin-sidebar/SECTION_EXPAND'),
|
||||||
TOGGLE: type('dspace/admin-sidebar-sectio/TOGGLE'),
|
SECTION_TOGGLE: type('dspace/admin-sidebar/SECTION_TOGGLE'),
|
||||||
|
COLLAPSE: type('dspace/admin-sidebar/COLLAPSE'),
|
||||||
|
EXPAND: type('dspace/admin-sidebar/EXPAND'),
|
||||||
|
TOGGLE: type('dspace/admin-sidebar/TOGGLE'),
|
||||||
};
|
};
|
||||||
|
|
||||||
export class AdminSidebarSectionAction implements Action {
|
/* tslint:disable:max-classes-per-file */
|
||||||
|
export class AdminSidebarAction implements Action {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of action that will be performed
|
||||||
|
*/
|
||||||
|
type;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AdminSidebarSectionAction extends AdminSidebarAction {
|
||||||
/**
|
/**
|
||||||
* Name of the section the action is performed on, used to identify the section
|
* Name of the section the action is performed on, used to identify the section
|
||||||
*/
|
*/
|
||||||
sectionName: string;
|
sectionName: string;
|
||||||
|
|
||||||
/**
|
|
||||||
* Type of action that will be performed
|
|
||||||
*/
|
|
||||||
type;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize with the section's name
|
* Initialize with the section's name
|
||||||
* @param {string} name of the section
|
* @param {string} name of the section
|
||||||
*/
|
*/
|
||||||
constructor(name: string) {
|
constructor(name: string) {
|
||||||
|
super();
|
||||||
this.sectionName = name;
|
this.sectionName = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* tslint:disable:max-classes-per-file */
|
/* tslint:disable:max-classes-per-file */
|
||||||
|
/**
|
||||||
|
* Used to collapse the sidebar
|
||||||
|
*/
|
||||||
|
export class AdminSidebarCollapseAction extends AdminSidebarAction {
|
||||||
|
type = AdminSidebarActionTypes.COLLAPSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to expand the sidebar
|
||||||
|
*/
|
||||||
|
export class AdminSidebarExpandAction extends AdminSidebarAction {
|
||||||
|
type = AdminSidebarActionTypes.EXPAND;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to collapse the sidebar when it's expanded and expand it when it's collapsed
|
||||||
|
*/
|
||||||
|
export class AdminSidebarToggleAction extends AdminSidebarAction {
|
||||||
|
type = AdminSidebarActionTypes.TOGGLE;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to collapse a section
|
* Used to collapse a section
|
||||||
*/
|
*/
|
||||||
export class AdminSidebarSectionCollapseAction extends AdminSidebarSectionAction {
|
export class AdminSidebarSectionCollapseAction extends AdminSidebarSectionAction {
|
||||||
type = AdminSidebarSectionActionTypes.COLLAPSE;
|
type = AdminSidebarActionTypes.SECTION_COLLAPSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to expand a section
|
* Used to expand a section
|
||||||
*/
|
*/
|
||||||
export class AdminSidebarSectionExpandAction extends AdminSidebarSectionAction {
|
export class AdminSidebarSectionExpandAction extends AdminSidebarSectionAction {
|
||||||
type = AdminSidebarSectionActionTypes.EXPAND;
|
type = AdminSidebarActionTypes.SECTION_EXPAND;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to collapse a section when it's expanded and expand it when it's collapsed
|
* Used to collapse a section when it's expanded and expand it when it's collapsed
|
||||||
*/
|
*/
|
||||||
export class AdminSidebarSectionToggleAction extends AdminSidebarSectionAction {
|
export class AdminSidebarSectionToggleAction extends AdminSidebarSectionAction {
|
||||||
type = AdminSidebarSectionActionTypes.TOGGLE;
|
type = AdminSidebarActionTypes.SECTION_TOGGLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* tslint:enable:max-classes-per-file */
|
/* tslint:enable:max-classes-per-file */
|
||||||
|
@@ -1,17 +1,21 @@
|
|||||||
<nav class="navbar navbar-dark bg-dark p-0">
|
<nav class="navbar navbar-dark bg-dark p-0" [ngClass]="{'active': (sidebarCollapsed | async)}"
|
||||||
|
[@slideSidebar]="{
|
||||||
|
value: ((sidebarCollapsed | async) ? 'collapsed' : 'expanded'),
|
||||||
|
params: {sidebarWidth: (sidebarWidth | async)}
|
||||||
|
}">
|
||||||
<div class="sidebar-top-level-items">
|
<div class="sidebar-top-level-items">
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
<li class="admin-menu-header">
|
<li class="admin-menu-header">
|
||||||
<a class="shortcuts-tree navbar-brand" href="#">
|
<a class="shortcuts-tree navbar-brand" href="#">
|
||||||
<img class="admin-logo mr-2" src="assets/images/dspace-logo-mini.svg">
|
<img class="admin-logo mr-2" src="assets/images/dspace-logo-mini.svg">
|
||||||
<h4 class="nav-item-name">Admin</h4>
|
<h4 class="section-header-text">Admin</h4>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li [ngClass]="{'active': (active('new') | async)}">
|
<li [ngClass]="{'active': (active('new') | async)}">
|
||||||
<a class="nav-item nav-link shortcuts-tree" href="#"
|
<a class="nav-item nav-link shortcuts-tree" href="#"
|
||||||
(click)="toggle($event, 'new')">
|
(click)="toggleSection($event, 'new')">
|
||||||
<i class="fas fa-plus-circle fa-fw"></i>
|
<i class="fas fa-plus-circle fa-fw"></i>
|
||||||
New
|
<span class="section-header-text">New</span>
|
||||||
<i class="fas fa-chevron-right fa-pull-right fa-xxs"
|
<i class="fas fa-chevron-right fa-pull-right fa-xxs"
|
||||||
[ngClass]="{'fa-rotate-90': (active('new') | async)}"></i>
|
[ngClass]="{'fa-rotate-90': (active('new') | async)}"></i>
|
||||||
</a>
|
</a>
|
||||||
@@ -24,9 +28,9 @@
|
|||||||
</li>
|
</li>
|
||||||
<li [ngClass]="{'active': (active('edit') | async)}">
|
<li [ngClass]="{'active': (active('edit') | async)}">
|
||||||
<a class="nav-item nav-link shortcuts-tree" href="#"
|
<a class="nav-item nav-link shortcuts-tree" href="#"
|
||||||
(click)="toggle($event, 'edit')">
|
(click)="toggleSection($event, 'edit')">
|
||||||
<i class="fas fa-pencil-alt fa-fw"></i>
|
<i class="fas fa-pencil-alt fa-fw"></i>
|
||||||
Edit
|
<span class="section-header-text">Edit</span>
|
||||||
<i class="fas fa-chevron-right fa-pull-right fa-xxs"
|
<i class="fas fa-chevron-right fa-pull-right fa-xxs"
|
||||||
[ngClass]="{'fa-rotate-90': (active('edit') | async)}"></i>
|
[ngClass]="{'fa-rotate-90': (active('edit') | async)}"></i>
|
||||||
</a>
|
</a>
|
||||||
@@ -38,9 +42,9 @@
|
|||||||
</li>
|
</li>
|
||||||
<li [ngClass]="{'active': (active('import') | async)}">
|
<li [ngClass]="{'active': (active('import') | async)}">
|
||||||
<a class="nav-item nav-link shortcuts-tree" href="#"
|
<a class="nav-item nav-link shortcuts-tree" href="#"
|
||||||
(click)="toggle($event, 'import')">
|
(click)="toggleSection($event, 'import')">
|
||||||
<i class="fas fa-arrow-circle-up fa-fw"></i>
|
<i class="fas fa-arrow-circle-up fa-fw"></i>
|
||||||
Import
|
<span class="section-header-text">Import</span>
|
||||||
<i class="fas fa-chevron-right fa-pull-right fa-xxs"
|
<i class="fas fa-chevron-right fa-pull-right fa-xxs"
|
||||||
[ngClass]="{'fa-rotate-90': (active('import') | async)}"></i>
|
[ngClass]="{'fa-rotate-90': (active('import') | async)}"></i>
|
||||||
</a>
|
</a>
|
||||||
@@ -51,9 +55,9 @@
|
|||||||
</li>
|
</li>
|
||||||
<li [ngClass]="{'active': (active('export') | async)}">
|
<li [ngClass]="{'active': (active('export') | async)}">
|
||||||
<a class="nav-item nav-link shortcuts-tree" href="#"
|
<a class="nav-item nav-link shortcuts-tree" href="#"
|
||||||
(click)="toggle($event, 'export')">
|
(click)="toggleSection($event, 'export')">
|
||||||
<i class="fas fa-arrow-circle-down fa-fw"></i>
|
<i class="fas fa-arrow-circle-down fa-fw"></i>
|
||||||
Export
|
<span class="section-header-text">Export</span>
|
||||||
<i class="fas fa-chevron-right fa-pull-right fa-xxs"
|
<i class="fas fa-chevron-right fa-pull-right fa-xxs"
|
||||||
[ngClass]="{'fa-rotate-90': (active('export') | async)}"></i>
|
[ngClass]="{'fa-rotate-90': (active('export') | async)}"></i>
|
||||||
</a>
|
</a>
|
||||||
@@ -66,9 +70,9 @@
|
|||||||
</li>
|
</li>
|
||||||
<li [ngClass]="{'active': (active('access_control') | async)}">
|
<li [ngClass]="{'active': (active('access_control') | async)}">
|
||||||
<a class="nav-item nav-link shortcuts-tree" href="#"
|
<a class="nav-item nav-link shortcuts-tree" href="#"
|
||||||
(click)="toggle($event, 'access_control')">
|
(click)="toggleSection($event, 'access_control')">
|
||||||
<i class="fas fa-key fa-fw"></i>
|
<i class="fas fa-key fa-fw"></i>
|
||||||
Access Control
|
<span class="section-header-text">Access Control</span>
|
||||||
<i class="fas fa-chevron-right fa-pull-right fa-xxs"
|
<i class="fas fa-chevron-right fa-pull-right fa-xxs"
|
||||||
[ngClass]="{'fa-rotate-90': (active('access_control') | async)}"></i>
|
[ngClass]="{'fa-rotate-90': (active('access_control') | async)}"></i>
|
||||||
</a>
|
</a>
|
||||||
@@ -81,9 +85,9 @@
|
|||||||
</li>
|
</li>
|
||||||
<li [ngClass]="{'active': (active('find') | async)}">
|
<li [ngClass]="{'active': (active('find') | async)}">
|
||||||
<a class="nav-item nav-link shortcuts-tree" href="#"
|
<a class="nav-item nav-link shortcuts-tree" href="#"
|
||||||
(click)="toggle($event, 'find')">
|
(click)="toggleSection($event, 'find')">
|
||||||
<i class="fas fa-search fa-fw"></i>
|
<i class="fas fa-search fa-fw"></i>
|
||||||
Find
|
<span class="section-header-text">Find</span>
|
||||||
<i class="fas fa-chevron-right fa-pull-right fa-xxs"
|
<i class="fas fa-chevron-right fa-pull-right fa-xxs"
|
||||||
[ngClass]="{'fa-rotate-90': (active('find') | async)}"></i>
|
[ngClass]="{'fa-rotate-90': (active('find') | async)}"></i>
|
||||||
</a>
|
</a>
|
||||||
@@ -95,9 +99,9 @@
|
|||||||
</li>
|
</li>
|
||||||
<li [ngClass]="{'active': (active('registries') | async)}">
|
<li [ngClass]="{'active': (active('registries') | async)}">
|
||||||
<a class="nav-item nav-link shortcuts-tree" href="#"
|
<a class="nav-item nav-link shortcuts-tree" href="#"
|
||||||
(click)="toggle($event, 'registries')">
|
(click)="toggleSection($event, 'registries')">
|
||||||
<i class="fas fa-list fa-fw"></i>
|
<i class="fas fa-list fa-fw"></i>
|
||||||
Registries
|
<span class="section-header-text">Registries</span>
|
||||||
<i class="fas fa-chevron-right fa-pull-right fa-xxs"
|
<i class="fas fa-chevron-right fa-pull-right fa-xxs"
|
||||||
[ngClass]="{'fa-rotate-90': (active('registries') | async)}"></i>
|
[ngClass]="{'fa-rotate-90': (active('registries') | async)}"></i>
|
||||||
</a>
|
</a>
|
||||||
@@ -109,27 +113,28 @@
|
|||||||
<li>
|
<li>
|
||||||
<a class="nav-item nav-link shortcuts-tree" href="#">
|
<a class="nav-item nav-link shortcuts-tree" href="#">
|
||||||
<i class="fas fa-filter fa-fw"></i>
|
<i class="fas fa-filter fa-fw"></i>
|
||||||
Curation Tasks
|
<span class="section-header-text">Curation Tasks</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a class="nav-item nav-link shortcuts-tree" href="#">
|
<a class="nav-item nav-link shortcuts-tree" href="#">
|
||||||
<i class="fas fa-chart-bar fa-fw"></i>
|
<i class="fas fa-chart-bar fa-fw"></i>
|
||||||
Statistics
|
<span class="section-header-text">Statistics</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a class="nav-item nav-link shortcuts-tree" href="#">
|
<a class="nav-item nav-link shortcuts-tree" href="#">
|
||||||
<i class="fas fa-cogs fa-fw"></i>
|
<i class="fas fa-cogs fa-fw"></i>
|
||||||
Control Panel
|
<span class="section-header-text">Control Panel</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="navbar-nav">
|
<div class="navbar-nav">
|
||||||
<a class="nav-item nav-link shortcuts-tree" href="#">
|
<a class="nav-item nav-link shortcuts-tree" href="#"
|
||||||
|
(click)="toggle($event)">
|
||||||
<i class="fas fa-fw fa-angle-double-right"></i>
|
<i class="fas fa-fw fa-angle-double-right"></i>
|
||||||
Collapse
|
<span class="section-header-text">Collapse</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
@@ -1,21 +1,36 @@
|
|||||||
@import '../../../styles/variables.scss';
|
@import '../../../styles/variables.scss';
|
||||||
|
|
||||||
:host {
|
:host {
|
||||||
position: fixed;
|
position: sticky;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 100vh;
|
||||||
|
flex: 1 1 auto;
|
||||||
nav {
|
nav {
|
||||||
height: 100vh;
|
height: 100%;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
>div.sidebar-top-level-items {
|
> div {
|
||||||
flex: 1;
|
width: 100%;
|
||||||
overflow: auto;
|
&.sidebar-top-level-items {
|
||||||
|
flex: 1;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.section-header-text {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
.navbar-nav {
|
.navbar-nav {
|
||||||
min-width: $admin-sidebar-width;
|
> li, > a {
|
||||||
> * {
|
|
||||||
padding: $spacer/2 $spacer;
|
padding: $spacer/2 $spacer;
|
||||||
&.active {
|
&.active {
|
||||||
background-color: $admin-sidebar-dark;
|
background-color: $admin-sidebar-dark;
|
||||||
}
|
}
|
||||||
|
.fa-fw {
|
||||||
|
text-align: left;
|
||||||
|
width: 1.75em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.sidebar-sub-level-items {
|
.sidebar-sub-level-items {
|
||||||
list-style: disc;
|
list-style: disc;
|
||||||
@@ -28,13 +43,15 @@
|
|||||||
.admin-menu-header {
|
.admin-menu-header {
|
||||||
background-color: $admin-sidebar-dark;
|
background-color: $admin-sidebar-dark;
|
||||||
img {
|
img {
|
||||||
|
|
||||||
height: 1em;
|
height: 1em;
|
||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
}
|
}
|
||||||
h4 {
|
}
|
||||||
display: inline;
|
}
|
||||||
}
|
|
||||||
|
&.active {
|
||||||
|
.section-header-text, .fa-chevron-right {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,34 +1,41 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
|
import { MemoizedSelector, select, Store } from '@ngrx/store';
|
||||||
import { AdminSidebarSectionsState, AdminSidebarSectionState } from './admin-sidebar.reducer';
|
import { AdminSidebarSectionState, AdminSidebarState, } from './admin-sidebar.reducer';
|
||||||
import { hasValue } from '../../shared/empty.util';
|
import { hasValue } from '../../shared/empty.util';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { AdminSidebarSectionToggleAction } from './admin-sidebar.actions';
|
import { AdminSidebarSectionToggleAction, AdminSidebarToggleAction } from './admin-sidebar.actions';
|
||||||
|
import { AppState, keySelector } from '../../app.reducer';
|
||||||
|
import { slideSidebar } from '../../shared/animations/slide';
|
||||||
|
import { CSSVariableService } from '../../shared/sass-helper/sass-helper.service';
|
||||||
|
|
||||||
const sidebarSectionStateSelector = (state: AdminSidebarSectionsState) => state.adminSidebarSection;
|
const sidebarSectionStateSelector = (state: AppState) => state.adminSidebar.sections;
|
||||||
|
const sidebarStateSelector = (state) => state.adminSidebar;
|
||||||
|
|
||||||
const sectionByNameSelector = (name: string): MemoizedSelector<AdminSidebarSectionsState, AdminSidebarSectionState> => {
|
const sectionByNameSelector = (name: string): MemoizedSelector<AppState, AdminSidebarSectionState> => {
|
||||||
return keySelector<AdminSidebarSectionState>(name);
|
return keySelector<AdminSidebarSectionState>(name, sidebarSectionStateSelector);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function keySelector<T>(key: string): MemoizedSelector<AdminSidebarSectionsState, T> {
|
|
||||||
return createSelector(sidebarSectionStateSelector, (state: AdminSidebarSectionState) => {
|
|
||||||
if (hasValue(state)) {
|
|
||||||
return state[key];
|
|
||||||
} else {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-admin-sidebar',
|
selector: 'ds-admin-sidebar',
|
||||||
templateUrl: './admin-sidebar.component.html',
|
templateUrl: './admin-sidebar.component.html',
|
||||||
styleUrls: ['./admin-sidebar.component.scss'],
|
styleUrls: ['./admin-sidebar.component.scss'],
|
||||||
|
animations: [slideSidebar]
|
||||||
})
|
})
|
||||||
export class AdminSidebarComponent {
|
export class AdminSidebarComponent implements OnInit {
|
||||||
constructor(private store: Store<AdminSidebarSectionsState>) {
|
sidebarCollapsed: Observable<boolean>;
|
||||||
|
sidebarWidth: Observable<string>;
|
||||||
|
|
||||||
|
constructor(private store: Store<AdminSidebarState>,
|
||||||
|
private variableService: CSSVariableService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.sidebarWidth = this.variableService.getVariable('adminSidebarWidth')
|
||||||
|
this.sidebarCollapsed = this.store.pipe(
|
||||||
|
select(sidebarStateSelector),
|
||||||
|
map((state: AdminSidebarState) => state.collapsed)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public active(name: string): Observable<boolean> {
|
public active(name: string): Observable<boolean> {
|
||||||
@@ -38,8 +45,13 @@ export class AdminSidebarComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggle(event: Event, name: string) {
|
toggleSection(event: Event, name: string) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.store.dispatch(new AdminSidebarSectionToggleAction(name));
|
this.store.dispatch(new AdminSidebarSectionToggleAction(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggle(event: Event) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.store.dispatch(new AdminSidebarToggleAction());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,56 +1,100 @@
|
|||||||
import { AdminSidebarSectionAction, AdminSidebarSectionActionTypes } from './admin-sidebar.actions';
|
import {
|
||||||
|
AdminSidebarAction,
|
||||||
|
AdminSidebarSectionAction,
|
||||||
|
AdminSidebarActionTypes
|
||||||
|
} from './admin-sidebar.actions';
|
||||||
import { hasValue } from '../../shared/empty.util';
|
import { hasValue } from '../../shared/empty.util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface that represents the state for a single section
|
* Interface that represents the state for a single sidebar section
|
||||||
*/
|
*/
|
||||||
export interface AdminSidebarSectionState {
|
export interface AdminSidebarSectionState {
|
||||||
sectionCollapsed: boolean,
|
sectionCollapsed: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface that represents the state for all available sections
|
|
||||||
*/
|
|
||||||
export interface AdminSidebarSectionsState {
|
export interface AdminSidebarSectionsState {
|
||||||
[name: string]: AdminSidebarSectionState
|
[name: string]: AdminSidebarSectionState;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: AdminSidebarSectionsState = Object.create(null);
|
|
||||||
const initiallyCollapsed = true;
|
|
||||||
/**
|
/**
|
||||||
* Performs a search section action on the current state
|
* Interface that represents the state of the admin sidebar and its sections
|
||||||
* @param {AdminSidebarSectionsState} state The state before the action is performed
|
|
||||||
* @param {AdminSidebarSectionAction} action The action that should be performed
|
|
||||||
* @returns {AdminSidebarSectionsState} The state after the action is performed
|
|
||||||
*/
|
*/
|
||||||
export function sidebarSectionReducer(state = initialState, action: AdminSidebarSectionAction): AdminSidebarSectionsState {
|
export interface AdminSidebarState {
|
||||||
|
sections: AdminSidebarSectionsState,
|
||||||
|
collapsed: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: AdminSidebarState = Object.create(null);
|
||||||
|
const initiallySectionCollapsed = true;
|
||||||
|
const initiallyCollapsed = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a sidebar action on the current state
|
||||||
|
* @param {AdminSidebarState} state The state before the action is performed
|
||||||
|
* @param {AdminSidebarAction} action The action that should be performed
|
||||||
|
* @returns {AdminSidebarState} The state after the action is performed
|
||||||
|
*/
|
||||||
|
export function adminSidebarReducer(state = initialState, action: AdminSidebarAction): AdminSidebarState {
|
||||||
|
|
||||||
|
if (action instanceof AdminSidebarSectionAction) {
|
||||||
|
return reduceSectionAction(state, action);
|
||||||
|
} else {
|
||||||
|
switch (action.type) {
|
||||||
|
case AdminSidebarActionTypes.COLLAPSE: {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
collapsed: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
case AdminSidebarActionTypes.EXPAND: {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
collapsed: false
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
case AdminSidebarActionTypes.TOGGLE: {
|
||||||
|
const currentState = state.collapsed;
|
||||||
|
const collapsed = hasValue(currentState) ? currentState : initiallyCollapsed;
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
collapsed: !collapsed
|
||||||
|
});
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reduceSectionAction(state: AdminSidebarState, action: AdminSidebarSectionAction): AdminSidebarState {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case AdminSidebarSectionActionTypes.COLLAPSE: {
|
case AdminSidebarActionTypes.SECTION_COLLAPSE: {
|
||||||
return Object.assign({}, state, {
|
const sections = Object.assign({}, state.sections, {
|
||||||
[action.sectionName]: {
|
[action.sectionName]: {
|
||||||
sectionCollapsed: true,
|
sectionCollapsed: true,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return Object.assign({}, state, { sections });
|
||||||
}
|
}
|
||||||
|
|
||||||
case AdminSidebarSectionActionTypes.EXPAND: {
|
case AdminSidebarActionTypes.SECTION_EXPAND: {
|
||||||
return Object.assign({}, state, {
|
const sections = Object.assign({}, state.sections, {
|
||||||
[action.sectionName]: {
|
[action.sectionName]: {
|
||||||
sectionCollapsed: false,
|
sectionCollapsed: false,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return Object.assign({}, state, { sections });
|
||||||
|
|
||||||
}
|
}
|
||||||
case AdminSidebarSectionActionTypes.TOGGLE: {
|
case AdminSidebarActionTypes.SECTION_TOGGLE: {
|
||||||
const currentState = state[action.sectionName];
|
const currentState = state.sections;
|
||||||
const collapsed = hasValue(currentState) ? currentState.sectionCollapsed : initiallyCollapsed;
|
const collapsed = (hasValue(currentState) && currentState[action.sectionName]) ? currentState[action.sectionName].sectionCollapsed : initiallySectionCollapsed;
|
||||||
return Object.assign({}, state, {
|
const sections = Object.assign({}, state.sections, {
|
||||||
[action.sectionName]: {
|
[action.sectionName]: {
|
||||||
sectionCollapsed: !collapsed,
|
sectionCollapsed: !collapsed,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return Object.assign({}, state, { sections });
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
|
@@ -8,6 +8,9 @@
|
|||||||
|
|
||||||
.dspace-logo-container {
|
.dspace-logo-container {
|
||||||
margin: 10px 20px 0px 20px;
|
margin: 10px 20px 0px 20px;
|
||||||
|
.display-3 {
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dspace-logo-container img {
|
.dspace-logo-container img {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
@import '../styles/variables.scss';
|
@import '../styles/variables.scss';
|
||||||
@import '../styles/font-awesome-imports.scss';
|
@import '../styles/helpers/font_awesome_imports.scss';
|
||||||
@import '../../node_modules/bootstrap/scss/bootstrap.scss';
|
@import '../../node_modules/bootstrap/scss/bootstrap.scss';
|
||||||
@import '../../node_modules/nouislider/distribute/nouislider.min.css';
|
@import '../../node_modules/nouislider/distribute/nouislider.min.css';
|
||||||
|
|
||||||
|
@@ -23,6 +23,8 @@ import { NativeWindowRef, NativeWindowService } from './shared/services/window.s
|
|||||||
import { isAuthenticated } from './core/auth/selectors';
|
import { isAuthenticated } from './core/auth/selectors';
|
||||||
import { AuthService } from './core/auth/auth.service';
|
import { AuthService } from './core/auth/auth.service';
|
||||||
import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
|
import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
|
||||||
|
import variables from '../styles/_exposed_variables.scss';
|
||||||
|
import { CSSVariableService } from './shared/sass-helper/sass-helper.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-app',
|
selector: 'ds-app',
|
||||||
@@ -42,7 +44,8 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
private metadata: MetadataService,
|
private metadata: MetadataService,
|
||||||
private angulartics2GoogleAnalytics: Angulartics2GoogleAnalytics,
|
private angulartics2GoogleAnalytics: Angulartics2GoogleAnalytics,
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
private router: Router
|
private router: Router,
|
||||||
|
private cssService: CSSVariableService
|
||||||
) {
|
) {
|
||||||
// this language will be used as a fallback when a translation isn't found in the current language
|
// this language will be used as a fallback when a translation isn't found in the current language
|
||||||
translate.setDefaultLang('en');
|
translate.setDefaultLang('en');
|
||||||
@@ -54,6 +57,8 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
if (config.debug) {
|
if (config.debug) {
|
||||||
console.info(config);
|
console.info(config);
|
||||||
}
|
}
|
||||||
|
this.storeCSSVariables();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@@ -67,7 +72,13 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
first(),
|
first(),
|
||||||
filter((authenticated) => !authenticated)
|
filter((authenticated) => !authenticated)
|
||||||
).subscribe((authenticated) => this.authService.checkAuthenticationToken());
|
).subscribe((authenticated) => this.authService.checkAuthenticationToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
private storeCSSVariables() {
|
||||||
|
const vars = variables.locals;
|
||||||
|
Object.keys(vars).forEach((name: string) => {
|
||||||
|
this.cssService.addCSSVariable(name, vars[name]);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { ActionReducerMap } from '@ngrx/store';
|
import { ActionReducerMap, createSelector, MemoizedSelector } from '@ngrx/store';
|
||||||
import * as fromRouter from '@ngrx/router-store';
|
import * as fromRouter from '@ngrx/router-store';
|
||||||
import { hostWindowReducer, HostWindowState } from './shared/host-window.reducer';
|
import { hostWindowReducer, HostWindowState } from './shared/host-window.reducer';
|
||||||
import { formReducer, FormState } from './shared/form/form.reducer';
|
import { formReducer, FormState } from './shared/form/form.reducer';
|
||||||
@@ -17,9 +17,11 @@ import {
|
|||||||
import { truncatableReducer, TruncatablesState } from './shared/truncatable/truncatable.reducer';
|
import { truncatableReducer, TruncatablesState } from './shared/truncatable/truncatable.reducer';
|
||||||
import { navbarReducer, NavbarState } from './navbar/navbar.reducer';
|
import { navbarReducer, NavbarState } from './navbar/navbar.reducer';
|
||||||
import {
|
import {
|
||||||
AdminSidebarSectionsState,
|
adminSidebarReducer,
|
||||||
sidebarSectionReducer
|
AdminSidebarState
|
||||||
} from './+admin/admin-sidebar/admin-sidebar.reducer';
|
} from './+admin/admin-sidebar/admin-sidebar.reducer';
|
||||||
|
import { hasValue } from './shared/empty.util';
|
||||||
|
import { cssVariablesReducer, CSSVariablesState } from './shared/sass-helper/sass-helper.reducer';
|
||||||
|
|
||||||
export interface AppState {
|
export interface AppState {
|
||||||
router: fromRouter.RouterReducerState;
|
router: fromRouter.RouterReducerState;
|
||||||
@@ -30,7 +32,8 @@ export interface AppState {
|
|||||||
searchSidebar: SearchSidebarState;
|
searchSidebar: SearchSidebarState;
|
||||||
searchFilter: SearchFiltersState;
|
searchFilter: SearchFiltersState;
|
||||||
truncatable: TruncatablesState;
|
truncatable: TruncatablesState;
|
||||||
adminSidebarSection: AdminSidebarSectionsState;
|
adminSidebar: AdminSidebarState;
|
||||||
|
cssVariables: CSSVariablesState;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const appReducers: ActionReducerMap<AppState> = {
|
export const appReducers: ActionReducerMap<AppState> = {
|
||||||
@@ -42,7 +45,18 @@ export const appReducers: ActionReducerMap<AppState> = {
|
|||||||
searchSidebar: sidebarReducer,
|
searchSidebar: sidebarReducer,
|
||||||
searchFilter: filterReducer,
|
searchFilter: filterReducer,
|
||||||
truncatable: truncatableReducer,
|
truncatable: truncatableReducer,
|
||||||
adminSidebarSection: sidebarSectionReducer
|
adminSidebar: adminSidebarReducer,
|
||||||
|
cssVariables: cssVariablesReducer,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const routerStateSelector = (state: AppState) => state.router;
|
export const routerStateSelector = (state: AppState) => state.router;
|
||||||
|
|
||||||
|
export function keySelector<T>(key: string, selector): MemoizedSelector<AppState, T> {
|
||||||
|
return createSelector(selector, (state) => {
|
||||||
|
if (hasValue(state)) {
|
||||||
|
return state[key];
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@@ -64,6 +64,7 @@ import { NotificationsService } from '../shared/notifications/notifications.serv
|
|||||||
import { UploaderService } from '../shared/uploader/uploader.service';
|
import { UploaderService } from '../shared/uploader/uploader.service';
|
||||||
import { BrowseItemsResponseParsingService } from './data/browse-items-response-parsing-service';
|
import { BrowseItemsResponseParsingService } from './data/browse-items-response-parsing-service';
|
||||||
import { DSpaceObjectDataService } from './data/dspace-object-data.service';
|
import { DSpaceObjectDataService } from './data/dspace-object-data.service';
|
||||||
|
import { CSSVariableService } from '../shared/sass-helper/sass-helper.service';
|
||||||
|
|
||||||
const IMPORTS = [
|
const IMPORTS = [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@@ -128,6 +129,7 @@ const PROVIDERS = [
|
|||||||
UploaderService,
|
UploaderService,
|
||||||
UUIDService,
|
UUIDService,
|
||||||
DSpaceObjectDataService,
|
DSpaceObjectDataService,
|
||||||
|
CSSVariableService,
|
||||||
// register AuthInterceptor as HttpInterceptor
|
// register AuthInterceptor as HttpInterceptor
|
||||||
{
|
{
|
||||||
provide: HTTP_INTERCEPTORS,
|
provide: HTTP_INTERCEPTORS,
|
||||||
|
@@ -17,7 +17,7 @@ export class NavbarComponent {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private store: Store<AppState>,
|
private store: Store<AppState>,
|
||||||
protected windowService: HostWindowService
|
public windowService: HostWindowService
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,18 +2,18 @@ import { animate, state, transition, trigger, style } from '@angular/animations'
|
|||||||
|
|
||||||
export const focusShadow = trigger('focusShadow', [
|
export const focusShadow = trigger('focusShadow', [
|
||||||
|
|
||||||
state('focus', style({ 'box-shadow': 'rgba(119, 119, 119, 0.6) 0px 0px 6px' })),
|
state('focus', style({ boxShadow: 'rgba(119, 119, 119, 0.6) 0px 0px 6px' })),
|
||||||
|
|
||||||
state('blur', style({ 'box-shadow': 'none' })),
|
state('blur', style({ boxShadow: 'none' })),
|
||||||
|
|
||||||
transition('focus <=> blur', animate(250))
|
transition('focus <=> blur', [animate('250ms')])
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const focusBackground = trigger('focusBackground', [
|
export const focusBackground = trigger('focusBackground', [
|
||||||
|
|
||||||
state('focus', style({ 'background-color': 'rgba(119, 119, 119, 0.1)' })),
|
state('focus', style({ backgroundColor: 'rgba(119, 119, 119, 0.1)' })),
|
||||||
|
|
||||||
state('blur', style({ 'background-color': 'transparent' })),
|
state('blur', style({ backgroundColor: 'transparent' })),
|
||||||
|
|
||||||
transition('focus <=> blur', animate(250))
|
transition('focus <=> blur', [animate('250ms')])
|
||||||
]);
|
]);
|
||||||
|
@@ -6,7 +6,7 @@ export const slide = trigger('slide', [
|
|||||||
|
|
||||||
state('collapsed', style({ height: 0 })),
|
state('collapsed', style({ height: 0 })),
|
||||||
|
|
||||||
transition('expanded <=> collapsed', animate(250))
|
transition('expanded <=> collapsed', animate('250ms'))
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const slideMobileNav = trigger('slideMobileNav', [
|
export const slideMobileNav = trigger('slideMobileNav', [
|
||||||
@@ -15,5 +15,17 @@ export const slideMobileNav = trigger('slideMobileNav', [
|
|||||||
|
|
||||||
state('collapsed', style({ height: 0 })),
|
state('collapsed', style({ height: 0 })),
|
||||||
|
|
||||||
transition('expanded <=> collapsed', animate(300))
|
transition('expanded <=> collapsed', animate('300ms'))
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const slideSidebar = trigger('slideSidebar', [
|
||||||
|
|
||||||
|
state('expanded',
|
||||||
|
style({ width: '{{ sidebarWidth }}' }),
|
||||||
|
{ params: { sidebarWidth: '*' } }
|
||||||
|
),
|
||||||
|
|
||||||
|
state('collapsed', style({ width: '*' })),
|
||||||
|
|
||||||
|
transition('expanded <=> collapsed', animate('300ms ease-in-out')),
|
||||||
]);
|
]);
|
||||||
|
@@ -3,7 +3,13 @@ import { cold, hot } from 'jasmine-marbles';
|
|||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
import { AppState } from '../app.reducer';
|
import { AppState } from '../app.reducer';
|
||||||
|
|
||||||
import { GridBreakpoint, HostWindowService, WidthCategory } from './host-window.service';
|
import { HostWindowService, WidthCategory } from './host-window.service';
|
||||||
|
enum GridBreakpoint {
|
||||||
|
SM_MIN = 576,
|
||||||
|
MD_MIN = 768,
|
||||||
|
LG_MIN = 992,
|
||||||
|
XL_MIN = 1200
|
||||||
|
}
|
||||||
|
|
||||||
describe('HostWindowService', () => {
|
describe('HostWindowService', () => {
|
||||||
let service: HostWindowService;
|
let service: HostWindowService;
|
||||||
|
@@ -1,20 +1,13 @@
|
|||||||
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
||||||
|
|
||||||
import { filter, distinctUntilChanged, map } from 'rxjs/operators';
|
import { filter, distinctUntilChanged, map, first } from 'rxjs/operators';
|
||||||
import { HostWindowState } from './host-window.reducer';
|
import { HostWindowState } from './host-window.reducer';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { createSelector, select, Store } from '@ngrx/store';
|
import { createSelector, select, Store } from '@ngrx/store';
|
||||||
|
|
||||||
import { hasValue } from './empty.util';
|
import { hasValue } from './empty.util';
|
||||||
import { AppState } from '../app.reducer';
|
import { AppState } from '../app.reducer';
|
||||||
|
import { CSSVariableService } from './sass-helper/sass-helper.service';
|
||||||
// TODO: ideally we should get these from sass somehow
|
|
||||||
export enum GridBreakpoint {
|
|
||||||
SM_MIN = 576,
|
|
||||||
MD_MIN = 768,
|
|
||||||
LG_MIN = 992,
|
|
||||||
XL_MIN = 1200
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum WidthCategory {
|
export enum WidthCategory {
|
||||||
XS,
|
XS,
|
||||||
@@ -29,10 +22,19 @@ const widthSelector = createSelector(hostWindowStateSelector, (hostWindow: HostW
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class HostWindowService {
|
export class HostWindowService {
|
||||||
|
private breakPoints: { XS_MIN, SM_MIN, MD_MIN, LG_MIN, XL_MIN } = {} as any;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private store: Store<AppState>
|
private store: Store<AppState>,
|
||||||
|
private variableService: CSSVariableService
|
||||||
) {
|
) {
|
||||||
|
/* See _exposed_variables.scss */
|
||||||
|
variableService.getAllVariables().pipe(first()).subscribe((variables) => {
|
||||||
|
this.breakPoints.XL_MIN = parseInt(variables.xlMin, 10);
|
||||||
|
this.breakPoints.LG_MIN = parseInt(variables.lgMin, 10);
|
||||||
|
this.breakPoints.MD_MIN = parseInt(variables.mdMin, 10);
|
||||||
|
this.breakPoints.SM_MIN = parseInt(variables.smMin, 10);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private getWidthObs(): Observable<number> {
|
private getWidthObs(): Observable<number> {
|
||||||
@@ -45,13 +47,13 @@ export class HostWindowService {
|
|||||||
get widthCategory(): Observable<WidthCategory> {
|
get widthCategory(): Observable<WidthCategory> {
|
||||||
return this.getWidthObs().pipe(
|
return this.getWidthObs().pipe(
|
||||||
map((width: number) => {
|
map((width: number) => {
|
||||||
if (width < GridBreakpoint.SM_MIN) {
|
if (width < this.breakPoints.SM_MIN) {
|
||||||
return WidthCategory.XS
|
return WidthCategory.XS
|
||||||
} else if (width >= GridBreakpoint.SM_MIN && width < GridBreakpoint.MD_MIN) {
|
} else if (width >= this.breakPoints.SM_MIN && width < this.breakPoints.MD_MIN) {
|
||||||
return WidthCategory.SM
|
return WidthCategory.SM
|
||||||
} else if (width >= GridBreakpoint.MD_MIN && width < GridBreakpoint.LG_MIN) {
|
} else if (width >= this.breakPoints.MD_MIN && width < this.breakPoints.LG_MIN) {
|
||||||
return WidthCategory.MD
|
return WidthCategory.MD
|
||||||
} else if (width >= GridBreakpoint.LG_MIN && width < GridBreakpoint.XL_MIN) {
|
} else if (width >= this.breakPoints.LG_MIN && width < this.breakPoints.XL_MIN) {
|
||||||
return WidthCategory.LG
|
return WidthCategory.LG
|
||||||
} else {
|
} else {
|
||||||
return WidthCategory.XL
|
return WidthCategory.XL
|
||||||
|
29
src/app/shared/sass-helper/sass-helper.actions.ts
Normal file
29
src/app/shared/sass-helper/sass-helper.actions.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Action } from '@ngrx/store';
|
||||||
|
import { type } from '../../shared/ngrx/type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For each action type in an action group, make a simple
|
||||||
|
* enum object for all of this group's action types.
|
||||||
|
*
|
||||||
|
* The 'type' utility function coerces strings into string
|
||||||
|
* literal types and runs a simple check to guarantee all
|
||||||
|
* action types in the application are unique.
|
||||||
|
*/
|
||||||
|
export const CSSVariableActionTypes = {
|
||||||
|
ADD: type('dspace/css-variables/ADD'),
|
||||||
|
};
|
||||||
|
|
||||||
|
export class AddCSSVariableAction implements Action {
|
||||||
|
type = CSSVariableActionTypes.ADD;
|
||||||
|
payload: {
|
||||||
|
name: string,
|
||||||
|
value: string
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(name: string, value: string) {
|
||||||
|
this.payload = {name, value};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* tslint:enable:max-classes-per-file */
|
||||||
|
|
||||||
|
export type CSSVariableAction = AddCSSVariableAction
|
20
src/app/shared/sass-helper/sass-helper.reducer.ts
Normal file
20
src/app/shared/sass-helper/sass-helper.reducer.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { CSSVariableAction, CSSVariableActionTypes } from './sass-helper.actions';
|
||||||
|
|
||||||
|
export interface CSSVariablesState {
|
||||||
|
[name: string]: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: CSSVariablesState = Object.create({});
|
||||||
|
|
||||||
|
export function cssVariablesReducer(state = initialState, action: CSSVariableAction): CSSVariablesState {
|
||||||
|
switch (action.type) {
|
||||||
|
case CSSVariableActionTypes.ADD: {
|
||||||
|
const variable = action.payload;
|
||||||
|
const t = Object.assign({}, state, { [variable.name]: variable.value });
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
src/app/shared/sass-helper/sass-helper.service.ts
Normal file
33
src/app/shared/sass-helper/sass-helper.service.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { Inject, Injectable } from '@angular/core';
|
||||||
|
import { AppState, keySelector } from '../../app.reducer';
|
||||||
|
import { MemoizedSelector, select, Store } from '@ngrx/store';
|
||||||
|
import { GLOBAL_CONFIG } from '../../../config';
|
||||||
|
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||||
|
import { AddCSSVariableAction } from './sass-helper.actions';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CSSVariableService {
|
||||||
|
constructor(
|
||||||
|
protected store: Store<AppState>,
|
||||||
|
@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig) {
|
||||||
|
}
|
||||||
|
|
||||||
|
addCSSVariable(name: string, value: string) {
|
||||||
|
this.store.dispatch(new AddCSSVariableAction(name, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
getVariable(name: string) {
|
||||||
|
return this.store.pipe(select(themeVariableByNameSelector(name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllVariables() {
|
||||||
|
return this.store.pipe(select(themeVariablesSelector));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const themeVariablesSelector = (state: AppState) => state.cssVariables;
|
||||||
|
|
||||||
|
const themeVariableByNameSelector = (name: string): MemoizedSelector<AppState, string> => {
|
||||||
|
return keySelector<string>(name, themeVariablesSelector);
|
||||||
|
};
|
5
src/app/typings.d.ts
vendored
5
src/app/typings.d.ts
vendored
@@ -82,3 +82,8 @@ declare module '*.json' {
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare module 'reflect-metadata';
|
declare module 'reflect-metadata';
|
||||||
|
|
||||||
|
declare module '*.scss' {
|
||||||
|
const content: any;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
|
9
src/styles/_exposed_variables.scss
Normal file
9
src/styles/_exposed_variables.scss
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
@import '_variables.scss';
|
||||||
|
|
||||||
|
:export {
|
||||||
|
adminSidebarWidth: $admin-sidebar-width;
|
||||||
|
xlMin: map-get($grid-breakpoints, xl);
|
||||||
|
mdMin: map-get($grid-breakpoints, md);
|
||||||
|
lgMin: map-get($grid-breakpoints, lg);
|
||||||
|
smMin: map-get($grid-breakpoints, sm);
|
||||||
|
}
|
@@ -1,5 +1,5 @@
|
|||||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||||
|
const path = require('path');
|
||||||
const {
|
const {
|
||||||
root,
|
root,
|
||||||
join
|
join
|
||||||
@@ -37,7 +37,8 @@ module.exports = {
|
|||||||
{
|
{
|
||||||
loader: 'css-loader',
|
loader: 'css-loader',
|
||||||
options: {
|
options: {
|
||||||
sourceMap: true
|
sourceMap: true,
|
||||||
|
modules: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -50,7 +51,9 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.scss$/,
|
test: /\.scss$/,
|
||||||
exclude: /node_modules/,
|
exclude: [/node_modules/,
|
||||||
|
path.resolve(__dirname, '..', 'src/styles/_exposed_variables.scss')
|
||||||
|
],
|
||||||
use: [{
|
use: [{
|
||||||
loader: 'to-string-loader',
|
loader: 'to-string-loader',
|
||||||
options: {
|
options: {
|
||||||
@@ -62,12 +65,6 @@ module.exports = {
|
|||||||
sourceMap: true
|
sourceMap: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
loader: 'postcss-loader',
|
|
||||||
options: {
|
|
||||||
sourceMap: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
loader: 'resolve-url-loader',
|
loader: 'resolve-url-loader',
|
||||||
options: {
|
options: {
|
||||||
@@ -82,6 +79,15 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
test: /_exposed_variables.scss$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: [{
|
||||||
|
loader: "css-loader" // translates CSS into CommonJS
|
||||||
|
}, {
|
||||||
|
loader: "sass-loader" // compiles Sass to CSS
|
||||||
|
}]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
test: /\.html$/,
|
test: /\.html$/,
|
||||||
loader: 'raw-loader'
|
loader: 'raw-loader'
|
||||||
|
Reference in New Issue
Block a user