Vastly improved design

This commit is contained in:
Dan Brown 2015-07-15 22:55:49 +01:00
parent c4d0f38a67
commit 46217a5880
24 changed files with 439 additions and 140 deletions

View File

@ -107,13 +107,14 @@ class BookController extends Controller
'name' => 'required|string|max:255', 'name' => 'required|string|max:255',
'description' => 'string|max:1000' 'description' => 'string|max:1000'
]); ]);
$book->fill($request->all());
$slug = Str::slug($book->name); $slug = Str::slug($book->name);
while($this->bookRepo->countBySlug($slug) > 0 && $book->slug != $slug) { while($this->bookRepo->countBySlug($slug) > 0 && $book->slug != $slug) {
$slug += '1'; $slug += '1';
} }
$book->slug = $slug; $book->slug = $slug;
$book->save(); $book->save();
return redirect('/books'); return redirect($book->getUrl());
} }
/** /**

View File

@ -85,7 +85,8 @@ class PageController extends Controller
*/ */
public function show($bookSlug, $pageSlug) public function show($bookSlug, $pageSlug)
{ {
$page = $this->pageRepo->getBySlug($pageSlug); $book = $this->bookRepo->getBySlug($bookSlug);
$page = $this->pageRepo->getBySlug($pageSlug, $book->id);
return view('pages/show', ['page' => $page]); return view('pages/show', ['page' => $page]);
} }
@ -98,7 +99,8 @@ class PageController extends Controller
*/ */
public function edit($bookSlug, $pageSlug) public function edit($bookSlug, $pageSlug)
{ {
$page = $this->pageRepo->getBySlug($pageSlug); $book = $this->bookRepo->getBySlug($bookSlug);
$page = $this->pageRepo->getBySlug($pageSlug, $book->id);
return view('pages/edit', ['page' => $page]); return view('pages/edit', ['page' => $page]);
} }
@ -112,8 +114,8 @@ class PageController extends Controller
*/ */
public function update(Request $request, $bookSlug, $pageSlug) public function update(Request $request, $bookSlug, $pageSlug)
{ {
$page = $this->pageRepo->getBySlug($pageSlug);
$book = $this->bookRepo->getBySlug($bookSlug); $book = $this->bookRepo->getBySlug($bookSlug);
$page = $this->pageRepo->getBySlug($pageSlug, $book->id);
$page->fill($request->all()); $page->fill($request->all());
$slug = Str::slug($page->name); $slug = Str::slug($page->name);
while($this->pageRepo->countBySlug($slug, $book->id) > 0 && $slug != $pageSlug) { while($this->pageRepo->countBySlug($slug, $book->id) > 0 && $slug != $pageSlug) {

View File

@ -27,9 +27,9 @@ class PageRepo
return $this->page->all(); return $this->page->all();
} }
public function getBySlug($slug) public function getBySlug($slug, $bookId)
{ {
return $this->page->where('slug', '=', $slug)->first(); return $this->page->where('slug', '=', $slug)->where('book_id', '=', $bookId)->first();
} }
public function newFromInput($input) public function newFromInput($input)

View File

@ -15,6 +15,8 @@
], ],
"dependencies": { "dependencies": {
"dropzone": "~4.0.1", "dropzone": "~4.0.1",
"tinymce-dist": "~4.2.1" "tinymce-dist": "~4.2.1",
"bootstrap": "~3.3.5",
"jquery-sortable": "~0.9.13"
} }
} }

View File

@ -1,4 +1,5 @@
var elixir = require('laravel-elixir'); var elixir = require('laravel-elixir');
require('laravel-elixir-livereload');
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -12,5 +13,5 @@ var elixir = require('laravel-elixir');
*/ */
elixir(function(mix) { elixir(function(mix) {
mix.sass('styles.scss'); mix.sass('styles.scss').livereload();
}); });

View File

