]> BookStack Code Mirror - bookstack/blob - resources/js/components/shelf-sort.js
01ca11a333f10289f1fd185dbce66bc3a704316b
[bookstack] / resources / js / components / shelf-sort.js
1 import Sortable from 'sortablejs';
2 import {Component} from './component';
3
4 /**
5  * @type {Object<string, function(HTMLElement, HTMLElement, HTMLElement)>}
6  */
7 const itemActions = {
8     move_up(item) {
9         const list = item.parentNode;
10         const index = Array.from(list.children).indexOf(item);
11         const newIndex = Math.max(index - 1, 0);
12         list.insertBefore(item, list.children[newIndex] || null);
13     },
14     move_down(item) {
15         const list = item.parentNode;
16         const index = Array.from(list.children).indexOf(item);
17         const newIndex = Math.min(index + 2, list.children.length);
18         list.insertBefore(item, list.children[newIndex] || null);
19     },
20     remove(item, shelfBooksList, allBooksList) {
21         allBooksList.appendChild(item);
22     },
23     add(item, shelfBooksList) {
24         shelfBooksList.appendChild(item);
25     },
26 };
27
28 export class ShelfSort extends Component {
29
30     setup() {
31         this.elem = this.$el;
32         this.input = this.$refs.input;
33         this.shelfBookList = this.$refs.shelfBookList;
34         this.allBookList = this.$refs.allBookList;
35         this.bookSearchInput = this.$refs.bookSearch;
36         this.sortButtonContainer = this.$refs.sortButtonContainer;
37
38         this.lastSort = null;
39
40         this.initSortable();
41         this.setupListeners();
42     }
43
44     initSortable() {
45         const scrollBoxes = this.elem.querySelectorAll('.scroll-box');
46         for (const scrollBox of scrollBoxes) {
47             new Sortable(scrollBox, {
48                 group: 'shelf-books',
49                 ghostClass: 'primary-background-light',
50                 handle: '.handle',
51                 animation: 150,
52                 onSort: this.onChange.bind(this),
53             });
54         }
55     }
56
57     setupListeners() {
58         this.elem.addEventListener('click', event => {
59             const sortItemAction = event.target.closest('.scroll-box-item button[data-action]');
60             if (sortItemAction) {
61                 this.sortItemActionClick(sortItemAction);
62             }
63         });
64
65         this.bookSearchInput.addEventListener('input', () => {
66             this.filterBooksByName(this.bookSearchInput.value);
67         });
68
69         this.sortButtonContainer.addEventListener('click', event => {
70             const button = event.target.closest('button[data-sort]');
71             if (button) {
72                 this.sortShelfBooks(button.dataset.sort);
73             }
74         });
75     }
76
77     /**
78      * @param {String} filterVal
79      */
80     filterBooksByName(filterVal) {
81         // Set height on first search, if not already set, to prevent the distraction
82         // of the list height jumping around
83         if (!this.allBookList.style.height) {
84             this.allBookList.style.height = `${this.allBookList.getBoundingClientRect().height}px`;
85         }
86
87         const books = this.allBookList.children;
88         const lowerFilter = filterVal.trim().toLowerCase();
89
90         for (const bookEl of books) {
91             const show = !filterVal || bookEl.textContent.toLowerCase().includes(lowerFilter);
92             bookEl.style.display = show ? null : 'none';
93         }
94     }
95
96     /**
97      * Called when a sort item action button is clicked.
98      * @param {HTMLElement} sortItemAction
99      */
100     sortItemActionClick(sortItemAction) {
101         const sortItem = sortItemAction.closest('.scroll-box-item');
102         const {action} = sortItemAction.dataset;
103
104         const actionFunction = itemActions[action];
105         actionFunction(sortItem, this.shelfBookList, this.allBookList);
106
107         this.onChange();
108     }
109
110     onChange() {
111         const shelfBookElems = Array.from(this.shelfBookList.querySelectorAll('[data-id]'));
112         this.input.value = shelfBookElems.map(elem => elem.getAttribute('data-id')).join(',');
113     }
114
115     sortShelfBooks(sortProperty) {
116         const books = Array.from(this.shelfBookList.children);
117         const reverse = sortProperty === this.lastSort;
118
119         books.sort((bookA, bookB) => {
120             const aProp = bookA.dataset[sortProperty].toLowerCase();
121             const bProp = bookB.dataset[sortProperty].toLowerCase();
122
123             if (reverse) {
124                 return bProp.localeCompare(aProp);
125             }
126
127             return aProp.localeCompare(bProp);
128         });
129
130         for (const book of books) {
131             this.shelfBookList.append(book);
132         }
133
134         this.lastSort = (this.lastSort === sortProperty) ? null : sortProperty;
135         this.onChange();
136     }
137
138 }