]> BookStack Code Mirror - bookstack/blobdiff - resources/js/components/shelf-sort.js
Opensearch: Fixed XML declaration when php short tags enabled
[bookstack] / resources / js / components / shelf-sort.js
index 38e8ae8d3e2a40c2acde17725a448f9d880943cd..b56b01980a147cc17f9cda63bdff62b2ccdf47e4 100644 (file)
@@ -1,11 +1,18 @@
-import Sortable from "sortablejs";
+import Sortable from 'sortablejs';
+import {Component} from './component';
+import {buildListActions, sortActionClickListener} from '../services/dual-lists.ts';
 
-class ShelfSort {
+export class ShelfSort extends Component {
 
-    constructor(elem) {
-        this.elem = elem;
-        this.input = document.getElementById('books-input');
-        this.shelfBooksList = elem.querySelector('[shelf-sort-assigned-books]');
+    setup() {
+        this.elem = this.$el;
+        this.input = this.$refs.input;
+        this.shelfBookList = this.$refs.shelfBookList;
+        this.allBookList = this.$refs.allBookList;
+        this.bookSearchInput = this.$refs.bookSearch;
+        this.sortButtonContainer = this.$refs.sortButtonContainer;
+
+        this.lastSort = null;
 
         this.initSortable();
         this.setupListeners();
@@ -13,10 +20,11 @@ class ShelfSort {
 
     initSortable() {
         const scrollBoxes = this.elem.querySelectorAll('.scroll-box');
-        for (let scrollBox of scrollBoxes) {
+        for (const scrollBox of scrollBoxes) {
             new Sortable(scrollBox, {
                 group: 'shelf-books',
                 ghostClass: 'primary-background-light',
+                handle: '.handle',
                 animation: 150,
                 onSort: this.onChange.bind(this),
             });
@@ -24,33 +32,67 @@ class ShelfSort {
     }
 
     setupListeners() {
-        this.elem.addEventListener('click', event => {
-            const sortItem = event.target.closest('.scroll-box-item:not(.instruction)');
-            if (sortItem) {
-                event.preventDefault();
-                this.sortItemClick(sortItem);
+        const listActions = buildListActions(this.allBookList, this.shelfBookList);
+        const sortActionListener = sortActionClickListener(listActions, this.onChange.bind(this));
+        this.elem.addEventListener('click', sortActionListener);
+
+        this.bookSearchInput.addEventListener('input', () => {
+            this.filterBooksByName(this.bookSearchInput.value);
+        });
+
+        this.sortButtonContainer.addEventListener('click', event => {
+            const button = event.target.closest('button[data-sort]');
+            if (button) {
+                this.sortShelfBooks(button.dataset.sort);
             }
         });
     }
 
     /**
-     * Called when a sort item is clicked.
-     * @param {Element} sortItem
+     * @param {String} filterVal
      */
-    sortItemClick(sortItem) {
-        const lists = this.elem.querySelectorAll('.scroll-box');
-        const newList = Array.from(lists).filter(list => sortItem.parentElement !== list);
-        if (newList.length > 0) {
-            newList[0].appendChild(sortItem);
+    filterBooksByName(filterVal) {
+        // Set height on first search, if not already set, to prevent the distraction
+        // of the list height jumping around
+        if (!this.allBookList.style.height) {
+            this.allBookList.style.height = `${this.allBookList.getBoundingClientRect().height}px`;
+        }
+
+        const books = this.allBookList.children;
+        const lowerFilter = filterVal.trim().toLowerCase();
+
+        for (const bookEl of books) {
+            const show = !filterVal || bookEl.textContent.toLowerCase().includes(lowerFilter);
+            bookEl.style.display = show ? null : 'none';
         }
-        this.onChange();
     }
 
     onChange() {
-        const shelfBookElems = Array.from(this.shelfBooksList.querySelectorAll('[data-id]'));
+        const shelfBookElems = Array.from(this.shelfBookList.querySelectorAll('[data-id]'));
         this.input.value = shelfBookElems.map(elem => elem.getAttribute('data-id')).join(',');
     }
 
-}
+    sortShelfBooks(sortProperty) {
+        const books = Array.from(this.shelfBookList.children);
+        const reverse = sortProperty === this.lastSort;
+
+        books.sort((bookA, bookB) => {
+            const aProp = bookA.dataset[sortProperty].toLowerCase();
+            const bProp = bookB.dataset[sortProperty].toLowerCase();
 
-export default ShelfSort;
\ No newline at end of file
+            if (reverse) {
+                return bProp.localeCompare(aProp);
+            }
+
+            return aProp.localeCompare(bProp);
+        });
+
+        for (const book of books) {
+            this.shelfBookList.append(book);
+        }
+
+        this.lastSort = (this.lastSort === sortProperty) ? null : sortProperty;
+        this.onChange();
+    }
+
+}