@ -1,7 +1,8 @@
{ {
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"gulp": "^3.8.8" "gulp": "^3.8.8",
"laravel-elixir-livereload": "0.0.3"
}, },
"dependencies": { "dependencies": {
"laravel-elixir": "^2.0.0", "laravel-elixir": "^2.0.0",

View File

@ -32,7 +32,7 @@ $button-border-radius: 3px;
@include generate-button-colors(#EEE, $primary); @include generate-button-colors(#EEE, $primary);
} }
.button, button[type="button"], input[type="button"], input[type="submit"] { .button, input[type="button"], input[type="submit"] {
@extend .button-base; @extend .button-base;
&.pos { &.pos {
@include generate-button-colors(#EEE, $positive); @include generate-button-colors(#EEE, $positive);

View File

@ -45,3 +45,40 @@ input[type="text"], input[type="number"], input[type="email"], input[type="searc
.form-group { .form-group {
margin-bottom: $-s; margin-bottom: $-s;
} }
.inline-input-style {
border: 2px dotted #BBB;
display: block;
width: 100%;
padding: $-xs $-s;
}
.title-input label, .description-input label{
margin-top: $-m;
color: #666;
}
.title-input input[type="text"] {
@extend h1;
@extend .inline-input-style;
margin-top: 0;
color: #444;
}
.title-input.page-title {
padding: $-s;
}
.title-input.page-title input[type="text"]{
//border: 2px dotted #BBB;
margin-bottom: 0;
}
.edit-area {
padding: 0 $-s $-s $-s;
}
.description-input textarea {
@extend .inline-input-style;
font-size: $fs-m;
color: #666;
width: 100%;
}

View File

@ -2,7 +2,7 @@
box-sizing: border-box; box-sizing: border-box;
} }
html { html {
background-color: #FFF; background-color: #f8f8f8;
} }
body { body {
font-family: $text; font-family: $text;

View File

@ -23,11 +23,12 @@ h3 {
h4 { h4 {
font-size: 1em; font-size: 1em;
line-height: 1.375em; line-height: 1.375em;
margin-top: 1.375em; margin-top: 0.78571429em;
margin-bottom: 1.375em; margin-bottom: 0.43137255em;
} }
h1, h2, h3, h4 { h1, h2, h3, h4 {
font-weight: 500;
.subheader { .subheader {
display: block; display: block;
font-size: 0.5em; font-size: 0.5em;
@ -48,6 +49,9 @@ a {
text-decoration: underline; text-decoration: underline;
color: darken($primary, 20%); color: darken($primary, 20%);
} }
i {
padding-right: $-s;
}
} }
/* /*
@ -176,3 +180,13 @@ p.secondary, p .secondary, span.secondary, .text-secondary {
.text-right { .text-right {
text-align: right; text-align: right;
} }
/**
* Grouping
*/
.header-group {
margin: $-m 0;
h1, h2, h3, h4, h5, h6 {
margin: 0;
}
}

View File

@ -0,0 +1,18 @@
//.mce-container {
// .mce-menubar, .mce-toolbar-grp {
// position: fixed;
// width: 100%;
// }
// .mce-container-body {
// position: relative;
// overflow: hidden;
// display: block;
// }
//}
.mce-tinymce.mce-container.fullscreen {
position: fixed;
top: 0;
height: 100%;
}

View File

@ -7,6 +7,20 @@
@import "blocks"; @import "blocks";
@import "buttons"; @import "buttons";
@import "forms"; @import "forms";
@import "tinymce";
header {
background-color: #f8f8f8;
position: fixed;
display: block;
width: 100%;
z-index: -1;
top: 0;
}
body {
margin-top: 64px;
}
header hr { header hr {
margin-top: 0; margin-top: 0;
@ -27,6 +41,81 @@ header .menu {
font-size: 1.4em; font-size: 1.4em;
} }
.affix {
position: fixed;
}
.page-style {
background-color: #FFF;
padding: $-s $-xxl $-xxl $-xxl;
border-radius: 4px;
box-shadow: 0 0 9px 0 rgba(0, 0, 0, 0.15);
margin-bottom: $-xxl;
max-width: 100%;
}
.page-style.editor {
padding: 0 !important;
}
.page-content {
@extend .page-style;
min-height: 70vh;
&.right {
float: right;
}
&.left {
float: left;
}
}
.page-list {
a {
display: block;
padding: $-s 0;
border-bottom: 2px dotted #CCC;
&:first-child {
border-top: 2px dotted #CCC;
}
}
}
.page-menu {
opacity: 0.6;
transition: opacity ease-in-out 120ms;
&:hover {
opacity: 1;
}
}
.page-nav-list {
$nav-indent: $-s;
li {
//border-left: 1px solid rgba(0, 0, 0, 0.1);
padding-left: $-xs;
}
.nav-H2 {
margin-left: $nav-indent;
font-size: 0.95em;
}
.nav-H3 {
margin-left: $nav-indent*2;
font-size: 0.90em
}
.nav-H4 {
margin-left: $nav-indent*3;
font-size: 0.85em
}
.nav-H5 {
margin-left: $nav-indent*4;
font-size: 0.80em
}
.nav-H6 {
margin-left: $nav-indent*5;
font-size: 0.75em
}
}
.overlay { .overlay {
background-color: rgba(0, 0, 0, 0.2); background-color: rgba(0, 0, 0, 0.2);
position: fixed; position: fixed;

View File

@ -7,18 +7,28 @@
<link href='http://fonts.googleapis.com/css?family=Roboto:400,400italic,500,500italic,700,700italic,300italic,100,300' rel='stylesheet' type='text/css'> <link href='http://fonts.googleapis.com/css?family=Roboto:400,400italic,500,500italic,700,700italic,300italic,100,300' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="/bower/bootstrap/dist/js/bootstrap.js"></script>
<script>
$.fn.smoothScrollTo = function() {
$('body').animate({
scrollTop: this.offset().top - 60 // Adjust to change final scroll position top margin
}, 800); // Adjust to change animations speed (ms)
return this;
};
</script>
@yield('head') @yield('head')
</head> </head>
<body> <body>
<header class="container"> <header>
<div class="container">
<div class="padded-vertical clearfix"> <div class="padded-vertical clearfix">
<div class="logo float left">Oxbow</div> <div class="logo float left">Oxbow</div>
<ul class="menu float right"> <ul class="menu float right">
<li><a href="/books">Books</a></li> <li><a href="/books"><i class="fa fa-book"></i>Books</a></li>
</ul> </ul>
</div> </div>
<hr> </div>
</header> </header>
<section class="container"> <section class="container">

View File

@ -1,10 +1,19 @@
@extends('base') @extends('base')
@section('content') @section('content')
<h2>New Book</h2>
<div class="row">
<div class="col-md-3 page-menu">
<h4>You are creating a new book.</h4>
</div>
<div class="col-md-9 page-content">
<form action="/books" method="POST"> <form action="/books" method="POST">
{{ csrf_field() }}
@include('books/form') @include('books/form')
</form> </form>
</div>
</div>
@stop @stop

View File

@ -1,11 +1,26 @@
@extends('base') @extends('base')
@section('content') @section('content')
<h2>Edit Book</h2>
<div class="row">
<div class="col-md-3 page-menu">
<h4>You are editing the details for the book '{{$book->name}}'.</h4>
<hr>
@include('form/delete-button', ['url' => '/books/' . $book->id . '/destroy', 'text' => 'Delete this book'])
</div>
<div class="col-md-9 page-content">
<form action="/books/{{$book->slug}}" method="POST"> <form action="/books/{{$book->slug}}" method="POST">
{{ csrf_field() }}
<input type="hidden" name="_method" value="PUT"> <input type="hidden" name="_method" value="PUT">
@include('books/form', ['model' => $book]) @include('books/form', ['model' => $book])
</form> </form>
</div>
</div>
@stop @stop

View File

@ -1,8 +1,10 @@
<div class="form-group">
<label for="name">Name</label> {{ csrf_field() }}
<div class="form-group title-input">
<label for="name">Book Name</label>
@include('form/text', ['name' => 'name']) @include('form/text', ['name' => 'name'])
</div> </div>
<div class="form-group"> <div class="form-group description-input">
<label for="description">Description</label> <label for="description">Description</label>
@include('form/textarea', ['name' => 'description']) @include('form/textarea', ['name' => 'description'])
</div> </div>

View File

@ -2,24 +2,31 @@
@section('content') @section('content')
<div class="row"> <div class="row">
<div class="col-md-6">
<div class="col-md-3 page-menu">
<h4>Books</h4>
<a href="/books/create">+ Add new book</a> <a href="/books/create">+ Add new book</a>
</div> </div>
</div>
<div class="col-md-9">
<div class="row"> <div class="row">
@foreach($books as $book) @foreach($books as $book)
<div class="col-md-4 shaded book"> <div class="col-md-6">
<div class="book page-style">
<h3><a href="{{$book->getUrl()}}">{{$book->name}}</a></h3> <h3><a href="{{$book->getUrl()}}">{{$book->name}}</a></h3>
<p>{{$book->description}}</p> <p class="text-muted">{{$book->description}}</p>
<div class="buttons">
<a href="{{$book->getEditUrl()}}" class="button secondary">Edit</a>
@include('form/delete-button', ['url' => '/books/' . $book->id . '/destroy', 'text' => 'Delete'])
</div> </div>
</div> </div>
@endforeach @endforeach
</div> </div>
</div>
</div>
@stop @stop

View File

@ -2,16 +2,42 @@
@section('content') @section('content')
<h2>{{$book->name}}</h2>
<p class="text-muted">{{$book->description}}</p>
<a href="{{$book->getUrl() . '/page/create'}}">+ New Page</a>
<h4>Pages:</h4> <div class="row">
<div class="col-md-3 page-menu">
<h4>Book Actions</h4>
<div class="buttons">
<a href="{{$book->getEditUrl()}}"><i class="fa fa-pencil"></i>Edit Book</a>
</div>
</div>
<div class="page-content col-md-9 float left">
<h1>{{$book->name}}</h1>
<p class="text-muted">{{$book->description}}</p>
<div class="clearfix header-group">
<h4 class="float">Pages</h4>
<a href="{{$book->getUrl() . '/page/create'}}" class="text-pos float right">+ New Page</a>
</div>
<div class="page-list">
@if(count($book->pages) > 0) @if(count($book->pages) > 0)
@foreach($book->pages as $page) @foreach($book->pages as $page)
<a href="{{$page->getUrl()}}">{{$page->name}}</a><br> <a href="{{$page->getUrl()}}">{{$page->name}}</a>
@endforeach @endforeach
@else @else
<p class="text-muted">This book has no pages</p> <p class="text-muted">This book has no pages</p>
@endif @endif
</div>
<p>
</p>
</div>
</div>
@stop @stop

View File

@ -1,4 +1,4 @@
<form action="{{$url}}" method="POST"> <form action="{{$url}}" method="POST" class="inline">
{{ csrf_field() }} {{ csrf_field() }}
<input type="hidden" name="_method" value="DELETE"> <input type="hidden" name="_method" value="DELETE">
<button type="submit" class="button neg">{{ isset($text) ? $text : 'Delete' }}</button> <button type="submit" class="button neg">{{ isset($text) ? $text : 'Delete' }}</button>

View File

@ -1,4 +1,4 @@
<textarea id="{{ $name }}" name="{{ $name }}" <textarea id="{{ $name }}" name="{{ $name }}" rows="5"
@if($errors->has($name)) class="neg" @endif>@if(isset($model) || old($name)){{ old($name) ? old($name) : $model->$name}}@endif</textarea> @if($errors->has($name)) class="neg" @endif>@if(isset($model) || old($name)){{ old($name) ? old($name) : $model->$name}}@endif</textarea>
@if($errors->has($name)) @if($errors->has($name))
<div class="text-neg text-small">{{ $errors->first($name) }}</div> <div class="text-neg text-small">{{ $errors->first($name) }}</div>

View File

@ -1,9 +1,9 @@
@extends('base') @extends('base')
@section('head') @section('head')
<link rel="stylesheet" href="/plugins/css/froala_editor.min.css"> <script src="/bower/tinymce-dist/tinymce.jquery.min.js"></script>
<link rel="stylesheet" href="/plugins/css/froala_style.min.css"> <script src="/bower/dropzone/dist/min/dropzone.min.js"></script>
<script src="/plugins/js/froala_editor.min.js"></script> <script src="/js/image-manager.js"></script>
@stop @stop
@section('content') @section('content')

View File

@ -7,57 +7,12 @@
@stop @stop
@section('content') @section('content')
<div class="row">
<form action="{{$page->getUrl()}}" method="POST"> <form action="{{$page->getUrl()}}" method="POST">
<input type="hidden" name="_method" value="PUT"> <input type="hidden" name="_method" value="PUT">
@include('pages/form', ['model' => $page]) @include('pages/form', ['model' => $page])
</form> </form>
<section class="overlay" style="display:none;">
<div id="image-manager">
<div class="image-manager-left">
<div class="image-manager-header">
<button type="button" class="button neg float right" data-action="close">Close</button>
<div class="image-manager-title">Image Library</div>
</div> </div>
<div class="image-manager-display">
</div>
<form action="/upload/image" class="image-manager-dropzone">
{{ csrf_field() }}
Drag images or click here to upload
</form>
</div>
{{--<div class="sidebar">--}}
{{--</div>--}}
</div>
</section>
<script>
$(function() {
//ImageManager.show('#image-manager');
tinymce.init({
selector: '.edit-area textarea',
content_css: '/css/app.css',
body_class: 'container',
plugins: "autoresize image table textcolor paste link imagetools",
content_style: "body {padding-left: 15px !important; padding-right: 15px !important;}",
file_browser_callback: function(field_name, url, type, win) {
ImageManager.show('#image-manager', function(image) {
win.document.getElementById(field_name).value = image.url;
if ("createEvent" in document) {
var evt = document.createEvent("HTMLEvents");
evt.initEvent("change", false, true);
win.document.getElementById(field_name).dispatchEvent(evt);
} else {
win.document.getElementById(field_name).fireEvent("onchange");
}
});
}
});
});
</script>
@stop @stop

View File

@ -1,13 +1,80 @@
<div class="col-md-3 page-menu">
<h4>You are editing a page</h4>
<button type="submit" class="button pos">Save Page</button>
</div>
<div class="col-md-9 page-style editor">
{{ csrf_field() }} {{ csrf_field() }}
<div class="page-title row"> <div class="title-input page-title">
<div class="col-md-10">
@include('form/text', ['name' => 'name', 'placeholder' => 'Enter Page Title']) @include('form/text', ['name' => 'name', 'placeholder' => 'Enter Page Title'])
</div> </div>
<div class="col-md-2">
<button type="submit" class="button pos">Save</button>
</div>
</div>
<div class="edit-area"> <div class="edit-area">
@include('form/textarea', ['name' => 'html']) @include('form/textarea', ['name' => 'html'])
</div> </div>
</div>
<section class="overlay" style="display:none;">
<div id="image-manager">
<div class="image-manager-left">
<div class="image-manager-header">
<button type="button" class="button neg float right" data-action="close">Close</button>
<div class="image-manager-title">Image Library</div>
</div>
<div class="image-manager-display">
</div>
<form action="/upload/image" class="image-manager-dropzone">
{{ csrf_field() }}
Drag images or click here to upload
</form>
</div>
{{--<div class="sidebar">--}}
{{--</div>--}}
</div>
</section>
<script>
$(function() {
//ImageManager.show('#image-manager');
tinymce.init({
selector: '.edit-area textarea',
content_css: '/css/app.css',
body_class: 'container',
relative_urls: false,
height: 600,
plugins: "image table textcolor paste link imagetools",
toolbar: "undo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | table | fontsizeselect full",
content_style: "body {padding-left: 15px !important; padding-right: 15px !important; margin:0!important}",
file_browser_callback: function(field_name, url, type, win) {
ImageManager.show('#image-manager', function(image) {
win.document.getElementById(field_name).value = image.url;
if ("createEvent" in document) {
var evt = document.createEvent("HTMLEvents");
evt.initEvent("change", false, true);
win.document.getElementById(field_name).dispatchEvent(evt);
} else {
win.document.getElementById(field_name).fireEvent("onchange");
}
});
},
setup: function(editor) {
editor.addButton('full', {
text: 'Expand',
icon: false,
onclick: function() {
var container = $(editor.getContainer()).toggleClass('fullscreen');
}
});
}
});
});
</script>

View File

@ -2,11 +2,54 @@
@section('content') @section('content')
<a href="{{$page->getUrl() . '/edit'}}" class="button primary float right">Edit Page</a> <div class="row">
<div class="page-menu col-md-3">
<div class="page-nav">
<h4>Navigation</h4>
<ul class="page-nav-list"></ul>
</div>
<div class="page-actions">
<h4>Actions</h4>
<a href="{{$page->getUrl() . '/edit'}}" class="muted"><i class="fa fa-pencil"></i>Edit this page</a>
</div>
</div>
<div class="page-content right col-md-9">
<h1>{{$page->name}}</h1> <h1>{{$page->name}}</h1>
<div class="page-content">
{!! $page->html !!} {!! $page->html !!}
</div> </div>
</div>
<script>
$(document).ready(function() {
// Set up fixed side menu
$('.page-menu').affix({
offset: {
top: 10,
bottom: function () {
return (this.bottom = $('.footer').outerHeight(true))
}
}
});
// Set up document navigation
var pageNav = $('.page-nav-list');
var pageContent = $('.page-content');
var headers = pageContent.find('h1, h2, h3, h4, h5, h6');
var sortedHeaders = [];
headers.each(function() {
var header = $(this);
var tag = header.prop('tagName');
var listElem = $('<li></li>').addClass('nav-'+tag);
var link = $('<a></a>').text(header.text().trim()).attr('href', '#');
listElem.append(link);
pageNav.append(listElem);
link.click(function(e) {
e.preventDefault();
header.smoothScrollTo();
})
});
});
</script>
@stop @stop