]> BookStack Code Mirror - bookstack/commitdiff
Merge branch 'master' into 2019-design
authorDan Brown <redacted>
Sun, 13 Jan 2019 14:10:27 +0000 (14:10 +0000)
committerDan Brown <redacted>
Sun, 13 Jan 2019 14:10:27 +0000 (14:10 +0000)
57 files changed:
app/Entities/Entity.php
app/Entities/Page.php
app/Entities/Repos/EntityRepo.php
app/Http/Controllers/BookController.php
app/Http/Controllers/Controller.php
app/Http/Controllers/UserController.php
app/Settings/SettingService.php
resources/assets/icons/add.svg
resources/assets/icons/bookshelf.svg
resources/assets/icons/chevron-right.svg [new file with mode: 0644]
resources/assets/icons/sort-down.svg [new file with mode: 0644]
resources/assets/icons/sort-up.svg [new file with mode: 0644]
resources/assets/js/components/header-mobile-toggle.js [new file with mode: 0644]
resources/assets/js/components/index.js
resources/assets/js/components/list-sort-control.js [new file with mode: 0644]
resources/assets/js/components/page-display.js
resources/assets/js/components/tri-layout.js [new file with mode: 0644]
resources/assets/sass/_blocks.scss
resources/assets/sass/_colors.scss [new file with mode: 0644]
resources/assets/sass/_forms.scss
resources/assets/sass/_grid.scss
resources/assets/sass/_header.scss
resources/assets/sass/_html.scss
resources/assets/sass/_lists.scss
resources/assets/sass/_mixins.scss
resources/assets/sass/_pages.scss
resources/assets/sass/_text.scss
resources/assets/sass/_variables.scss
resources/assets/sass/styles.scss
resources/lang/en/common.php
resources/views/base.blade.php
resources/views/books/create.blade.php
resources/views/books/form.blade.php
resources/views/books/grid-item.blade.php
resources/views/books/index.blade.php
resources/views/books/list-item.blade.php
resources/views/books/list.blade.php
resources/views/books/view-toggle.blade.php
resources/views/chapters/child-menu.blade.php [new file with mode: 0644]
resources/views/common/header.blade.php [new file with mode: 0644]
resources/views/common/home.blade.php
resources/views/pages/_breadcrumbs.blade.php
resources/views/pages/list-item.blade.php
resources/views/pages/show.blade.php
resources/views/partials/_header-dropdown.blade.php [deleted file]
resources/views/partials/activity-item.blade.php
resources/views/partials/book-tree.blade.php
resources/views/partials/breadcrumbs.blade.php [new file with mode: 0644]
resources/views/partials/entity-list-item-basic.blade.php [new file with mode: 0644]
resources/views/partials/entity-list-item.blade.php [new file with mode: 0644]
resources/views/partials/entity-list.blade.php
resources/views/partials/sort.blade.php [new file with mode: 0644]
resources/views/public.blade.php
resources/views/sidebar-layout.blade.php
resources/views/simple-layout.blade.php
resources/views/tri-layout.blade.php [new file with mode: 0644]
routes/web.php

index 21d172e708ad48be7a969a5977c3c4897a9cee33..d648f68e45869b1e8c514249a87c513ed88037e7 100644 (file)
@@ -102,6 +102,11 @@ class Entity extends Ownable
         return $this->morphMany(View::class, 'viewable');
     }
 
+    public function viewCountQuery()
+    {
+        return $this->views()->selectRaw('viewable_id, sum(views) as view_count')->groupBy('viewable_id');
+    }
+
     /**
      * Get the Tag models that have been user assigned to this entity.
      * @return \Illuminate\Database\Eloquent\Relations\MorphMany
@@ -218,6 +223,20 @@ class Entity extends Ownable
         return $this->{$this->textField};
     }
 
+    /**
+     * Get an excerpt of this entity's descriptive content to the specified length.
+     * @param int $length
+     * @return mixed
+     */
+    public function getExcerpt(int $length = 100)
+    {
+        $text = $this->getText();
+        if (mb_strlen($text) > $length) {
+            $text = mb_substr($text, 0, $length-3) . '...';
+        }
+        return  trim($text);
+    }
+
     /**
      * Return a generalised, common raw query that can be 'unioned' across entities.
      * @return string
index ea7df16f4021025b048f91dd4e26f50741310e46..1c2cc5cff69c29daa5385d963bc89ab2ad4612ff 100644 (file)
@@ -102,17 +102,6 @@ class Page extends Entity
         return baseUrl('/books/' . urlencode($bookSlug) . $midText . $idComponent);
     }
 
-    /**
-     * Get an excerpt of this page's content to the specified length.
-     * @param int $length
-     * @return mixed
-     */
-    public function getExcerpt($length = 100)
-    {
-        $text = strlen($this->text) > $length ? substr($this->text, 0, $length-3) . '...' : $this->text;
-        return mb_convert_encoding($text, 'UTF-8');
-    }
-
     /**
      * Return a generalised, common raw query that can be 'unioned' across entities.
      * @param bool $withContent
index 576ed70f00bdedb45597da098cf85e5c0c15a902..15340c90693706340234ab7b959a1cfbaab41d1f 100644 (file)
@@ -15,6 +15,7 @@ use BookStack\Exceptions\NotFoundException;
 use BookStack\Exceptions\NotifyException;
 use BookStack\Uploads\AttachmentService;
 use DOMDocument;
+use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Http\Request;
 use Illuminate\Support\Collection;
 
@@ -179,11 +180,27 @@ class EntityRepo
      * Get all entities in a paginated format
      * @param $type
      * @param int $count
+     * @param string $sort
+     * @param string $order
      * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
      */
-    public function getAllPaginated($type, $count = 10)
+    public function getAllPaginated($type, int $count = 10, string $sort = 'name', string $order = 'asc')
     {
-        return $this->entityQuery($type)->orderBy('name', 'asc')->paginate($count);
+        $query = $this->entityQuery($type);
+        $query = $this->addSortToQuery($query, $sort, $order);
+        return $query->paginate($count);
+    }
+
+    protected function addSortToQuery(Builder $query, string $sort = 'name', string $order = 'asc')
+    {
+        $order = ($order === 'asc') ? 'asc' : 'desc';
+        $propertySorts = ['name', 'created_at', 'updated_at'];
+
+        if (in_array($sort, $propertySorts)) {
+            return $query->orderBy($sort, $order);
+        }
+
+        return $query;
     }
 
     /**
index 44368a9c4fb66afb47dc2ed62b70f7a51719707e..b5e2a4a8553257d7228607b33074026d5c97b943 100644 (file)
@@ -36,18 +36,30 @@ class BookController extends Controller
      */
     public function index()
     {
-        $books = $this->entityRepo->getAllPaginated('book', 18);
+        $view = setting()->getUser($this->currentUser, 'books_view_type', config('app.views.books'));
+        $sort = setting()->getUser($this->currentUser, 'books_sort', 'name');
+        $order = setting()->getUser($this->currentUser, 'books_sort_order', 'asc');
+        $sortOptions = [
+            'name' => trans('common.sort_name'),
+            'created_at' => trans('common.sort_created_at'),
+            'updated_at' => trans('common.sort_updated_at'),
+        ];
+
+        $books = $this->entityRepo->getAllPaginated('book', 18, $sort, $order);
         $recents = $this->signedIn ? $this->entityRepo->getRecentlyViewed('book', 4, 0) : false;
         $popular = $this->entityRepo->getPopular('book', 4, 0);
         $new = $this->entityRepo->getRecentlyCreated('book', 4, 0);
-        $booksViewType = setting()->getUser($this->currentUser, 'books_view_type', config('app.views.books', 'list'));
+
         $this->setPageTitle(trans('entities.books'));
         return view('books/index', [
             'books' => $books,
             'recents' => $recents,
             'popular' => $popular,
             'new' => $new,
-            'booksViewType' => $booksViewType
+            'view' => $view,
+            'sort' => $sort,
+            'order' => $order,
+            'sortOptions' => $sortOptions,
         ]);
     }
 
index 80f567eaa80279e8f49599f9703f5ab29e6276c3..fc4f184fcabdff0bf292b6f6c257912ce6d98a2d 100644 (file)
@@ -123,6 +123,20 @@ abstract class Controller extends BaseController
         return true;
     }
 
+    /**
+     * Check if the current user has a permission or bypass if the provided user
+     * id matches the current user.
+     * @param string $permissionName
+     * @param int $userId
+     * @return bool
+     */
+    protected function checkPermissionOrCurrentUser(string $permissionName, int $userId)
+    {
+        return $this->checkPermissionOr($permissionName, function() use ($userId) {
+            return $userId === $this->currentUser->id;
+        });
+    }
+
     /**
      * Send back a json error message.
      * @param string $messageText
index cc5ada3f283d2bd4e24f01cc9235e637b0ffa9a1..933b3d59411667d6eeb47e6c7550cfd65e276b77 100644 (file)
@@ -251,41 +251,83 @@ class UserController extends Controller
      */
     public function switchBookView($id, Request $request)
     {
-        $this->checkPermissionOr('users-manage', function () use ($id) {
-            return $this->currentUser->id == $id;
-        });
+        return $this->switchViewType($id, $request, 'books');
+    }
+
+    /**
+     * Update the user's preferred shelf-list display setting.
+     * @param $id
+     * @param Request $request
+     * @return \Illuminate\Http\RedirectResponse
+     */
+    public function switchShelfView($id, Request $request)
+    {
+        return $this->switchViewType($id, $request, 'bookshelves');
+    }
+
+    /**
+     * For a type of list, switch with stored view type for a user.
+     * @param integer $userId
+     * @param Request $request
+     * @param string $listName
+     * @return \Illuminate\Http\RedirectResponse
+     */
+    protected function switchViewType($userId, Request $request, string $listName)
+    {
+        $this->checkPermissionOrCurrentUser('users-manage', $userId);
 
         $viewType = $request->get('view_type');
         if (!in_array($viewType, ['grid', 'list'])) {
             $viewType = 'list';
         }
 
-        $user = $this->user->findOrFail($id);
-        setting()->putUser($user, 'books_view_type', $viewType);
+        $user = $this->userRepo->getById($id);
+        $key = $listName . '_view_type';
+        setting()->putUser($user, $key, $viewType);
 
-        return redirect()->back(302, [], "/settings/users/$id");
+        return redirect()->back(302, [], "/settings/users/$userId");
     }
 
     /**
-     * Update the user's preferred shelf-list display setting.
+     * Change the stored sort type for the books view.
      * @param $id
      * @param Request $request
      * @return \Illuminate\Http\RedirectResponse
      */
-    public function switchShelfView($id, Request $request)
+    public function changeBooksSort($id, Request $request)
     {
-        $this->checkPermissionOr('users-manage', function () use ($id) {
-            return $this->currentUser->id == $id;
-        });
+        // TODO - Test this endpoint
+        return $this->changeListSort($id, $request, 'books');
+    }
 
-        $viewType = $request->get('view_type');
-        if (!in_array($viewType, ['grid', 'list'])) {
-            $viewType = 'list';
+    /**
+     * Changed the stored preference for a list sort order.
+     * @param int $userId
+     * @param Request $request
+     * @param string $listName
+     * @return \Illuminate\Http\RedirectResponse
+     */
+    protected function changeListSort(int $userId, Request $request, string $listName)
+    {
+        $this->checkPermissionOrCurrentUser('users-manage', $userId);
+
+        $sort = $request->get('sort');
+        if (!in_array($sort, ['name', 'created_at', 'updated_at'])) {
+            $sort = 'name';
         }
 
-        $user = $this->userRepo->getById($id);
-        setting()->putUser($user, 'bookshelves_view_type', $viewType);
+        $order = $request->get('order');
+        if (!in_array($order, ['asc', 'desc'])) {
+            $order = 'asc';
+        }
 
-        return redirect()->back(302, [], "/settings/users/$id");
+        $user = $this->user->findOrFail($userId);
+        $sortKey = $listName . '_sort';
+        $orderKey = $listName . '_sort_order';
+        setting()->putUser($user, $sortKey, $sort);
+        setting()->putUser($user, $orderKey, $order);
+
+        return redirect()->back(302, [], "/settings/users/$userId");
     }
+
 }
index c903bd60a5dc4b811bcf2604965aa3692d020382..42a3810608750c9afbdf4e3352c29350d9511d4d 100644 (file)
@@ -60,6 +60,9 @@ class SettingService
      */
     public function getUser($user, $key, $default = false)
     {
+        if ($user->isDefault()) {
+            return session()->get($key, $default);
+        }
         return $this->get($this->userKey($user->id, $key), $default);
     }
 
@@ -179,6 +182,9 @@ class SettingService
      */
     public function putUser($user, $key, $value)
     {
+        if ($user->isDefault()) {
+            return session()->put($key, $value);
+        }
         return $this->put($this->userKey($user->id, $key), $value);
     }
 
