From a3e7e754b94a77e6608e1bd35316f849c514a8e5 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Fri, 27 Jan 2023 11:16:17 +0000
Subject: [PATCH] Improves sortable ux

- Fixes multi-select functionality.
- Updated other books to be sticky.
- Added some general intro/desc text.
- Updated sort boxes to be collapsible.
- Cleaned up other books styling.
---
 resources/js/components/book-sort.js          | 12 +++++++-----
 resources/lang/en/entities.php                |  2 ++
 resources/sass/_layout.scss                   |  5 +++++
 resources/sass/_lists.scss                    | 14 ++++++++++++++
 resources/sass/styles.scss                    |  3 +++
 .../views/books/parts/sort-box.blade.php      | 19 +++++++++++++------
 resources/views/books/sort.blade.php          | 11 +++++++----
 7 files changed, 51 insertions(+), 15 deletions(-)

diff --git a/resources/js/components/book-sort.js b/resources/js/components/book-sort.js
index 3ffadf991..2722eb586 100644
--- a/resources/js/components/book-sort.js
+++ b/resources/js/components/book-sort.js
@@ -1,4 +1,4 @@
-import Sortable from "sortablejs";
+import Sortable, {MultiDrag} from "sortablejs";
 import {Component} from "./component";
 import {htmlToDom} from "../services/dom";
 
