feat: v4.3.0 - cross-day drag (blocks can move between day rows)
Some checks failed
Build & Push Docker / build (push) Has been cancelled
Some checks failed
Build & Push Docker / build (push) Has been cancelled
This commit is contained in:
@@ -259,22 +259,21 @@ const Canvas = {
|
||||
App.openBlockModal(block.id);
|
||||
});
|
||||
|
||||
// interact.js drag (horizontal only)
|
||||
// interact.js drag (X = time, Y = cross-day)
|
||||
if (window.interact) {
|
||||
interact(el)
|
||||
.draggable({
|
||||
axis: 'x',
|
||||
listeners: {
|
||||
move: (event) => this._onDragMove(event, block),
|
||||
end: (event) => this._onDragEnd(event, block),
|
||||
start: (event) => this._onDragStart(event, block),
|
||||
move: (event) => this._onDragMove(event, block),
|
||||
end: (event) => this._onDragEnd(event, block),
|
||||
},
|
||||
modifiers: [
|
||||
interact.modifiers.snap({
|
||||
targets: [interact.snappers.grid({ x: this._minutesPx(this.GRID_MINUTES), y: 1000 })],
|
||||
targets: [interact.snappers.grid({ x: this._minutesPx(this.GRID_MINUTES), y: 10000 })],
|
||||
range: Infinity,
|
||||
relativePoints: [{ x: 0, y: 0 }]
|
||||
relativePoints: [{ x: 0, y: 0 }],
|
||||
}),
|
||||
interact.modifiers.restrict({ restriction: 'parent' })
|
||||
],
|
||||
inertia: false,
|
||||
})
|
||||
@@ -300,29 +299,72 @@ const Canvas = {
|
||||
return minutes * this.pxPerMinute;
|
||||
},
|
||||
|
||||
_onDragStart(event, block) {
|
||||
const target = event.target;
|
||||
target.dataset.dy = '0';
|
||||
target.style.zIndex = '500';
|
||||
// Enable cross-row overflow while dragging
|
||||
const dayRows = document.getElementById('dayRows');
|
||||
if (dayRows) dayRows.classList.add('dragging');
|
||||
},
|
||||
|
||||
_onDragMove(event, block) {
|
||||
const target = event.target;
|
||||
const x = (parseFloat(target.style.left) || 0);
|
||||
// Convert delta px to minutes
|
||||
const deltaPx = event.dx;
|
||||
const deltaMin = deltaPx / this.pxPerMinute;
|
||||
const newStartMin = this.snapMinutes(this.parseTime(block.start) + deltaMin);
|
||||
|
||||
// ── X axis: update time ──────────────────────────────────────
|
||||
const deltaMin = event.dx / this.pxPerMinute;
|
||||
const duration = this.parseTime(block.end) - this.parseTime(block.start);
|
||||
const newStartMin = this.snapMinutes(this.parseTime(block.start) + deltaMin);
|
||||
const clampedStart = Math.max(this._startMin, Math.min(this._endMin - duration, newStartMin));
|
||||
const newEnd = clampedStart + duration;
|
||||
|
||||
block.start = this.formatTime(clampedStart);
|
||||
block.end = this.formatTime(newEnd);
|
||||
|
||||
block.end = this.formatTime(clampedStart + duration);
|
||||
const totalRange = this._endMin - this._startMin;
|
||||
const leftPct = (clampedStart - this._startMin) / totalRange * 100;
|
||||
target.style.left = leftPct + '%';
|
||||
target.style.left = ((clampedStart - this._startMin) / totalRange * 100) + '%';
|
||||
|
||||
// ── Y axis: visual shift + row highlight ─────────────────────
|
||||
const dy = (parseFloat(target.dataset.dy) || 0) + event.dy;
|
||||
target.dataset.dy = dy;
|
||||
target.style.transform = `translateY(${dy}px)`;
|
||||
|
||||
this._highlightTargetRow(target);
|
||||
},
|
||||
|
||||
_onDragEnd(event, block) {
|
||||
const target = event.target;
|
||||
|
||||
// Detect which day-timeline the block's vertical center is over
|
||||
const rect = target.getBoundingClientRect();
|
||||
const centerY = rect.top + rect.height / 2;
|
||||
for (const row of document.querySelectorAll('.day-timeline')) {
|
||||
const rr = row.getBoundingClientRect();
|
||||
if (centerY >= rr.top && centerY <= rr.bottom) {
|
||||
block.date = row.dataset.date;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
document.querySelectorAll('.day-timeline.drag-target')
|
||||
.forEach(r => r.classList.remove('drag-target'));
|
||||
const dayRows = document.getElementById('dayRows');
|
||||
if (dayRows) dayRows.classList.remove('dragging');
|
||||
target.style.transform = '';
|
||||
target.style.zIndex = '';
|
||||
delete target.dataset.dy;
|
||||
|
||||
App.renderCanvas();
|
||||
},
|
||||
|
||||
_highlightTargetRow(el) {
|
||||
const rect = el.getBoundingClientRect();
|
||||
const centerY = rect.top + rect.height / 2;
|
||||
document.querySelectorAll('.day-timeline').forEach(row => {
|
||||
const rr = row.getBoundingClientRect();
|
||||
const isTarget = centerY >= rr.top && centerY <= rr.bottom;
|
||||
row.classList.toggle('drag-target', isTarget);
|
||||
});
|
||||
},
|
||||
|
||||
_onResizeMove(event, block) {
|
||||
const target = event.target;
|
||||
const newWidthPx = event.rect.width;
|
||||
|
||||
Reference in New Issue
Block a user