Moving away from Azure Devops #1
2 changed files with 61 additions and 110 deletions
|
|
@ -180,9 +180,6 @@ export class DragDropManager {
|
||||||
const columnRect = columnElement.getBoundingClientRect();
|
const columnRect = columnElement.getBoundingClientRect();
|
||||||
const targetY = e.clientY - columnRect.top - mouseOffset.y;
|
const targetY = e.clientY - columnRect.top - mouseOffset.y;
|
||||||
|
|
||||||
// Reset scroll compensation
|
|
||||||
this.scrollDeltaY = 0;
|
|
||||||
|
|
||||||
// Initialize drag state
|
// Initialize drag state
|
||||||
this.dragState = {
|
this.dragState = {
|
||||||
eventId,
|
eventId,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
/**
|
/**
|
||||||
* EdgeScrollManager - Auto-scroll when dragging near edges
|
* EdgeScrollManager - Auto-scroll when dragging near viewport edges
|
||||||
* Uses time-based scrolling with 2-zone system for variable speed
|
|
||||||
*
|
*
|
||||||
* Copied from V1 with minor adaptations for V2 event names.
|
* 2-zone system:
|
||||||
|
* - Inner zone (0-50px): Fast scroll (640 px/sec)
|
||||||
|
* - Outer zone (50-100px): Slow scroll (140 px/sec)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IEventBus } from '../types/CalendarTypes';
|
import { IEventBus } from '../types/CalendarTypes';
|
||||||
|
|
@ -19,13 +20,11 @@ export class EdgeScrollManager {
|
||||||
private lastTs = 0;
|
private lastTs = 0;
|
||||||
private rect: DOMRect | null = null;
|
private rect: DOMRect | null = null;
|
||||||
private initialScrollTop = 0;
|
private initialScrollTop = 0;
|
||||||
private scrollListener: ((e: Event) => void) | null = null;
|
|
||||||
|
|
||||||
// Constants
|
|
||||||
private readonly OUTER_ZONE = 100;
|
private readonly OUTER_ZONE = 100;
|
||||||
private readonly INNER_ZONE = 50;
|
private readonly INNER_ZONE = 50;
|
||||||
private readonly SLOW_SPEED_PXS = 140;
|
private readonly SLOW_SPEED = 140;
|
||||||
private readonly FAST_SPEED_PXS = 640;
|
private readonly FAST_SPEED = 640;
|
||||||
|
|
||||||
constructor(private eventBus: IEventBus) {
|
constructor(private eventBus: IEventBus) {
|
||||||
this.subscribeToEvents();
|
this.subscribeToEvents();
|
||||||
|
|
@ -35,13 +34,7 @@ export class EdgeScrollManager {
|
||||||
init(scrollableContent: HTMLElement): void {
|
init(scrollableContent: HTMLElement): void {
|
||||||
this.scrollableContent = scrollableContent;
|
this.scrollableContent = scrollableContent;
|
||||||
this.timeGrid = scrollableContent.querySelector('swp-time-grid');
|
this.timeGrid = scrollableContent.querySelector('swp-time-grid');
|
||||||
|
|
||||||
// Disable smooth scroll for instant auto-scroll
|
|
||||||
this.scrollableContent.style.scrollBehavior = 'auto';
|
this.scrollableContent.style.scrollBehavior = 'auto';
|
||||||
|
|
||||||
// Add scroll listener to detect actual scrolling
|
|
||||||
this.scrollListener = this.handleScroll.bind(this);
|
|
||||||
this.scrollableContent.addEventListener('scroll', this.scrollListener, { passive: true });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private trackMouse = (e: PointerEvent): void => {
|
private trackMouse = (e: PointerEvent): void => {
|
||||||
|
|
@ -64,123 +57,84 @@ export class EdgeScrollManager {
|
||||||
private startDrag(): void {
|
private startDrag(): void {
|
||||||
this.isDragging = true;
|
this.isDragging = true;
|
||||||
this.isScrolling = false;
|
this.isScrolling = false;
|
||||||
this.lastTs = performance.now();
|
this.lastTs = 0;
|
||||||
|
this.initialScrollTop = this.scrollableContent?.scrollTop ?? 0;
|
||||||
if (this.scrollableContent) {
|
|
||||||
this.initialScrollTop = this.scrollableContent.scrollTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.scrollRAF === null) {
|
if (this.scrollRAF === null) {
|
||||||
this.scrollRAF = requestAnimationFrame((ts) => this.scrollTick(ts));
|
this.scrollRAF = requestAnimationFrame(this.scrollTick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private stopDrag(): void {
|
private stopDrag(): void {
|
||||||
this.isDragging = false;
|
this.isDragging = false;
|
||||||
|
this.setScrollingState(false);
|
||||||
if (this.isScrolling) {
|
|
||||||
this.isScrolling = false;
|
|
||||||
this.eventBus.emit(CoreEvents.EDGE_SCROLL_STOPPED, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.scrollRAF !== null) {
|
if (this.scrollRAF !== null) {
|
||||||
cancelAnimationFrame(this.scrollRAF);
|
cancelAnimationFrame(this.scrollRAF);
|
||||||
this.scrollRAF = null;
|
this.scrollRAF = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.rect = null;
|
this.rect = null;
|
||||||
this.lastTs = 0;
|
this.lastTs = 0;
|
||||||
this.initialScrollTop = 0;
|
this.initialScrollTop = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleScroll(): void {
|
private calculateVelocity(): number {
|
||||||
if (!this.isDragging || !this.scrollableContent) return;
|
if (!this.rect) return 0;
|
||||||
|
|
||||||
const currentScrollTop = this.scrollableContent.scrollTop;
|
|
||||||
const scrollDelta = Math.abs(currentScrollTop - this.initialScrollTop);
|
|
||||||
|
|
||||||
if (scrollDelta > 1 && !this.isScrolling) {
|
|
||||||
this.isScrolling = true;
|
|
||||||
this.eventBus.emit(CoreEvents.EDGE_SCROLL_STARTED, {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private scrollTick(ts: number): void {
|
|
||||||
const dt = this.lastTs ? (ts - this.lastTs) / 1000 : 0;
|
|
||||||
this.lastTs = ts;
|
|
||||||
|
|
||||||
if (!this.scrollableContent) {
|
|
||||||
this.stopDrag();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache rect for performance
|
|
||||||
if (!this.rect) {
|
|
||||||
this.rect = this.scrollableContent.getBoundingClientRect();
|
|
||||||
}
|
|
||||||
|
|
||||||
let vy = 0;
|
|
||||||
if (this.isDragging) {
|
|
||||||
const distTop = this.mouseY - this.rect.top;
|
const distTop = this.mouseY - this.rect.top;
|
||||||
const distBot = this.rect.bottom - this.mouseY;
|
const distBot = this.rect.bottom - this.mouseY;
|
||||||
|
|
||||||
// Check top edge
|
if (distTop < this.INNER_ZONE) return -this.FAST_SPEED;
|
||||||
if (distTop < this.INNER_ZONE) {
|
if (distTop < this.OUTER_ZONE) return -this.SLOW_SPEED;
|
||||||
vy = -this.FAST_SPEED_PXS;
|
if (distBot < this.INNER_ZONE) return this.FAST_SPEED;
|
||||||
} else if (distTop < this.OUTER_ZONE) {
|
if (distBot < this.OUTER_ZONE) return this.SLOW_SPEED;
|
||||||
vy = -this.SLOW_SPEED_PXS;
|
|
||||||
}
|
return 0;
|
||||||
// Check bottom edge
|
|
||||||
else if (distBot < this.INNER_ZONE) {
|
|
||||||
vy = this.FAST_SPEED_PXS;
|
|
||||||
} else if (distBot < this.OUTER_ZONE) {
|
|
||||||
vy = this.SLOW_SPEED_PXS;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vy !== 0 && this.isDragging && this.timeGrid && this.draggedElement) {
|
private isAtBoundary(velocity: number): boolean {
|
||||||
const currentScrollTop = this.scrollableContent.scrollTop;
|
if (!this.scrollableContent || !this.timeGrid || !this.draggedElement) return false;
|
||||||
const cloneRect = this.draggedElement.getBoundingClientRect();
|
|
||||||
const cloneBottom = cloneRect.bottom;
|
|
||||||
const timeGridRect = this.timeGrid.getBoundingClientRect();
|
|
||||||
const timeGridBottom = timeGridRect.bottom;
|
|
||||||
|
|
||||||
// Check boundaries
|
const atTop = this.scrollableContent.scrollTop <= 0 && velocity < 0;
|
||||||
const atTop = currentScrollTop <= 0 && vy < 0;
|
const atBottom = velocity > 0 &&
|
||||||
const atBottom = (cloneBottom >= timeGridBottom) && vy > 0;
|
this.draggedElement.getBoundingClientRect().bottom >=
|
||||||
|
this.timeGrid.getBoundingClientRect().bottom;
|
||||||
|
|
||||||
if (atTop || atBottom) {
|
return atTop || atBottom;
|
||||||
if (this.isScrolling) {
|
}
|
||||||
this.isScrolling = false;
|
|
||||||
this.initialScrollTop = this.scrollableContent.scrollTop;
|
private setScrollingState(scrolling: boolean): void {
|
||||||
|
if (this.isScrolling === scrolling) return;
|
||||||
|
|
||||||
|
this.isScrolling = scrolling;
|
||||||
|
if (scrolling) {
|
||||||
|
this.eventBus.emit(CoreEvents.EDGE_SCROLL_STARTED, {});
|
||||||
|
} else {
|
||||||
|
this.initialScrollTop = this.scrollableContent?.scrollTop ?? 0;
|
||||||
this.eventBus.emit(CoreEvents.EDGE_SCROLL_STOPPED, {});
|
this.eventBus.emit(CoreEvents.EDGE_SCROLL_STOPPED, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isDragging) {
|
|
||||||
this.scrollRAF = requestAnimationFrame((ts) => this.scrollTick(ts));
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Apply scroll
|
private scrollTick = (ts: number): void => {
|
||||||
const scrollDelta = vy * dt;
|
if (!this.isDragging || !this.scrollableContent) return;
|
||||||
|
|
||||||
|
const dt = this.lastTs ? (ts - this.lastTs) / 1000 : 0;
|
||||||
|
this.lastTs = ts;
|
||||||
|
this.rect ??= this.scrollableContent.getBoundingClientRect();
|
||||||
|
|
||||||
|
const velocity = this.calculateVelocity();
|
||||||
|
|
||||||
|
if (velocity !== 0 && !this.isAtBoundary(velocity)) {
|
||||||
|
const scrollDelta = velocity * dt;
|
||||||
this.scrollableContent.scrollTop += scrollDelta;
|
this.scrollableContent.scrollTop += scrollDelta;
|
||||||
this.rect = null;
|
this.rect = null;
|
||||||
|
|
||||||
// Emit tick for DragDropManager compensation
|
|
||||||
this.eventBus.emit(CoreEvents.EDGE_SCROLL_TICK, { scrollDelta });
|
this.eventBus.emit(CoreEvents.EDGE_SCROLL_TICK, { scrollDelta });
|
||||||
|
this.setScrollingState(true);
|
||||||
this.scrollRAF = requestAnimationFrame((ts) => this.scrollTick(ts));
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (this.isScrolling) {
|
this.setScrollingState(false);
|
||||||
this.isScrolling = false;
|
|
||||||
this.initialScrollTop = this.scrollableContent.scrollTop;
|
|
||||||
this.eventBus.emit(CoreEvents.EDGE_SCROLL_STOPPED, {});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isDragging) {
|
this.scrollRAF = requestAnimationFrame(this.scrollTick);
|
||||||
this.scrollRAF = requestAnimationFrame((ts) => this.scrollTick(ts));
|
};
|
||||||
} else {
|
|
||||||
this.stopDrag();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue