diff --git a/ui/src/components/CalendarImage.vue b/ui/src/components/CalendarImage.vue index 9288f65..9cf4e89 100644 --- a/ui/src/components/CalendarImage.vue +++ b/ui/src/components/CalendarImage.vue @@ -1,9 +1,185 @@ \ No newline at end of file +class Vector2D { + public x: number; + public y: number; + + constructor(x = 0, y = 0) { + this.x = x; + this.y = y; + } + + public copy(): Vector2D { + return new Vector2D(this.x, this.y); + } + + public minus(other: Vector2D): Vector2D { + return new Vector2D(this.x - other.x, this.y - other.y); + } +} + +class Rectangle { + private origin: Vector2D; + private size: Vector2D; + + constructor(corner1: Vector2D, corner2: Vector2D) { + this.origin = corner1.copy(); + this.size = corner2.minus(corner1); + } + + public normalize() { + if (this.size.x < 0) { + this.size.x *= -1; + this.origin.x -= this.size.x; + } + + if (this.size.y < 0) { + this.size.y *= -1; + this.origin.y -= this.size.y; + } + } +} + +class PreviewState { + public visible = false; + private down_location = new Vector2D(); + private move_location = new Vector2D(); + + public mouse_down(location: Vector2D) { + this.down_location = location; + this.move_location = location; + } + + public mouse_move(location: Vector2D) { + this.move_location = location; + } + + public get_rect(): Rectangle { + return new Rectangle(this.down_location, this.move_location); + } +} + +function get_event_thous(event: MouseEvent): Vector2D { + if (event.currentTarget === null) { + return new Vector2D(); + } + + let target = event.currentTarget as Element; + + return new Vector2D( + Math.round((event.offsetX / target.clientWidth) * 1000), + Math.round((event.offsetY / target.clientHeight) * 1000) + ); +} + +export default class CalendarImage extends Vue { + // "preview" rectangle on click-drag + + private preview_state = new PreviewState(); + + private on_mousedown(event: MouseEvent) { + this.preview_state.visible = true; + this.preview_state.mouse_down(get_event_thous(event)); + } + + private on_mousemove(event: MouseEvent) { + this.preview_state.mouse_move(get_event_thous(event)); + } + + private on_mouseup() { + this.preview_state.visible = false; + } + + private get preview_rectangle(): Rectangle { + let rect = this.preview_state.get_rect(); + rect.normalize(); + + return rect; + } + + // Hook "resize" events + + private resize_observer?: ResizeObserver; + + declare $refs: { + container: HTMLDivElement; + background: HTMLImageElement; + }; + + private on_resize() { + this.$refs.container.style.setProperty( + "height", + this.$refs.background.offsetHeight + "px" + ); + } + + public mounted() { + this.resize_observer = new ResizeObserver(this.on_resize); + this.resize_observer.observe(this.$refs.background); + } + + public unmounted() { + if (this.resize_observer instanceof ResizeObserver) { + this.resize_observer.disconnect(); + delete this.resize_observer; + } + } +} + + + \ No newline at end of file