@@ -44,6 +44,8 @@ export class BookSort extends Component {
         this.sortContainer = this.$refs.sortContainer;
         this.input = this.$refs.input;
 
+        Sortable.mount(new MultiDrag());
+
         const initialSortBox = this.container.querySelector('.sort-box');
         this.setupBookSortable(initialSortBox);
         this.setupSortPresets();
@@ -104,7 +106,7 @@ export class BookSort extends Component {
     }
 
     /**
-     * Setup the given book container element to have sortable items.
+     * Set up the given book container element to have sortable items.
      * @param {Element} bookContainer
      */
     setupBookSortable(bookContainer) {
@@ -125,8 +127,8 @@ export class BookSort extends Component {
             }
         };
 
-        for (let sortElem of sortElems) {
-            new Sortable(sortElem, {
+        for (const sortElem of sortElems) {
+            Sortable.create(sortElem, {
                 group: sortElem.classList.contains('sort-list') ? bookGroupConfig : chapterGroupConfig,
                 animation: 150,
                 fallbackOnBody: true,
@@ -135,7 +137,7 @@ export class BookSort extends Component {
                 dragClass: 'bg-white',
                 ghostClass: 'primary-background-light',
                 multiDrag: true,
-                multiDragKey: 'CTRL',
+                multiDragKey: 'Control',
                 selectedClass: 'sortable-selected',
             });
         }
diff --git a/resources/lang/en/entities.php b/resources/lang/en/entities.php
index fa2586f8d..5b019e848 100644
--- a/resources/lang/en/entities.php
+++ b/resources/lang/en/entities.php
@@ -141,6 +141,7 @@ return [
     'books_search_this' => 'Search this book',
     'books_navigation' => 'Book Navigation',
     'books_sort' => 'Sort Book Contents',
+    'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books.',
     'books_sort_named' => 'Sort Book :bookName',
     'books_sort_name' => 'Sort by Name',
     'books_sort_created' => 'Sort by Created Date',
@@ -148,6 +149,7 @@ return [
     'books_sort_chapters_first' => 'Chapters First',
     'books_sort_chapters_last' => 'Chapters Last',
     'books_sort_show_other' => 'Show Other Books',
+    'books_sort_show_other_desc' => 'Add other books here to include them in the sort operation, and allow easy cross-book reorganisation.',
     'books_sort_save' => 'Save New Order',
     'books_copy' => 'Copy Book',
     'books_copy_success' => 'Book successfully copied',
diff --git a/resources/sass/_layout.scss b/resources/sass/_layout.scss
index 4c7de600b..3fc419046 100644
--- a/resources/sass/_layout.scss
+++ b/resources/sass/_layout.scss
@@ -268,6 +268,11 @@ body.flexbox {
   }
 }
 
+.sticky-top-m {
+  position: sticky;
+  top: $-m;
+}
+
 /**
  * Visibility
  */
diff --git a/resources/sass/_lists.scss b/resources/sass/_lists.scss
index 86a89051f..1ae801267 100644
--- a/resources/sass/_lists.scss
+++ b/resources/sass/_lists.scss
@@ -302,6 +302,20 @@
 .sortable-page-list li.placeholder:before {
   position: absolute;
 }
+.sort-box summary {
+  list-style: none;
+  font-size: .9rem;
+  cursor: pointer;
+}
+.sort-box summary::-webkit-details-marker {
+  display: none;
+}
+details.sort-box summary .caret-container svg {
+  transition: transform ease-in-out 120ms;
+}
+details.sort-box[open] summary .caret-container svg {
+  transform: rotate(90deg);
+}
 
 .activity-list-item {
   padding: $-s 0;
diff --git a/resources/sass/styles.scss b/resources/sass/styles.scss
index 23959d1f8..398d16fac 100644
--- a/resources/sass/styles.scss
+++ b/resources/sass/styles.scss
@@ -199,6 +199,9 @@ $loadingSize: 10px;
     .entity-item-snippet {
       display: none;
     }
+    h4 {
+      font-size: 14px;
+    }
   }
 }
 
diff --git a/resources/views/books/parts/sort-box.blade.php b/resources/views/books/parts/sort-box.blade.php
index ef9929e46..819f1e063 100644
--- a/resources/views/books/parts/sort-box.blade.php
+++ b/resources/views/books/parts/sort-box.blade.php
@@ -1,8 +1,15 @@
-<div class="sort-box" data-type="book" data-id="{{ $book->id }}">
-    <h5 class="text-book entity-list-item no-hover py-xs pl-none">
-        <span>@icon('book')</span>
-        <span>{{ $book->name }}</span>
-    </h5>
+<details class="sort-box" data-type="book" data-id="{{ $book->id }}" open>
+    <summary>
+        <h5 class="flex-container-row items-center justify-flex-start gap-xs">
+            <div class="text-book text-bigger caret-container">
+                @icon('caret-right')
+            </div>
+            <div class="entity-list-item no-hover py-s text-book px-none">
+                <span>@icon('book')</span>
+                <span>{{ $book->name }}</span>
+            </div>
+        </h5>
+    </summary>
     <div class="sort-box-options pb-sm">
         <button type="button" data-sort="name" class="button outline small">{{ trans('entities.books_sort_name') }}</button>
         <button type="button" data-sort="created" class="button outline small">{{ trans('entities.books_sort_created') }}</button>
@@ -45,4 +52,4 @@
         @endforeach
 
     </ul>
-</div>
\ No newline at end of file
+</details>
\ No newline at end of file
diff --git a/resources/views/books/sort.blade.php b/resources/views/books/sort.blade.php
index 077da101d..b778398a8 100644
--- a/resources/views/books/sort.blade.php
+++ b/resources/views/books/sort.blade.php
@@ -16,8 +16,10 @@
 
         <div class="grid left-focus gap-xl">
             <div>
-                <div component="book-sort" class="card content-wrap">
-                    <h1 class="list-heading mb-l">{{ trans('entities.books_sort') }}</h1>
+                <div component="book-sort" class="card content-wrap auto-height">
+                    <h1 class="list-heading">{{ trans('entities.books_sort') }}</h1>
+                    <p class="text-muted">{{ trans('entities.books_sort_desc') }}</p>
+
                     <div refs="book-sort@sortContainer">
                         @include('books.parts.sort-box', ['book' => $book, 'bookChildren' => $bookChildren])
                     </div>
@@ -35,8 +37,9 @@
             </div>
 
             <div>
-                <main class="card content-wrap">
-                    <h2 class="list-heading mb-m">{{ trans('entities.books_sort_show_other') }}</h2>
+                <main class="card content-wrap auto-height sticky-top-m">
+                    <h2 class="list-heading">{{ trans('entities.books_sort_show_other') }}</h2>
+                    <p class="text-muted">{{ trans('entities.books_sort_show_other_desc') }}</p>
 
                     @include('entities.selector', ['name' => 'books_list', 'selectorSize' => 'compact', 'entityTypes' => 'book', 'entityPermission' => 'update', 'showAdd' => true])