blob: e7b4c1e3f81598e707780f314706eff71eaf0c2a [file] [log] [blame]
Copybara854996b2021-09-07 19:36:02 +00001/* 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 */
21function 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}