Merge pull request #10 from wwelling/bootstrap

Added bootstrap with ng-bootstrap: this closes #4
This commit is contained in:
Art Lowel
2016-12-07 18:00:40 +01:00
committed by GitHub
19 changed files with 242 additions and 147 deletions

2
.gitignore vendored
View File

@@ -19,3 +19,5 @@ webpack.records.json
/npm-debug.log.*
morgan.log
*.css

View File

@@ -7,102 +7,118 @@
"url": "https://github.com/dspace/dspace-angular.git"
},
"scripts": {
"watch": "webpack --watch",
"watch:dev": "npm run server & npm run watch",
"clean:dist": "rimraf dist",
"clean:ngc": "rimraf **/*.ngfactory.ts **/*.css.shim.ts **/*.scss.shim.ts",
"prebuild": "npm run clean:dist",
"build": "webpack --progress",
"build:prod:ngc": "npm run clean:ngc && npm run ngc && npm run clean:dist && npm run build:prod",
"build:prod:ngc:json": "npm run clean:ngc && npm run ngc && npm run clean:dist && npm run build:prod:json",
"clean:log": "rimraf *.log*",
"clean:dist": "rimraf dist/*",
"clean:node": "rimraf node_modules/*",
"clean:ngc": "rimraf **/*.ngfactory.ts",
"clean:json": "rimraf *.records.json",
"clean:css": "rimraf **/*.css",
"clean:css:ts": "rimraf **/*.css.ts",
"clean:scss:ts": "rimraf **/*.scss.ts",
"clean:css:shim:ts": "rimraf **/*.css.shim.ts",
"clean:scss:shim:ts": "rimraf **/*.scss.shim.ts",
"clean:prod": "npm run clean:ngc && npm run clean:json && npm run clean:css && npm run clean:css:ts && npm run clean:scss:ts && npm run clean:css:shim:ts && npm run clean:scss:shim:ts && npm run clean:dist",
"clean": "npm run clean:log && npm run clean:dist && npm run clean:prod && npm run clean:node",
"sass": "node-sass src -o src --include-path node_modules --output-style compressed -q",
"sass:watch": "node-sass -w src -o src --include-path node_modules --output-style compressed -q",
"prebuild": "npm run clean:dist && npm run sass",
"build": "webpack --progress",
"build:prod": "webpack --config webpack.prod.config.ts",
"build:prod:ngc": "npm run clean:prod && npm run sass && npm run ngc && npm run clean:dist && npm run build:prod",
"build:prod:ngc:json": "npm run clean:prod && npm run sass && npm run ngc && npm run clean:dist && npm run build:prod:json",
"build:prod:json": "webpack --config webpack.prod.config.ts --json | webpack-bundle-size-analyzer",
"ngc": "ngc -p tsconfig.aot.json",
"prestart": "npm run build",
"server": "nodemon dist/server/index.js",
"debug:server": "node-nightly --inspect --debug-brk dist/server/index.js",
"prestart": "npm run build:prod:ngc:json",
"server": "node dist/server/index.js",
"server:dev": "nodemon --debug dist/server/index.js",
"start": "npm run server",
"start:prod:node": "node dist/server/index.js",
"start:prod": "npm run build:prod:ngc:json && npm run start:prod:node",
"debug:start": "npm run build && npm run debug:server",
"start:dev": "npm run clean:prod && npm run build && npm run server",
"watch": "webpack -w & npm run sass:watch",
"watch:dev:server": "concurrently \"npm run server:dev\" \"npm run watch\"",
"watch:dev": "npm run clean:prod && npm run build && npm run watch:dev:server",
"watch:prod:server": "concurrently \"npm run server\" \"npm run watch\"",
"watch:prod": "npm run build:prod:ngc:json && npm run watch:prod:server",
"predebug": "npm run build",
"debug": "node --debug-brk dist/server/index.js",
"debug:server": "node-nightly --inspect --debug-brk dist/server/index.js",
"debug:start": "npm run build && npm run debug:server",
"debug:build": "node-nightly --inspect --debug-brk node_modules/webpack/bin/webpack.js",
"debug:build:prod": "node-nightly --inspect --debug-brk node_modules/webpack/bin/webpack.js --config webpack.prod.config.ts",
"debug": "node --debug-brk dist/server/index.js",
"lint": "tslint \"src/**/*.ts\" || true"
},
"dependencies": {
"@angular/common": "~2.2.4",
"@angular/compiler": "~2.2.4",
"@angular/compiler-cli": "~2.2.4",
"@angular/core": "~2.2.4",
"@angular/forms": "~2.2.4",
"@angular/http": "~2.2.4",
"@angular/platform-browser": "~2.2.4",
"@angular/platform-browser-dynamic": "~2.2.4",
"@angular/platform-server": "~2.2.4",
"@angular/router": "~3.2.4",
"@angular/upgrade": "~2.2.4",
"@angularclass/bootloader": "~1.0.1",
"@angularclass/idle-preload": "~1.0.4",
"angular2-express-engine": "~2.1.0-rc.1",
"angular2-platform-node": "~2.1.0-rc.1",
"angular2-universal": "~2.1.0-rc.1",
"angular2-universal-polyfills": "~2.1.0-rc.1",
"body-parser": "^1.15.2",
"compression": "^1.6.2",
"express": "^4.14.0",
"@angular/common": "2.2.4",
"@angular/compiler": "2.2.4",
"@angular/compiler-cli": "2.2.4",
"@angular/core": "2.2.4",
"@angular/forms": "2.2.4",
"@angular/http": "2.2.4",
"@angular/platform-browser": "2.2.4",
"@angular/platform-browser-dynamic": "2.2.4",
"@angular/platform-server": "2.2.4",
"@angular/router": "3.2.4",
"@angular/upgrade": "2.2.4",
"@angularclass/bootloader": "1.0.1",
"@angularclass/idle-preload": "1.0.4",
"@ng-bootstrap/ng-bootstrap": "1.0.0-alpha.14",
"angular2-express-engine": "2.1.0-rc.1",
"angular2-platform-node": "2.1.0-rc.1",
"angular2-universal": "2.1.0-rc.1",
"angular2-universal-polyfills": "2.1.0-rc.1",
"body-parser": "1.15.2",
"bootstrap": "4.0.0-alpha.5",
"compression": "1.6.2",
"express": "4.14.0",
"font-awesome": "4.7.0",
"js.clone": "0.0.3",
"methods": "~1.1.2",
"morgan": "^1.7.0",
"ng2-translate": "~4.0.1",
"preboot": "~4.5.2",
"methods": "1.1.2",
"morgan": "1.7.0",
"ng2-translate": "4.2.0",
"preboot": "4.5.2",
"rxjs": "5.0.0-beta.12",
"webfontloader": "^1.6.27",
"zone.js": "~0.6.26"
"webfontloader": "1.6.27",
"zone.js": "0.6.26"
},
"devDependencies": {
"@ngtools/webpack": "1.1.7",
"@types/body-parser": "0.0.33",
"@types/compression": "0.0.33",
"@types/cookie-parser": "^1.3.30",
"@types/express": "^4.0.34",
"@types/express-serve-static-core": "^4.0.39",
"@types/hammerjs": "^2.0.33",
"@types/cookie-parser": "1.3.30",
"@types/express": "4.0.34",
"@types/express-serve-static-core": "4.0.39",
"@types/hammerjs": "2.0.33",
"@types/memory-cache": "0.0.29",
"@types/mime": "0.0.29",
"@types/morgan": "^1.7.32",
"@types/node": "^6.0.51",
"@types/serve-static": "^1.7.31",
"@types/webfontloader": "^1.6.27",
"accepts": "^1.3.3",
"angular2-template-loader": "^0.6.0",
"awesome-typescript-loader": "^2.2.4",
"@types/morgan": "1.7.32",
"@types/node": "6.0.51",
"@types/serve-static": "1.7.31",
"@types/webfontloader": "1.6.27",
"angular2-template-loader": "0.6.0",
"autoprefixer": "6.5.3",
"awesome-typescript-loader": "2.2.4",
"codelyzer": "2.0.0-beta.1",
"cookie-parser": "^1.4.3",
"copy-webpack-plugin": "~4.0.1",
"express-interceptor": "^1.2.0",
"iltorb": "^1.0.13",
"imports-loader": "^0.6.5",
"json-loader": "^0.5.4",
"memory-cache": "^0.1.6",
"node-sass": "^3.13.0",
"nodemon": "^1.11.0",
"raw-loader": "^0.5.1",
"concurrently": "3.1.0",
"cookie-parser": "1.4.3",
"copy-webpack-plugin": "4.0.1",
"imports-loader": "0.6.5",
"json-loader": "0.5.4",
"node-sass": "3.13.0",
"nodemon": "1.11.0",
"raw-loader": "0.5.1",
"reflect-metadata": "0.1.8",
"rimraf": "^2.5.4",
"sass-loader": "^4.0.2",
"string-replace-loader": "^1.0.5",
"ts-helpers": "^1.1.2",
"ts-node": "^1.7.0",
"rimraf": "2.5.4",
"string-replace-loader": "1.0.5",
"ts-helpers": "1.1.2",
"ts-node": "1.7.0",
"tslint": "4.0.2",
"tslint-loader": "3.2.1",
"tslint-loader": "3.3.0",
"typedoc": "0.5.1",
"typescript": "2.0.10",
"v8-lazy-parse-webpack-plugin": "^0.3.0",
"v8-lazy-parse-webpack-plugin": "0.3.0",
"webpack": "2.1.0-beta.27",
"webpack-bundle-analyzer": "1.4.1",
"webpack-dev-middleware": "^1.8.4",
"webpack-dev-middleware": "1.8.4",
"webpack-dev-server": "2.1.0-beta.11",
"webpack-merge": "~1.0.2"
"webpack-merge": "1.0.2"
}
}

