X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/f413fc528a8136c2ad8d33a57b3615fa22e55e93..06901b878f2c8057a6f9b7d2e0adfda425c68dee:/resources/js/components/shelf-sort.js diff --git a/resources/js/components/shelf-sort.js b/resources/js/components/shelf-sort.js index 38e8ae8d3..01ca11a33 100644 --- a/resources/js/components/shelf-sort.js +++ b/resources/js/components/shelf-sort.js @@ -1,11 +1,41 @@ -import Sortable from "sortablejs"; +import Sortable from 'sortablejs'; +import {Component} from './component'; -class ShelfSort { +/** + * @type {Object} + */ +const itemActions = { + move_up(item) { + const list = item.parentNode; + const index = Array.from(list.children).indexOf(item); + const newIndex = Math.max(index - 1, 0); + list.insertBefore(item, list.children[newIndex] || null); + }, + move_down(item) { + const list = item.parentNode; + const index = Array.from(list.children).indexOf(item); + const newIndex = Math.min(index + 2, list.children.length); + list.insertBefore(item, list.children[newIndex] || null); + }, + remove(item, shelfBooksList, allBooksList) { + allBooksList.appendChild(item); + }, + add(item, shelfBooksList) { + shelfBooksList.appendChild(item); + }, +}; - constructor(elem) { - this.elem = elem; - this.input = document.getElementById('books-input'); - this.shelfBooksList = elem.querySelector('[shelf-sort-assigned-books]'); +export class ShelfSort extends Component { + + 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 +43,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), }); @@ -25,32 +56,83 @@ 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 sortItemAction = event.target.closest('.scroll-box-item button[data-action]'); + if (sortItemAction) { + this.sortItemActionClick(sortItemAction); + } + }); + + 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'; + } + } + + /** + * Called when a sort item action button is clicked. + * @param {HTMLElement} sortItemAction + */ + sortItemActionClick(sortItemAction) { + const sortItem = sortItemAction.closest('.scroll-box-item'); + const {action} = sortItemAction.dataset; + + const actionFunction = itemActions[action]; + actionFunction(sortItem, this.shelfBookList, this.allBookList); + 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(); + + if (reverse) { + return bProp.localeCompare(aProp); + } + + return aProp.localeCompare(bProp); + }); + + for (const book of books) { + this.shelfBookList.append(book); + } -export default ShelfSort; \ No newline at end of file + this.lastSort = (this.lastSort === sortProperty) ? null : sortProperty; + this.onChange(); + } + +}