import Sortable from "sortablejs";
import {Component} from "./component";
+/**
+ * @type {Object<string, function(HTMLElement, HTMLElement, HTMLElement)>}
+ */
+const itemActions = {
+ move_up(item, shelfBooksList, allBooksList) {
+ 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, shelfBooksList, allBooksList) {
+ 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, allBooksList) {
+ shelfBooksList.appendChild(item);
+ },
+};
+
export class ShelfSort extends Component {
setup() {
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();
setupListeners() {
this.elem.addEventListener('click', event => {
- const sortItem = event.target.closest('.scroll-box-item');
- 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', event => {
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
+ * Called when a sort item action button is clicked.
+ * @param {HTMLElement} sortItemAction
*/
- 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);
- }
+ sortItemActionClick(sortItemAction) {
+ const sortItem = sortItemAction.closest('.scroll-box-item');
+ const action = sortItemAction.dataset.action;
+
+ const actionFunction = itemActions[action];
+ actionFunction(sortItem, this.shelfBookList, this.allBookList);
+
this.onChange();
}
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 aProp < bProp ? (aProp === bProp ? 0 : 1) : -1;
+ }
+
+ return aProp < bProp ? (aProp === bProp ? 0 : -1) : 1;
+ });
+
+ for (const book of books) {
+ this.shelfBookList.append(book);
+ }
+
+ this.lastSort = (this.lastSort === sortProperty) ? null : sortProperty;
+ this.onChange();
+ }
+
}
\ No newline at end of file
list-style: none;
padding: 0;
margin: 0;
- max-height: 250px;
+ max-height: 280px;
overflow-y: scroll;
border: 1px solid;
@include lightDark(border-color, #DDD, #000);
input.scroll-box-search, .scroll-box-header-item {
font-size: 0.8rem;
- padding: $-xs $-m;
border: 1px solid;
@include lightDark(border-color, #DDD, #000);
@include lightDark(background-color, #FFF, #222);
.scroll-box[refs="shelf-sort@shelf-book-list"] [data-action="add"] {
display: none;
}
-.scroll-box[refs="shelf-sort@all-book-list"] [data-action="remove"] {
+.scroll-box[refs="shelf-sort@all-book-list"] [data-action="remove"],
+.scroll-box[refs="shelf-sort@all-book-list"] [data-action="move_up"],
+.scroll-box[refs="shelf-sort@all-book-list"] [data-action="move_down"],
+{
display: none;
}
\ No newline at end of file
<label for="books">{{ trans('entities.shelves_books') }}</label>
<input refs="shelf-sort@input" type="hidden" name="books"
value="{{ isset($shelf) ? $shelf->visibleBooks->implode('id', ',') : '' }}">
- <div class="scroll-box-header-item">{{ trans('entities.shelves_drag_books') }}</div>
+ <div class="scroll-box-header-item flex-container-row items-center py-xs">
+ <span class="px-m py-xs">{{ trans('entities.shelves_drag_books') }}</span>
+ <div class="dropdown-container ml-auto" component="dropdown">
+ <button refs="dropdown@toggle"
+ type="button"
+ title="{{ trans('common.more') }}"
+ class="icon-button px-xs py-xxs mx-xs text-bigger"
+ aria-haspopup="true"
+ aria-expanded="false">
+ @icon('more')
+ </button>
+ <div refs="dropdown@menu shelf-sort@sort-button-container" class="dropdown-menu" role="menu">
+ <button type="button" class="text-item" data-sort="name">{{ trans('entities.books_sort_name') }}</button>
+ <button type="button" class="text-item" data-sort="created">{{ trans('entities.books_sort_created') }}</button>
+ <button type="button" class="text-item" data-sort="updated">{{ trans('entities.books_sort_updated') }}</button>
+ </div>
+ </div>
+ </div>
<ul refs="shelf-sort@shelf-book-list" class="scroll-box">
@foreach (($shelf->visibleBooks ?? []) as $book)
@include('shelves.parts.shelf-sort-book-item', ['book' => $book])