index 75e3753dc4147668c1c5a1d21b14b036ee9dc948..edd367b2d8c17c8e5d65b7e2ae14e729ac949193 100644 (file)
@@ -1,4 +1 @@
-<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
-    <path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
-    <path d="M0 0h24v24H0z" fill="none"/>
-</svg>
\ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 13.3h-5.7V19h-2.6v-5.7H5v-2.6h5.7V5h2.6v5.7H19z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
\ No newline at end of file
index 03da68f9629a049c4e8adde61080eb2fc6e51e00..f1e45eaf99639e344fb6ede008eab2dafb8abf36 100644 (file)
@@ -1,2 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M1.088 2.566h17.42v17.42H1.088z" fill="none"/><path d="M4 20.058h15.892V22H4z"/><path d="M2.902 1.477h17.42v17.42H2.903z" fill="none"/><g><path d="M6.658 3.643V18h-2.38V3.643zM11.326 3.643V18H8.947V3.643zM14.722 3.856l5.613 13.214-2.19.93-5.613-13.214z"/></g></svg>
-
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M1.088 2.566h17.42v17.42H1.088z" fill="none"/><path d="M4 20.058h15.892V22H4z"/><path d="M2.902 1.477h17.42v17.42H2.903z" fill="none"/><g><path d="M6.658 3.643V18h-2.38V3.643zM11.326 3.643V18H8.947V3.643zM14.722 3.856l5.613 13.214-2.19.93-5.613-13.214z"/></g></svg>
\ No newline at end of file
diff --git a/resources/assets/icons/chevron-right.svg b/resources/assets/icons/chevron-right.svg
new file mode 100644 (file)
index 0000000..96540b9
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
\ No newline at end of file
diff --git a/resources/assets/icons/sort-down.svg b/resources/assets/icons/sort-down.svg
new file mode 100644 (file)
index 0000000..61fa6c7
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.034l6.57-6.554h-4.927V2.966h-3.286V14.48H5.43z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
\ No newline at end of file
diff --git a/resources/assets/icons/sort-up.svg b/resources/assets/icons/sort-up.svg
new file mode 100644 (file)
index 0000000..985cc62
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 2.966L5.43 9.52h4.927v11.514h3.286V9.52h4.927z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
\ No newline at end of file
diff --git a/resources/assets/js/components/header-mobile-toggle.js b/resources/assets/js/components/header-mobile-toggle.js
new file mode 100644 (file)
index 0000000..eccd4b8
--- /dev/null
@@ -0,0 +1,31 @@
+
+class HeaderMobileToggle {
+
+    constructor(elem) {
+        this.elem = elem;
+        this.toggleButton = elem.querySelector('.mobile-menu-toggle');
+        this.menu = elem.querySelector('.header-links');
+        this.open = false;
+
+        this.toggleButton.addEventListener('click', this.onToggle.bind(this));
+        this.onWindowClick = this.onWindowClick.bind(this);
+    }
+
+    onToggle(event) {
+        this.open = !this.open;
+        this.menu.classList.toggle('show', this.open);
+        if (this.open) {
+            window.addEventListener('click', this.onWindowClick)
+        } else {
+            window.removeEventListener('click', this.onWindowClick)
+        }
+        event.stopPropagation();
+    }
+
+    onWindowClick(event) {
+        this.onToggle(event);
+    }
+
+}
+
+module.exports = HeaderMobileToggle;
\ No newline at end of file
index bf6fbf619701baf82059d294487f3c3ad3f4dcaf..37e78e383051499a5955de873dc9342322eda9c7 100644 (file)
@@ -18,6 +18,9 @@ import toggleSwitch from "./toggle-switch";
 import pageDisplay from "./page-display";
 import shelfSort from "./shelf-sort";
 import homepageControl from "./homepage-control";
