]> BookStack Code Mirror - bookstack/blob - resources/js/components/shelf-sort.js
Sorting: Added sort set form manager UI JS
[bookstack] / resources / js / components / shelf-sort.js
1 import Sortable from 'sortablejs';
2 import {Component} from './component';
3 import {buildListActions, sortActionClickListener} from '../services/dual-lists.ts';
4
5 export class ShelfSort extends Component {
6
7     setup() {
8         this.elem = this.$el;
9         this.input = this.$refs.input;
10         this.shelfBookList = this.$refs.shelfBookList;
11         this.allBookList = this.$refs.allBookList;
12         this.bookSearchInput = this.$refs.bookSearch;
13         this.sortButtonContainer = this.$refs.sortButtonContainer;
14
15         this.lastSort = null;
16
17         this.initSortable();
18         this.setupListeners();
19     }
20
21     initSortable() {
22         const scrollBoxes = this.elem.querySelectorAll('.scroll-box');
23         for (const scrollBox of scrollBoxes) {
24             new Sortable(scrollBox, {
25                 group: 'shelf-books',
26                 ghostClass: 'primary-background-light',
27                 handle: '.handle',
28                 animation: 150,
29                 onSort: this.onChange.bind(this),
30             });
31         }
32     }
33
34     setupListeners() {
35         const listActions = buildListActions(this.allBookList, this.shelfBookList);
36         const sortActionListener = sortActionClickListener(listActions, this.onChange.bind(this));
37         this.elem.addEventListener('click', sortActionListener);
38
39         this.bookSearchInput.addEventListener('input', () => {
40             this.filterBooksByName(this.bookSearchInput.value);
41         });
42
43         this.sortButtonContainer.addEventListener('click', event => {
44             const button = event.target.closest('button[data-sort]');
45             if (button) {
46                 this.sortShelfBooks(button.dataset.sort);
47             }
48         });
49     }
50
51     /**
52      * @param {String} filterVal
53      */
54     filterBooksByName(filterVal) {
55         // Set height on first search, if not already set, to prevent the distraction
56         // of the list height jumping around
57         if (!this.allBookList.style.height) {
58             this.allBookList.style.height = `${this.allBookList.getBoundingClientRect().height}px`;
59         }
60
61         const books = this.allBookList.children;
62         const lowerFilter = filterVal.trim().toLowerCase();
63
64         for (const bookEl of books) {
65             const show = !filterVal || bookEl.textContent.toLowerCase().includes(lowerFilter);
66             bookEl.style.display = show ? null : 'none';
67         }
68     }
69
70     onChange() {
71         const shelfBookElems = Array.from(this.shelfBookList.querySelectorAll('[data-id]'));
72         this.input.value = shelfBookElems.map(elem => elem.getAttribute('data-id')).join(',');
73     }
74
75     sortShelfBooks(sortProperty) {
76         const books = Array.from(this.shelfBookList.children);
77         const reverse = sortProperty === this.lastSort;
78
79         books.sort((bookA, bookB) => {
80             const aProp = bookA.dataset[sortProperty].toLowerCase();
81             const bProp = bookB.dataset[sortProperty].toLowerCase();
82
83             if (reverse) {
84                 return bProp.localeCompare(aProp);
85             }
86
87             return aProp.localeCompare(bProp);
88         });
89
90         for (const book of books) {
91             this.shelfBookList.append(book);
92         }
93
94         this.lastSort = (this.lastSort === sortProperty) ? null : sortProperty;
95         this.onChange();
96     }
97
98 }