1 @extends('simple-layout')
3 {{--TODO - Load books in via selector interface--}}
7 <div class="container">
10 @include('partials.breadcrumbs', ['crumbs' => [
12 $book->getUrl('/sort') => [
13 'text' => trans('entities.books_sort'),
19 <div class="grid left-focus gap-xl">
21 <div class="card content-wrap">
22 <h1 class="list-heading">{{ trans('entities.books_sort') }}</h1>
24 @include('books.sort-box', ['book' => $book, 'bookChildren' => $bookChildren])
27 <form action="{{ $book->getUrl('/sort') }}" method="POST">
29 <input type="hidden" name="_method" value="PUT">
30 <input type="hidden" id="sort-tree-input" name="sort-tree">
31 <div class="list text-right">
32 <a href="{{ $book->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
33 <button class="button primary" type="submit">{{ trans('entities.books_sort_save') }}</button>
40 @if(count($books) > 1)
41 <div class="card content-wrap">
42 <h2 class="list-heading">{{ trans('entities.books_sort_show_other') }}</h2>
43 <div id="additional-books">
44 @foreach($books as $otherBook)
45 @if($otherBook->id !== $book->id)
47 <a href="{{ $otherBook->getUrl('/sort-item') }}" class="text-book">@icon('book'){{ $otherBook->name }}</a>
62 <script src="{{ baseUrl("/libs/jquery-sortable/jquery-sortable.min.js") }}"></script>
64 $(document).ready(function() {
66 const $container = $('#sort-boxes');
69 const sortableOptions = {
70 group: 'serialization',
71 containerSelector: 'ul',
74 onDrop: function ($item, container, _super) {
76 _super($item, container);
78 isValidTarget: function ($item, container) {
79 // Prevent nested chapters
80 return !($item.is('[data-type="chapter"]') && container.target.closest('li').attr('data-type') === 'chapter');
84 // Create our sortable group
85 let group = $('.sort-list').sortable(sortableOptions);
87 // Add additional books into the view on select.
88 $('#additional-books').on('click', 'a', function(e) {
91 const $link = $(this);
92 const url = $link.attr('href');
93 $.get(url, function(data) {
94 $container.append(data);
95 group.sortable("destroy");
96 group = $('.sort-list').sortable(sortableOptions);
102 * Update the input with our sort data.
104 function updateMapInput() {
105 const pageMap = buildEntityMap();
106 $('#sort-tree-input').val(JSON.stringify(pageMap));
110 * Build up a mapping of entities with their ordering and nesting.
113 function buildEntityMap() {
114 const entityMap = [];
115 const $lists = $('.sort-list');
116 $lists.each(function(listIndex) {
117 const $list = $(this);
118 const bookId = $list.closest('[data-type="book"]').attr('data-id');
119 const $directChildren = $list.find('> [data-type="page"], > [data-type="chapter"]');
120 $directChildren.each(function(directChildIndex) {
121 const $childElem = $(this);
122 const type = $childElem.attr('data-type');
123 const parentChapter = false;
124 const childId = $childElem.attr('data-id');
128 sort: directChildIndex,
129 parentChapter: parentChapter,
134 $childElem.find('[data-type="page"]').each(function(pageIndex) {
135 const $chapterChild = $(this);
137 id: $chapterChild.attr('data-id'),
139 parentChapter: childId,
152 const sortOperations = {
153 name: function(a, b) {
154 const aName = a.getAttribute('data-name').trim().toLowerCase();
155 const bName = b.getAttribute('data-name').trim().toLowerCase();
156 return aName.localeCompare(bName);
158 created: function(a, b) {
159 const aTime = Number(a.getAttribute('data-created'));
160 const bTime = Number(b.getAttribute('data-created'));
161 return bTime - aTime;
163 updated: function(a, b) {
164 const aTime = Number(a.getAttribute('data-update'));
165 const bTime = Number(b.getAttribute('data-update'));
166 return bTime - aTime;
168 chaptersFirst: function(a, b) {
169 const aType = a.getAttribute('data-type');
170 const bType = b.getAttribute('data-type');
171 if (aType === bType) {
174 return (aType === 'chapter' ? -1 : 1);
176 chaptersLast: function(a, b) {
177 const aType = a.getAttribute('data-type');
178 const bType = b.getAttribute('data-type');
179 if (aType === bType) {
182 return (aType === 'chapter' ? 1 : -1);
188 const reversableTypes = ['name', 'created', 'updated'];
190 $container.on('click', '.sort-box-options [data-sort]', function(event) {
191 event.preventDefault();
192 const $sortLists = $(this).closest('.sort-box').find('ul');
193 const sort = $(this).attr('data-sort');
195 reverse = (lastSort === sort) ? !reverse : false;
196 let sortFunction = sortOperations[sort];
197 if (reverse && reversableTypes.includes(sort)) {
198 sortFunction = function(a, b) {
199 return 0 - sortOperations[sort](a, b)
203 $sortLists.each(function() {
204 const $list = $(this);
205 $list.children('li').sort(sortFunction).appendTo($list);