refactor drag & drop treeview
gitea/salix/1757-drag_and_drop_department This commit looks good
Details
gitea/salix/1757-drag_and_drop_department This commit looks good
Details
This commit is contained in:
parent
4b235faa8a
commit
7aaa8f4a5e
|
@ -0,0 +1,20 @@
|
||||||
|
<div class="node clickable">
|
||||||
|
<vn-icon
|
||||||
|
class="arrow"
|
||||||
|
ng-class="{invisible: !$ctrl.item.sons}"
|
||||||
|
icon="keyboard_arrow_down"
|
||||||
|
translate-attr="::{title: 'Toggle'}">
|
||||||
|
</vn-icon>
|
||||||
|
<section class="content"></section>
|
||||||
|
<section class="buttons" ng-if="::!$ctrl.treeview.readOnly">
|
||||||
|
<vn-icon-button translate-attr="::{title: 'Remove'}"
|
||||||
|
icon="delete"
|
||||||
|
ng-click="$ctrl.treeview.onRemove($ctrl.item)"
|
||||||
|
ng-if="$ctrl.item.parent">
|
||||||
|
</vn-icon-button>
|
||||||
|
<vn-icon-button translate-attr="::{title: 'Create'}"
|
||||||
|
icon="add_circle"
|
||||||
|
ng-click="$ctrl.treeview.onCreate($ctrl.item)">
|
||||||
|
</vn-icon-button>
|
||||||
|
</section>
|
||||||
|
</div>
|
|
@ -2,22 +2,33 @@ import ngModule from '../../module';
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
constructor($element, $scope, $compile) {
|
constructor($element, $scope, $compile) {
|
||||||
this.$element = $element;
|
|
||||||
this.$scope = $scope;
|
this.$scope = $scope;
|
||||||
this.$compile = $compile;
|
this.$compile = $compile;
|
||||||
|
this.element = $element[0];
|
||||||
|
|
||||||
|
this.element.$ctrl = this;
|
||||||
|
this.element.droppable = true;
|
||||||
|
this.dropCount = 0;
|
||||||
|
this.element.classList.add('vn-droppable');
|
||||||
}
|
}
|
||||||
|
|
||||||
$onInit() {
|
$onInit() {
|
||||||
|
const transcludeElement = this.element.querySelector('.content');
|
||||||
|
const content = angular.element(transcludeElement);
|
||||||
|
|
||||||
if (this.item.parent) {
|
if (this.item.parent) {
|
||||||
this.treeview.$transclude(($clone, $scope) => {
|
this.treeview.$transclude(($clone, $scope) => {
|
||||||
this.$contentScope = $scope;
|
this.$contentScope = $scope;
|
||||||
$scope.item = this.item;
|
$scope.item = this.item;
|
||||||
this.$element.append($clone);
|
content.append($clone);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.element.draggable = true;
|
||||||
|
this.element.classList.add('vn-draggable');
|
||||||
} else {
|
} else {
|
||||||
let template = `<span translate>{{$ctrl.treeview.rootLabel}}</span>`;
|
let template = `<span translate>{{$ctrl.treeview.rootLabel}}</span>`;
|
||||||
let $clone = this.$compile(template)(this.$scope);
|
let $clone = this.$compile(template)(this.$scope);
|
||||||
this.$element.append($clone);
|
content.append($clone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,10 +36,21 @@ class Controller {
|
||||||
if (this.$contentScope)
|
if (this.$contentScope)
|
||||||
this.$contentScope.$destroy();
|
this.$contentScope.$destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dragEnter() {
|
||||||
|
this.dropCount++;
|
||||||
|
|
||||||
|
if (element != this.dropping) {
|
||||||
|
this.undrop();
|
||||||
|
if (element) element.classList.add('dropping');
|
||||||
|
this.dropping = element;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Controller.$inject = ['$element', '$scope', '$compile'];
|
Controller.$inject = ['$element', '$scope', '$compile'];
|
||||||
|
|
||||||
ngModule.component('vnTreeviewContent', {
|
ngModule.component('vnTreeviewChild', {
|
||||||
|
template: require('./child.html'),
|
||||||
controller: Controller,
|
controller: Controller,
|
||||||
bindings: {
|
bindings: {
|
||||||
item: '<'
|
item: '<'
|
|
@ -1,34 +1,11 @@
|
||||||
|
|
||||||
<ul ng-if="$ctrl.items">
|
<ul ng-if="$ctrl.items">
|
||||||
<li ng-repeat="item in $ctrl.items" >
|
<li ng-repeat="item in $ctrl.items">
|
||||||
<div
|
<vn-treeview-child item="item" ng-class="{expanded: item.active}"
|
||||||
ng-class="{expanded: item.active}"
|
ng-click="$ctrl.onClick($event, item)">
|
||||||
ng-click="$ctrl.onClick($event, item)"
|
</vn-treeview-child>
|
||||||
class="node clickable">
|
|
||||||
<vn-icon
|
|
||||||
class="arrow"
|
|
||||||
ng-class="{invisible: !item.sons}"
|
|
||||||
icon="keyboard_arrow_down"
|
|
||||||
translate-attr="::{title: 'Toggle'}">
|
|
||||||
</vn-icon>
|
|
||||||
<vn-treeview-content
|
|
||||||
item="::item">
|
|
||||||
</vn-treeview-content>
|
|
||||||
<section class="buttons" ng-if="::!$ctrl.treeview.readOnly">
|
|
||||||
<vn-icon-button translate-attr="::{title: 'Remove'}"
|
|
||||||
icon="delete"
|
|
||||||
ng-click="$ctrl.treeview.onRemove(item)"
|
|
||||||
ng-if="item.parent">
|
|
||||||
</vn-icon-button>
|
|
||||||
<vn-icon-button translate-attr="::{title: 'Create'}"
|
|
||||||
icon="add_circle"
|
|
||||||
ng-click="$ctrl.treeview.onCreate(item)">
|
|
||||||
</vn-icon-button>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
<vn-treeview-childs
|
<vn-treeview-childs
|
||||||
items="item.childs"
|
items="item.childs">
|
||||||
parent="::item">
|
|
||||||
</vn-treeview-childs>
|
</vn-treeview-childs>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
|
@ -1,18 +1,113 @@
|
||||||
import ngModule from '../../module';
|
import ngModule from '../../module';
|
||||||
import Component from '../../lib/component';
|
import Component from '../../lib/component';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
import './childs';
|
import './childs';
|
||||||
import './content';
|
import './child';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Treeview
|
* Treeview
|
||||||
*/
|
*/
|
||||||
export default class Treeview extends Component {
|
export default class Treeview extends Component {
|
||||||
constructor($element, $scope, $transclude) {
|
constructor($element, $scope, $transclude, $window) {
|
||||||
super($element, $scope);
|
super($element, $scope);
|
||||||
this.$transclude = $transclude;
|
this.$transclude = $transclude;
|
||||||
|
this.$window = $window;
|
||||||
this.readOnly = true;
|
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) {
|
||||||
|
let scrollY = this.$window.scrollY;
|
||||||
|
|
||||||
|
if (event.clientY < this.draggingY)
|
||||||
|
scrollY -= 2;
|
||||||
|
else scrollY += 2;
|
||||||
|
|
||||||
|
this.draggingY = event.clientY;
|
||||||
|
this.$window.scrollTo(0, scrollY);
|
||||||
|
|
||||||
|
// Prevents page reload
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
dragStart(event) {
|
||||||
|
event.target.classList.add('dragging');
|
||||||
|
event.dataTransfer.setData('text', event.target.id);
|
||||||
|
|
||||||
|
const element = this.findDroppable(event);
|
||||||
|
this.dragging = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
dragEnd(event) {
|
||||||
|
event.target.classList.remove('dragging');
|
||||||
|
this.undrop();
|
||||||
|
this.dropCount = 0;
|
||||||
|
this.dragging = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.active && $dropped.sons) {
|
||||||
|
this.unfold($dropped).then(() => {
|
||||||
|
this.emit('drop', {$dropped, $dragged});
|
||||||
|
});
|
||||||
|
} else
|
||||||
|
this.emit('drop', {$dropped, $dragged});
|
||||||
}
|
}
|
||||||
|
|
||||||
get data() {
|
get data() {
|
||||||
|
@ -100,8 +195,14 @@ export default class Treeview extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
onCreate(parent) {
|
onCreate(parent) {
|
||||||
if (this.createFunc)
|
if (this.createFunc) {
|
||||||
this.createFunc({$parent: parent});
|
if (!parent.active && parent.sons) {
|
||||||
|
this.unfold(parent).then(() => {
|
||||||
|
this.createFunc({$parent: parent});
|
||||||
|
});
|
||||||
|
} else
|
||||||
|
this.createFunc({$parent: parent});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
create(item) {
|
create(item) {
|
||||||
|
@ -120,12 +221,24 @@ export default class Treeview extends Component {
|
||||||
if (parent) parent.sons++;
|
if (parent) parent.sons++;
|
||||||
}
|
}
|
||||||
|
|
||||||
onDrop(item, dragged, dropped) {
|
move(item, newParent) {
|
||||||
this.emit('drop', {item, dragged, dropped});
|
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;
|
||||||
|
this.create(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Treeview.$inject = ['$element', '$scope', '$transclude'];
|
Treeview.$inject = ['$element', '$scope', '$transclude', '$window'];
|
||||||
|
|
||||||
ngModule.component('vnTreeview', {
|
ngModule.component('vnTreeview', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
|
|
|
@ -10,22 +10,6 @@ vn-treeview-childs {
|
||||||
li {
|
li {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
|
||||||
& > .node {
|
|
||||||
@extend %clickable;
|
|
||||||
display: flex;
|
|
||||||
padding: 5px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > div > .arrow {
|
|
||||||
min-width: 24px;
|
|
||||||
margin-right: 10px;
|
|
||||||
transition: transform 200ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > div.expanded > .arrow {
|
|
||||||
transform: rotate(180deg);
|
|
||||||
}
|
|
||||||
ul {
|
ul {
|
||||||
padding-left: 2.2em;
|
padding-left: 2.2em;
|
||||||
}
|
}
|
||||||
|
@ -45,8 +29,27 @@ vn-treeview-childs {
|
||||||
.node:hover > .buttons {
|
.node:hover > .buttons {
|
||||||
display: block
|
display: block
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
flex-grow: 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vn-treeview-content {
|
vn-treeview-child {
|
||||||
flex-grow: 1
|
display: block;
|
||||||
}
|
|
||||||
|
.node {
|
||||||
|
@extend %clickable;
|
||||||
|
display: flex;
|
||||||
|
padding: 5px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
& > div > .arrow {
|
||||||
|
min-width: 24px;
|
||||||
|
margin-right: 10px;
|
||||||
|
transition: transform 200ms;
|
||||||
|
}
|
||||||
|
&.expanded > div > .arrow {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,43 +0,0 @@
|
||||||
import ngModule from '../module';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables a draggable element and his drag events
|
|
||||||
*
|
|
||||||
* @return {Object} The directive
|
|
||||||
*/
|
|
||||||
export function directive() {
|
|
||||||
return {
|
|
||||||
restrict: 'A',
|
|
||||||
link: function($scope, $element, $attrs) {
|
|
||||||
const element = $element[0];
|
|
||||||
const isDraggable = $attrs.vnDraggable === 'true';
|
|
||||||
|
|
||||||
if (!isDraggable) return;
|
|
||||||
|
|
||||||
// Set draggable style properties
|
|
||||||
element.style.cursor = 'move';
|
|
||||||
|
|
||||||
|
|
||||||
// Enable as draggable element
|
|
||||||
element.setAttribute('draggable', true);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fires when a drag event starts
|
|
||||||
*/
|
|
||||||
element.addEventListener('dragstart', event => {
|
|
||||||
element.style.opacity = 0.5;
|
|
||||||
event.stopPropagation();
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fires when a drag event ends
|
|
||||||
*/
|
|
||||||
element.addEventListener('dragend', event => {
|
|
||||||
element.style.opacity = 1;
|
|
||||||
event.stopPropagation();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ngModule.directive('vnDraggable', directive);
|
|
|
@ -1,68 +1,43 @@
|
||||||
import ngModule from '../module';
|
import ngModule from '../module';
|
||||||
import './droppable.scss';
|
import './droppable.scss';
|
||||||
|
|
||||||
export function directive($parse) {
|
class Controller {
|
||||||
return {
|
constructor($element, $, $attrs) {
|
||||||
restrict: 'A',
|
this.element = $element[0];
|
||||||
link: function($scope, $element, $attrs) {
|
this.$ = $;
|
||||||
const element = $element[0];
|
this.$attrs = $attrs;
|
||||||
const onDropEvent = $parse($attrs.onDrop);
|
|
||||||
const isDroppable = $attrs.vnDroppable === 'true';
|
|
||||||
|
|
||||||
if (!isDroppable) return;
|
this.element.addEventListener('dragover',
|
||||||
|
event => event.preventDefault()); // Prevents page reload
|
||||||
|
this.element.addEventListener('drop',
|
||||||
|
event => this.drop(event));
|
||||||
|
this.element.addEventListener('dragenter',
|
||||||
|
event => this.dragEnter(event));
|
||||||
|
this.element.addEventListener('dragleave',
|
||||||
|
event => this.dragLeave(event));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
dragEnter(event) {
|
||||||
* Captures current dragging element
|
this.droppedElement = event.target;
|
||||||
*/
|
this.element.classList.add('dropping');
|
||||||
element.addEventListener('dragstart', () => {
|
}
|
||||||
this.dragged = element;
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
dragLeave(event) {
|
||||||
* Enter droppable area event
|
if (this.droppedElement === event.target)
|
||||||
*/
|
this.element.classList.remove('dropping');
|
||||||
element.addEventListener('dragenter', event => {
|
}
|
||||||
element.classList.add('active');
|
|
||||||
|
|
||||||
event.stopImmediatePropagation();
|
drop(event) {
|
||||||
event.preventDefault();
|
if (event.defaultPrevented) return;
|
||||||
}, false);
|
event.preventDefault();
|
||||||
|
this.element.classList.remove('dropping');
|
||||||
|
this.$.$eval(this.$attrs.vnDroppable, {$event: event});
|
||||||
/**
|
}
|
||||||
* Exit droppable area event
|
|
||||||
*/
|
|
||||||
element.addEventListener('dragleave', event => {
|
|
||||||
element.classList.remove('active');
|
|
||||||
|
|
||||||
event.stopImmediatePropagation();
|
|
||||||
event.preventDefault();
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prevent dragover for allowing
|
|
||||||
* dispatch drop event
|
|
||||||
*/
|
|
||||||
element.addEventListener('dragover', event => {
|
|
||||||
event.stopPropagation();
|
|
||||||
event.preventDefault();
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fires when a drop events
|
|
||||||
*/
|
|
||||||
element.addEventListener('drop', event => {
|
|
||||||
element.classList.remove('active');
|
|
||||||
|
|
||||||
onDropEvent($scope, {event});
|
|
||||||
|
|
||||||
event.stopPropagation();
|
|
||||||
event.preventDefault();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
Controller.$inject = ['$element', '$scope', '$attrs'];
|
||||||
|
|
||||||
directive.$inject = ['$parse'];
|
ngModule.directive('vnDroppable', () => {
|
||||||
|
return {
|
||||||
ngModule.directive('vnDroppable', directive);
|
controller: Controller
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
|
@ -1,11 +1,25 @@
|
||||||
@import "./variables";
|
@import "./variables";
|
||||||
|
|
||||||
|
|
||||||
|
.vn-droppable,
|
||||||
|
.vn-draggable,
|
||||||
[vn-droppable] {
|
[vn-droppable] {
|
||||||
border: 2px dashed transparent;
|
border: 2px dashed transparent;
|
||||||
|
border-radius: 0.5em;
|
||||||
transition: all 0.5s;
|
transition: all 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
&.active {
|
.vn-droppable,
|
||||||
|
[vn-droppable] {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
&.dropping {
|
||||||
background-color: $color-hover-cd;
|
background-color: $color-hover-cd;
|
||||||
border: 2px dashed $color-bg-dark;
|
border-color: $color-bg-dark;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vn-draggable.dragging {
|
||||||
|
background-color: $color-main-light;
|
||||||
|
border-color: $color-main;
|
||||||
}
|
}
|
|
@ -11,7 +11,6 @@ import './bind';
|
||||||
import './repeat-last';
|
import './repeat-last';
|
||||||
import './title';
|
import './title';
|
||||||
import './uvc';
|
import './uvc';
|
||||||
import './draggable';
|
|
||||||
import './droppable';
|
import './droppable';
|
||||||
import './http-click';
|
import './http-click';
|
||||||
import './http-submit';
|
import './http-submit';
|
||||||
|
|
|
@ -24,10 +24,12 @@ export default class EventEmitter {
|
||||||
*/
|
*/
|
||||||
off(callback) {
|
off(callback) {
|
||||||
if (!this.$events) return;
|
if (!this.$events) return;
|
||||||
for (let event in this.$events)
|
for (let event in this.$events) {
|
||||||
for (let i = 0; i < event.length; i++)
|
for (let i = 0; i < event.length; i++) {
|
||||||
if (event[i].callback === callback)
|
if (event[i].callback === callback)
|
||||||
event.splice(i--, 1);
|
event.splice(i--, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,10 +39,12 @@ export default class EventEmitter {
|
||||||
*/
|
*/
|
||||||
disconnect(thisArg) {
|
disconnect(thisArg) {
|
||||||
if (!this.$events) return;
|
if (!this.$events) return;
|
||||||
for (let event in this.$events)
|
for (let event in this.$events) {
|
||||||
for (let i = 0; i < event.length; i++)
|
for (let i = 0; i < event.length; i++) {
|
||||||
if (event[i].thisArg === thisArg)
|
if (event[i].thisArg === thisArg)
|
||||||
event.splice(i--, 1);
|
event.splice(i--, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -72,9 +76,10 @@ export default class EventEmitter {
|
||||||
if (this[prop])
|
if (this[prop])
|
||||||
this[prop].disconnect(this);
|
this[prop].disconnect(this);
|
||||||
this[prop] = value;
|
this[prop] = value;
|
||||||
if (value)
|
if (value) {
|
||||||
for (let event in handlers)
|
for (let event in handlers)
|
||||||
value.on(event, handlers[event], this);
|
value.on(event, handlers[event], this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,5 +108,7 @@
|
||||||
"This postal code is not valid": "This postal code is not valid",
|
"This postal code is not valid": "This postal code is not valid",
|
||||||
"is invalid": "is invalid",
|
"is invalid": "is invalid",
|
||||||
"The postcode doesn't exists. Ensure you put the correct format": "El código postal no existe. Asegúrate de ponerlo con el formato correcto",
|
"The postcode doesn't exists. Ensure you put the correct format": "El código postal no existe. Asegúrate de ponerlo con el formato correcto",
|
||||||
"The department name can't be repeated": "El nombre del departamento no puede repetirse"
|
"The department name can't be repeated": "El nombre del departamento no puede repetirse",
|
||||||
|
"You cannot move a parent to any of its sons": "You cannot move a parent to any of its sons",
|
||||||
|
"You cannot move a parent to its own sons": "You cannot move a parent to its own sons"
|
||||||
}
|
}
|
|
@ -5,7 +5,7 @@
|
||||||
data="$ctrl.photos">
|
data="$ctrl.photos">
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
|
|
||||||
<section class="drop-zone" vn-droppable="true" on-drop="$ctrl.onDrop(event)">
|
<section class="drop-zone" vn-droppable="$ctrl.onDrop($event)">
|
||||||
<section><vn-icon icon="add_circle"></vn-icon></section>
|
<section><vn-icon icon="add_circle"></vn-icon></section>
|
||||||
<section translate>Drag & Drop files here...</section>
|
<section translate>Drag & Drop files here...</section>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -35,8 +35,9 @@ class Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onDrop(event) {
|
onDrop($event) {
|
||||||
const files = event.dataTransfer.files;
|
console.log($event);
|
||||||
|
const files = $event.dataTransfer.files;
|
||||||
this.setDefaultParams().then(() => {
|
this.setDefaultParams().then(() => {
|
||||||
this.dms.files = files;
|
this.dms.files = files;
|
||||||
this.create();
|
this.create();
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('moveChild', {
|
||||||
|
description: 'Changes the parent of a child department',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'Number',
|
||||||
|
description: 'The department id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
}, {
|
||||||
|
arg: 'parentId',
|
||||||
|
type: 'Number',
|
||||||
|
description: 'New parent id',
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: 'Object',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:id/moveChild`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.moveChild = async(id, parentId = null) => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const child = await models.Department.findById(id);
|
||||||
|
|
||||||
|
if (id == parentId) return;
|
||||||
|
|
||||||
|
if (parentId) {
|
||||||
|
const parent = await models.Department.findById(parentId);
|
||||||
|
|
||||||
|
if (child.lft < parent.lft && child.rgt > parent.rgt)
|
||||||
|
throw new UserError('You cannot move a parent to its own sons');
|
||||||
|
}
|
||||||
|
|
||||||
|
return child.updateAttribute('parentFk', parentId);
|
||||||
|
};
|
||||||
|
};
|
|
@ -2,4 +2,5 @@ module.exports = Self => {
|
||||||
require('../methods/department/getLeaves')(Self);
|
require('../methods/department/getLeaves')(Self);
|
||||||
require('../methods/department/createChild')(Self);
|
require('../methods/department/createChild')(Self);
|
||||||
require('../methods/department/removeChild')(Self);
|
require('../methods/department/removeChild')(Self);
|
||||||
|
require('../methods/department/moveChild')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,6 +16,15 @@
|
||||||
},
|
},
|
||||||
"parentFk": {
|
"parentFk": {
|
||||||
"type": "Number"
|
"type": "Number"
|
||||||
|
},
|
||||||
|
"lft": {
|
||||||
|
"type": "Number"
|
||||||
|
},
|
||||||
|
"rgt": {
|
||||||
|
"type": "Number"
|
||||||
|
},
|
||||||
|
"sons": {
|
||||||
|
"type": "Number"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,10 @@
|
||||||
fetch-func="$ctrl.onFetch($item)"
|
fetch-func="$ctrl.onFetch($item)"
|
||||||
remove-func="$ctrl.onRemove($item)"
|
remove-func="$ctrl.onRemove($item)"
|
||||||
create-func="$ctrl.onCreate($parent)"
|
create-func="$ctrl.onCreate($parent)"
|
||||||
sort-func="$ctrl.onSort($a, $b)">
|
sort-func="$ctrl.onSort($a, $b)"
|
||||||
|
on-drop="$ctrl.onDrop($dropped, $dragged)"
|
||||||
|
on-drag-start="$ctrl.onDragStart(item)"
|
||||||
|
on-drag-end="$ctrl.onDragEnd(item)">
|
||||||
{{::item.name}}
|
{{::item.name}}
|
||||||
</vn-treeview>
|
</vn-treeview>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
|
|
|
@ -23,19 +23,21 @@ class Controller {
|
||||||
return a.name.localeCompare(b.name);
|
return a.name.localeCompare(b.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* onDrop(item, dragged, dropped) {
|
onDrop(dropped, dragged) {
|
||||||
if (dropped.scope.item) {
|
if (!dropped.active) {
|
||||||
const droppedItem = dropped.scope.item;
|
this.$.treeview.unfold(dropped).then(() =>
|
||||||
const draggedItem = dragged.scope.item;
|
this.move(dropped, dragged));
|
||||||
|
} else
|
||||||
|
this.move(dropped, dragged);
|
||||||
|
}
|
||||||
|
|
||||||
if (droppedItem.childs)
|
move(dropped, dragged) {
|
||||||
droppedItem.childs.push(Object.assign({}, draggedItem));
|
const params = dropped ? {parentId: dropped.id} : null;
|
||||||
|
const query = `/api/departments/${dragged.id}/moveChild`;
|
||||||
dragged.element.remove();
|
this.$http.post(query, params).then(() => {
|
||||||
|
this.$.treeview.move(dragged, dropped);
|
||||||
this.$scope.$apply();
|
});
|
||||||
}
|
}
|
||||||
} */
|
|
||||||
|
|
||||||
onCreate(parent) {
|
onCreate(parent) {
|
||||||
this.newChild = {
|
this.newChild = {
|
||||||
|
@ -62,9 +64,6 @@ class Controller {
|
||||||
if (parent && parent.id)
|
if (parent && parent.id)
|
||||||
params.parentId = parent.id;
|
params.parentId = parent.id;
|
||||||
|
|
||||||
if (!parent.active)
|
|
||||||
this.$.treeview.unfold(parent);
|
|
||||||
|
|
||||||
const query = `/api/departments/createChild`;
|
const query = `/api/departments/createChild`;
|
||||||
this.$http.post(query, params).then(res => {
|
this.$http.post(query, params).then(res => {
|
||||||
const parent = this.newChild.parent;
|
const parent = this.newChild.parent;
|
||||||
|
|
Loading…
Reference in New Issue