Refactors resize and position utility handling

Extracts position calculation logic into a separate utility class

Introduces PositionUtils to centralize pixel and minute conversion methods
Removes redundant calculation methods from ResizeHandleManager
Updates Configuration to include default API endpoint

Simplifies resize and height management across components
This commit is contained in:
Janus C. H. Knudsen 2025-11-05 21:53:08 +01:00
parent a1bee99d8e
commit 202074e391
3 changed files with 11 additions and 22 deletions

View file

@ -66,6 +66,7 @@ export class Configuration {
public timeFormatConfig: ITimeFormatConfig; public timeFormatConfig: ITimeFormatConfig;
public currentWorkWeek: string; public currentWorkWeek: string;
public selectedDate: Date; public selectedDate: Date;
public apiEndpoint: string = '/api';
constructor( constructor(
config: ICalendarConfig, config: ICalendarConfig,

View file

@ -2,6 +2,7 @@ import { eventBus } from '../core/EventBus';
import { CoreEvents } from '../constants/CoreEvents'; import { CoreEvents } from '../constants/CoreEvents';
import { Configuration } from '../configurations/CalendarConfig'; import { Configuration } from '../configurations/CalendarConfig';
import { IResizeEndEventPayload } from '../types/EventTypes'; import { IResizeEndEventPayload } from '../types/EventTypes';
import { PositionUtils } from '../utils/PositionUtils';
type SwpEventEl = HTMLElement & { updateHeight?: (h: number) => void }; type SwpEventEl = HTMLElement & { updateHeight?: (h: number) => void };
@ -18,7 +19,6 @@ export class ResizeHandleManager {
private startDurationMin = 0; private startDurationMin = 0;
private direction: 'grow' | 'shrink' = 'grow'; private direction: 'grow' | 'shrink' = 'grow';
private hourHeightPx: number;
private snapMin: number; private snapMin: number;
private minDurationMin: number; private minDurationMin: number;
private animationId: number | null = null; private animationId: number | null = null;
@ -30,11 +30,12 @@ export class ResizeHandleManager {
private pointerCaptured = false; private pointerCaptured = false;
private prevZ?: string; private prevZ?: string;
private config: Configuration; private config: Configuration;
private positionUtils: PositionUtils;
constructor(config: Configuration) { constructor(config: Configuration, positionUtils: PositionUtils) {
this.config = config; this.config = config;
this.positionUtils = positionUtils;
const grid = this.config.gridSettings; const grid = this.config.gridSettings;
this.hourHeightPx = grid.hourHeight;
this.snapMin = grid.snapInterval; this.snapMin = grid.snapInterval;
this.minDurationMin = this.snapMin; // Use snap interval as minimum duration this.minDurationMin = this.snapMin; // Use snap interval as minimum duration
} }
@ -53,19 +54,6 @@ export class ResizeHandleManager {
this.unsubscribers.forEach(u => u()); this.unsubscribers.forEach(u => u());
} }
private minutesPerPx(): number {
return 60 / this.hourHeightPx;
}
private pxFromMinutes(min: number): number {
return (min / 60) * this.hourHeightPx;
}
private roundSnap(min: number, dir: 'grow' | 'shrink'): number {
const q = min / this.snapMin;
return (dir === 'grow' ? Math.ceil(q) : Math.floor(q)) * this.snapMin;
}
private refreshEventCache(): void { private refreshEventCache(): void {
this.cachedEvents = Array.from( this.cachedEvents = Array.from(
document.querySelectorAll<SwpEventEl>('swp-day-columns swp-event') document.querySelectorAll<SwpEventEl>('swp-day-columns swp-event')
@ -167,7 +155,7 @@ export class ResizeHandleManager {
const startHeight = el.offsetHeight; const startHeight = el.offsetHeight;
this.startDurationMin = Math.max( this.startDurationMin = Math.max(
this.minDurationMin, this.minDurationMin,
Math.round(startHeight * this.minutesPerPx()) Math.round(this.positionUtils.pixelsToMinutes(startHeight))
); );
this.prevZ = (el.closest<HTMLElement>('swp-event-group') ?? el).style.zIndex; this.prevZ = (el.closest<HTMLElement>('swp-event-group') ?? el).style.zIndex;
@ -193,9 +181,9 @@ export class ResizeHandleManager {
this.direction = dy >= 0 ? 'grow' : 'shrink'; this.direction = dy >= 0 ? 'grow' : 'shrink';
// Calculate raw height from pixel delta (no snapping - 100% smooth like drag & drop) // Calculate raw height from pixel delta (no snapping - 100% smooth like drag & drop)
const startHeight = this.pxFromMinutes(this.startDurationMin); const startHeight = this.positionUtils.minutesToPixels(this.startDurationMin);
const rawHeight = startHeight + dy; const rawHeight = startHeight + dy;
const minHeight = this.pxFromMinutes(this.minDurationMin); const minHeight = this.positionUtils.minutesToPixels(this.minDurationMin);
this.targetHeight = Math.max(minHeight, rawHeight); // Raw height, no snap this.targetHeight = Math.max(minHeight, rawHeight); // Raw height, no snap
@ -228,9 +216,9 @@ export class ResizeHandleManager {
// Snap to grid on pointer up (like DragDropManager does on mouseUp) // Snap to grid on pointer up (like DragDropManager does on mouseUp)
const currentHeight = this.targetEl.offsetHeight; const currentHeight = this.targetEl.offsetHeight;
const snapDistancePx = this.pxFromMinutes(this.snapMin); const snapDistancePx = this.positionUtils.minutesToPixels(this.snapMin);
const snappedHeight = Math.round(currentHeight / snapDistancePx) * snapDistancePx; const snappedHeight = Math.round(currentHeight / snapDistancePx) * snapDistancePx;
const minHeight = this.pxFromMinutes(this.minDurationMin); const minHeight = this.positionUtils.minutesToPixels(this.minDurationMin);
const finalHeight = Math.max(minHeight, snappedHeight) - 3; // lille gap til grid-linjer const finalHeight = Math.max(minHeight, snappedHeight) - 3; // lille gap til grid-linjer
this.targetEl.updateHeight?.(finalHeight); this.targetEl.updateHeight?.(finalHeight);

View file

@ -17,7 +17,7 @@ export class ApiEventRepository {
private apiEndpoint: string; private apiEndpoint: string;
constructor(config: Configuration) { constructor(config: Configuration) {
this.apiEndpoint = config.apiEndpoint || '/api'; this.apiEndpoint = config.apiEndpoint;
} }
/** /**