266 lines
6.5 KiB
JavaScript
266 lines
6.5 KiB
JavaScript
import ngModule from '../../module';
|
|
import Component from '../../lib/component';
|
|
import './style.scss';
|
|
import './childs';
|
|
import './child';
|
|
|
|
/**
|
|
* Treeview
|
|
*/
|
|
export default class Treeview extends Component {
|
|
constructor($element, $scope, $transclude, $window) {
|
|
super($element, $scope);
|
|
this.$transclude = $transclude;
|
|
this.$window = $window;
|
|
this.readOnly = true;
|
|
|
|
this.element.addEventListener('dragstart',
|
|
event => this.dragStart(event));
|
|
this.element.addEventListener('dragend',
|
|
event => this.dragEnd(event));
|
|
|
|
this.element.addEventListener('dragover',
|
|
event => this.dragOver(event));
|
|
this.element.addEventListener('drop',
|
|
event => this.drop(event));
|
|
this.element.addEventListener('dragenter',
|
|
event => this.dragEnter(event));
|
|
this.element.addEventListener('dragleave',
|
|
event => this.dragLeave(event));
|
|
|
|
this.dropCount = 0;
|
|
}
|
|
|
|
undrop() {
|
|
if (!this.dropping) return;
|
|
this.dropping.classList.remove('dropping');
|
|
this.dropping = null;
|
|
}
|
|
|
|
findDroppable(event) {
|
|
let element = event.target;
|
|
while (element != this.element && !element.droppable)
|
|
element = element.parentNode;
|
|
if (element == this.element)
|
|
return null;
|
|
return element;
|
|
}
|
|
|
|
dragOver(event) {
|
|
this.dragClientY = event.clientY;
|
|
|
|
// Prevents page reload
|
|
event.preventDefault();
|
|
}
|
|
|
|
onDragInterval() {
|
|
if (this.dragClientY > 0 && this.dragClientY < 75)
|
|
this.$window.scrollTo(0, this.$window.scrollY - 25);
|
|
}
|
|
|
|
dragStart(event) {
|
|
event.target.classList.add('dragging');
|
|
event.dataTransfer.setData('text', event.target.id);
|
|
|
|
const element = this.findDroppable(event);
|
|
this.dragging = element;
|
|
|
|
this.interval = setInterval(() => this.onDragInterval(), 100);
|
|
}
|
|
|
|
dragEnd(event) {
|
|
event.target.classList.remove('dragging');
|
|
this.undrop();
|
|
this.dropCount = 0;
|
|
this.dragging = null;
|
|
clearInterval(this.interval);
|
|
}
|
|
|
|
dragEnter(event) {
|
|
let element = this.findDroppable(event);
|
|
if (element) this.dropCount++;
|
|
|
|
if (element != this.dropping) {
|
|
this.undrop();
|
|
if (element) element.classList.add('dropping');
|
|
this.dropping = element;
|
|
}
|
|
}
|
|
|
|
dragLeave(event) {
|
|
let element = this.findDroppable(event);
|
|
|
|
if (element) {
|
|
this.dropCount--;
|
|
if (this.dropCount == 0) this.undrop();
|
|
}
|
|
}
|
|
|
|
drop(event) {
|
|
event.preventDefault();
|
|
this.element.classList.remove('dropping');
|
|
|
|
const $dropped = this.dropping.$ctrl.item;
|
|
const $dragged = this.dragging.$ctrl.item;
|
|
|
|
if ($dropped != $dragged.parent)
|
|
this.emit('drop', {$dropped, $dragged});
|
|
}
|
|
|
|
get data() {
|
|
return this._data;
|
|
}
|
|
|
|
set data(value) {
|
|
this._data = value;
|
|
|
|
const sons = value.length;
|
|
const rootElement = [{
|
|
childs: value,
|
|
active: true,
|
|
sons: sons
|
|
}];
|
|
|
|
this.setParent(rootElement[0], value);
|
|
|
|
this.items = rootElement;
|
|
}
|
|
|
|
fetch() {
|
|
return this.fetchFunc().then(res =>
|
|
this.data = res
|
|
);
|
|
}
|
|
|
|
setParent(parent, childs) {
|
|
childs.forEach(child => {
|
|
child.parent = parent;
|
|
|
|
if (child.childs)
|
|
this.setParent(parent, child.childs);
|
|
});
|
|
}
|
|
|
|
onToggle(event, item) {
|
|
let ignoreEvent = event.defaultPrevented
|
|
|| event == this.lastActionEvent
|
|
|| !item.sons;
|
|
if (ignoreEvent) return;
|
|
|
|
if (item.active)
|
|
this.fold(item);
|
|
else
|
|
this.unfold(item);
|
|
}
|
|
|
|
fold(item) {
|
|
item.childs = undefined;
|
|
item.active = false;
|
|
}
|
|
|
|
unfold(item) {
|
|
return this.fetchFunc({$item: item}).then(newData => {
|
|
this.setParent(item, newData);
|
|
|
|
const childs = item.childs;
|
|
if (childs) {
|
|
childs.forEach(child => {
|
|
let index = newData.findIndex(newChild => {
|
|
return newChild.id == child.id;
|
|
});
|
|
newData[index] = child;
|
|
});
|
|
}
|
|
|
|
if (this.sortFunc) {
|
|
item.childs = newData.sort((a, b) =>
|
|
this.sortFunc({$a: a, $b: b})
|
|
);
|
|
}
|
|
}).then(() => item.active = true);
|
|
}
|
|
|
|
onRemove(event, item) {
|
|
this.lastActionEvent = event;
|
|
if (this.removeFunc)
|
|
this.removeFunc({$item: item});
|
|
}
|
|
|
|
remove(item) {
|
|
const parent = item.parent;
|
|
let childs = parent.childs;
|
|
|
|
if (!childs) childs = [];
|
|
|
|
let index = childs.indexOf(item);
|
|
childs.splice(index, 1);
|
|
if (parent) parent.sons--;
|
|
}
|
|
|
|
onCreate(event, parent) {
|
|
this.lastActionEvent = event;
|
|
if (this.createFunc)
|
|
this.createFunc({$parent: parent});
|
|
}
|
|
|
|
create(item) {
|
|
const parent = item.parent;
|
|
let childs = parent.childs;
|
|
if (!childs) childs = [];
|
|
|
|
if (!parent.active)
|
|
this.unfold(parent);
|
|
else
|
|
childs.push(item);
|
|
|
|
if (this.sortFunc) {
|
|
childs = childs.sort((a, b) =>
|
|
this.sortFunc({$a: a, $b: b})
|
|
);
|
|
}
|
|
|
|
if (parent) parent.sons++;
|
|
}
|
|
|
|
move(item, newParent) {
|
|
if (newParent == item) return;
|
|
|
|
if (item.parent) {
|
|
const parent = item.parent;
|
|
const childs = parent.childs;
|
|
const index = childs.indexOf(item);
|
|
parent.sons--;
|
|
|
|
childs.splice(index, 1);
|
|
}
|
|
|
|
item.parent = newParent;
|
|
|
|
if (!newParent.active) {
|
|
this.unfold(newParent).then(() => {
|
|
item.parent.sons++;
|
|
});
|
|
} else
|
|
this.create(item);
|
|
}
|
|
}
|
|
|
|
Treeview.$inject = ['$element', '$scope', '$transclude', '$window'];
|
|
|
|
ngModule.component('vnTreeview', {
|
|
template: require('./index.html'),
|
|
controller: Treeview,
|
|
bindings: {
|
|
rootLabel: '@',
|
|
data: '<?',
|
|
draggable: '<?',
|
|
droppable: '<?',
|
|
fetchFunc: '&',
|
|
removeFunc: '&?',
|
|
createFunc: '&?',
|
|
sortFunc: '&?',
|
|
readOnly: '<?'
|
|
},
|
|
transclude: true
|
|
});
|