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