+import headerMobileToggle from "./header-mobile-toggle";
+import listSortControl from "./list-sort-control";
+import triLayout from "./tri-layout";
 
 
 const componentMapping = {
@@ -41,6 +44,9 @@ const componentMapping = {
     'page-display': pageDisplay,
     'shelf-sort': shelfSort,
     'homepage-control': homepageControl,
+    'header-mobile-toggle': headerMobileToggle,
+    'list-sort-control': listSortControl,
+    'tri-layout': triLayout,
 };
 
 window.components = {};
@@ -79,4 +85,4 @@ function initAll(parentElement) {
 
 window.components.init = initAll;
 
-export default initAll;
\ No newline at end of file
+export default initAll;
diff --git a/resources/assets/js/components/list-sort-control.js b/resources/assets/js/components/list-sort-control.js
new file mode 100644 (file)
index 0000000..d463ed0
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * ListSortControl
+ * Manages the logic for the control which provides list sorting options.
+ */
+class ListSortControl {
+
+    constructor(elem) {
+        this.elem = elem;
+
+        this.sortInput = elem.querySelector('[name="sort"]');
+        this.orderInput = elem.querySelector('[name="order"]');
+        this.form = elem.querySelector('form');
+
+        this.elem.addEventListener('click', event => {
+            if (event.target.closest('[data-sort-value]') !== null) {
+                this.sortOptionClick(event);
+            }
+            if (event.target.closest('[data-sort-dir]') !== null) {
+                this.sortDirectionClick(event);
+            }
+        })
+
+    }
+
+    sortOptionClick(event) {
+        const sortOption = event.target.closest('[data-sort-value]');
+        this.sortInput.value = sortOption.getAttribute('data-sort-value');
+        event.preventDefault();
+        this.form.submit();
+    }
+
+    sortDirectionClick(event) {
+        const currentDir = this.orderInput.value;
+        const newDir = (currentDir === 'asc') ? 'desc' : 'asc';
+        this.orderInput.value = newDir;
+        event.preventDefault();
+        this.form.submit();
+    }
+
+}
+
+export default ListSortControl;
\ No newline at end of file
index cbb672222c264cc1ab5114269771989f9f1aa9aa..e7dacea64cc9c82c0e21f8ac565d5e6cf67686c6 100644 (file)
@@ -136,7 +136,7 @@ class PageDisplay {
 
         // Fix the tree as a sidebar
         function stickTree() {
-            $sidebar.width($bookTreeParent.width() + 15);
+            $sidebar.css('width', $bookTreeParent.width());
             $sidebar.addClass("fixed");
             isFixed = true;
         }
diff --git a/resources/assets/js/components/tri-layout.js b/resources/assets/js/components/tri-layout.js
new file mode 100644 (file)
index 0000000..d18d37d
--- /dev/null
@@ -0,0 +1,83 @@
+
+class TriLayout {
+
+    constructor(elem) {
+        this.elem = elem;
+        this.middle = elem.querySelector('.tri-layout-middle');
+        this.right = elem.querySelector('.tri-layout-right');
+        this.left = elem.querySelector('.tri-layout-left');
+
+        this.lastLayoutType = 'none';
+        this.onDestroy = null;
+
+
+        this.updateLayout();
+        window.addEventListener('resize', event => {
+            this.updateLayout();
+        });
+    }
+
+    updateLayout() {
+        let newLayout = 'tablet';
+        if (window.innerWidth <= 1000) newLayout =  'mobile';
+        if (window.innerWidth >= 1400) newLayout =  'desktop';
+        if (newLayout === this.lastLayoutType) return;
+
+        if (this.onDestroy) {
+            this.onDestroy();
+            this.onDestroy = null;
+        }
+
+        if (newLayout === 'desktop') {
+            this.setupDesktop();
+        } else if (newLayout === 'mobile') {
+            this.setupMobile();
+        }
+
+        this.lastLayoutType = newLayout;
+    }
+
+    setupMobile() {
+        const mobileSidebarClickBound = this.mobileSidebarClick.bind(this);
+        const mobileContentClickBound = this.mobileContentClick.bind(this);
+        this.left.addEventListener('click', mobileSidebarClickBound);
+        this.right.addEventListener('click', mobileSidebarClickBound);
+        this.middle.addEventListener('click', mobileContentClickBound);
+
+        this.onDestroy = () => {
+            this.left.removeEventListener('click', mobileSidebarClickBound);
+            this.right.removeEventListener('click', mobileSidebarClickBound);
+            this.middle.removeEventListener('click', mobileContentClickBound);
+        }
+    }
+
+    setupDesktop() {
+        //
+    }
+
+    /**
+     * Slide the main content back into view if clicked and
+     * currently slid out of view.
+     * @param event
+     */
+    mobileContentClick(event) {
+        this.elem.classList.remove('mobile-open');
+    }
+
+    /**
+     * On sidebar click, Show the content by sliding the main content out.
+     * @param event
+     */
+    mobileSidebarClick(event) {
+        if (this.elem.classList.contains('mobile-open')) {
+            this.elem.classList.remove('mobile-open');
+        } else {
+            event.preventDefault();
+            event.stopPropagation();
+            this.elem.classList.add('mobile-open');
+        }
+    }
+
+}
+
+export default TriLayout;
\ No newline at end of file
index c0f02ed7d78f34b48908f02d2ca7be84476e12c6..b000c7ed6f240f8f4958ca4647e094bd55c9af16 100644 (file)
@@ -1,56 +1,12 @@
 
 /*
-* This file container all block styling including background shading,
-* margins, paddings & borders.
+* This file container all block styling including margins, paddings & borders.
 */
 
 
-/*
-* Background Shading
-*/
-.shaded {
-  background-color: #f1f1f1;
-  &.pos {
-    background-color: lighten($positive, 40%);
-  }
-  &.neg {
-    background-color: lighten($negative, 20%);
-  }
-  &.primary {
-    background-color: lighten($primary, 40%);
-  }
-  &.secondary {
-    background-color: lighten($secondary, 30%);
-  }
-}
-
-/*
-* Bordering
-*/
-.bordered {
-  border: 1px solid #BBB;
-  &.pos {
-    border-color: $positive;
-  }
-  &.neg {
-    border-color: $negative;
-  }
-  &.primary {
-    border-color: $primary;
-  }
-  &.secondary {
-    border-color: $secondary;
-  }
-  &.thick {
-    border-width: 2px;
-  }
-}
-.rounded {
-  border-radius: 3px;
-}
-
 /*
 * Padding
+* TODO - Remove these older styles
 */
 .nopadding {
   padding: 0;
@@ -94,6 +50,7 @@
 
 /*
 * Margins
+* TODO - Remove these older styles
 */
 .margins {
   margin: $-l;
   }
 }
 
+@mixin spacing($prop, $propLetter) {
+  @each $sizeLetter, $size in $spacing {
+    .#{$propLetter}-#{$sizeLetter} {
+      #{$prop}: $size;
+    }
+    .#{$propLetter}x-#{$sizeLetter} {
+      #{$prop}-left: $size;
+      #{$prop}-right: $size;
+    }
+    .#{$propLetter}y-#{$sizeLetter} {
+      #{$prop}-top: $size;
+      #{$prop}-bottom: $size;
+    }
+    .#{$propLetter}t-#{$sizeLetter} {
+      #{$prop}-top: $size;
+    }
+    .#{$propLetter}r-#{$sizeLetter} {
+      #{$prop}-right: $size;
+    }
+    .#{$propLetter}b-#{$sizeLetter} {
+      #{$prop}-bottom: $size;
+    }
+    .#{$propLetter}l-#{$sizeLetter} {
+      #{$prop}-left: $size;
+    }
+  }
+
+}
+
+@include spacing('margin', 'm')
+@include spacing('padding', 'p')
+
 
 /**
  * Callouts
 }
 
 .card {
-  margin: $-m;
   background-color: #FFF;
-  box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.2);
+  box-shadow: $bs-card;
+  border-radius: 3px;
+  padding-bottom: $-xs;
   h3 {
     padding: $-m;
-    border-bottom: 1px solid #E8E8E8;
+    padding-bottom: $-xs;
     margin: 0;
-    font-size: $fs-s;
-    color: #888;
-    fill: #888;
+    font-size: $fs-m;
+    color: #222;
+    fill: #222;
     font-weight: 400;
-    text-transform: uppercase;
   }
   h3 a {
     line-height: 1;
 }
 
 .sidebar .card {
-  h3, .body, .empty-text {
+  .body, .empty-text {
     padding: $-s $-m;
   }
+  h3 + .body {
+    padding-top: $-xs;
+  }
 }
 
 .card.drag-card {
diff --git a/resources/assets/sass/_colors.scss b/resources/assets/sass/_colors.scss
new file mode 100644 (file)
index 0000000..d89b264
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Text colors
+ */
+p.pos, p .pos, span.pos, .text-pos {
+  color: $positive;
+  fill: $positive;
+  &:hover {
+    color: $positive;
+    fill: $positive;
+  }
+}
+
+p.neg, p .neg, span.neg, .text-neg {
+  color: $negative;
+  fill: $negative;
+  &:hover {
+    color: $negative;
+    fill: $negative;
+  }
+}
+
+p.muted, p .muted, span.muted, .text-muted {
+  color: lighten($text-dark, 26%);
+  fill: lighten($text-dark, 26%);
+  &.small, .small {
+    color: lighten($text-dark, 32%);
+    fill: lighten($text-dark, 32%);
+  }
+}
+
+p.primary, p .primary, span.primary, .text-primary {
+  color: $primary;
+  fill: $primary;
+  &:hover {
+    color: $primary;
+    fill: $primary;
+  }
+}
+
+p.secondary, p .secondary, span.secondary, .text-secondary {
+  color: $secondary;
+  fill: $secondary;
+  &:hover {
+    color: $secondary;
+    fill: $secondary;
+  }
+}
+
+.text-bookshelf {
+  color: $color-bookshelf;
+  fill: $color-bookshelf;
+  &:hover {
+    color: $color-bookshelf;
+    fill: $color-bookshelf;
+  }
+}
+.text-book {
+  color: $color-book;
+  fill: $color-book;
+  &:hover {
+    color: $color-book;
+    fill: $color-book;
+  }
+}
+.text-page {
+  color: $color-page;
+  fill: $color-page;
+  &:hover {
+    color: $color-page;
+    fill: $color-page;
+  }
+  &.draft {
+    color: $color-page-draft;
+    fill: $color-page-draft;
+  }
+  &.draft:hover {
+    color: $color-page-draft;
+    fill: $color-page-draft;
+  }
+}
+.text-chapter {
+  color: $color-chapter;
+  fill: $color-chapter;
+  &:hover {
+    color: $color-chapter;
+    fill: $color-chapter;
+  }
+}
+.faded .text-book:hover {
+  color: $color-book !important;
+  fill: $color-book !important;
+}
+.faded .text-chapter:hover {
+  color: $color-chapter !important;
+  fill: $color-chapter !important;
+}
+.faded .text-page:hover {
+  color: $color-page !important;
+  fill: $color-page !important;
+}
+
+.bg-book {
+  background-color: $color-book;
+}
\ No newline at end of file
index 6b3ed381549e3c1c8f3dedfb99b2a1ba06b09566..d955b7efc13f9923752dc4cec3a3661f86931341 100644 (file)
@@ -206,11 +206,9 @@ input:checked + .toggle-switch {
 }
 
 .form-group[collapsible] {
-  margin-left: -$-m;
-  margin-right: -$-m;
   padding: 0 $-m;
-  border-top: 1px solid #DDD;
-  border-bottom: 1px solid #DDD;
+  border: 1px solid #DDD;
+  border-radius: 4px;
   .collapse-title {
     margin-left: -$-m;
     margin-right: -$-m;
@@ -238,9 +236,6 @@ input:checked + .toggle-switch {
   &.open .collapse-title label:before {
     transform: rotate(90deg);
   }
-  &+.form-group[collapsible] {
-    margin-top: -($-s + 1);
-  }
 }
 
 .inline-input-style {
index 0e1f85ce62cd1e801d006814b4e47ddaabab49d5..f326b2acf76f7eb7d700eba9c84938aaac1f0876 100644 (file)
@@ -50,110 +50,117 @@ body.flexbox {
   flex: 1;
 }
 
-.flex.sidebar {
-  flex: 1;
-  background-color: #F2F2F2;
-  max-width: 360px;
-  min-height: 90vh;
-  section {
-    margin: $-m;
-  }
-}
-.flex.sidebar + .flex.content {
-  flex: 3;
-  background-color: #FFFFFF;
-  padding: 0 $-l;
-  border-left: 1px solid #DDD;
-  max-width: 100%;
+.content-wrap.card {
+  padding: $-l $-xxl;
+  margin-left: auto;
+  margin-right: auto;
+  margin-bottom: $-xl;
+  overflow: auto;
 }
-.flex.sidebar .sidebar-toggle {
-  display: none;
-}
-
-@include smaller-than($xl) {
-  body.sidebar-layout {
-    padding-left: 30px;
-  }
-  .flex.sidebar {
-    position: fixed;
-    top: 0;
-    left: 0;
-    bottom: 0;
-    z-index: 100;
-    padding-right: 30px;
-    width: 360px;
-    box-shadow: none;
-    transform: translate3d(-330px, 0, 0);
-    transition: transform ease-in-out 120ms;
-    display: flex;
-    flex-direction: column;
+
+.tri-layout-container {
+  display: grid;
+  grid-template-columns: 1fr minmax(auto, 940px) 1fr;
+  grid-template-areas: "a b c";
+  .tri-layout-right-contents, .tri-layout-left-contents {
+    padding-right: $-xl;
+    padding-left: $-xl;
+  }
+  .tri-layout-right {
+    grid-area: c;
+  }
+  .tri-layout-left {
+    grid-area: a;
+  }
+  .tri-layout-middle {
+    grid-area: b;
+  }
+}
+@include smaller-than($xxl) {
+  .tri-layout-container {
+    grid-template-areas:  "c b b"
+                          "a b b";
+    grid-template-columns: 1fr 3fr;
+    grid-template-rows: max-content min-content;
+    padding-right: $-l;
+    .content-wrap.card {
+      padding: $-l $-l;
+    }
   }
-  .flex.sidebar.open {
-    box-shadow: 1px 2px 2px 1px rgba(0,0,0,.10);
-    transform: translate3d(0, 0, 0);
-    .sidebar-toggle i {
-      transform: rotate(180deg);
+}
+@include larger-than($xxl) {
+  .tri-layout-left-contents, .tri-layout-right-contents {
+    position: sticky;
+    top: $-m;
+    max-height: 100vh;
+    min-height: 50vh;
+    overflow-y: scroll;
+    overflow-x: visible;
+    scrollbar-width: none;
+    -ms-overflow-style: none;
+    &::-webkit-scrollbar {
+      display: none;
     }
   }
-  .flex.sidebar .sidebar-toggle {
-    display: block;
-    position: absolute;
-    opacity: 0.9;
-    right: 0;
-    top: 0;
-    bottom: 0;
-    width: 30px;
-    fill: #666;
-    font-size: 20px;
-    vertical-align: middle;
-    text-align: center;
-    border: 1px solid #DDD;
-    border-top: 1px solid #BBB;
-    padding-top: $-m;
-    cursor: pointer;
-    svg {
-      opacity: 0.5;
-      transition: all ease-in-out 120ms;
-      margin: 0;
+}
+
+@include smaller-than($l) {
+  .tri-layout-container {
+    grid-template-areas:  none;
+    grid-template-columns: 10% 90%;
+    grid-column-gap: 0;
+    .tri-layout-left-contents, .tri-layout-right-contents {
+      padding-left: $-m;
+      padding-right: $-m;
     }
-    &:hover i {
-      opacity: 1;
+    .tri-layout-right, .tri-layout-left {
+      opacity: 0.6;
+      z-index: 0;
+    }
+    .tri-layout-left > *, .tri-layout-right > * {
+      pointer-events: none;
+    }
+    .tri-layout-right, .tri-layout-left, .tri-layout-middle {
+      grid-area: none;
+      grid-column: 1/3;
+      grid-row: 1;
+    }
+    .tri-layout-middle {
+      grid-row: 1/3;
+      grid-column: 2/3;
+      z-index: 1;
+      transition: transform ease-in-out 240ms;
+    }
+    .tri-layout-left {
+      grid-row: 2;
+    }
+    &.mobile-open {
+      overflow: hidden;
+      .tri-layout-middle {
+        transform: translateX(90%);
+      }
+      .tri-layout-right  > *, .tri-layout-left > * {
+        pointer-events: auto;
+      }
     }
-  }
-  .sidebar .scroll-body {
-    flex: 1;
-    overflow-y: scroll;
-  }
-  #sidebar .scroll-body.fixed {
-    width: auto !important;
   }
 }
 
-@include larger-than($xl) {
-  #sidebar .scroll-body.fixed {
-    z-index: 5;
-    position: fixed;
-    top: 0;
-    padding-right: $-m;
-    width: 30%;
-    left: 0;
-    height: 100%;
-    overflow-y: auto;
-    -ms-overflow-style: none;
-    //background-color: $primary-faded;
-    border-left: 1px solid #DDD;
-    &::-webkit-scrollbar { width: 0 !important }
+.tri-layout-left, .tri-layout-right {
+  opacity: 0.7;
+  transition: opacity ease-in-out 120ms;
+  &:hover {
+    opacity: 1;
   }
 }
 
-
 /** Rules for all columns */
 div[class^="col-"] img {
   max-width: 100%;
 }
 
 .container {
-  max-width: $max-width;
+  max-width: $xxl;
   margin-left: auto;
   margin-right: auto;
   padding-left: $-m;
@@ -182,6 +189,9 @@ div[class^="col-"] img {
   display: grid;
   grid-column-gap: $-l;
   grid-row-gap: $-l;
+  &.half {
+    grid-template-columns: 1fr 1fr;
+  }
   &.third {
     grid-template-columns: 1fr 1fr 1fr;
   }
@@ -191,18 +201,22 @@ div[class^="col-"] img {
   display: flex;
   flex-direction: column;
   border: 1px solid #ddd;
+  margin-bottom: $-l;
+  border-radius: 4px;
+  overflow: hidden;
   min-width: 100px;
+  color: $text-dark;
+  transition: border-color ease-in-out 120ms, box-shadow ease-in-out 120ms;
+  &:hover {
+    color: $text-dark;
+    text-decoration: none;
+    box-shadow: $bs-card;
+  }
   h2 {
     width: 100%;
     font-size: 1.5em;
     margin: 0 0 10px;
   }
-  h2 a {
-    display: block;
-    width: 100%;
-    line-height: 1.2;
-    text-decoration: none;
-  }
   p {
     font-size: .85em;
     margin: 0;
@@ -221,11 +235,6 @@ div[class^="col-"] img {
   }
 }
 
-.book-grid-item .grid-card-content h2 a  {
-    color: $color-book;
-    fill: $color-book;
-}
-
 .bookshelf-grid-item .grid-card-content h2 a  {
   color: $color-bookshelf;
   fill: $color-bookshelf;
@@ -270,6 +279,8 @@ div[class^="col-"] img {
   display: inline-block;
 }
 
+
+// TODO - Remove old BS grid system
 .col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {
   position: relative;
   min-height: 1px;
@@ -908,18 +919,12 @@ div[class^="col-"] img {
 }
 .clearfix:before,
 .clearfix:after,
-.container:before,
-.container:after,
-.container-fluid:before,
-.container-fluid:after,
 .row:before,
 .row:after {
   content: " ";
   display: table;
 }
 .clearfix:after,
-.container:after,
-.container-fluid:after,
 .row:after {
   clear: both;
 }
@@ -928,3 +933,67 @@ div[class^="col-"] img {
   margin-left: auto;
   margin-right: auto;
 }
+
+
+
+
+
+
+.grid {
+  display: grid;
+  grid-column-gap: $-m;
+  grid-row-gap: 0;
+  &.contained {
+    max-width: $xxl;
+    padding-left: $-m;
+    padding-right: $-m;
+    margin-left: auto;
+    margin-right: auto;
+  }
+  &.v-center {
+    align-items: center;
+  }
+}
+
+@each $sizeLetter, $size in $spacing {
+  .grid.contained.space-#{$sizeLetter} {
+    padding-left: $size;
+    padding-right: $size;
+    grid-column-gap: $size;
+  }
+}
+
+@mixin grid-layout($name, $times) {
+  .grid.#{$name} {
+    grid-template-columns: repeat(#{$times}, 1fr);
+  }
+}
+
+@include grid-layout('thirds', 3)
+@include grid-layout('halves', 2)
+
+@each $sizeLetter, $size in $screen-sizes {
+  @include smaller-than($size) {
+    .grid.break-#{$sizeLetter} {
+      grid-template-columns: 1fr;
+    }
+  }
+}
+
+
+/**
+ * Visibility
+ */
+
+@each $sizeLetter, $size in $screen-sizes {
+  @include smaller-than($size) {
+    .hide-under-#{$sizeLetter} {
+      display: none !important;
+    }
+  }
+  @include larger-than($size) {
+    .hide-over-#{$sizeLetter} {
+      display: none !important;
+    }
+  }
+}
\ No newline at end of file
index b66bab394d54ec732e12ec36b75620ffb0fe9ef8..3f4841a7fbec462ffb27652abc9d37fd07ac8ede 100644 (file)
@@ -2,21 +2,24 @@
  * Includes the main navigation header and the faded toolbar.
  */
 
+header .grid {
+  grid-template-columns: auto min-content auto;
+}
+
 header {
+  position: relative;
   display: block;
   z-index: 2;
   top: 0;
   background-color: $primary-dark;
   color: #fff;
   fill: #fff;
-  .padded {
-    padding: $-m;
-  }
   border-bottom: 1px solid #DDD;
+  box-shadow: $bs-card;
+  padding: $-xxs 0;
   .links {
     display: inline-block;
     vertical-align: top;
-    margin-left: $-m;
   }
   .links a {
     display: inline-block;
@@ -28,15 +31,6 @@ header {
     padding-left: $-m;
     padding-right: 0;
   }
-  @include smaller-than($screen-md) {
-    .links a {
-      padding-left: $-s;
-      padding-right: $-s;
-    }
-    .dropdown-container {
-      padding-left: $-s;
-    }
-  }
   .avatar, .user-name {
     display: inline-block;
   }
@@ -63,27 +57,17 @@ header {
       padding-top: 4px;
       font-size: 18px;
     }
-    @include smaller-than($screen-md) {
+    @include between($l, $xl) {
       padding-left: $-xs;
       .name {
         display: none;
       }
     }
   }
-  @include smaller-than($screen-sm) {
-    text-align: center;
-    .float.right {
-      float: none;
-    }
-    .links a {
-      padding: $-s;
-    }
-    .user-name {
-      padding-top: $-s;
-    }
-  }
 }
 
+
+
 .header-search {
   display: inline-block;
 }
@@ -92,13 +76,16 @@ header .search-box {
   margin-top: 10px;
   input {
     background-color: rgba(0, 0, 0, 0.2);
-    border: 1px solid rgba(255, 255, 255, 0.3);
+    border: 1px solid rgba(255, 255, 255, 0.2);
+    border-radius: 40px;
     color: #EEE;
     z-index: 2;
+    padding-left: 40px;
   }
   button {
     fill: #EEE;
     z-index: 1;
+    left: 16px;
     svg {
       margin-right: 0;
     }
@@ -115,20 +102,11 @@ header .search-box {
   :-moz-placeholder { /* Firefox 18- */
     color: #DDD;
   }
-  @include smaller-than($screen-lg) {
-    max-width: 250px;
-  }
-  @include smaller-than($l) {
+  @include between($l, $xl) {
     max-width: 200px;
   }
 }
 
-@include smaller-than($s) {
-  .header-search {
-    display: block;
-  }
-}
-
 .logo {
   display: inline-block;
   &:hover {
@@ -151,10 +129,87 @@ header .search-box {
   height: 43px;
 }
 
-.breadcrumbs span.sep {
-  color: #aaa;
+.mobile-menu-toggle {
+  color: #FFF;
+  fill: #FFF;
+  font-size: 2em;
+  border: 2px solid rgba(255, 255, 255, 0.8);
+  border-radius: 4px;
   padding: 0 $-xs;
+  position: absolute;
+  right: $-m;
+  top: 8px;
+  line-height: 1;
+  cursor: pointer;
+  user-select: none;
+  svg {
+    margin: 0;
+  }
 }
+
+@include smaller-than($l) {
+  header .header-links {
+    display: none;
+    background-color: #FFF;
+    z-index: 10;
+    right: $-m;
+    border-radius: 4px;
+    overflow: hidden;
+    position: absolute;
+    box-shadow: $bs-hover;
+    margin-top: -$-xs;
+    &.show {
+      display: block;
+    }
+  }
+  header .links a, header .dropdown-container ul li a {
+    text-align: left;
+    display: block;
+    padding: $-s $-m;
+    color: $text-dark;
+    fill: $text-dark;
+    svg {
+      margin-right: $-s;
+    }
+    &:hover {
+      background-color: #EEE;
+      color: #444;
+      fill: #444;
+      text-decoration: none;
+    }
+  }
+  header .dropdown-container {
+    display: block;
+    padding-left: 0;
+  }
+  header .links {
+    display: block;
+  }
+  header .dropdown-container ul {
+    display: block !important;
+    position: relative;
+    background-color: transparent;
+    border: 0;
+    padding: 0;
+    margin: 0;
+    box-shadow: none;
+  }
+}
+
+.breadcrumbs {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+}
+
+.breadcrumbs .separator {
+  fill: #aaa;
+  font-size: 1.6em;
+  line-height: 0.8;
+  margin: 0 $-xs;
+  margin-top: -2px;
+}
+
 .faded {
   a, button, span, span > div {
     color: #666;
@@ -182,13 +237,24 @@ header .search-box {
   background-color: $primary-faded;
 }
 
-.toolbar-container {
-  background-color: #FFF;
+.toolbar {
+  position: relative;
+  > .grid > div {
+    opacity: 0.8;
+    transition: opacity ease-in-out 120ms;
+    &:hover {
+      opacity: 1;
+    }
+  }
+  .text-button {
+    color: #666;
+    fill: #666;
+  }
 }
 
-.breadcrumbs .text-button, .action-buttons .text-button {
+.action-buttons .text-button {
   display: inline-block;
-  padding: $-s;
+  padding: $-xs $-s;
   &:last-child {
     padding-right: 0;
   }
@@ -217,25 +283,12 @@ header .search-box {
 }
 
 @include smaller-than($m) {
-  .breadcrumbs .text-button, .action-buttons .text-button {
+  .action-buttons .text-button {
     padding: $-xs $-xs;
   }
   .action-buttons .dropdown-container:last-child a {
     padding-left: $-xs;
   }
-  .breadcrumbs .text-button {
-    font-size: 0;
-  }
-  .breadcrumbs .text-button svg {
-    font-size: $fs-m;
-  }
-  .breadcrumbs a i {
-    font-size: $fs-m;
-    padding-right: 0;
-  }
-  .breadcrumbs span.sep {
-    padding: 0 $-xxs;
-  }
   .toolbar .col-xs-1:first-child {
     padding-right: 0;
   }
index 65f05a71d5c69370707cca7b45b4510395ff8b05..a89d030a1df5d1bd254f7324b9f377454975f545 100644 (file)
@@ -3,25 +3,20 @@
 }
 
 html {
-  background-color: #FFFFFF;
   height: 100%;
   overflow-y: scroll;
+  background-color: #F2F2F2;
   &.flexbox {
     overflow-y: hidden;
   }
-  &.shaded {
-    background-color: #F2F2F2;
-  }
 }
 
 body {
   font-size: $fs-m;
   line-height: 1.6;
-  color: #616161;
+  color: #444;
   -webkit-font-smoothing: antialiased;
-  &.shaded {
-    background-color: #F2F2F2;
-  }
+  background-color: #F2F2F2;
 }
 
 button {
index 18a7ea9cee0d52b617c8b5784d36f8abbf35b846..d26997c8f59a21ac088ac09f8f75fbc7be3e213f 100644 (file)
 }
 
 // Sidebar list
-.book-tree {
-  transition: ease-in-out 240ms;
-  transition-property: right, border;
-}
-.book-tree h4 {
-  padding: $-m $-s 0 $-s;
-  i {
-    padding-right: $-s;
+.book-tree .book.entity-list-item {
+  font-size: 0.6rem;
+  h4 {
+    font-size: 1rem;
+    margin: 0;
   }
 }
 .book-tree .sidebar-page-list {
   list-style: none;
   margin: $-xs 0 0;
   padding-left: 0;
-  border-left: 5px solid $color-book;
-  li a {
-    display: block;
-    border-bottom: none;
-    padding: $-xs 0 $-xs $-s;
-    &:hover {
-      text-decoration: none;
-    }
-  }
-  li a i {
-    padding-right: $-xs + 2px;
-  }
-  li, a {
-    display: block;
-  }
-  a.bold {
-    color: #EEE !important;
-    fill: #EEE !important;
-  }
+  padding-right: 0;
+  position: relative;
   ul {
     list-style: none;
+    padding-left: 1rem;
+    padding-right: 0;
+  }
+  .entity-icon {
+    font-size: 12px;
+    z-index: 2;
+    background-color: #FFF;
+  }
+  .entity-list-item-name {
+    font-size: 1em;
     margin: 0;
   }
-  .book {
-    color: $color-book !important;
-    fill: $color-book !important;
-    &.selected {
-      background-color: rgba($color-book, 0.29);
-    }
+  .entity-list-item {
+    font-size: 0.8rem;
   }
-  .chapter {
-    color: $color-chapter !important;
-    fill: $color-chapter !important;
-    &.selected {
-      background-color: rgba($color-chapter, 0.12);
-    }
+  .entity-list-item.selected {
+    background-color: #F2F2F2;
   }
-  .page {
-    color: $color-page !important;
-    fill: $color-page !important;
-    border-bottom: none;
-    &.selected {
-      background-color: rgba($color-page, 0.1);
-    }
+  .chapter-child-menu {
+    font-size: 12px;
+    padding-left: 2rem;
+    margin-top: -.2rem;
   }
   [chapter-toggle] {
-    padding-left: $-s;
+    padding-left: 1.5rem;
+    padding-bottom: .2rem;
   }
-  .list-item-chapter {
-    border-left: 5px solid $color-chapter;
-    margin: 10px 10px;
+  &:after, .sub-menu:after {
+    content: '';
     display: block;
+    position: absolute;
+    left: 1.6rem;
+    top: 1rem;
+    bottom: 1rem;
+    border-left: 2px solid #DDD;
+    opacity: 0.6;
+    z-index: 1;
   }
-  .list-item-page {
-    border-bottom: none;
-    border-left: 5px solid $color-page;
-    margin: 10px 10px;
-  }
-  .list-item-page.draft {
-    border-left: 5px solid $color-page-draft;
-  }
-  .page.draft .page, .list-item-page.draft a.page {
-    color: $color-page-draft !important;
-    fill: $color-page-draft !important;
-  }
-  .sub-menu {
+}
+
+.chapter-child-menu {
+  ul.sub-menu {
     display: none;
     padding-left: 0;
+    position: relative;
   }
   [chapter-toggle].open + .sub-menu {
     display: block;
 }
 
 .activity-list-item {
-  padding: $-s 0;
+  padding: $-s $-m;
+  display: grid;
+  grid-template-columns: min-content 1fr;
+  grid-column-gap: $-m;
   color: #888;
   fill: #888;
-  border-bottom: 1px solid #EEE;
   font-size: 0.9em;
-  .left {
-    float: left;
-  }
-  .left + .right {
-    margin-left: 30px + $-s;
-  }
-  &:last-of-type {
-    border-bottom: 0;
-  }
 }
 
 ul.pagination {
@@ -280,10 +251,8 @@ ul.pagination {
   margin: 0;
 }
 
-.entity-list {
-  > div {
-    padding: $-m 0;
-  }
+.entity-list, .icon-list {
+  margin: 0 (-$-m);
   h4 {
     margin: 0;
   }
@@ -302,15 +271,49 @@ ul.pagination {
     color: $color-page-draft;
     fill: $color-page-draft;
   }
+  > .dropdown-container {
+    display: block;
+  }
+}
+
+.entity-list-item, .icon-list-item {
+  padding: $-s $-m;
+  display: grid;
+  grid-template-columns: min-content 1fr;
+  grid-column-gap: $-m;
+  align-items: top;
+  > .content {
+    padding-top: 2px;
+  }
+  .icon {
+    font-size: 1rem;
+  }
+  h4 a {
+    color: #666;
+  }
+  &:hover {
+    text-decoration: none;
+    background-color: #DDD;
+    border-radius: 4px;
+  }
 }
 
-.card .entity-list-item, .card .activity-list-item {
-  padding-left: $-m;
-  padding-right: $-m;
+.card a.entity-list-item:hover {
+  background-color: #F2F2F2;
+}
+
+.entity-list-item-image {
+  width: 140px;
+  background-size: cover;
+  background-position: 50% 50%;
+  border-radius: 3px;
+  @include smaller-than($m) {
+    width: 80px;
+  }
 }
 
 .entity-list.compact {
-  font-size: 0.6em;
+  font-size: 0.6 * $fs-m;
   h4, a {
     line-height: 1.2;
   }
@@ -331,6 +334,11 @@ ul.pagination {
   hr {
     margin: 0;
   }
+  @include smaller-than($m) {
+    h4 {
+      font-size: 1.666em;
+    }
+  }
 }
 
 .dropdown-container {
@@ -367,6 +375,9 @@ ul.pagination {
     padding: $-xs $-m;
     line-height: 1.2;
   }
+  li.active a {
+    font-weight: 600;
+  }
   a, button {
     display: block;
     padding: $-xs $-m;
@@ -396,7 +407,6 @@ ul.pagination {
 .featured-image-container {
   position: relative;
   overflow: hidden;
-  background: #F2F2F2;
   a {
     display: block;
   }
@@ -407,9 +417,19 @@ ul.pagination {
     height: auto;
     transition: all .5s ease-in-out;
   }
-  img:hover {
-    transform: scale(1.15);
-    opacity: .5;
-  }
+}
+.grid-card:hover .featured-image-container img {
+  transform: scale(1.15);
+  opacity: .5;
+}
+
+.action-link-list {
+  //padding: $-s 0;
+}
+.action-link {
+  background: transparent;
+  border: none;
+  color: currentColor;
+  padding: $-m 0;
 }
 
index 3d3101ca7130d152d1237976892c08a0d1b2c85b..13c81ae9eeb6da22f9496370c5f797f29545be8a 100644 (file)
@@ -5,6 +5,9 @@
 @mixin larger-than($size) {
     @media screen and (min-width: $size) { @content; }
 }
+@mixin between($min, $max) {
+  @media screen and (min-width: $min) and (max-width: $max) { @content; }
+}
 @mixin clearfix() {
   &:after {
     display: block;
index 21fdf90dea234ad25c95365f6a45e89a20045ca0..8cc7c830f453c84b1c6e0e9a2ca3e9528b5df165 100755 (executable)
   width: 100%;
   max-width: 840px;
   margin: 0 auto;
-  margin-top: $-xxl;
   overflow-wrap: break-word;
-  &.flex {
-    margin-top: $-m;
-  }
   .align-left {
     text-align: left;
   }
   }
 }
 
-.comments-container {
-  width: 100%;
-  border-top: 1px solid #DDD;
-  margin-top: $-xl;
-  margin-bottom: $-m;
-  h5 {
-    color: #888;
-    font-weight: normal;
-    margin-top: 0.5em;
-  }
+.comments-container h5 {
+  color: #888;
+  font-weight: normal;
+  margin-top: 0.5em;
 }
 
 .comment-editor .CodeMirror, .comment-editor .CodeMirror-scroll {
   .mce-open {
     display: none;
   }
+}
+
+.entity-list-item > .icon, .icon-list-item > .icon {
+  font-size: 0.8rem;
+  width: 1.88em;
+  height: 1.88em;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  text-align: center;
+  border-radius: 1em;
+  position: relative;
+  overflow: hidden;
+  svg {
+    margin: 0;
+    bottom: 0;
+  }
+  &:after {
+    content: '';
+    position: absolute;
+    background-color: currentColor;
+    opacity: 0.2;
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 100%;
+  }
+}
+
+.entity-chip {
+  display: inline-block;
+  align-items: center;
+  justify-content: center;
+  text-align: center;
+  font-size: 0.9em;
+  border-radius: 2em;
+  position: relative;
+  overflow: hidden;
+  padding: $-xs $-m;
+  color: #666;
+  fill: currentColor;
+  &:after {
+    content: '';
+    position: absolute;
+    background-color: currentColor;
+    opacity: 0.2;
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 100%;
+  }
 }
\ No newline at end of file
index 0063c4672cca2c9d257c28e57c449b483c74f499..43b3e58989159bccc764c7a8c7e1cde15f4c852c 100644 (file)
@@ -42,7 +42,7 @@ h1, h2, h3, h4, h5, h6 {
   font-weight: 400;
   position: relative;
   display: block;
-  color: #555;
+  color: #222;
   .subheader {
     font-size: 0.5em;
     line-height: 1em;
@@ -79,6 +79,10 @@ h5, h6 {
   }
 }
 
+.list-heading {
+  font-size: 2rem;
+}
+
 /*
  * Link styling
  */
@@ -233,106 +237,6 @@ pre code {
   display: block;
   line-height: 1.6;
 }
-/*
- * Text colors
- */
-p.pos, p .pos, span.pos, .text-pos {
-  color: $positive;
-  fill: $positive;
-  &:hover {
-    color: $positive;
-    fill: $positive;
-  }
-}
-
-p.neg, p .neg, span.neg, .text-neg {
-  color: $negative;
-  fill: $negative;
-  &:hover {
-    color: $negative;
-    fill: $negative;
-  }
-}
-
-p.muted, p .muted, span.muted, .text-muted {
-       color: lighten($text-dark, 26%);
-       fill: lighten($text-dark, 26%);
-    &.small, .small {
-      color: lighten($text-dark, 32%);
-      fill: lighten($text-dark, 32%);
-    }
-}
-
-p.primary, p .primary, span.primary, .text-primary {
-       color: $primary;
-       fill: $primary;
-  &:hover {
-    color: $primary;
-    fill: $primary;
-  }
-}
-
-p.secondary, p .secondary, span.secondary, .text-secondary {
-       color: $secondary;
-       fill: $secondary;
-  &:hover {
-    color: $secondary;
-    fill: $secondary;
-  }
-}
-
-.text-bookshelf {
-  color: $color-bookshelf;
-  fill: $color-bookshelf;
-  &:hover {
-    color: $color-bookshelf;
-    fill: $color-bookshelf;
-  }
-}
-.text-book {
-  color: $color-book;
-  fill: $color-book;
-  &:hover {
-    color: $color-book;
-    fill: $color-book;
-  }
-}
-.text-page {
-  color: $color-page;
-  fill: $color-page;
-  &:hover {
-    color: $color-page;
-    fill: $color-page;
-  }
-  &.draft {
-    color: $color-page-draft;
-    fill: $color-page-draft;
-  }
-  &.draft:hover {
-    color: $color-page-draft;
-    fill: $color-page-draft;
-  }
-}
-.text-chapter {
-  color: $color-chapter;
-  fill: $color-chapter;
-  &:hover {
-    color: $color-chapter;
-    fill: $color-chapter;
-  }
-}
-.faded .text-book:hover {
-  color: $color-book !important;
-  fill: $color-book !important;
-}
-.faded .text-chapter:hover {
-  color: $color-chapter !important;
-  fill: $color-chapter !important;
-}
-.faded .text-page:hover {
-  color: $color-page !important;
-  fill: $color-page !important;
-}
 
 span.highlight {
   //background-color: rgba($primary, 0.2);
index 006d1b3f08d105b40a54f2dc0f8ee2d11501be67..07820c57e0d6dd4322cd52a610c3fc63e80f7a35 100644 (file)
@@ -1,14 +1,12 @@
 // Variables
 ///////////////
 
-// Sizes
-$max-width: 1400px;
-
 // Screen breakpoints
+$xxl: 1400px;
 $xl: 1100px;
 $ipad-width: 1028px; // Is actually 1024 but we go over to ensure functionality.
 $l: 1000px;
-$m: 800px;
+$m: 880px;
 $s: 600px;
 $xs: 400px;
 $xxs: 360px;
@@ -16,6 +14,8 @@ $screen-lg: 1200px;
 $screen-md: 992px;
 $screen-sm: 768px;
 
+$screen-sizes: (('xxs', $xxs), ('xs', $xs), ('s', $s), ('m', $m), ('l', $l), ('xl', $xl));
+
 // Spacing (Margins+Padding)
 $-xxxl: 64px;
 $-xxl: 48px;
@@ -26,6 +26,8 @@ $-s: 12px;
 $-xs: 6px;
 $-xxs: 3px;
 
+$spacing: (('none', 0), ('xxs', $-xxs), ('xs', $-xs), ('s', $-s), ('m', $-m), ('l', $-l), ('xl', $-xl), ('xxl', $-xxl));
+
 // Fonts
 $text: -apple-system, BlinkMacSystemFont,
 "Segoe UI", "Oxygen", "Ubuntu", "Roboto", "Cantarell",
@@ -33,8 +35,8 @@ $text: -apple-system, BlinkMacSystemFont,
 sans-serif;
 $mono: "Lucida Console", "DejaVu Sans Mono", "Ubunto Mono", Monaco, monospace;
 $heading: $text;
-$fs-m: 15px;
-$fs-s: 14px;
+$fs-m: 14px;
+$fs-s: 12px;
 
 // Colours
 $primary: #0288D1;
@@ -49,7 +51,7 @@ $primary-faded: rgba(21, 101, 192, 0.15);
 // Item Colors
 $color-bookshelf: #af5a5a;
 $color-book: #009688;
-$color-chapter: #ef7c3c;
+$color-chapter: #d7804a;
 $color-page: $primary;
 $color-page-draft: #9A60DA;
 
@@ -60,5 +62,5 @@ $text-light: #EEE;
 // Shadows
 $bs-light: 0 0 4px 1px #CCC;
 $bs-med: 0 1px 3px 1px rgba(76, 76, 76, 0.26);
-$bs-card: 0 1px 3px 1px rgba(76, 76, 76, 0.26), 0 1px 12px 0px rgba(76, 76, 76, 0.2);
+$bs-card: 0 1px 6px -1px rgba(0, 0, 0, 0.1);
 $bs-hover: 0 2px 2px 1px rgba(0,0,0,.13);
\ No newline at end of file
index 49ef77f3938255cf55e45278d19ec2b3a2520c56..7792aee83056ee6b5779786dc845037efddeb7f6 100644 (file)
@@ -3,6 +3,7 @@
 @import "mixins";
 @import "html";
 @import "text";
+@import "colors";
 @import "grid";
 @import "blocks";
 @import "buttons";
@@ -254,3 +255,39 @@ $btt-size: 40px;
   height:100%;
   z-index: 150;
 }
+
+.list-sort-container {
+  display: inline-block;
+  form {
+    display: inline-block;
+  }
+  .list-sort {
+    display: inline-grid;
+    margin-left: $-s;
+    grid-template-columns: 120px 40px;
+    border: 2px solid #DDD;
+    border-radius: 4px;
+  }
+  .list-sort-label {
+    font-weight: bold;
+    display: inline-block;
+    color: #888;
+  }
+  .list-sort-type {
+    text-align: left;
+  }
+  .list-sort-type, .list-sort-dir {
+    padding: $-xs $-s;
+    cursor: pointer;
+  }
+  .list-sort-dir {
+    border-left: 2px solid #DDD;
+    fill: #888;
+    .svg-icon {
+      transition: transform ease-in-out 120ms;
+    }
+    &:hover .svg-icon {
+      transform: rotate(180deg);
+    }
+  }
+}
\ No newline at end of file
index ac2edc621f9256b80eb1c1729b2bf91aafd3bbba..1f71c327238ab3fb3f4bae4489c5a8bda48d4183 100644 (file)
@@ -37,6 +37,11 @@ return [
     'remove' => 'Remove',
     'add' => 'Add',
 
+    // Sort Options
+    'sort_name' => 'Name',
+    'sort_created_at' => 'Created Date',
+    'sort_updated_at' => 'Updated Date',
+
     // Misc
     'deleted_user' => 'Deleted User',
     'no_activity' => 'No activity to show',
@@ -56,4 +61,4 @@ return [
     // Email Content
     'email_action_help' => 'If you’re having trouble clicking the ":actionText" button, copy and paste the URL below into your web browser:',
     'email_rights' => 'All rights reserved',
-];
\ No newline at end of file
+];
index c7a5acca80a0398fdfe5bb58fedf9c88f7bf4ef5..bc139e17fa8c389f07ea79373c3ea893d52f29f3 100644 (file)
     <script src="{{ baseUrl('/translations') }}"></script>
 
     @yield('head')
-
-    @include('partials/custom-styles')
-
+    @include('partials.custom-styles')
     @include('partials.custom-head')
-</head>
-<body class="@yield('body-class')" ng-app="bookStack">
-
-    @include('partials/notifications')
 
-    <header id="header">
-        <div class="container fluid">
-            <div class="row">
-                <div class="col-sm-4 col-md-3">
-                    <a href="{{ baseUrl('/') }}" class="logo">
-                        @if(setting('app-logo', '') !== 'none')
-                            <img class="logo-image" src="{{ setting('app-logo', '') === '' ? baseUrl('/logo.png') : baseUrl(setting('app-logo', '')) }}" alt="Logo">
-                        @endif
-                        @if (setting('app-name-header'))
-                            <span class="logo-text">{{ setting('app-name') }}</span>
-                        @endif
-                    </a>
-                </div>
-                <div class="col-sm-8 col-md-9">
-                    <div class="float right">
-                        <div class="header-search">
-                            <form action="{{ baseUrl('/search') }}" method="GET" class="search-box">
-                                <button id="header-search-box-button" type="submit">@icon('search') </button>
-                                <input id="header-search-box-input" type="text" name="term" tabindex="2" placeholder="{{ trans('common.search') }}" value="{{ isset($searchTerm) ? $searchTerm : '' }}">
-                            </form>
-                        </div>
-                        <div class="links text-center">
-                            @if(userCan('bookshelf-view-all') || userCan('bookshelf-view-own'))
-                                <a href="{{ baseUrl('/shelves') }}">@icon('bookshelf'){{ trans('entities.shelves') }}</a>
-                            @endif
-                            <a href="{{ baseUrl('/books') }}">@icon('book'){{ trans('entities.books') }}</a>
-                            @if(signedInUser() && userCan('settings-manage'))
-                                <a href="{{ baseUrl('/settings') }}">@icon('settings'){{ trans('settings.settings') }}</a>
-                            @endif
-                            @if(signedInUser() && userCan('users-manage') && !userCan('settings-manage'))
-                                <a href="{{ baseUrl('/settings/users') }}">@icon('users'){{ trans('settings.users') }}</a>
-                            @endif
-                            @if(!signedInUser())
-                                @if(setting('registration-enabled', false))
-                                    <a href="{{ baseUrl("/register") }}">@icon('new-user') {{ trans('auth.sign_up') }}</a>
-                                @endif
-                                <a href="{{ baseUrl('/login') }}">@icon('login') {{ trans('auth.log_in') }}</a>
-                            @endif
-                        </div>
-                        @if(signedInUser())
-                            @include('partials._header-dropdown', ['currentUser' => user()])
-                        @endif
+</head>
+<body class="@yield('body-class')">
 
-                    </div>
-                </div>
-            </div>
-        </div>
-    </header>
+    @include('partials.notifications')
+    @include('common.header')
 
     <section id="content" class="block">
         @yield('content')
             @icon('chevron-up') <span>{{ trans('common.back_to_top') }}</span>
         </div>
     </div>
-@yield('bottom')
-<script src="{{ versioned_asset('dist/app.js') }}"></script>
-@yield('scripts')
+
+    @yield('bottom')
+    <script src="{{ versioned_asset('dist/app.js') }}"></script>
+    @yield('scripts')
+
 </body>
 </html>
index 2d5e6c4558ebfd37332316774777bcbfdfbea9db..b21fb3012396c77535f915540f1fae47eca1aefe 100644 (file)
@@ -1,28 +1,24 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    <div class="col-sm-8 faded">
-        <div class="breadcrumbs">
-            <a href="{{ baseUrl('/books') }}" class="text-button">@icon('book'){{ trans('entities.books') }}</a>
-            <span class="sep">&raquo;</span>
-            <a href="{{ baseUrl('/create-book') }}" class="text-button">@icon('add'){{ trans('entities.books_create') }}</a>
-        </div>
-    </div>
-@stop
-
 @section('body')
+    <div class="container small">
+        <div class="breadcrumbs my-l">
+            <a href="{{  baseUrl('/books')  }}" class="">
+                @icon('book'){{ trans('entities.books') }}
+            </a>
+            <div class="separator">@icon('chevron-right')</div>
+            <a href="{{  baseUrl('/create-book')  }}" class="">
+                @icon('add'){{ trans('entities.books_create') }}
+            </a>
+        </div>
 
-<div class="container small">
-    <p>&nbsp;</p>
-    <div class="card">
-        <h3>@icon('add') {{ trans('entities.books_create') }}</h3>
-        <div class="body">
+        <div class="content-wrap card">
+            <h1 class="list-heading">{{ trans('entities.books_create') }}</h1>
             <form action="{{ baseUrl("/books") }}" method="POST" enctype="multipart/form-data">
                 @include('books/form')
             </form>
         </div>
     </div>
-</div>
-<p class="margin-top large"><br></p>
+
     @include('components.image-manager', ['imageType' => 'cover'])
 @stop
\ No newline at end of file
index bf94b5b076ed189a3dc723ad6ce5013522daf049..97ddd9681341284d7323a50cc506bf52c5cf5f09 100644 (file)
@@ -41,5 +41,5 @@
 
 <div class="form-group text-right">
     <a href="{{ isset($book) ? $book->getUrl() : baseUrl('/books') }}" class="button outline">{{ trans('common.cancel') }}</a>
-    <button type="submit" class="button pos">{{ trans('entities.books_save') }}</button>
+    <button type="submit" class="button primary">{{ trans('entities.books_save') }}</button>
 </div>
\ No newline at end of file
index 9bbf691006822cdd4935dc6c9f46c3b93c850e5c..fd576e7ed4a4ce79fc3d0f30540a1b1cd6a00c26 100644 (file)
@@ -1,18 +1,18 @@
-<div class="book-grid-item grid-card"  data-entity-type="book" data-entity-id="{{$book->id}}">
-    <div class="featured-image-container">
-        <a href="{{$book->getUrl()}}" title="{{$book->name}}">
-            <img src="{{$book->getBookCover()}}" alt="{{$book->name}}">
-        </a>
+<a href="{{$book->getUrl()}}" class="grid-card"  data-entity-type="book" data-entity-id="{{$book->id}}">
+    <div class="featured-image-container bg-book">
+        <img src="{{$book->getBookCover()}}" alt="{{$book->name}}">
     </div>
     <div class="grid-card-content">
-        <h2><a class="break-text" href="{{$book->getUrl()}}" title="{{$book->name}}">{{$book->getShortName(35)}}</a></h2>
+        <h2>{{$book->getShortName(35)}}</h2>
         @if(isset($book->searchSnippet))
-            <p >{!! $book->searchSnippet !!}</p>
+            <p class="text-muted">{!! $book->searchSnippet !!}</p>
         @else
-            <p >{{ $book->getExcerpt(130) }}</p>
+            <p class="text-muted">{{ $book->getExcerpt(130) }}</p>
         @endif
     </div>
     <div class="grid-card-footer text-muted text-small">
-        <span>@include('partials.entity-meta', ['entity' => $book])</span>
+        @icon('star')<span title="{{$book->created_at->toDayDateTimeString()}}">{{ trans('entities.meta_created', ['timeLength' => $book->created_at->diffForHumans()]) }}</span>
+        <br>
+        @icon('edit')<span title="{{ $book->updated_at->toDayDateTimeString() }}">{{ trans('entities.meta_updated', ['timeLength' => $book->updated_at->diffForHumans()]) }}</span>
     </div>
-</div>
\ No newline at end of file
+</a>
\ No newline at end of file
index 84150203f081e2345f466a0f0b3399da3acb2298..fcf9cb4bb8fef819b7a79f84f1cab8f3673a5a38 100644 (file)
@@ -1,41 +1,28 @@
-@extends('sidebar-layout')
+@extends('tri-layout')
 
-@section('toolbar')
-    <div class="col-xs-6">
-        <div class="action-buttons text-left">
-            @include('books/view-toggle', ['booksViewType' => $booksViewType])
-        </div>
-    </div>
-    <div class="col-xs-6 faded">
-        <div class="action-buttons">
-            @if($currentUser->can('book-create-all'))
-                <a href="{{ baseUrl("/create-book") }}" class="text-pos text-button">@icon('add'){{ trans('entities.books_create') }}</a>
-            @endif
-        </div>
-    </div>
-@stop
+@section('container-classes', 'mt-xl')
 
-@section('sidebar')
+@section('left')
     @if($recents)
-        <div id="recents" class="card">
-            <h3>@icon('view') {{ trans('entities.recently_viewed') }}</h3>
-            @include('partials/entity-list', ['entities' => $recents, 'style' => 'compact'])
+        <div id="recents" class="mb-xl">
+            <h5>{{ trans('entities.recently_viewed') }}</h5>
+            @include('partials.entity-list', ['entities' => $recents, 'style' => 'compact'])
         </div>
     @endif
 
-    <div id="popular" class="card">
-        <h3>@icon('popular') {{ trans('entities.books_popular') }}</h3>
+    <div id="popular" class="mb-xl">
+        <h5>{{ trans('entities.books_popular') }}</h5>
         @if(count($popular) > 0)
-            @include('partials/entity-list', ['entities' => $popular, 'style' => 'compact'])
+            @include('partials.entity-list', ['entities' => $popular, 'style' => 'compact'])
         @else
             <div class="body text-muted">{{ trans('entities.books_popular_empty') }}</div>
         @endif
     </div>
 
-    <div id="new" class="card">
-        <h3>@icon('star-circle') {{ trans('entities.books_new') }}</h3>
+    <div id="new" class="mb-xl">
+        <h5>{{ trans('entities.books_new') }}</h5>
         @if(count($popular) > 0)
-            @include('partials/entity-list', ['entities' => $new, 'style' => 'compact'])
+            @include('partials.entity-list', ['entities' => $new, 'style' => 'compact'])
         @else
             <div class="body text-muted">{{ trans('entities.books_new_empty') }}</div>
         @endif
 @stop
 
 @section('body')
-    @include('books/list', ['books' => $books, 'bookViewType' => $booksViewType])
+    @include('books.list', ['books' => $books, 'view' => $view])
+@stop
+
+@section('right')
+
+    <div class="actions mb-xl">
+        <h5>Actions</h5>
+        <div class="icon-list text-primary">
+            @if($currentUser->can('book-create-all'))
+                <a href="{{ baseUrl("/create-book") }}" class="icon-list-item">
+                    <span class="icon">@icon('add')</span>
+                    <span>{{ trans('entities.books_create') }}</span>
+                </a>
+            @endif
+            @include('books.view-toggle', ['view' => $view])
+        </div>
+    </div>
+
 @stop
\ No newline at end of file
index 05d7e90ef73a9846a8ba7bb48ddc6b50bc6cf951..966f67b226f9d8a28cb807b930e5b6f9a2e3d903 100644 (file)
@@ -1,10 +1,10 @@
-<div class="book entity-list-item"  data-entity-type="book" data-entity-id="{{$book->id}}">
-    <h4 class="text-book"><a class="text-book entity-list-item-link" href="{{$book->getUrl()}}">@icon('book')<span class="entity-list-item-name break-text">{{$book->name}}</span></a></h4>
-    <div class="entity-item-snippet">
-        @if(isset($book->searchSnippet))
-            <p class="text-muted break-text">{!! $book->searchSnippet !!}</p>
-        @else
-            <p class="text-muted break-text">{{ $book->getExcerpt() }}</p>
-        @endif
+<a href="{{ $book->getUrl() }}" class="book entity-list-item" data-entity-type="book" data-entity-id="{{$book->id}}">
+    <div class="entity-list-item-image bg-book" style="background-image: url('{{ $book->getBookCover() }}')">
     </div>
-</div>
\ No newline at end of file
+    <div class="content">
+        <h4 class="entity-list-item-name break-text">{{ $book->name }}</h4>
+        <div class="entity-item-snippet">
+            <p class="text-muted break-text mb-s">{{ $book->getExcerpt() }}</p>
+        </div>
+    </div>
+</a>
\ No newline at end of file
index 9459cc0088b9864c6badfa5a44246565b47e84ab..5e077727b6f307539916cf15aea834f5a463eca0 100644 (file)
@@ -1,23 +1,30 @@
 
-<div class="container{{ $booksViewType === 'list' ? ' small' : '' }}">
-    <h1>{{ trans('entities.books') }}</h1>
+<div class="content-wrap card">
+    <div class="grid halves v-center">
+        <h1 class="list-heading">{{ trans('entities.books') }}</h1>
+        <div class="text-right">
+
+            @include('partials.sort', ['options' => $sortOptions, 'order' => $order, 'sort' => $sort])
+
+        </div>
+    </div>
     @if(count($books) > 0)
-        @if($booksViewType === 'list')
-            @foreach($books as $book)
-                @include('books/list-item', ['book' => $book])
-                <hr>
-            @endforeach
-            {!! $books->render() !!}
+        @if($view === 'list')
+            <div class="entity-list">
+                @foreach($books as $book)
+                    @include('books.list-item', ['book' => $book])
+                @endforeach
+            </div>
         @else
              <div class="grid third">
                 @foreach($books as $key => $book)
-                        @include('books/grid-item', ['book' => $book])
+                    @include('books.grid-item', ['book' => $book])
                 @endforeach
              </div>
-            <div>
-                {!! $books->render() !!}
-            </div>
         @endif
+        <div>
+            {!! $books->render() !!}
+        </div>
     @else
         <p class="text-muted">{{ trans('entities.books_empty') }}</p>
         @if(userCan('books-create-all'))
index 63eb9b9d3f4abf781e53d96142e84cbac8d45d2b..c0f8b3f15c207d1451c215b62f394dadfcbb49f1 100644 (file)
@@ -1,10 +1,18 @@
-<form action="{{ baseUrl("/settings/users/{$currentUser->id}/switch-book-view") }}" method="POST" class="inline">
-    {!! csrf_field() !!}
-    {!! method_field('PATCH') !!}
-    <input type="hidden" value="{{ $booksViewType === 'list'? 'grid' : 'list' }}" name="view_type">
-    @if ($booksViewType === 'list')
-        <button type="submit" class="text-pos text-button">@icon('grid'){{ trans('common.grid_view') }}</button>
-    @else
-        <button type="submit" class="text-pos text-button">@icon('list'){{ trans('common.list_view') }}</button>
-    @endif
-</form>
\ No newline at end of file
+<div>
+    <form action="{{ baseUrl("/settings/users/{$currentUser->id}/switch-book-view") }}" method="POST" class="inline">
+        {!! csrf_field() !!}
+        {!! method_field('PATCH') !!}
+        <input type="hidden" value="{{ $view === 'list'? 'grid' : 'list' }}" name="view_type">
+        @if ($view === 'list')
+            <a onclick="this.closest('form').submit()" type="submit" class="icon-list-item">
+                <span class="icon">@icon('grid')</span>
+                <span>{{ trans('common.grid_view') }}</span>
+            </a>
+        @else
+            <a onclick="this.closest('form').submit()" type="submit" class="icon-list-item">
+                <span class="icon">@icon('list')</span>
+                <span>{{ trans('common.list_view') }}</span>
+            </a>
+        @endif
+    </form>
+</div>
\ No newline at end of file
diff --git a/resources/views/chapters/child-menu.blade.php b/resources/views/chapters/child-menu.blade.php
new file mode 100644 (file)
index 0000000..36c7f9a
--- /dev/null
@@ -0,0 +1,12 @@
+<div class="chapter-child-menu">
+    <p chapter-toggle class="text-muted @if($bookChild->matchesOrContains($current)) open @endif">
+        @icon('caret-right') @icon('page') <span>{{ trans_choice('entities.x_pages', $bookChild->pages->count()) }}</span>
+    </p>
+    <ul class="sub-menu inset-list @if($bookChild->matchesOrContains($current)) open @endif">
+        @foreach($bookChild->pages as $childPage)
+            <li class="list-item-page {{ $childPage->isA('page') && $childPage->draft ? 'draft' : '' }}">
+                @include('partials.entity-list-item-basic', ['entity' => $childPage, 'classes' => $current->matches($childPage)? 'selected' : '' ])
+            </li>
+        @endforeach
+    </ul>
+</div>
\ No newline at end of file
diff --git a/resources/views/common/header.blade.php b/resources/views/common/header.blade.php
new file mode 100644 (file)
index 0000000..67aa675
--- /dev/null
@@ -0,0 +1,64 @@
+<header id="header" header-mobile-toggle>
+    <div class="grid break-l mx-l">
+        <div>
+            <a href="{{ baseUrl('/') }}" class="logo">
+                @if(setting('app-logo', '') !== 'none')
+                    <img class="logo-image" src="{{ setting('app-logo', '') === '' ? baseUrl('/logo.png') : baseUrl(setting('app-logo', '')) }}" alt="Logo">
+                @endif
+                @if (setting('app-name-header'))
+                    <span class="logo-text">{{ setting('app-name') }}</span>
+                @endif
+            </a>
+            <div class="mobile-menu-toggle hide-over-l">@icon('more')</div>
+        </div>
+        <div class="header-search hide-under-l">
+            <form action="{{ baseUrl('/search') }}" method="GET" class="search-box">
+                <button id="header-search-box-button" type="submit">@icon('search') </button>
+                <input id="header-search-box-input" type="text" name="term" tabindex="2" placeholder="{{ trans('common.search') }}" value="{{ isset($searchTerm) ? $searchTerm : '' }}">
+            </form>
+        </div>
+        <div class="text-right">
+            <div class="header-links">
+                <div class="links text-center">
+                    <a class="hide-over-l" href="{{ baseUrl('/search') }}">@icon('search'){{ trans('common.search') }}</a>
+                    @if(userCan('bookshelf-view-all') || userCan('bookshelf-view-own'))
+                        <a href="{{ baseUrl('/shelves') }}">@icon('bookshelf'){{ trans('entities.shelves') }}</a>
+                    @endif
+                    <a href="{{ baseUrl('/books') }}">@icon('book'){{ trans('entities.books') }}</a>
+                    @if(signedInUser() && userCan('settings-manage'))
+                        <a href="{{ baseUrl('/settings') }}">@icon('settings'){{ trans('settings.settings') }}</a>
+                    @endif
+                    @if(signedInUser() && userCan('users-manage') && !userCan('settings-manage'))
+                        <a href="{{ baseUrl('/settings/users') }}">@icon('users'){{ trans('settings.users') }}</a>
+                    @endif
+                    @if(!signedInUser())
+                        @if(setting('registration-enabled', false))
+                            <a href="{{ baseUrl("/register") }}">@icon('new-user') {{ trans('auth.sign_up') }}</a>
+                        @endif
+                        <a href="{{ baseUrl('/login') }}">@icon('login') {{ trans('auth.log_in') }}</a>
+                    @endif
+                </div>
+                @if(signedInUser())
+                    <?php $currentUser = user(); ?>
+                    <div class="dropdown-container" dropdown>
+                        <span class="user-name hide-under-l" dropdown-toggle>
+                            <img class="avatar" src="{{$currentUser->getAvatar(30)}}" alt="{{ $currentUser->name }}">
+                            <span class="name">{{ $currentUser->getShortName(9) }}</span> @icon('caret-down')
+                        </span>
+                        <ul>
+                            <li>
+                                <a href="{{ baseUrl("/user/{$currentUser->id}") }}" class="text-primary">@icon('user'){{ trans('common.view_profile') }}</a>
+                            </li>
+                            <li>
+                                <a href="{{ baseUrl("/settings/users/{$currentUser->id}") }}" class="text-primary">@icon('edit'){{ trans('common.edit_profile') }}</a>
+                            </li>
+                            <li>
+                                <a href="{{ baseUrl('/logout') }}" class="text-neg">@icon('logout'){{ trans('auth.logout') }}</a>
+                            </li>
+                        </ul>
+                    </div>
+                @endif
+            </div>
+        </div>
+    </div>
+</header>
\ No newline at end of file
index cc20fc68e2d91d97a7dc89236bb82e052f86fb9b..89583e97a84efe9470c8839a3d954c8e2ac29419 100644 (file)
@@ -1,57 +1,57 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    <div class="col-sm-6 faded">
-        <div class="action-buttons text-left">
-            <a expand-toggle=".entity-list.compact .entity-item-snippet" class="text-primary text-button">@icon('expand-text'){{ trans('common.toggle_details') }}</a>
-        </div>
-    </div>
-@stop
 
 @section('body')
 
-    <div class="container" id="home-default">
-        <div class="row">
+    <div class="container px-xl py-l">
+        <a expand-toggle=".entity-list.compact .entity-item-snippet" class="text-muted">@icon('expand-text'){{ trans('common.toggle_details') }}</a>
+    </div>
 
-            <div class="col-sm-4">
-                @if(count($draftPages) > 0)
-                    <div id="recent-drafts" class="card">
-                        <h3>@icon('edit') {{ trans('entities.my_recent_drafts') }}</h3>
+    <div class="grid contained thirds space-xl break-m" id="home-default">
+        <div>
+            @if(count($draftPages) > 0)
+                <div id="recent-drafts" class="card mb-xl">
+                    <h3>{{ trans('entities.my_recent_drafts') }}</h3>
+                    <div class="px-m">
                         @include('partials/entity-list', ['entities' => $draftPages, 'style' => 'compact'])
                     </div>
-                @endif
+                </div>
+            @endif
 
-                <div class="card">
-                    <h3>@icon($signedIn ? 'view' : 'star-circle') {{ trans('entities.' . ($signedIn ? 'my_recently_viewed' : 'books_recent')) }}</h3>
+            <div id="{{ $signedIn ? 'recently-viewed' : 'recent-books' }}" class="card mb-xl">
+                <h3>{{ trans('entities.' . ($signedIn ? 'my_recently_viewed' : 'books_recent')) }}</h3>
+                <div class="px-m">
                     @include('partials/entity-list', [
-                        'entities' => $recents,
-                        'style' => 'compact',
-                        'emptyText' => $signedIn ? trans('entities.no_pages_viewed') : trans('entities.books_empty')
-                        ])
+                    'entities' => $recents,
+                    'style' => 'compact',
+                    'emptyText' => $signedIn ? trans('entities.no_pages_viewed') : trans('entities.books_empty')
+                    ])
                 </div>
             </div>
+        </div>
 
-            <div class="col-sm-4">
-                <div class="card">
-                    <h3>@icon('file') <a class="no-color" href="{{ baseUrl("/pages/recently-updated") }}">{{ trans('entities.recently_updated_pages') }}</a></h3>
-                    <div id="recently-updated-pages">
-                        @include('partials/entity-list', [
-                        'entities' => $recentlyUpdatedPages,
-                        'style' => 'compact',
-                        'emptyText' => trans('entities.no_pages_recently_updated')
-                        ])
-                    </div>
+        <div>
+            <div id="recent-pages" class="card mb-xl">
+                <h3><a class="no-color" href="{{ baseUrl("/pages/recently-updated") }}">{{ trans('entities.recently_updated_pages') }}</a></h3>
+                <div id="recently-updated-pages" class="px-m">
+                    @include('partials/entity-list', [
+                    'entities' => $recentlyUpdatedPages,
+                    'style' => 'compact',
+                    'emptyText' => trans('entities.no_pages_recently_updated')
+                    ])
                 </div>
             </div>
+        </div>
 
-            <div class="col-sm-4" id="recent-activity">
-                <div class="card">
-                    <h3>@icon('time') {{ trans('entities.recent_activity') }}</h3>
+        <div>
+            <div id="recent-activity">
+                <div class="card mb-xl">
+                    <h3>{{ trans('entities.recent_activity') }}</h3>
                     @include('partials/activity-list', ['activity' => $activity])
                 </div>
             </div>
-
         </div>
+
     </div>
 
 
index 19bab40e0967ba68f32a40a58393a5a6dbd6940e..f10f7ebe8bc48d9a71febc3fc7edd7aab3480522 100644 (file)
@@ -1,14 +1,5 @@
-<div class="breadcrumbs">
-    @if (userCan('view', $page->book))
-        <a href="{{ $page->book->getUrl() }}" class="text-book text-button">@icon('book'){{ $page->book->getShortName() }}</a>
-        <span class="sep">&raquo;</span>
-    @endif
-    @if($page->hasChapter() && userCan('view', $page->chapter))
-        <a href="{{ $page->chapter->getUrl() }}" class="text-chapter text-button">
-            @icon('chapter')
-            {{ $page->chapter->getShortName() }}
-        </a>
-        <span class="sep">&raquo;</span>
-    @endif
-    <a href="{{ $page->getUrl() }}" class="text-page text-button">@icon('page'){{ $page->getShortName() }}</a>
-</div>
\ No newline at end of file
+@include('partials.breadcrumbs', [
+        'page' => $page,
+        'chapter' => $page->hasChapter() ? $page->chapter : null,
+        'book' => $page->book,
+])
\ No newline at end of file
index b13bb0f120211dda32f3f60fc1c7acaa5cda059f..f3c79791a792a647df9a8a1005357625b3aa8cf4 100644 (file)
@@ -1,44 +1,51 @@
 <div class="page {{$page->draft ? 'draft' : ''}} entity-list-item" data-entity-type="page" data-entity-id="{{$page->id}}">
-    <h4>
-        @if (isset($showPath) && $showPath)
-            <a href="{{ $page->book->getUrl() }}" class="text-book">
-                @icon('book'){{ $page->book->getShortName() }}
-            </a>
-            <span class="text-muted">&nbsp;&nbsp;&raquo;&nbsp;&nbsp;</span>
-            @if($page->chapter)
-                <a href="{{ $page->chapter->getUrl() }}" class="text-chapter">
-                    @icon('chapter'){{ $page->chapter->getShortName() }}
+    <div class="entity-icon text-page">@icon('page')</div>
+    <div class="content">
+
+        <h4>
+            @if (isset($showPath) && $showPath)
+                <a href="{{ $page->book->getUrl() }}" class="text-book">
+                    @icon('book'){{ $page->book->getShortName() }}
                 </a>
                 <span class="text-muted">&nbsp;&nbsp;&raquo;&nbsp;&nbsp;</span>
+                @if($page->chapter)
+                    <a href="{{ $page->chapter->getUrl() }}" class="text-chapter">
+                        @icon('chapter'){{ $page->chapter->getShortName() }}
+                    </a>
+                    <span class="text-muted">&nbsp;&nbsp;&raquo;&nbsp;&nbsp;</span>
+                @endif
             @endif
+            <a href="{{ $page->getUrl() }}" class="entity-list-item-link"><span class="entity-list-item-name break-text">{{ $page->name }}</span></a>
+        </h4>
+
+
+        <div class="entity-item-snippet">
+            @if(isset($page->searchSnippet))
+                <p class="text-muted break-text">{!! $page->searchSnippet !!}</p>
+            @else
+                <p class="text-muted break-text">{{ $page->getExcerpt() }}</p>
+            @endif
+        </div>
+
+        @if(isset($style) && $style === 'detailed')
+            <div class="row meta text-muted text-small">
+                <div class="col-md-6">
+                    @include('partials.entity-meta', ['entity' => $page])
+                </div>
+                <div class="col-md-6">
+                    <a class="text-book" href="{{ $page->book->getUrl() }}">@icon('book'){{ $page->book->getShortName(30) }}</a>
+                    <br>
+                    @if($page->chapter)
+                        <a class="text-chapter" href="{{ $page->chapter->getUrl() }}">@icon('chapter'){{ $page->chapter->getShortName(30) }}</a>
+                    @else
+                        @icon('chapter') {{ trans('entities.pages_not_in_chapter') }}
+                    @endif
+                </div>
+            </div>
         @endif
-        <a href="{{ $page->getUrl() }}" class="text-page entity-list-item-link">@icon('page')<span class="entity-list-item-name break-text">{{ $page->name }}</span></a>
-    </h4>
-
-    <div class="entity-item-snippet">
-        @if(isset($page->searchSnippet))
-            <p class="text-muted break-text">{!! $page->searchSnippet !!}</p>
-        @else
-            <p class="text-muted break-text">{{ $page->getExcerpt() }}</p>
-        @endif
+
     </div>
 
-    @if(isset($style) && $style === 'detailed')
-        <div class="row meta text-muted text-small">
-            <div class="col-md-6">
-                @include('partials.entity-meta', ['entity' => $page])
-            </div>
-            <div class="col-md-6">
-                <a class="text-book" href="{{ $page->book->getUrl() }}">@icon('book'){{ $page->book->getShortName(30) }}</a>
-                <br>
-                @if($page->chapter)
-                    <a class="text-chapter" href="{{ $page->chapter->getUrl() }}">@icon('chapter'){{ $page->chapter->getShortName(30) }}</a>
-                @else
-                    @icon('chapter') {{ trans('entities.pages_not_in_chapter') }}
-                @endif
-            </div>
-        </div>
-    @endif
 
 
 </div>
\ No newline at end of file
index afe007d45fe057df7dc24f58065d40a1985b41a0..f7f8e2cf23f7d8f7b893b1af190c9138bf7a6207 100644 (file)
@@ -1,48 +1,7 @@
-@extends('sidebar-layout')
+@extends('tri-layout')
+@section('container-classes', 'mt-xl')
 
-@section('toolbar')
-    <div class="col-sm-8 col-xs-5 faded">
-        @include('pages._breadcrumbs', ['page' => $page])
-    </div>
-    <div class="col-sm-4 col-xs-7 faded">
-        <div class="action-buttons">
-            <span dropdown class="dropdown-container">
-                <div dropdown-toggle class="text-button text-primary">@icon('export'){{ trans('entities.export') }}</div>
-                <ul class="wide">
-                    <li><a href="{{ $page->getUrl('/export/html') }}" target="_blank">{{ trans('entities.export_html') }} <span class="text-muted float right">.html</span></a></li>
-                    <li><a href="{{ $page->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
-                    <li><a href="{{ $page->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.export_text') }} <span class="text-muted float right">.txt</span></a></li>
-                </ul>
-            </span>
-            @if(userCan('page-update', $page))
-                <a href="{{ $page->getUrl('/edit') }}" class="text-primary text-button" >@icon('edit'){{ trans('common.edit') }}</a>
-            @endif
-            @if(userCan('page-update', $page) || userCan('restrictions-manage', $page) || userCan('page-delete', $page))
-                <div dropdown class="dropdown-container">
-                    <a dropdown-toggle class="text-primary text-button">@icon('more') {{ trans('common.more') }}</a>
-                    <ul>
-                        @if(userCan('page-update', $page))
-                            <li><a href="{{ $page->getUrl('/copy') }}" class="text-primary" >@icon('copy'){{ trans('common.copy') }}</a></li>
-                            @if(userCan('page-delete', $page))
-                                <li><a href="{{ $page->getUrl('/move') }}" class="text-primary" >@icon('folder'){{ trans('common.move') }}</a></li>
-                            @endif
-                            <li><a href="{{ $page->getUrl('/revisions') }}" class="text-primary">@icon('history'){{ trans('entities.revisions') }}</a></li>
-                        @endif
-                        @if(userCan('restrictions-manage', $page))
-                            <li><a href="{{ $page->getUrl('/permissions') }}" class="text-primary">@icon('lock'){{ trans('entities.permissions') }}</a></li>
-                        @endif
-                        @if(userCan('page-delete', $page))
-                            <li><a href="{{ $page->getUrl('/delete') }}" class="text-neg">@icon('delete'){{ trans('common.delete') }}</a></li>
-                        @endif
-                    </ul>
-                </div>
-            @endif
-
-        </div>
-    </div>
-@stop
-
-@section('sidebar')
+@section('left')
 
     @if($page->tags->count() > 0)
         <section>
@@ -51,8 +10,8 @@
     @endif
 
     @if ($page->attachments->count() > 0)
-        <div class="card">
-            <h3>@icon('attach') {{ trans('entities.pages_attachments') }}</h3>
+        <div id="page-attachments" class="mb-xl">
+            <h5>{{ trans('entities.pages_attachments') }}</h5>
             <div class="body">
                 @foreach($page->attachments as $attachment)
                     <div class="attachment">
@@ -64,8 +23,8 @@
     @endif
 
     @if (isset($pageNav) && count($pageNav))
-        <div class="card">
-            <h3>@icon('open-book') {{ trans('entities.pages_navigation') }}</h3>
+        <div id="page-navigation" class="mb-xl">
+            <h5>{{ trans('entities.pages_navigation') }}</h5>
             <div class="body">
                 <div class="sidebar-page-nav menu">
                     @foreach($pageNav as $navItem)
@@ -78,8 +37,8 @@
         </div>
     @endif
 
-    <div class="card entity-details">
-        <h3>@icon('info') {{ trans('common.details') }}</h3>
+    <div id="page-details" class="entity-details mb-xl">
+        <h5>{{ trans('common.details') }}</h5>
         <div class="body text-muted text-small blended-links">
             @include('partials.entity-meta', ['entity' => $page])
 
         </div>
     </div>
 
-    @include('partials/book-tree', ['book' => $book, 'sidebarTree' => $sidebarTree])
-
+    @include('partials.book-tree', ['book' => $book, 'sidebarTree' => $sidebarTree])
 @stop
 
-@section('body-wrap-classes', 'flex-fill columns')
-
 @section('body')
 
-    <div class="page-content flex" page-display="{{ $page->id }}">
+    <div class="mb-m">
+        @include('pages._breadcrumbs', ['page' => $page])
+    </div>
+
+    <div class="content-wrap card">
+        <div class="page-content flex" page-display="{{ $page->id }}">
 
-        <div class="pointer-container" id="pointer">
-            <div class="pointer anim {{ userCan('page-update', $page) ? 'is-page-editable' : ''}}" >
-                <span class="icon text-primary">@icon('link') @icon('include', ['style' => 'display:none;'])</span>
-                <span class="input-group">
+            <div class="pointer-container" id="pointer">
+                <div class="pointer anim {{ userCan('page-update', $page) ? 'is-page-editable' : ''}}" >
+                    <span class="icon text-primary">@icon('link') @icon('include', ['style' => 'display:none;'])</span>
+                    <span class="input-group">
                     <input readonly="readonly" type="text" id="pointer-url" placeholder="url">
                     <button class="button icon" data-clipboard-target="#pointer-url" type="button" title="{{ trans('entities.pages_copy_link') }}">@icon('copy')</button>
                 </span>
-                @if(userCan('page-update', $page))
-                    <a href="{{ $page->getUrl('/edit') }}" id="pointer-edit" data-edit-href="{{ $page->getUrl('/edit') }}"
-                        class="button icon heading-edit-icon" title="{{ trans('entities.pages_edit_content_link')}}">@icon('edit')</a>
-                @endif
+                    @if(userCan('page-update', $page))
+                        <a href="{{ $page->getUrl('/edit') }}" id="pointer-edit" data-edit-href="{{ $page->getUrl('/edit') }}"
+                           class="button icon heading-edit-icon" title="{{ trans('entities.pages_edit_content_link')}}">@icon('edit')</a>
+                    @endif
+                </div>
             </div>
-        </div>
 
-        @include('pages/page-display')
+            @include('pages.page-display')
+        </div>
     </div>
 
     @if ($commentsEnabled)
-      <div class="container small nopad comments-container">
-          @include('comments/comments', ['page' => $page])
+      <div class="container small nopad comments-container mb-l">
+          @include('comments.comments', ['page' => $page])
+          <div class="clearfix"></div>
       </div>
     @endif
 @stop
+
+@section('right')
+    <div class="actions mb-xl">
+        <h5>Actions</h5>
+
+        <div class="icon-list text-primary">
+            {{--Export--}}
+            <div dropdown class="dropdown-container block">
+                <div dropdown-toggle class="icon-list-item">
+                    <span class="icon">@icon('export')</span>
+                    <span>{{ trans('entities.export') }}</span>
+                </div>
+                <ul class="wide">
+                    <li><a href="{{ $page->getUrl('/export/html') }}" target="_blank">{{ trans('entities.export_html') }} <span class="text-muted float right">.html</span></a></li>
+                    <li><a href="{{ $page->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
+                    <li><a href="{{ $page->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.export_text') }} <span class="text-muted float right">.txt</span></a></li>
+                </ul>
+            </div>
+
+            {{--User Actions--}}
+            @if(userCan('page-update', $page))
+                <a href="{{ $page->getUrl('/edit') }}" class="icon-list-item">
+                    <span class="icon">@icon('edit')</span>
+                    <span>{{ trans('common.edit') }}</span>
+                </a>
+                <a href="{{ $page->getUrl('/copy') }}" class="icon-list-item">
+                    <span class="icon">@icon('copy')</span>
+                    <span>{{ trans('common.copy') }}</span>
+                </a>
+                @if(userCan('page-delete', $page))
+                       <a href="{{ $page->getUrl('/move') }}" class="icon-list-item">
+                           <span class="icon">@icon('folder')</span>
+                           <span>{{ trans('common.move') }}</span>
+                       </a>
+                @endif
+                <a href="{{ $page->getUrl('/revisions') }}" class="icon-list-item">
+                    <span class="icon">@icon('history')</span>
+                    <span>{{ trans('entities.revisions') }}</span>
+                </a>
+            @endif
+            @if(userCan('restrictions-manage', $page))
+                <a href="{{ $page->getUrl('/permissions') }}" class="icon-list-item">
+                    <span class="icon">@icon('lock')</span>
+                    <span>{{ trans('entities.permissions') }}</span>
+                </a>
+            @endif
+            @if(userCan('page-delete', $page))
+                <a href="{{ $page->getUrl('/delete') }}" class="icon-list-item">
+                    <span class="icon">@icon('delete')</span>
+                    <span>{{ trans('common.delete') }}</span>
+                </a>
+            @endif
+        </div>
+
+    </div>
+@stop
diff --git a/resources/views/partials/_header-dropdown.blade.php b/resources/views/partials/_header-dropdown.blade.php
deleted file mode 100644 (file)
index 176e007..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<div class="dropdown-container" dropdown>
-    <span class="user-name" dropdown-toggle>
-        <img class="avatar" src="{{$currentUser->getAvatar(30)}}" alt="{{ $currentUser->name }}">
-        <span class="name">{{ $currentUser->getShortName(9) }}</span> @icon('caret-down')
-    </span>
-    <ul>
-        <li>
-            <a href="{{ baseUrl("/user/{$currentUser->id}") }}" class="text-primary">@icon('user') {{ trans('common.view_profile') }}</a>
-        </li>
-        <li>
-            <a href="{{ baseUrl("/settings/users/{$currentUser->id}") }}" class="text-primary">@icon('edit') {{ trans('common.edit_profile') }}</a>
-        </li>
-        <li>
-            <a href="{{ baseUrl('/logout') }}" class="text-neg">@icon('logout') {{ trans('auth.logout') }}</a>
-        </li>
-    </ul>
-</div>
\ No newline at end of file
index 1dbfc9de81f1e00a705ff97d763b53f0e29e546f..39fb35fe2728c4c50c2ee5392414f9dfdee0a5c8 100644 (file)
@@ -1,13 +1,13 @@
 
 {{--Requires an Activity item with the name $activity passed in--}}
 
-@if($activity->user)
-    <div class="left">
-        <img class="avatar" src="{{ $activity->user->getAvatar(30) }}" alt="{{ $activity->user->name }}">
-    </div>
-@endif
+<div>
+    @if($activity->user)
+    <img class="avatar" src="{{ $activity->user->getAvatar(30) }}" alt="{{ $activity->user->name }}">
+    @endif
+</div>
 
-<div class="right" v-pre>
+<div v-pre>
     @if($activity->user)
         <a href="{{ $activity->user->getProfileUrl() }}">{{ $activity->user->name }}</a>
     @else
index 0f895cf59fed2d620f79d21b9fcad47fc4ce2306..360162b2fd185b31f1c40e9194249a180f01b16f 100644 (file)
@@ -1,36 +1,22 @@
-<div class="card book-tree" v-pre>
-    <h3>@icon('book') {{ trans('entities.books_navigation') }}</h3>
-    <div class="body">
-        <ul class="sidebar-page-list menu">
+<div id="book-tree" class="book-tree mb-xl" v-pre>
+    <h5>{{ trans('entities.books_navigation') }}</h5>
+    @if (userCan('view', $book))
+        <div class="entity-list">
+            @include('partials.entity-list-item-basic', ['entity' => $book, 'classes' => ($current->matches($book)? 'selected' : '')])
+        </div>
+    @endif
 
-            @if (userCan('view', $book))
-                <li class="book-header"><a href="{{ $book->getUrl() }}" class="book {{ $current->matches($book)? 'selected' : '' }}">@icon('book'){{$book->name}}</a></li>
-            @endif
+    <ul class="sidebar-page-list menu entity-list">
 
-            @foreach($sidebarTree as $bookChild)
-                <li class="list-item-{{ $bookChild->getClassName() }} {{ $bookChild->getClassName() }} {{ $bookChild->isA('page') && $bookChild->draft ? 'draft' : '' }}">
-                    <a href="{{ $bookChild->getUrl() }}" class="{{ $bookChild->getClassName() }} {{ $current->matches($bookChild)? 'selected' : '' }}">
-                        @if($bookChild->isA('chapter'))@icon('chapter')@else @icon('page')@endif{{ $bookChild->name }}
-                    </a>
+        @foreach($sidebarTree as $bookChild)
+            <li class="list-item-{{ $bookChild->getClassName() }} {{ $bookChild->getClassName() }} {{ $bookChild->isA('page') && $bookChild->draft ? 'draft' : '' }}">
+                @include('partials.entity-list-item-basic', ['entity' => $bookChild, 'classes' => $current->matches($bookChild)? 'selected' : ''])
 
-                    @if($bookChild->isA('chapter') && count($bookChild->pages) > 0)
-                        <p chapter-toggle class="text-muted @if($bookChild->matchesOrContains($current)) open @endif">
-                            @icon('caret-right') @icon('page') <span>{{ trans_choice('entities.x_pages', $bookChild->pages->count()) }}</span>
-                        </p>
-                        <ul class="menu sub-menu inset-list @if($bookChild->matchesOrContains($current)) open @endif">
-                            @foreach($bookChild->pages as $childPage)
-                                <li class="list-item-page {{ $childPage->isA('page') && $childPage->draft ? 'draft' : '' }}">
-                                    <a href="{{ $childPage->getUrl() }}" class="page {{ $current->matches($childPage)? 'selected' : '' }}">
-                                        @icon('page') {{ $childPage->name }}
-                                    </a>
-                                </li>
-                            @endforeach
-                        </ul>
-                    @endif
+                @if($bookChild->isA('chapter') && count($bookChild->pages) > 0)
+                    @include('chapters.child-menu', ['chapter' => $bookChild, 'current' => $current])
+                @endif
 
-
-                </li>
-            @endforeach
-        </ul>
-    </div>
+            </li>
+        @endforeach
+    </ul>
 </div>
\ No newline at end of file
diff --git a/resources/views/partials/breadcrumbs.blade.php b/resources/views/partials/breadcrumbs.blade.php
new file mode 100644 (file)
index 0000000..cda9e50
--- /dev/null
@@ -0,0 +1,19 @@
+<div class="breadcrumbs text-center">
+    @if (isset($book) && userCan('view', $book))
+        <a href="{{ $book->getUrl() }}" class="text-book">
+            @icon('book'){{ $book->getShortName() }}
+        </a>
+        <div class="separator">@icon('chevron-right')</div>
+    @endif
+    @if(isset($chapter) && userCan('view', $chapter))
+        <a href="{{ $chapter->getUrl() }}" class="text-chapter">
+            @icon('chapter'){{ $chapter->getShortName() }}
+        </a>
+        <div class="separator">@icon('chevron-right')</div>
+    @endif
+    @if(isset($page) && userCan('view', $page))
+        <a href="{{ $page->getUrl() }}" class="text-page">
+            @icon('page'){{ $page->getShortName() }}
+        </a>
+    @endif
+</div>
\ No newline at end of file
diff --git a/resources/views/partials/entity-list-item-basic.blade.php b/resources/views/partials/entity-list-item-basic.blade.php
new file mode 100644 (file)
index 0000000..0f80d7d
--- /dev/null
@@ -0,0 +1,8 @@
+<?php $type = $entity->getType(); ?>
+<a href="{{ $entity->getUrl() }}" class="{{$type}} {{$type === 'page' && $entity->draft ? 'draft' : ''}} {{$classes ?? ''}} entity-list-item" data-entity-type="{{$type}}" data-entity-id="{{$entity->id}}">
+    <div class="icon text-{{$type}}">@icon($type)</div>
+    <div class="content">
+            <h4 class="entity-list-item-name break-text">{{ $entity->name }}</h4>
+            {{ $slot ?? '' }}
+    </div>
+</a>
\ No newline at end of file
diff --git a/resources/views/partials/entity-list-item.blade.php b/resources/views/partials/entity-list-item.blade.php
new file mode 100644 (file)
index 0000000..d971ae0
--- /dev/null
@@ -0,0 +1,5 @@
+@component('partials.entity-list-item-basic', ['entity' => $entity])
+<div class="entity-item-snippet">
+    <p class="text-muted break-text">{{ $entity->getExcerpt() }}</p>
+</div>
+@endcomponent
\ No newline at end of file
index 371f38d71c6b5e0c7a9de5a09d1c5e61f7ec38cc..b2a26f1e497465169c5ccdb30fbdc25c66f6a678 100644 (file)
@@ -1,25 +1,12 @@
 
-<div class="entity-list @if(isset($style)){{ $style }}@endif">
+<div class="entity-list {{ $style ?? '' }}">
     @if(count($entities) > 0)
         @foreach($entities as $index => $entity)
-            @if($entity->isA('page'))
-                @include('pages/list-item', ['page' => $entity])
-            @elseif($entity->isA('book'))
-                @include('books/list-item', ['book' => $entity])
-            @elseif($entity->isA('chapter'))
-                @include('chapters/list-item', ['chapter' => $entity, 'hidePages' => true])
-            @elseif($entity->isA('bookshelf'))
-                @include('shelves/list-item', ['bookshelf' => $entity])
-            @endif
-
-            @if($index !== count($entities) - 1)
-                <hr>
-            @endif
-
+            @include('partials.entity-list-item', ['entity' => $entity])
         @endforeach
     @else
         <p class="text-muted empty-text">
-            {{ $emptyText or trans('common.no_items') }}
+            {{ $emptyText ?? trans('common.no_items') }}
         </p>
     @endif
 </div>
\ No newline at end of file
diff --git a/resources/views/partials/sort.blade.php b/resources/views/partials/sort.blade.php
new file mode 100644 (file)
index 0000000..03eab84
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+    $selectedSort = (isset($sort) && array_key_exists($sort, $options)) ? $sort : array_keys($options)[0];
+    $order = (isset($order) && in_array($order, ['asc', 'desc'])) ? $order : 'asc';
+?>
+<div class="list-sort-container" list-sort-control>
+    <div class="list-sort-label">{{ trans('common.sort') }}</div>
+    <form action="{{ baseUrl("/settings/users/{$currentUser->id}/change-books-sort") }}" method="post">
+
+        {!! csrf_field() !!}
+        {!! method_field('PATCH') !!}
+        <input type="hidden" value="{{ $selectedSort }}" name="sort">
+        <input type="hidden" value="{{ $order }}" name="order">
+
+        <div class="list-sort">
+            <div class="list-sort-type dropdown-container" dropdown>
+                <div dropdown-toggle>{{ $options[$selectedSort] }}</div>
+                <ul>
+                    @foreach($options as $key => $label)
+                        <li @if($key === $selectedSort) class="active" @endif><a href="#" data-sort-value="{{$key}}">{{ $label }}</a></li>
+                    @endforeach
+                </ul>
+            </div>
+            <div class="list-sort-dir" data-sort-dir>
+                @if($order === 'desc')
+                    @icon('sort-up')
+                @else
+                    @icon('sort-down')
+                @endif
+            </div>
+        </div>
+    </form>
+</div>
\ No newline at end of file
index f6135cd1bce897cff531da4ffdeb339add2e6ea6..54918b19c8ce8325fe9a4f8d96f82b622ecffcf0 100644 (file)
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<html class="shaded">
+<html>
 <head>
     <title>{{ setting('app-name') }}</title>
 
index bf853c09ef44505910bf54fdef2a4296ad9ac271..512b64e91fc87664dbb55c9b6af49c1a7fc4b0f7 100644 (file)
@@ -4,14 +4,8 @@
 
 @section('content')
 
-    <div class="toolbar-container">
-        <div class="faded-small toolbar">
-            <div class="container fluid">
-                <div class="row">
-                    @yield('toolbar')
-                </div>
-            </div>
-        </div>
+    <div class="toolbar px-xl py-m">
+        @yield('toolbar')
     </div>
 
 
         <div sidebar class="sidebar flex print-hidden" id="sidebar">
             <div class="sidebar-toggle primary-background-light">@icon('caret-right-circle')
             </div>
-            <div class="scroll-body">
+            <div class="scroll-body px-xl">
                 @yield('sidebar')
             </div>
         </div>
 
-        <div class="content flex @yield('body-wrap-classes')">
+        <div class="mr-xl flex @yield('body-wrap-classes')">
             @yield('body')
         </div>
     </div>
index eeb4129e0497f32132b9d8c6915bec2d9def7307..27ba079659ccdc4651db22963ad9bef51e254c45 100644 (file)
@@ -5,16 +5,9 @@
 @section('content')
 
     <div class="toolbar-container">
-        <div class="faded-small toolbar">
-            <div class="container fluid">
-                <div class="row">
-                    @yield('toolbar')
-                </div>
-            </div>
-        </div>
+        @yield('toolbar')
     </div>
 
-
     <div class="flex-fill flex">
         <div class="content flex">
             <div class="scroll-body">
diff --git a/resources/views/tri-layout.blade.php b/resources/views/tri-layout.blade.php
new file mode 100644 (file)
index 0000000..9ae65b0
--- /dev/null
@@ -0,0 +1,30 @@
+@extends('base')
+
+@section('body-class', 'tri-layout')
+
+@section('content')
+
+    <div class="toolbar px-xl">
+        @yield('toolbar')
+    </div>
+
+    <div class="tri-layout-container @yield('container-classes')" tri-layout @yield('container-attrs') >
+
+        <div class="tri-layout-left print-hidden " id="sidebar">
+            <div class="tri-layout-left-contents">
+                @yield('left')
+            </div>
+        </div>
+
+        <div class="@yield('body-wrap-classes') tri-layout-middle">
+            @yield('body')
+        </div>
+
+        <div class="tri-layout-right print-hidden">
+            <div class="tri-layout-right-contents">
+                @yield('right')
+            </div>
+        </div>
+    </div>
+
+@stop
index d3c5f46d3ccdce717e167d4e545b4f19639feb68..bfdd0e580d43c5e613988e5d175c76639b63513b 100644 (file)
@@ -176,6 +176,7 @@ Route::group(['middleware' => 'auth'], function () {
         Route::get('/users/{id}/delete', 'UserController@delete');
         Route::patch('/users/{id}/switch-book-view', 'UserController@switchBookView');
         Route::patch('/users/{id}/switch-shelf-view', 'UserController@switchShelfView');
+        Route::patch('/users/{id}/change-books-sort', 'UserController@changeBooksSort');
         Route::post('/users/create', 'UserController@store');
         Route::get('/users/{id}', 'UserController@edit');
         Route::put('/users/{id}', 'UserController@update');