View File

@@ -1,5 +1,5 @@
{
"title": "DSpace Universal",
"title": "DSpace",
"nav": {
"home": "Home"

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,7 +1,22 @@
<h3>{{ 'title' | translate }}</h3>
<nav>
<a routerLinkActive="router-link-active" routerLink="home">{{ 'nav.home' | translate }}</a>
</nav>
<header>
<nav class="navbar navbar-dark bg-inverse">
<button class="navbar-toggler hidden-sm-up" type="button" (click)="toggle()" aria-controls="collapsingNav" aria-expanded="false" aria-label="Toggle navigation">
<i class="fa fa-bars fa-fw" aria-hidden="true"></i>
</button>
<div [ngClass]="{'clearfix': !isNavBarCollaped()}">
<a class="navbar-brand" routerLink="/home">
<!-- TODO: add logo here -->{{ 'title' | translate }}</a>
</div>
<div [ngbCollapse]="isNavBarCollaped()" class="collapse navbar-toggleable-xs" id="collapsingNav">
<ul class="nav navbar-nav">
<li class="nav-item">
<a class="nav-link" routerLink="/home" routerLinkActive="active"><i class="fa fa-home fa-fw" aria-hidden="true"></i> {{ 'nav.home' | translate }}<span class="sr-only">(current)</span></a>
</li>
</ul>
</div>
</nav>
</header>
<div class="container-fluid">
<main>
<p>{{ 'example.with.data' | translate:data }}</p>

View File

@@ -1 +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;
}

View File

@@ -1,4 +1,5 @@
import { Component, ChangeDetectionStrategy, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { Component, HostListener, Input, ChangeDetectionStrategy, ViewEncapsulation, OnDestroy, OnInit } from '@angular/core';
import { Event, NavigationEnd, Router } from '@angular/router';
import { TranslateService } from 'ng2-translate';
@@ -7,10 +8,19 @@ import { TranslateService } from 'ng2-translate';
encapsulation: ViewEncapsulation.Emulated,
selector: 'ds-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
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;
data: any = {
@@ -18,25 +28,54 @@ export class AppComponent implements OnDestroy, OnInit {
recipient: 'World'
}
private registerSubscription: any;
constructor(public translate: TranslateService) {
constructor(public translate: TranslateService, private router: Router) {
// 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.registerSubscription = this.translate.get('example.with.data', { greeting: 'Hello', recipient: 'DSpace' }).subscribe((translation: string) => {
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.registerSubscription) {
this.registerSubscription.unsubscribe();
if (this.routerSubscription) {
this.routerSubscription.unsubscribe();
}
if (this.translateSubscription) {
this.translateSubscription.unsubscribe();
}
}
@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;
}
}

View File

@@ -1,8 +0,0 @@
blockquote {
border-left:5px #158126 solid;
background:#fff;
padding:20px 20px 20px 40px;
}
blockquote::before {
left: 1em;
}

View File

@@ -0,0 +1 @@
@import '../../styles/variables.scss';

View File

@@ -8,12 +8,15 @@ import { Component, ChangeDetectionStrategy, ViewEncapsulation } from '@angular/
templateUrl: './home.component.html'
})
export class HomeComponent {
data: any = {};
constructor() {
this.universalInit();
}
universalInit() {
}
}

