Added bootstrap with ng-bootstrap

This commit is contained in:
William Welling
2016-12-01 13:49:24 -06:00
parent 28e23f0cdd
commit b56fc730c5
20 changed files with 175 additions and 50 deletions

View File

@@ -8,10 +8,12 @@
}, },
"scripts": { "scripts": {
"watch": "webpack --watch", "watch": "webpack --watch",
"watch:dev": "npm run server & npm run watch", "watch:dev": "npm run server & npm run watch & npm run sass:watch",
"clean:dist": "rimraf dist", "clean:dist": "rimraf dist **/*.css",
"clean:ngc": "rimraf **/*.ngfactory.ts **/*.css.shim.ts", "clean:ngc": "rimraf **/*.ngfactory.ts **/*.css.shim.ts",
"prebuild": "npm run clean:dist", "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": "webpack --progress",
"build:prod:ngc": "npm run clean:ngc && npm run ngc && npm run clean:dist && npm run build:prod", "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", "build:prod:ngc:json": "npm run clean:ngc && npm run ngc && npm run clean:dist && npm run build:prod:json",
@@ -43,13 +45,16 @@
"@angular/upgrade": "~2.1.2", "@angular/upgrade": "~2.1.2",
"@angularclass/bootloader": "~1.0.1", "@angularclass/bootloader": "~1.0.1",
"@angularclass/idle-preload": "~1.0.4", "@angularclass/idle-preload": "~1.0.4",
"@ng-bootstrap/ng-bootstrap": "1.0.0-alpha.14",
"angular2-express-engine": "~2.1.0-rc.1", "angular2-express-engine": "~2.1.0-rc.1",
"angular2-platform-node": "~2.1.0-rc.1", "angular2-platform-node": "~2.1.0-rc.1",
"angular2-universal": "~2.1.0-rc.1", "angular2-universal": "~2.1.0-rc.1",
"angular2-universal-polyfills": "~2.1.0-rc.1", "angular2-universal-polyfills": "~2.1.0-rc.1",
"body-parser": "^1.15.2", "body-parser": "^1.15.2",
"bootstrap": "4.0.0-alpha.5",
"compression": "^1.6.2", "compression": "^1.6.2",
"express": "^4.14.0", "express": "^4.14.0",
"font-awesome": "^4.7.0",
"js.clone": "0.0.3", "js.clone": "0.0.3",
"methods": "~1.1.2", "methods": "~1.1.2",
"morgan": "^1.7.0", "morgan": "^1.7.0",
@@ -77,6 +82,7 @@
"awesome-typescript-loader": "^2.2.4", "awesome-typescript-loader": "^2.2.4",
"codelyzer": "1.0.0-beta.3", "codelyzer": "1.0.0-beta.3",
"cookie-parser": "^1.4.3", "cookie-parser": "^1.4.3",
"copy-webpack-plugin": "4.0.1",
"express-interceptor": "^1.2.0", "express-interceptor": "^1.2.0",
"iltorb": "^1.0.13", "iltorb": "^1.0.13",
"imports-loader": "^0.6.5", "imports-loader": "^0.6.5",

View File

View File

@@ -1,7 +1,21 @@
<h3>DSpace</h3> <header>
<nav> <nav class="navbar navbar-dark bg-inverse">
<a routerLinkActive="router-link-active" routerLink="home">Home</a> <button class="navbar-toggler hidden-sm-up" type="button" (click)="toggle()" aria-controls="collapsingNav" aria-expanded="false" aria-label="Toggle navigation">
</nav> <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 -->DSpace</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> Home<span class="sr-only">(current)</span></a>
</li>
</ul>
</div>
</nav>
</header>
<div class="container-fluid"> <div class="container-fluid">
<main> <main>
<router-outlet></router-outlet> <router-outlet></router-outlet>

View File

@@ -1 +1,11 @@
header nav.navbar {
border-radius: 0rem;
}
header nav.navbar .navbar-toggler {
float: right;
}
header nav.navbar .navbar-toggler:hover {
cursor: pointer;
}

View File

@@ -1,11 +1,59 @@
import { Component, ChangeDetectionStrategy, ViewEncapsulation } from '@angular/core'; import { Component, HostListener, Input, ChangeDetectionStrategy, ViewEncapsulation, OnDestroy, OnInit } from '@angular/core';
import { Event, NavigationEnd, Router } from '@angular/router';
@Component({ @Component({
changeDetection: ChangeDetectionStrategy.Default, changeDetection: ChangeDetectionStrategy.Default,
encapsulation: ViewEncapsulation.Emulated, encapsulation: ViewEncapsulation.Emulated,
selector: 'ds-app', selector: 'ds-app',
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrls: [ './app.component.scss' ] styleUrls: ['./app.component.scss']
}) })
export class AppComponent { export class AppComponent implements OnDestroy, OnInit {
// TODO: move header and all related properties into its own component
private navCollapsed: boolean;
private routerSubscription: any;
constructor(private router: Router) {
this.collapse();
}
@HostListener('window:resize', ['$event'])
private onResize(event): void {
this.collapse();
}
ngOnInit() {
this.routerSubscription = this.router.events.subscribe((event: Event) => {
if (event instanceof NavigationEnd) {
this.collapse();
}
});
}
ngOnDestroy() {
if (this.routerSubscription) {
this.routerSubscription.unsubscribe();
}
}
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

@@ -8,7 +8,7 @@ import { AppComponent } from './app.component';
@NgModule({ @NgModule({
declarations: [ AppComponent ], declarations: [AppComponent],
imports: [ imports: [
SharedModule, SharedModule,
HomeModule, HomeModule,

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

View File

@@ -4,7 +4,7 @@ import { Component, ChangeDetectionStrategy, ViewEncapsulation } from '@angular/
changeDetection: ChangeDetectionStrategy.Default, changeDetection: ChangeDetectionStrategy.Default,
encapsulation: ViewEncapsulation.Emulated, encapsulation: ViewEncapsulation.Emulated,
selector: 'ds-home', selector: 'ds-home',
styleUrls: [ './home.component.css' ], styleUrls: ['./home.component.scss'],
templateUrl: './home.component.html' templateUrl: './home.component.html'
}) })
export class HomeComponent { export class HomeComponent {

View File

@@ -2,6 +2,9 @@ import { NgModule, ModuleWithProviders } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { ApiService } from './api.service'; import { ApiService } from './api.service';
import { ModelService } from './model/model.service'; import { ModelService } from './model/model.service';
@@ -10,7 +13,8 @@ const MODULES = [
CommonModule, CommonModule,
RouterModule, RouterModule,
FormsModule, FormsModule,
ReactiveFormsModule ReactiveFormsModule,
NgbModule
]; ];
const PIPES = [ const PIPES = [

View File

@@ -4,6 +4,8 @@ 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 { 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 { IdlePreload, IdlePreloadModule } from '@angularclass/idle-preload';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { AppModule, AppComponent } from './app/app.module'; import { AppModule, AppComponent } from './app/app.module';
import { SharedModule } from './app/shared/shared.module'; import { SharedModule } from './app/shared/shared.module';
import { CacheService } from './app/shared/cache.service'; import { CacheService } from './app/shared/cache.service';
@@ -33,9 +35,10 @@ export function getResponse() {
export const UNIVERSAL_KEY = 'UNIVERSAL_CACHE'; export const UNIVERSAL_KEY = 'UNIVERSAL_CACHE';
@NgModule({ @NgModule({
bootstrap: [ AppComponent ], bootstrap: [AppComponent],
imports: [ imports: [
// MaterialModule.forRoot() should be included first NgbModule.forRoot(),
UniversalModule, // BrowserModule, HttpModule, and JsonpModule are included UniversalModule, // BrowserModule, HttpModule, and JsonpModule are included
FormsModule, FormsModule,

View File

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

View File

@@ -3,6 +3,8 @@ import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { UniversalModule, isBrowser, isNode } from 'angular2-universal/node'; // for AoT we need to manually split universal packages 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 { AppModule, AppComponent } from './app/app.module'; import { AppModule, AppComponent } from './app/app.module';
import { SharedModule } from './app/shared/shared.module'; import { SharedModule } from './app/shared/shared.module';
import { CacheService } from './app/shared/cache.service'; import { CacheService } from './app/shared/cache.service';
@@ -25,9 +27,10 @@ export function getResponse() {
export const UNIVERSAL_KEY = 'UNIVERSAL_CACHE'; export const UNIVERSAL_KEY = 'UNIVERSAL_CACHE';
@NgModule({ @NgModule({
bootstrap: [ AppComponent ], bootstrap: [AppComponent],
imports: [ imports: [
// MaterialModule.forRoot() should be included first NgbModule.forRoot(),
UniversalModule, // BrowserModule, HttpModule, and JsonpModule are included UniversalModule, // BrowserModule, HttpModule, and JsonpModule are included
FormsModule, FormsModule,

View File

@@ -55,10 +55,10 @@ app.set('json spaces', 2);
app.use(cookieParser('Angular 2 Universal')); app.use(cookieParser('Angular 2 Universal'));
app.use(bodyParser.json()); app.use(bodyParser.json());
app.use(interceptor((req, res)=>({ app.use(interceptor((req, res) => ({
// don't compress responses with this request header // don't compress responses with this request header
isInterceptable: () => (!req.headers['x-no-compression']), isInterceptable: () => (!req.headers['x-no-compression']),
intercept: ( body, send ) => { intercept: (body, send) => {
const encodings = new Set(accepts(req).encodings()); const encodings = new Set(accepts(req).encodings());
const bodyBuffer = new Buffer(body); const bodyBuffer = new Buffer(body);
// url specific key for response cache // url specific key for response cache
@@ -71,12 +71,12 @@ app.use(interceptor((req, res)=>({
// brotli // brotli
res.setHeader('Content-Encoding', 'br'); res.setHeader('Content-Encoding', 'br');
output = compressSync(bodyBuffer); output = compressSync(bodyBuffer);
mcache.put(key, {output, encoding: 'br'}); mcache.put(key, { output, encoding: 'br' });
} else if (encodings.has('gzip')) { } else if (encodings.has('gzip')) {
// gzip // gzip
res.setHeader('Content-Encoding', 'gzip'); res.setHeader('Content-Encoding', 'gzip');
output = gzipSync(bodyBuffer); output = gzipSync(bodyBuffer);
mcache.put(key, {output, encoding: 'gzip'}); mcache.put(key, { output, encoding: 'gzip' });
} }
} else { } else {
const { output, encoding } = mcache.get(key); const { output, encoding } = mcache.get(key);
@@ -87,7 +87,7 @@ app.use(interceptor((req, res)=>({
} }
}))); })));
const accessLogStream = fs.createWriteStream(ROOT + '/morgan.log', {flags: 'a'}) const accessLogStream = fs.createWriteStream(ROOT + '/morgan.log', { flags: 'a' })
app.use(morgan('common', { app.use(morgan('common', {
skip: (req, res) => res.statusCode < 400, skip: (req, res) => res.statusCode < 400,
@@ -100,8 +100,9 @@ function cacheControl(req, res, next) {
next(); next();
} }
// Serve static files // Serve static files
app.use('/assets', cacheControl, express.static(path.join(__dirname, 'assets'), {maxAge: 30})); app.use('/assets', cacheControl, express.static(path.join(__dirname, 'assets'), { maxAge: 30 }));
app.use(cacheControl, express.static(path.join(ROOT, 'dist/client'), {index: false})); app.use('/styles', cacheControl, express.static(path.join(__dirname, 'styles'), { maxAge: 30 }));
app.use(cacheControl, express.static(path.join(ROOT, 'dist/client'), { index: false }));
// //
///////////////////////// /////////////////////////
@@ -120,7 +121,7 @@ function ngApp(req, res) {
preboot: false, preboot: false,
baseUrl: '/', baseUrl: '/',
requestUrl: req.originalUrl, requestUrl: req.originalUrl,
originUrl: `http://localhost:${ app.get('port') }` originUrl: `http://localhost:${app.get('port')}`
}); });
} }

View File

@@ -57,8 +57,9 @@ function cacheControl(req, res, next) {
next(); next();
} }
// Serve static files // Serve static files
app.use('/assets', cacheControl, express.static(path.join(__dirname, 'assets'), {maxAge: 30})); app.use('/assets', cacheControl, express.static(path.join(__dirname, 'assets'), { maxAge: 30 }));
app.use(cacheControl, express.static(path.join(ROOT, 'dist/client'), {index: false})); app.use('/styles', cacheControl, express.static(path.join(__dirname, 'styles'), { maxAge: 30 }));
app.use(cacheControl, express.static(path.join(ROOT, 'dist/client'), { index: false }));
// //
///////////////////////// /////////////////////////
@@ -77,7 +78,7 @@ function ngApp(req, res) {
preboot: false, preboot: false,
baseUrl: '/', baseUrl: '/',
requestUrl: req.originalUrl, requestUrl: req.originalUrl,
originUrl: `http://localhost:${ app.get('port') }` originUrl: `http://localhost:${app.get('port')}`
}); });
} }

9
src/styles/main.css Normal file

File diff suppressed because one or more lines are too long

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

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

0
src/styles/variables.css Normal file
View File

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

@@ -0,0 +1,18 @@
// Colors
$gray-dark: #373a3c !default;
$gray: #55595c !default;
$gray-light: #818a91 !default;
$gray-lighter: #eceeef !default;
$gray-lightest: #f7f7f9 !default;
$brand-primary: #0275d8 !default;
$brand-success: #5cb85c !default;
$brand-info: #5bc0de !default;
$brand-warning: #f0ad4e !default;
$brand-danger: #d9534f !default;
$brand-inverse: $gray-dark !default;
// Fonts
$fa-font-path: "../assets/fonts";

View File

@@ -2,6 +2,7 @@ var webpack = require('webpack');
var path = require('path'); var path = require('path');
var clone = require('js.clone'); var clone = require('js.clone');
var webpackMerge = require('webpack-merge'); var webpackMerge = require('webpack-merge');
let CopyWebpackPlugin = require('copy-webpack-plugin');
export var commonPlugins = [ export var commonPlugins = [
new webpack.ContextReplacementPlugin( new webpack.ContextReplacementPlugin(
@@ -13,6 +14,12 @@ export var commonPlugins = [
} }
), ),
// Copy fonts
new CopyWebpackPlugin([{
from: path.join(__dirname, 'node_modules', 'font-awesome', 'fonts'),
to: path.join('assets', 'fonts')
}]),
// Loader options // Loader options
new webpack.LoaderOptionsPlugin({ new webpack.LoaderOptionsPlugin({
@@ -24,7 +31,7 @@ export var commonConfig = {
devtool: 'source-map', devtool: 'source-map',
resolve: { resolve: {
extensions: ['.ts', '.js', '.json'], extensions: ['.ts', '.js', '.json'],
modules: [ root('node_modules') ] modules: [root('node_modules')]
}, },
context: __dirname, context: __dirname,
output: { output: {