176 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			176 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * @copyright  (C) 2019 Open Source Matters, Inc. <https://www.joomla.org>
 | |
|  * @license    GNU General Public License version 2 or later; see LICENSE.txt
 | |
|  */
 | |
| // The container where the draggable will be enabled
 | |
| let url;
 | |
| let direction;
 | |
| let isNested;
 | |
| let dragElementIndex;
 | |
| let dropElementIndex;
 | |
| let container = document.querySelector('.js-draggable');
 | |
| let form;
 | |
| let formData;
 | |
| if (container) {
 | |
|   /** The script expects a form with a class js-form
 | |
|    *  A table with the tbody with a class js-draggable
 | |
|    *                         with a data-url with the ajax request end point and
 | |
|    *                         with a data-direction for asc/desc
 | |
|    */
 | |
|   url = container.dataset.url;
 | |
|   direction = container.dataset.direction;
 | |
|   isNested = container.dataset.nested;
 | |
| } else if (Joomla.getOptions('draggable-list')) {
 | |
|   const options = Joomla.getOptions('draggable-list');
 | |
|   container = document.querySelector(options.id);
 | |
|   /**
 | |
|    * This is here to make the transition to new forms easier.
 | |
|    */
 | |
|   if (!container.classList.contains('js-draggable')) {
 | |
|     container.classList.add('js-draggable');
 | |
|   }
 | |
|   ({
 | |
|     url
 | |
|   } = options);
 | |
|   ({
 | |
|     direction
 | |
|   } = options);
 | |
|   isNested = options.nested;
 | |
| }
 | |