View File

@@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from 'ng2-translate/ng2-translate';
import { ApiService } from './api.service';
@@ -14,7 +15,8 @@ const MODULES = [
RouterModule,
TranslateModule,
FormsModule,
ReactiveFormsModule
ReactiveFormsModule,
NgbModule
];
const PIPES = [

View File

@@ -5,6 +5,7 @@ import { RouterModule } from '@angular/router';
import { UniversalModule, isBrowser, isNode, AUTO_PREBOOT } from 'angular2-universal/browser'; // for AoT we need to manually split universal packages
import { IdlePreload, IdlePreloadModule } from '@angularclass/idle-preload';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateLoader, TranslateModule, TranslateStaticLoader } from 'ng2-translate';
import { AppModule, AppComponent } from './app/app.module';
@@ -47,6 +48,7 @@ export const UNIVERSAL_KEY = 'UNIVERSAL_CACHE';
useFactory: (createTranslateLoader),
deps: [Http]
}),
NgbModule.forRoot(),
UniversalModule, // BrowserModule, HttpModule, and JsonpModule are included

View File

@@ -2,22 +2,18 @@
<html>
<head>
<title>DSpace</title>
<meta charset="UTF-8">
<title>DSpace</title>
<meta name="viewport" content="width=device-width,minimum-scale=1">
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<link rel="stylesheet" href="/styles/main.css">
<base href="/">
</head>
<body>
<ds-app>
Loading DSpace ...
</ds-app>
<script async src="/main.bundle.js"></script>
</body>

