Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 1 | /* Copyright 2016 The Chromium Authors. All Rights Reserved. |
| 2 | * |
| 3 | * Use of this source code is governed by a BSD-style |
| 4 | * license that can be found in the LICENSE file or at |
| 5 | * https://developers.google.com/open-source/licenses/bsd |
| 6 | */ |
| 7 | |
| 8 | /** |
| 9 | * Functions used by Monorail to control drag-and-drop re-orderable lists |
| 10 | * |
| 11 | */ |
| 12 | |
| 13 | /** |
| 14 | * Initializes the drag-and-drop functionality on the elements of a |
| 15 | * container node. |
| 16 | * TODO(lukasperaza): allow bulk drag-and-drop |
| 17 | * @param {Element} container The HTML container element to turn into |
| 18 | * a drag-and-drop list. The items of the list must have the |
| 19 | * class 'drag_item' |
| 20 | */ |
| 21 | function TKR_initDragAndDrop(container, opt_onDrop, opt_preventMultiple) { |
| 22 | let dragSrc = null; |
| 23 | let dragLocation = null; |
| 24 | let dragItems = container.getElementsByClassName('drag_item'); |
| 25 | let target = null; |
| 26 | |
| 27 | opt_preventMultiple = opt_preventMultiple || false; |
| 28 | opt_onDrop = opt_onDrop || function() {}; |
| 29 | |
| 30 | function _handleMouseDown(event) { |
| 31 | target = event.target; |
| 32 | } |
| 33 | |
| 34 | function _handleDragStart(event) { |
| 35 | let el = event.currentTarget; |
| 36 | let gripper = el.getElementsByClassName('gripper'); |
| 37 | if (gripper.length && !gripper[0].contains(target)) { |
| 38 | event.preventDefault(); |
| 39 | return; |
| 40 | } |
| 41 | el.style.opacity = 0.4; |
| 42 | event.dataTransfer.setData('text/html', el.outerHTML); |
| 43 | event.dataTransfer.dropEffect = 'move'; |
| 44 | dragSrc = el; |
| 45 | } |
| 46 | |
| 47 | function inRect(rect, x, y) { |
| 48 | if (x < rect.left || x > rect.right) { |
| 49 | return ''; |
| 50 | } else if (rect.top <= y && y <= rect.top + rect.height / 2) { |
| 51 | return 'top'; |
| 52 | } else { |
| 53 | return 'bottom'; |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | function _handleDragOver(event) { |
| 58 | if (dragSrc == null) { |
| 59 | return true; |
| 60 | } |
| 61 | event.preventDefault(); |
| 62 | let el = event.currentTarget; |
| 63 | let rect = el.getBoundingClientRect(), |
| 64 | classes = el.classList; |
| 65 | let section = inRect(rect, event.clientX, event.clientY); |
| 66 | if (section == 'top' && !classes.contains('top')) { |
| 67 | dragLocation = 'top'; |
| 68 | classes.remove('bottom'); |
| 69 | classes.add('top'); |
| 70 | } else if (section == 'bottom' && !classes.contains('bottom')) { |
| 71 | dragLocation = 'bottom'; |
| 72 | classes.remove('top'); |
| 73 | classes.add('bottom'); |
| 74 | } |
| 75 | return false; |
| 76 | } |
| 77 | |
| 78 | function removeClasses(el) { |
| 79 | el.classList.remove('top'); |
| 80 | el.classList.remove('bottom'); |
| 81 | } |
| 82 | |
| 83 | function _handleDragDrop(event) { |
| 84 | let el = event.currentTarget; |
| 85 | if (dragSrc == null || el == dragSrc) { |
| 86 | return true; |
| 87 | } |
| 88 | |
| 89 | if (opt_preventMultiple) { |
| 90 | let dragItems = container.getElementsByClassName('drag_item'); |
| 91 | for (let i = 0; i < dragItems.length; i++) { |
| 92 | dragItems[i].setAttribute('draggable', false); |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | let srcID = dragSrc.getAttribute('data-id'); |
| 97 | let id = el.getAttribute('data-id'); |
| 98 | |
| 99 | if (dragLocation == 'top') { |
| 100 | el.parentNode.insertBefore(dragSrc, el); |
| 101 | opt_onDrop(srcID, id, 'above'); |
| 102 | } else if (dragLocation == 'bottom') { |
| 103 | el.parentNode.insertBefore(dragSrc, el.nextSibling); |
| 104 | opt_onDrop(srcID, id, 'below'); |
| 105 | } |
| 106 | dragSrc.style.opacity = 0.4; |
| 107 | dragSrc = null; |
| 108 | } |
| 109 | |
| 110 | function _handleDragEnd(event) { |
| 111 | if (dragSrc) { |
| 112 | dragSrc.style.opacity = 1; |
| 113 | dragSrc = null; |
| 114 | } |
| 115 | for (let i = 0; i < dragItems.length; i++) { |
| 116 | removeClasses(dragItems[i]); |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | for (let i = 0; i < dragItems.length; i++) { |
| 121 | let el = dragItems[i]; |
| 122 | el.setAttribute('draggable', true); |
| 123 | el.addEventListener('mousedown', _handleMouseDown); |
| 124 | el.addEventListener('dragstart', _handleDragStart); |
| 125 | el.addEventListener('dragover', _handleDragOver); |
| 126 | el.addEventListener('drop', _handleDragDrop); |
| 127 | el.addEventListener('dragend', _handleDragEnd); |
| 128 | el.addEventListener('dragleave', function(event) { |
| 129 | removeClasses(event.currentTarget); |
| 130 | }); |
| 131 | } |
| 132 | } |