]> BookStack Code Mirror - bookstack/commitdiff
Added ability for dropdown menu to be bottom of dom body
authorDan Brown <redacted>
Sun, 5 May 2019 13:43:26 +0000 (14:43 +0100)
committerDan Brown <redacted>
Sun, 5 May 2019 13:43:26 +0000 (14:43 +0100)
- Used when a dropdown is within a scrollable section such as editor
toolbar on mobile.
- Also made mobile page save button more obvious by increasing size and
inverting color.

12 files changed:
resources/assets/js/components/dropdown.js
resources/assets/js/components/list-sort-control.js
resources/assets/sass/_lists.scss
resources/assets/sass/_pages.scss
resources/views/books/show.blade.php
resources/views/chapters/show.blade.php
resources/views/comments/comment.blade.php
resources/views/common/header.blade.php
resources/views/pages/form.blade.php
resources/views/pages/revisions.blade.php
resources/views/pages/show.blade.php
resources/views/partials/sort.blade.php

index 400ddb576128bd1f1464700b923d4fc21012c3e5..ce797bbeb31976c351335888c92b80b30c8d2fdb 100644 (file)
@@ -6,24 +6,54 @@ class DropDown {
 
     constructor(elem) {
         this.container = elem;
-        this.menu = elem.querySelector('ul, [dropdown-menu]');
+        this.menu = elem.querySelector('.dropdown-menu, [dropdown-menu]');
+        this.moveMenu = elem.hasAttribute('dropdown-move-menu');
         this.toggle = elem.querySelector('[dropdown-toggle]');
+        this.body = document.body;
         this.setupListeners();
     }
 
-    show() {
+    show(event) {
+        this.hide();
+
         this.menu.style.display = 'block';
         this.menu.classList.add('anim', 'menuIn');
-        this.container.addEventListener('mouseleave', this.hide.bind(this));
+
+        if (this.moveMenu) {
+            // Move to body to prevent being trapped within scrollable sections
+            this.rect = this.menu.getBoundingClientRect();
+            this.body.appendChild(this.menu);
+            this.menu.style.position = 'fixed';
+            this.menu.style.left = `${this.rect.left}px`;
+            this.menu.style.top = `${this.rect.top}px`;
+            this.menu.style.width = `${this.rect.width}px`;
+        }
+
+        // Set listener to hide on mouse leave or window click
+        this.menu.addEventListener('mouseleave', this.hide.bind(this));
+        window.addEventListener('click', event => {
+            if (!this.menu.contains(event.target)) {
+                this.hide();
+            }
+        });
 
         // Focus on first input if existing
         let input = this.menu.querySelector('input');
         if (input !== null) input.focus();
+
+        event.stopPropagation();
     }
 
     hide() {
         this.menu.style.display = 'none';
         this.menu.classList.remove('anim', 'menuIn');
+        if (this.moveMenu) {
+            this.menu.style.position = '';
+            this.menu.style.left = '';
+            this.menu.style.top = '';
+            this.menu.style.width = '';
+            this.container.appendChild(this.menu);
+        }
     }
 
     setupListeners() {
index d463ed0b7df040642b2a0aefea78f23d675bc8b5..23fc64ae6a47cde19602f4af610c75926582d275 100644 (file)
@@ -6,20 +6,23 @@ class ListSortControl {
 
     constructor(elem) {
         this.elem = elem;
+        this.menu = elem.querySelector('ul');
 
         this.sortInput = elem.querySelector('[name="sort"]');
         this.orderInput = elem.querySelector('[name="order"]');
         this.form = elem.querySelector('form');
 
-        this.elem.addEventListener('click', event => {
+        this.menu.addEventListener('click', event => {
             if (event.target.closest('[data-sort-value]') !== null) {
                 this.sortOptionClick(event);
             }
+        });
+
+        this.elem.addEventListener('click', event => {
             if (event.target.closest('[data-sort-dir]') !== null) {
                 this.sortDirectionClick(event);
             }
-        })
-
+        });
     }
 
     sortOptionClick(event) {
index dc4dc8816cd0a4076be6fcb1c60403165b8fb9f3..565c3f0f8c92f7bd0be545e4a72dbff1ba6f74c9 100644 (file)
@@ -510,7 +510,7 @@ ul.pagination {
   position: relative;
 }
 
-.dropdown-container ul {
+.dropdown-menu {
   display: none;
   position: absolute;
   z-index: 999;
index 969682c3ba8c5947eb34977932f89353e1b7daaa..e3b1deb5b9e80ab225b750c23235b5af5a7bbdb5 100755 (executable)
@@ -20,7 +20,7 @@
   }
 }
 
-@include smaller-than($l) {
+@include smaller-than($m) {
   .page-edit-toolbar {
     overflow-x: scroll;
     overflow-y: visible;
   }
 }
 
-@include smaller-than($l) {
+@include smaller-than($m) {
   .page-edit-toolbar #save-button {
     position: fixed;
     z-index: 30;
-    background-color: #FFF;
     border-radius: 50%;
-    width: 42px;
-    height: 42px;
-    font-size: 16px;
+    width: 56px;
+    height: 56px;
+    font-size: 24px;
     right: $-m;
-    bottom: $-xs;
-    box-shadow: $bs-med;
+    bottom: $-s;
+    box-shadow: $bs-hover;
+    background-color: currentColor;
+    svg {
+      fill: #FFF;
+    }
     span {
       display: none;
     }
index 3141f5ab29c410cd88b699f2dc1d542bd46c9e4f..b709b29dcd7ac6ac2d1e0512ec62f0d69845c745 100644 (file)
                     <span>@icon('export')</span>
                     <span>{{ trans('entities.export') }}</span>
                 </div>
-                <ul class="wide">
+                <ul class="wide dropdown-menu">
                     <li><a href="{{ $book->getUrl('/export/html') }}" target="_blank">{{ trans('entities.export_html') }} <span class="text-muted float right">.html</span></a></li>
                     <li><a href="{{ $book->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
                     <li><a href="{{ $book->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.export_text') }} <span class="text-muted float right">.txt</span></a></li>
index f74c584e5fa5b0da7491c1c917342a4b4afcdf44..0915f9a16120afd97a9a9af1e4f0f0ed278fd36d 100644 (file)
                     <span>@icon('export')</span>
                     <span>{{ trans('entities.export') }}</span>
                 </div>
-                <ul class="wide">
+                <ul class="wide dropdown-menu">
                     <li><a href="{{ $chapter->getUrl('/export/html') }}" target="_blank">{{ trans('entities.export_html') }} <span class="text-muted float right">.html</span></a></li>
                     <li><a href="{{ $chapter->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
                     <li><a href="{{ $chapter->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.export_text') }} <span class="text-muted float right">.txt</span></a></li>
index b93e79ac819e4afaef1faf960fda7f304a1653fb..a855031784dc2c181b36691467564064386078c9 100644 (file)
@@ -29,7 +29,7 @@
                 @if(userCan('comment-delete', $comment))
                     <div dropdown class="dropdown-container">
                         <button type="button" dropdown-toggle class="text-button" title="{{ trans('common.delete') }}">@icon('delete')</button>
-                        <ul>
+                        <ul class="dropdown-menu">
                             <li class="px-m text-small text-muted pb-s">{{trans('entities.comment_delete_confirm')}}</li>
                             <li><a action="delete" class="text-button text-neg" >@icon('delete'){{ trans('common.delete') }}</a></li>
                         </ul>
index 7fab6cfda1c91c0387f8e0c5a919c2e4f2151575..734789899b1e804f65efc8ea2fecbbbc040fbef4 100644 (file)
@@ -53,7 +53,7 @@
                             <img class="avatar" src="{{$currentUser->getAvatar(30)}}" alt="{{ $currentUser->name }}">
                             <span class="name">{{ $currentUser->getShortName(9) }}</span> @icon('caret-down')
                         </span>
-                        <ul>
+                        <ul class="dropdown-menu">
                             <li>
                                 <a href="{{ baseUrl("/user/{$currentUser->id}") }}">@icon('user'){{ trans('common.view_profile') }}</a>
                             </li>
index 7a530b0a97b942001aa9e1ef0005ec2c9394bef3..34ded389ca9b9a9bdd1214eb796a8575925cedc3 100644 (file)
             </div>
 
             <div class="text-center px-m py-xs">
-                <div v-show="draftsEnabled" dropdown class="dropdown-container draft-display text">
-                    <a dropdown-toggle class="text-primary text-button"><span class="faded-text" v-text="draftText"></span>&nbsp; @icon('more')</a>
+                <div v-show="draftsEnabled" dropdown dropdown-move-menu class="dropdown-container draft-display text">
+                    <a dropdown-toggle  class="text-primary text-button"><span class="faded-text" v-text="draftText"></span>&nbsp; @icon('more')</a>
                     @icon('check-circle', ['class' => 'text-pos draft-notification svg-icon', ':class' => '{visible: draftUpdated}'])
-                    <ul>
+                    <ul class="dropdown-menu">
                         <li>
                             <a @click="saveDraft()" class="text-pos">@icon('save'){{ trans('entities.pages_edit_save_draft') }}</a>
                         </li>
             </div>
 
             <div class="action-buttons px-m py-xs" v-cloak>
-                <div dropdown class="dropdown-container">
+                <div dropdown dropdown-move-menu class="dropdown-container">
                     <a dropdown-toggle class="text-primary text-button">@icon('edit') <span v-text="changeSummaryShort"></span></a>
-                    <ul class="wide">
+                    <ul class="wide dropdown-menu">
                         <li class="px-l py-m">
                             <p class="text-muted pb-s">{{ trans('entities.pages_edit_enter_changelog_desc') }}</p>
                             <input name="summary" id="summary-input" type="text" placeholder="{{ trans('entities.pages_edit_enter_changelog') }}" v-model="changeSummary" />
                         </li>
                     </ul>
+                    <span>{{-- Prevents button jumping on menu show --}}</span>
                 </div>
 
                 <button type="submit" id="save-button" class="float-left text-primary text-button text-pos-hover">@icon('save')<span>{{ trans('entities.pages_save') }}</span></button>
index e5515a7c9d20fe514a61bdccdaca4d89949bc05d..f3fb048bc85ded30fe5fbd05ce2e3df452f8495e 100644 (file)
@@ -53,7 +53,7 @@
                                     <a href="{{ $revision->getUrl('restore') }}"></a>
                                     <div dropdown class="dropdown-container">
                                         <a dropdown-toggle>{{ trans('entities.pages_revisions_restore') }}</a>
-                                        <ul>
+                                        <ul class="dropdown-menu">
                                             <li class="px-m py-s"><small class="text-muted">{{trans('entities.revision_restore_confirm')}}</small></li>
                                             <li>
                                                 <form action="{{ $revision->getUrl('/restore') }}" method="POST">
@@ -67,7 +67,7 @@
                                     <span class="text-muted">&nbsp;|&nbsp;</span>
                                     <div dropdown class="dropdown-container">
                                         <a dropdown-toggle>{{ trans('common.delete') }}</a>
-                                        <ul>
+                                        <ul class="dropdown-menu">
                                             <li class="px-m py-s"><small class="text-muted">{{trans('entities.revision_delete_confirm')}}</small></li>
                                             <li>
                                                 <form action="{{ $revision->getUrl('/delete/') }}" method="POST">
index 91a90cb00d8d9e5c26356f629624837542b12b09..ff4db2eec4a84df6b787c7f129e1f3cfe262b61e 100644 (file)
                     <span>@icon('export')</span>
                     <span>{{ trans('entities.export') }}</span>
                 </div>
-                <ul class="wide">
+                <ul class="dropdown-menu 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>
index d7ed4d92e32c6bfd42afcc8ae0e3785857de6a4a..9544bcee1e3d828763d96b0870eb11ce088282a1 100644 (file)
         <div class="list-sort">
             <div class="list-sort-type dropdown-container" dropdown>
                 <div dropdown-toggle>{{ $options[$selectedSort] }}</div>
-                <ul>
+                <ul class="dropdown-menu">
                     @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
+                @icon($order === 'desc' ? 'sort-up' : 'sort-down')
             </div>
         </div>
     </form>