View File

@@ -4,6 +4,7 @@ import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { UniversalModule, isBrowser, isNode } from 'angular2-universal/node'; // for AoT we need to manually split universal packages
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateLoader, TranslateModule, TranslateStaticLoader } from 'ng2-translate';
import { AppModule, AppComponent } from './app/app.module';
@@ -39,6 +40,7 @@ export const UNIVERSAL_KEY = 'UNIVERSAL_CACHE';
useFactory: (createTranslateLoader),
deps: [Http]
}),
NgbModule.forRoot(),
UniversalModule, // BrowserModule, HttpModule, and JsonpModule are included

View File

@@ -8,16 +8,11 @@ import './__workaround.node'; // temporary until 2.1.1 things are patched in Cor
import * as fs from 'fs';
import * as path from 'path';
import * as morgan from 'morgan';
import * as express from 'express';
import * as bodyParser from 'body-parser';
import * as compression from 'compression';
import * as cookieParser from 'cookie-parser';
import * as morgan from 'morgan';
import * as mcache from 'memory-cache';
const { gzipSync } = require('zlib');
const accepts = require('accepts');
const { compressSync } = require('iltorb');
const interceptor = require('express-interceptor');
// Angular 2
import { enableProdMode } from '@angular/core';
@@ -54,38 +49,7 @@ app.set('json spaces', 2);
app.use(cookieParser('Angular 2 Universal'));
app.use(bodyParser.json());
app.use(interceptor((req, res) => ({
// don't compress responses with this request header
isInterceptable: () => (!req.headers['x-no-compression']),
intercept: (body, send) => {
const encodings = new Set(accepts(req).encodings());
const bodyBuffer = new Buffer(body);
// url specific key for response cache
const key = '__response__' + req.originalUrl || req.url;
let output = bodyBuffer;
// check if cache exists
if (mcache.get(key) === null) {
// check for encoding support
if (encodings.has('br')) {
// brotli
res.setHeader('Content-Encoding', 'br');
output = compressSync(bodyBuffer);
mcache.put(key, { output, encoding: 'br' });
} else if (encodings.has('gzip')) {
// gzip
res.setHeader('Content-Encoding', 'gzip');
output = gzipSync(bodyBuffer);
mcache.put(key, { output, encoding: 'gzip' });
}
} else {
const { output, encoding } = mcache.get(key);
res.setHeader('Content-Encoding', encoding);
send(output);
}
send(output);
}
})));
app.use(compression());
const accessLogStream = fs.createWriteStream(ROOT + '/morgan.log', { flags: 'a' })
@@ -101,6 +65,8 @@ function cacheControl(req, res, next) {
}
// Serve static files
app.use('/assets', cacheControl, express.static(path.join(__dirname, 'assets'), { maxAge: 30 }));
app.use('/styles', cacheControl, express.static(path.join(__dirname, 'styles'), { maxAge: 30 }));
app.use(cacheControl, express.static(path.join(ROOT, 'dist/client'), { index: false }));
//
@@ -117,7 +83,8 @@ function ngApp(req, res) {
req,
res,
// time: true, // use this to determine what part of your app is slow only in development
preboot: false,
async: true,
preboot: true,
baseUrl: '/',
requestUrl: req.originalUrl,
originUrl: `http://localhost:${app.get('port')}`

View File

@@ -7,11 +7,11 @@ import 'ts-helpers';
import './__workaround.node'; // temporary until 2.1.1 things are patched in Core
import * as path from 'path';
import * as morgan from 'morgan';
import * as express from 'express';
import * as bodyParser from 'body-parser';
import * as cookieParser from 'cookie-parser';
import * as morgan from 'morgan';
import * as compression from 'compression';
import * as cookieParser from 'cookie-parser';
// Angular 2
import { enableProdMode } from '@angular/core';
@@ -58,6 +58,8 @@ function cacheControl(req, res, next) {
}
// Serve static files
app.use('/assets', cacheControl, express.static(path.join(__dirname, 'assets'), { maxAge: 30 }));
app.use('/styles', cacheControl, express.static(path.join(__dirname, 'styles'), { maxAge: 30 }));
app.use(cacheControl, express.static(path.join(ROOT, 'dist/client'), { index: false }));
//
@@ -74,7 +76,8 @@ function ngApp(req, res) {
req,
res,
// time: true, // use this to determine what part of your app is slow only in development
preboot: false,
async: true,
preboot: true,
baseUrl: '/',
requestUrl: req.originalUrl,
originUrl: `http://localhost:${app.get('port')}`

10
src/styles/main.scss Normal file
View File

@@ -0,0 +1,10 @@
@import './variables.scss';
@import '../../node_modules/bootstrap/scss/bootstrap.scss';
@import "../../node_modules/font-awesome/scss/font-awesome.scss";
html {
position: relative;
min-height: 100%;
}

24
src/styles/variables.scss Normal file
View File

@@ -0,0 +1,24 @@
// Colors
$gray-base: #000 !default;
$gray-darker: lighten($gray-base, 13.5%) !default; // #222
$gray-dark: lighten($gray-base, 26.6%) !default; // #444
$gray: lighten($gray-base, 46.6%) !default; // #777
$gray-light: lighten($gray-base, 73.3%) !default; // #bbb
$gray-lighter: lighten($gray-base, 93.5%) !default; // #eee
$brand-primary: #2B4E72 !default;
$brand-success: #94BA65 !default;
$brand-info: #2790B0 !default;
$brand-warning: #EBBB54 !default;
$brand-danger: #CF4444 !default;
$brand-inverse: $brand-primary !default;
$link-color: $brand-info !default;
// Fonts
$fa-font-path: "../assets/fonts";
$image-path: "../assets/images";

View File

@@ -14,10 +14,20 @@ export var commonPlugins = [
}
),
new CopyWebpackPlugin([{
from: path.join(__dirname, 'resources', 'i18n'),
to: path.join('assets', 'i18n')
}]),
// Copy fonts, images and i18n to dist/assets
new CopyWebpackPlugin([
{
from: path.join(__dirname, 'node_modules', 'font-awesome', 'fonts'),
to: path.join('assets', 'fonts')
},
{
from: path.join(__dirname, 'resources', 'images'),
to: path.join('assets', 'images')
}, {
from: path.join(__dirname, 'resources', 'i18n'),
to: path.join('assets', 'i18n')
}
]),
// Loader options
new webpack.LoaderOptionsPlugin({
@@ -48,7 +58,6 @@ export var commonConfig = {
{ test: /\.ts$/, use: ['awesome-typescript-loader', 'angular2-template-loader'] },
{ test: /\.html$/, use: 'raw-loader' },
{ test: /\.css$/, use: 'raw-loader' },
{ test: /\.scss$/, use: ['raw-loader', 'sass-loader'] },
{ test: /\.json$/, use: 'json-loader' },
{
enforce: 'pre',