X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/022cbb9c004561c902bfea86221424cf2fdb03ac..9186e77d27ea9c620ba0e45de77bdb64c198ca8c:/resources/js/components/book-sort.js diff --git a/resources/js/components/book-sort.js b/resources/js/components/book-sort.js index e8ecd49a4..48557141f 100644 --- a/resources/js/components/book-sort.js +++ b/resources/js/components/book-sort.js @@ -1,25 +1,25 @@ -import Sortable, {MultiDrag} from "sortablejs"; -import {Component} from "./component"; -import {htmlToDom} from "../services/dom"; +import Sortable, {MultiDrag} from 'sortablejs'; +import {Component} from './component'; +import {htmlToDom} from '../services/dom.ts'; // Auto sort control const sortOperations = { - name: function(a, b) { + name(a, b) { const aName = a.getAttribute('data-name').trim().toLowerCase(); const bName = b.getAttribute('data-name').trim().toLowerCase(); return aName.localeCompare(bName); }, - created: function(a, b) { + created(a, b) { const aTime = Number(a.getAttribute('data-created')); const bTime = Number(b.getAttribute('data-created')); return bTime - aTime; }, - updated: function(a, b) { + updated(a, b) { const aTime = Number(a.getAttribute('data-updated')); const bTime = Number(b.getAttribute('data-updated')); return bTime - aTime; }, - chaptersFirst: function(a, b) { + chaptersFirst(a, b) { const aType = a.getAttribute('data-type'); const bType = b.getAttribute('data-type'); if (aType === bType) { @@ -27,7 +27,7 @@ const sortOperations = { } return (aType === 'chapter' ? -1 : 1); }, - chaptersLast: function(a, b) { + chaptersLast(a, b) { const aType = a.getAttribute('data-type'); const bType = b.getAttribute('data-type'); if (aType === bType) { @@ -45,22 +45,22 @@ const sortOperations = { */ const moveActions = { up: { - active(elem, parent, book) { + active(elem, parent) { return !(elem.previousElementSibling === null && !parent); }, - run(elem, parent, book) { + run(elem, parent) { const newSibling = elem.previousElementSibling || parent; newSibling.insertAdjacentElement('beforebegin', elem); - } + }, }, down: { - active(elem, parent, book) { + active(elem, parent) { return !(elem.nextElementSibling === null && !parent); }, - run(elem, parent, book) { + run(elem, parent) { const newSibling = elem.nextElementSibling || parent; newSibling.insertAdjacentElement('afterend', elem); - } + }, }, next_book: { active(elem, parent, book) { @@ -69,7 +69,7 @@ const moveActions = { run(elem, parent, book) { const newList = book.nextElementSibling.querySelector('ul'); newList.prepend(elem); - } + }, }, prev_book: { active(elem, parent, book) { @@ -78,13 +78,13 @@ const moveActions = { run(elem, parent, book) { const newList = book.previousElementSibling.querySelector('ul'); newList.appendChild(elem); - } + }, }, next_chapter: { - active(elem, parent, book) { + active(elem, parent) { return elem.dataset.type === 'page' && this.getNextChapter(elem, parent); }, - run(elem, parent, book) { + run(elem, parent) { const nextChapter = this.getNextChapter(elem, parent); nextChapter.querySelector('ul').prepend(elem); }, @@ -92,14 +92,14 @@ const moveActions = { const topLevel = (parent || elem); const topItems = Array.from(topLevel.parentElement.children); const index = topItems.indexOf(topLevel); - return topItems.slice(index + 1).find(elem => elem.dataset.type === 'chapter'); - } + return topItems.slice(index + 1).find(item => item.dataset.type === 'chapter'); + }, }, prev_chapter: { - active(elem, parent, book) { + active(elem, parent) { return elem.dataset.type === 'page' && this.getPrevChapter(elem, parent); }, - run(elem, parent, book) { + run(elem, parent) { const prevChapter = this.getPrevChapter(elem, parent); prevChapter.querySelector('ul').append(elem); }, @@ -107,40 +107,40 @@ const moveActions = { const topLevel = (parent || elem); const topItems = Array.from(topLevel.parentElement.children); const index = topItems.indexOf(topLevel); - return topItems.slice(0, index).reverse().find(elem => elem.dataset.type === 'chapter'); - } + return topItems.slice(0, index).reverse().find(item => item.dataset.type === 'chapter'); + }, }, book_end: { - active(elem, parent, book) { + active(elem, parent) { return parent || (parent === null && elem.nextElementSibling); }, run(elem, parent, book) { book.querySelector('ul').append(elem); - } + }, }, book_start: { - active(elem, parent, book) { + active(elem, parent) { return parent || (parent === null && elem.previousElementSibling); }, run(elem, parent, book) { book.querySelector('ul').prepend(elem); - } + }, }, before_chapter: { - active(elem, parent, book) { + active(elem, parent) { return parent; }, - run(elem, parent, book) { + run(elem, parent) { parent.insertAdjacentElement('beforebegin', elem); - } + }, }, after_chapter: { - active(elem, parent, book) { + active(elem, parent) { return parent; }, - run(elem, parent, book) { + run(elem, parent) { parent.insertAdjacentElement('afterend', elem); - } + }, }, }; @@ -158,7 +158,7 @@ export class BookSort extends Component { this.setupSortPresets(); this.setupMoveActions(); - window.$events.listen('entity-select-confirm', this.bookSelect.bind(this)); + window.$events.listen('entity-select-change', this.bookSelect.bind(this)); } /** @@ -196,12 +196,12 @@ export class BookSort extends Component { reverse = (lastSort === sort) ? !reverse : false; let sortFunction = sortOperations[sort]; if (reverse && reversibleTypes.includes(sort)) { - sortFunction = function(a, b) { - return 0 - sortOperations[sort](a, b) + sortFunction = function reverseSortOperation(a, b) { + return 0 - sortOperations[sort](a, b); }; } - for (let list of sortLists) { + for (const list of sortLists) { const directItems = Array.from(list.children).filter(child => child.matches('li')); directItems.sort(sortFunction).forEach(sortedItem => { list.appendChild(sortedItem); @@ -221,7 +221,7 @@ export class BookSort extends Component { const alreadyAdded = this.container.querySelector(`[data-type="book"][data-id="${entityInfo.id}"]`) !== null; if (alreadyAdded) return; - const entitySortItemUrl = entityInfo.link + '/sort-item'; + const entitySortItemUrl = `${entityInfo.link}/sort-item`; window.$http.get(entitySortItemUrl).then(resp => { const newBookContainer = htmlToDom(resp.data); this.sortContainer.append(newBookContainer); @@ -249,9 +249,9 @@ export class BookSort extends Component { const chapterGroupConfig = { name: 'chapter', pull: ['book', 'chapter'], - put: function(toList, fromList, draggedElem) { + put(toList, fromList, draggedElem) { return draggedElem.getAttribute('data-type') === 'page'; - } + }, }; for (const sortElem of sortElems) { @@ -260,7 +260,11 @@ export class BookSort extends Component { animation: 150, fallbackOnBody: true, swapThreshold: 0.65, - onSort: this.updateMapInput.bind(this), + onSort: () => { + this.ensureNoNestedChapters(); + this.updateMapInput(); + this.updateMoveActionStateForAll(); + }, dragClass: 'bg-white', ghostClass: 'primary-background-light', multiDrag: true, @@ -270,6 +274,20 @@ export class BookSort extends Component { } } + /** + * Handle nested chapters by moving them to the parent book. + * Needed since sorting with multi-sort only checks group rules based on the active item, + * not all in group, therefore need to manually check after a sort. + * Must be done before updating the map input. + */ + ensureNoNestedChapters() { + const nestedChapters = this.container.querySelectorAll('[data-type="chapter"] [data-type="chapter"]'); + for (const chapter of nestedChapters) { + const parentChapter = chapter.parentElement.closest('[data-type="chapter"]'); + parentChapter.insertAdjacentElement('afterend', chapter); + } + } + /** * Update the input with our sort data. */ @@ -286,7 +304,7 @@ export class BookSort extends Component { const entityMap = []; const lists = this.container.querySelectorAll('.sort-list'); - for (let list of lists) { + for (const list of lists) { const bookId = list.closest('[data-type="book"]').getAttribute('data-id'); const directChildren = Array.from(list.children) .filter(elem => elem.matches('[data-type="page"], [data-type="chapter"]')); @@ -314,9 +332,9 @@ export class BookSort extends Component { entityMap.push({ id: childId, sort: index, - parentChapter: parentChapter, - type: type, - book: bookId + parentChapter, + type, + book: bookId, }); const subPages = childElem.querySelectorAll('[data-type="page"]'); @@ -326,7 +344,7 @@ export class BookSort extends Component { sort: i, parentChapter: childId, type: 'page', - book: bookId + book: bookId, }); } } @@ -365,4 +383,5 @@ export class BookSort extends Component { this.updateMoveActionState(item); } } -} \ No newline at end of file + +}