| if (container) {
 | |
|   // Get the form
 | |
|   form = container.closest('form');
 | |
|   // Get the form data
 | |
|   formData = new FormData(form);
 | |
|   formData.delete('task');
 | |
|   formData.delete('order[]');
 | |
| 
 | |
|   // IOS 10 BUG
 | |
|   document.addEventListener('touchstart', () => {}, false);
 | |
|   const getOrderData = (rows, inputRows, dragIndex, dropIndex) => {
 | |
|     let i;
 | |
|     const result = [];
 | |
| 
 | |
|     // Element is moved down
 | |
|     if (dragIndex < dropIndex) {
 | |
|       rows[dropIndex].value = rows[dropIndex - 1].value;
 | |
|       for (i = dragIndex; i < dropIndex; i += 1) {
 | |
|         if (direction === 'asc') {
 | |
|           rows[i].value = parseInt(rows[i].value, 10) - 1;
 | |
|         } else {
 | |
|           rows[i].value = parseInt(rows[i].value, 10) + 1;
 | |
|         }
 | |
|       }
 | |
|     } else {
 | |
|       // Element is moved up
 | |
|       rows[dropIndex].value = rows[dropIndex + 1].value;
 | |
|       for (i = dropIndex + 1; i <= dragIndex; i += 1) {
 | |
|         if (direction === 'asc') {
 | |
|           rows[i].value = parseInt(rows[i].value, 10) + 1;
 | |
|         } else {
 | |
|           rows[i].value = parseInt(rows[i].value, 10) - 1;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     for (i = 0; i < rows.length - 1; i += 1) {
 | |
|       result.push(`order[]=${encodeURIComponent(rows[i].value)}`);
 | |
|       result.push(`cid[]=${encodeURIComponent(inputRows[i].value)}`);
 | |
|     }
 | |
|     return result;
 | |
|   };
 | |
|   const rearrangeChildren = $parent => {
 | |
|     if (!$parent.dataset.itemId) {
 | |
|       return;
 | |
|     }
 | |
|     const parentId = $parent.dataset.itemId;
 | |
|     // Get children list. Each child row should have
 | |
|     // an attribute data-parents=" 1 2 3" where the number is id of parent
 | |
|     const $children = container.querySelectorAll(`tr[data-parents~="${parentId}"]`);
 | |
|     if ($children.length) {
 | |
|       $parent.after(...$children);
 | |
|     }
 | |
|   };
 | |
|   const saveTheOrder = el => {
 | |
|     let orderSelector;
 | |
|     let inputSelector;
 | |
|     let rowSelector;
 | |
|     const groupId = el.dataset.draggableGroup;
 | |
|     if (groupId) {
 | |
|       rowSelector = `tr[data-draggable-group="${groupId}"]`;
 | |
|       orderSelector = `[data-draggable-group="${groupId}"] [name="order[]"]`;
 | |
|       inputSelector = `[data-draggable-group="${groupId}"] [name="cid[]"]`;
 | |
|     } else {
 | |
|       rowSelector = 'tr';
 | |
|       orderSelector = '[name="order[]"]';
 | |
|       inputSelector = '[name="cid[]"]';
 | |
|     }
 | |
|     const rowElements = [].slice.call(container.querySelectorAll(rowSelector));
 | |
|     const rows = [].slice.call(container.querySelectorAll(orderSelector));
 | |
|     const inputRows = [].slice.call(container.querySelectorAll(inputSelector));
 | |
|     dropElementIndex = rowElements.indexOf(el);
 | |
|     if (url) {
 | |
|       // Detach task field if exists
 | |
|       const task = document.querySelector('[name="task"]');
 | |
| 
 | |
|       // Detach task field if exists
 | |
|       if (task) {
 | |
|         task.setAttribute('name', 'some__Temporary__Name__');
 | |
|       }
 | |
| 
 | |
|       // Prepare the options
 | |
|       const ajaxOptions = {
 | |
|         url,
 | |
|         method: 'POST',
 | |
|         data: `${new URLSearchParams(formData).toString()}&${getOrderData(rows, inputRows, dragElementIndex, dropElementIndex).join('&')}`,
 | |
|         perform: true
 | |
|       };
 | |
|       Joomla.request(ajaxOptions);
 | |
| 
 | |
|       // Re-Append original task field
 | |
|       if (task) {
 | |
|         task.setAttribute('name', 'task');
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Update positions for a children of the moved item
 | |
|     rearrangeChildren(el);
 | |
|   };
 | |
| 
 | |
|   // eslint-disable-next-line no-undef
 | |
|   dragula([container], {
 | |
|     // Y axis is considered when determining where an element would be dropped
 | |
|     direction: 'vertical',
 | |
|     // elements are moved by default, not copied
 | |
|     copy: false,
 | |
|     // elements in copy-source containers can be reordered
 | |
|     // copySortSource: true,
 | |
|     // spilling will put the element back where it was dragged from, if this is true
 | |
|     revertOnSpill: true,
 | |
|     // spilling will `.remove` the element, if this is true
 | |
|     // removeOnSpill: false,
 | |
| 
 | |
|     accepts(el, target, source, sibling) {
 | |
|       if (isNested) {
 | |
|         if (sibling !== null) {
 | |
|           return sibling.dataset.draggableGroup && sibling.dataset.draggableGroup === el.dataset.draggableGroup;
 | |
|         }
 | |
|         return sibling === null || sibling && sibling.tagName.toLowerCase() === 'tr';
 | |
|       }
 | |
|       return sibling === null || sibling && sibling.tagName.toLowerCase() === 'tr';
 | |
|     },
 | |
|     mirrorContainer: container
 | |
|   }).on('drag', el => {
 | |
|     let rowSelector;
 | |
|     const groupId = el.dataset.draggableGroup;
 | |
|     if (groupId) {
 | |
|       rowSelector = `tr[data-draggable-group="${groupId}"]`;
 | |
|     } else {
 | |
|       rowSelector = 'tr';
 | |
|     }
 | |
|     const rowElements = [].slice.call(container.querySelectorAll(rowSelector));
 | |
|     dragElementIndex = rowElements.indexOf(el);
 | |
|   }).on('drop', el => {
 | |
|     saveTheOrder(el);
 | |
|   });
 | |
| }
 |