Skip to content

Commit

Permalink
Initial work for drag and drop support (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
cheton committed Jun 20, 2017
1 parent d9abd3d commit fb7cef7
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 4 deletions.
85 changes: 83 additions & 2 deletions examples/classic/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const tree = new InfiniteTree(document.querySelector('#classic [data-id="tree"]'
autoOpen: true, // Defaults to false
droppable: {
hoverClass: 'infinite-tree-drop-hover',
accept: function(opts) {
accept: (opts) => {
const { type, draggableTarget, droppableTarget, node } = opts;

if (elementClass(event.target).has('infinite-tree-overlay')) {
Expand All @@ -43,7 +43,7 @@ const tree = new InfiniteTree(document.querySelector('#classic [data-id="tree"]'

return true;
},
drop: function(e, opts) {
drop: (e, opts) => {
const { draggableTarget, droppableTarget, node } = opts;

if (elementClass(event.target).has('infinite-tree-overlay')) {
Expand Down Expand Up @@ -244,6 +244,87 @@ const load = () => {
}
};

let ghostElement = null;
let draggingX = 0;
let draggingY = 0;

addEventListener(document, 'dragstart', (e) => {
draggingX = 0;
draggingY = 0;
});

addEventListener(document, 'dragend', (e) => {
if (ghostElement) {
ghostElement.parentNode.removeChild(ghostElement);
ghostElement = null;
}
});

addEventListener(tree.contentElement, 'dragover', (e) => {
preventDefault(event);

event = event || window.event;

const movementX = event.x - (Number(draggingX) || event.x);
const movementY = event.y - (Number(draggingY) || event.y);

draggingX = event.x;
draggingY = event.y;

if (movementY === 0) {
return;
}

let el = document.elementFromPoint(event.x, event.y);
while (el && el.parentElement !== tree.contentElement) {
el = el.parentElement;
}
if (!el || el === ghostElement) {
return;
}

const id = el.getAttribute(tree.options.nodeIdAttr);
if (id === undefined) {
return;
}

const rect = el.getBoundingClientRect();
const tolerance = 5;

if (event.y <= rect.top + tolerance) {
if (ghostElement) {
ghostElement.parentNode.removeChild(ghostElement);
ghostElement = null;
}

if (el.parentNode) {
ghostElement = document.createElement('div');
ghostElement.style.height = '20px';
ghostElement.style.border = '1px dotted #ccc';
ghostElement.style.backgroundColor = '#f5f6f7';
el.parentNode.insertBefore(ghostElement, el);
}
} else if (rect.top + el.offsetHeight <= event.y) {
if (el.nextSibling !== ghostElement) {
if (ghostElement) {
ghostElement.parentNode.removeChild(ghostElement);
ghostElement = null;
}

if (el.parentNode) {
ghostElement = document.createElement('div');
ghostElement.style.height = '20px';
ghostElement.style.border = '1px dotted #ccc';
ghostElement.style.backgroundColor = '#f5f6f7';
el.parentNode.insertBefore(ghostElement, el.nextSibling);
}
}
} else if (ghostElement) {
ghostElement.parentNode.removeChild(ghostElement);
ghostElement = null;
}
});

window.examples = {
...window.examples,
classic: {
Expand Down
1 change: 1 addition & 0 deletions examples/classic/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ const renderer = (node, treeOptions) => {
}, toggler + icon + title + loadingIcon + count);

let treeNodeAttributes = {
'draggable': 'true',
'data-id': id,
'data-expanded': more && open,
'data-depth': depth,
Expand Down
87 changes: 85 additions & 2 deletions examples/default/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import classNames from 'classnames';
import elementClass from 'element-class';
import escapeHTML from 'escape-html';
import InfiniteTree from '../../src';
import renderer from './renderer';
import './index.styl';
import './animation.styl';
import { addEventListener, preventDefault, stopPropagation } from '../../src/dom-events';
Expand Down Expand Up @@ -30,7 +31,7 @@ const tree = new InfiniteTree(document.querySelector('#default [data-id="tree"]'
autoOpen: true, // Defaults to false
droppable: {
hoverClass: 'infinite-tree-drop-hover',
accept: function(event, opts) {
accept: (event, opts) => {
const { type, draggableTarget, droppableTarget, node } = opts;

if (elementClass(event.target).has('infinite-tree-overlay')) {
Expand All @@ -42,7 +43,7 @@ const tree = new InfiniteTree(document.querySelector('#default [data-id="tree"]'

return true;
},
drop: function(event, opts) {
drop: (event, opts) => {
const { draggableTarget, droppableTarget, node } = opts;

if (elementClass(event.target).has('infinite-tree-overlay')) {
Expand Down Expand Up @@ -73,6 +74,7 @@ const tree = new InfiniteTree(document.querySelector('#default [data-id="tree"]'
done(null, nodes);
}, 1000);
},
rowRenderer: renderer,
selectable: true, // Defaults to true
shouldSelectNode: (node) => { // Defaults to null
if (!node || (node === tree.getSelectedNode())) {
Expand Down Expand Up @@ -245,6 +247,87 @@ const load = () => {
}
};

let ghostElement = null;
let draggingX = 0;
let draggingY = 0;

addEventListener(document, 'dragstart', (e) => {
draggingX = 0;
draggingY = 0;
});

addEventListener(document, 'dragend', (e) => {
if (ghostElement) {
ghostElement.parentNode.removeChild(ghostElement);
ghostElement = null;
}
});

addEventListener(tree.contentElement, 'dragover', (e) => {
preventDefault(event);

event = event || window.event;

const movementX = event.x - (Number(draggingX) || event.x);
const movementY = event.y - (Number(draggingY) || event.y);

draggingX = event.x;
draggingY = event.y;

if (movementY === 0) {
return;
}

let el = document.elementFromPoint(event.x, event.y);
while (el && el.parentElement !== tree.contentElement) {
el = el.parentElement;
}
if (!el || el === ghostElement) {
return;
}

const id = el.getAttribute(tree.options.nodeIdAttr);
if (id === undefined) {
return;
}

const rect = el.getBoundingClientRect();
const tolerance = 5;

if (event.y <= rect.top + tolerance) {
if (ghostElement) {
ghostElement.parentNode.removeChild(ghostElement);
ghostElement = null;
}

if (el.parentNode) {
ghostElement = document.createElement('div');
ghostElement.style.height = '20px';
ghostElement.style.border = '1px dotted #ccc';
ghostElement.style.backgroundColor = '#f5f6f7';
el.parentNode.insertBefore(ghostElement, el);
}
} else if (rect.top + el.offsetHeight <= event.y) {
if (el.nextSibling !== ghostElement) {
if (ghostElement) {
ghostElement.parentNode.removeChild(ghostElement);
ghostElement = null;
}

if (el.parentNode) {
ghostElement = document.createElement('div');
ghostElement.style.height = '20px';
ghostElement.style.border = '1px dotted #ccc';
ghostElement.style.backgroundColor = '#f5f6f7';
el.parentNode.insertBefore(ghostElement, el.nextSibling);
}
}
} else if (ghostElement) {
ghostElement.parentNode.removeChild(ghostElement);
ghostElement = null;
}
});

window.examples = {
...window.examples,
default: {
Expand Down
60 changes: 60 additions & 0 deletions examples/default/renderer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/* eslint import/prefer-default-export: 0 */
import classNames from 'classnames';
import escapeHTML from 'escape-html';
import tag from 'html5-tag';

export default (node, treeOptions) => {
const { id, name, loadOnDemand = false, children, state } = node;
const droppable = treeOptions.droppable;
const { depth, open, path, total, selected = false } = state;
const childrenLength = Object.keys(children).length;
const more = node.hasChildren();

let togglerContent = '';
if (!more && loadOnDemand) {
togglerContent = '►';
}
if (more && open) {
togglerContent = '▼';
}
if (more && !open) {
togglerContent = '►';
}
const toggler = tag('a', {
'class': (() => {
if (!more && loadOnDemand) {
return classNames(treeOptions.togglerClass, 'infinite-tree-closed');
}
if (more && open) {
return classNames(treeOptions.togglerClass);
}
if (more && !open) {
return classNames(treeOptions.togglerClass, 'infinite-tree-closed');
}
return '';
})()
}, togglerContent);
const title = tag('span', {
'class': classNames('infinite-tree-title')
}, escapeHTML(name));
const treeNode = tag('div', {
'class': 'infinite-tree-node',
'style': `margin-left: ${depth * 18}px`
}, toggler + title);

return tag('div', {
'draggable': 'true',
'data-id': id,
'data-expanded': more && open,
'data-depth': depth,
'data-path': path,
'data-selected': selected,
'data-children': childrenLength,
'data-total': total,
'class': classNames(
'infinite-tree-item',
{ 'infinite-tree-selected': selected }
),
'droppable': droppable
}, treeNode);
};

0 comments on commit fb7cef7

Please sign in to comment.