232 lines
7.8 KiB
JavaScript
232 lines
7.8 KiB
JavaScript
// Drag & Drop directives for Budget Page
|
|
|
|
app.directive('draggableItem', function () {
|
|
return {
|
|
restrict: 'A',
|
|
scope: {
|
|
item: '=',
|
|
category: '=',
|
|
onItemDrop: '&'
|
|
},
|
|
link: function (scope, element) {
|
|
function isEditing() {
|
|
return scope.category && scope.category.editing;
|
|
}
|
|
|
|
function updateDraggableAttr() {
|
|
element[0].setAttribute('draggable', isEditing() ? 'true' : 'false');
|
|
element[0].classList.toggle('draggable-enabled', isEditing());
|
|
}
|
|
|
|
updateDraggableAttr();
|
|
scope.$watch('category.editing', updateDraggableAttr);
|
|
|
|
element[0].addEventListener('dragstart', function (e) {
|
|
if (!isEditing()) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
|
|
e.dataTransfer.setData('text/plain', JSON.stringify({
|
|
type: 'item',
|
|
fromCategoryId: scope.category.id,
|
|
itemId: scope.item.id
|
|
}));
|
|
|
|
const ghost = element[0].cloneNode(true);
|
|
ghost.style.position = 'absolute';
|
|
ghost.style.top = '-9999px';
|
|
ghost.style.left = '-9999px';
|
|
ghost.style.width = element[0].offsetWidth + 'px';
|
|
ghost.style.opacity = '0.7';
|
|
document.body.appendChild(ghost);
|
|
e.dataTransfer.setDragImage(ghost, 0, 0);
|
|
|
|
setTimeout(() => document.body.removeChild(ghost), 0);
|
|
|
|
document.querySelectorAll('.drop-zone').forEach(el => el.classList.add('active-drop-target'));
|
|
});
|
|
|
|
element[0].addEventListener('dragend', function () {
|
|
document.querySelectorAll('.drop-zone').forEach(el => el.classList.remove('active-drop-target'));
|
|
});
|
|
}
|
|
};
|
|
});
|
|
|
|
app.directive('dropPlaceholder', function () {
|
|
return {
|
|
restrict: 'A',
|
|
scope: {
|
|
category: '=',
|
|
index: '=',
|
|
onDropItem: '&'
|
|
},
|
|
link: function (scope, element) {
|
|
function isEditing() {
|
|
return scope.category && scope.category.editing;
|
|
}
|
|
|
|
element.addClass('drop-zone');
|
|
element.addClass('drop-placeholder');
|
|
|
|
const label = document.createElement('div');
|
|
label.innerText = 'DROP HERE';
|
|
label.style.textAlign = 'center';
|
|
label.style.fontSize = '12px';
|
|
label.style.color = '#166534';
|
|
label.style.display = 'none';
|
|
element[0].appendChild(label);
|
|
|
|
element[0].addEventListener('dragover', function (e) {
|
|
if (!isEditing()) return;
|
|
const rawData = e.dataTransfer.getData('text/plain');
|
|
if (!rawData.includes('"type":"item"')) return;
|
|
|
|
e.preventDefault();
|
|
element[0].classList.add('drag-over');
|
|
label.style.display = 'block';
|
|
});
|
|
|
|
element[0].addEventListener('dragleave', function () {
|
|
element[0].classList.remove('drag-over');
|
|
label.style.display = 'none';
|
|
});
|
|
|
|
element[0].addEventListener('drop', function (e) {
|
|
if (!isEditing()) return;
|
|
e.preventDefault();
|
|
element[0].classList.remove('drag-over');
|
|
label.style.display = 'none';
|
|
|
|
const dataText = e.dataTransfer.getData('text/plain');
|
|
try {
|
|
const data = JSON.parse(dataText);
|
|
if (data.type !== 'item') return;
|
|
|
|
scope.$apply(() => {
|
|
scope.onDropItem({
|
|
data: data,
|
|
targetCategory: scope.category,
|
|
targetIndex: scope.index
|
|
});
|
|
});
|
|
} catch (err) {
|
|
console.error("Error parsing drop data:", err);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
});
|
|
|
|
app.directive('dropFallback', function () {
|
|
return {
|
|
restrict: 'A',
|
|
scope: {
|
|
category: '=',
|
|
onDropItem: '&'
|
|
},
|
|
link: function (scope, element) {
|
|
element.addClass('drop-zone');
|
|
|
|
const label = document.createElement('div');
|
|
label.innerText = 'DROP HERE';
|
|
label.style.textAlign = 'center';
|
|
label.style.fontSize = '12px';
|
|
label.style.color = '#166534';
|
|
label.style.display = 'none';
|
|
element[0].appendChild(label);
|
|
|
|
element[0].addEventListener('dragover', function (e) {
|
|
const data = e.dataTransfer.getData('text/plain');
|
|
if (!data.includes('"type":"item"')) return;
|
|
e.preventDefault();
|
|
element[0].classList.add('drag-over');
|
|
label.style.display = 'block';
|
|
});
|
|
|
|
element[0].addEventListener('dragleave', function () {
|
|
element[0].classList.remove('drag-over');
|
|
label.style.display = 'none';
|
|
});
|
|
|
|
element[0].addEventListener('drop', function (e) {
|
|
e.preventDefault();
|
|
element[0].classList.remove('drag-over');
|
|
label.style.display = 'none';
|
|
|
|
const data = JSON.parse(e.dataTransfer.getData('text/plain'));
|
|
if (data.type !== 'item') return;
|
|
|
|
scope.$apply(() => {
|
|
scope.onDropItem({
|
|
data: data,
|
|
targetCategory: scope.category,
|
|
targetIndex: scope.category.items.length
|
|
});
|
|
});
|
|
});
|
|
}
|
|
};
|
|
});
|
|
|
|
app.directive('draggableCategory', function () {
|
|
return {
|
|
restrict: 'A',
|
|
scope: {
|
|
category: '=',
|
|
onCategoryDrop: '&'
|
|
},
|
|
link: function (scope, element) {
|
|
const header = element[0].querySelector('.card-header');
|
|
if (!header) return;
|
|
|
|
function updateDraggableAttr() {
|
|
header.setAttribute('draggable', scope.category.allowDrag ? 'true' : 'false');
|
|
}
|
|
|
|
scope.$watch('category.allowDrag', updateDraggableAttr);
|
|
updateDraggableAttr();
|
|
|
|
header.addEventListener('dragstart', function (e) {
|
|
if (!scope.category.allowDrag) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
e.dataTransfer.setData('text/plain', JSON.stringify({
|
|
type: 'category',
|
|
categoryId: scope.category.id
|
|
}));
|
|
});
|
|
|
|
element[0].addEventListener('dragover', function (e) {
|
|
e.preventDefault();
|
|
});
|
|
|
|
element[0].addEventListener('drop', function (e) {
|
|
e.preventDefault();
|
|
element[0].classList.remove('drag-over');
|
|
const data = JSON.parse(e.dataTransfer.getData('text/plain'));
|
|
if (data.type !== 'category') return;
|
|
|
|
scope.$apply(() => {
|
|
scope.onCategoryDrop({
|
|
data: data,
|
|
targetCategory: scope.category
|
|
});
|
|
});
|
|
});
|
|
|
|
element[0].addEventListener('dragenter', function (e) {
|
|
e.preventDefault();
|
|
element[0].classList.add('drag-over');
|
|
});
|
|
|
|
element[0].addEventListener('dragleave', function (e) {
|
|
e.preventDefault();
|
|
element[0].classList.remove('drag-over');
|
|
});
|
|
}
|
|
};
|
|
});
|