advent22/ui/src/components/editor/DoorCanvas.vue

157 lines
3.3 KiB
Vue

<template>
<ThouCanvas
@mousedown.left="draw_start"
@mouseup.left="draw_finish"
@mousedown.right="drag_start"
@mouseup.right="drag_finish"
@mousemove="on_mousemove"
@click.middle="remove_rect"
@dblclick.left="remove_rect"
>
<CalendarDoor
v-for="(door, index) in doors"
:key="`door-${index}`"
:door="door"
force_visible
/>
<SVGRect
v-if="preview_visible"
variant="success"
:rectangle="preview_rect"
visible
/>
</ThouCanvas>
</template>
<script lang="ts">
import { Door } from "@/lib/door";
import { Rectangle } from "@/lib/rectangle";
import { Vector2D } from "@/lib/vector2d";
import { Options, Vue } from "vue-class-component";
import CalendarDoor from "../calendar/CalendarDoor.vue";
import SVGRect from "../calendar/SVGRect.vue";
import ThouCanvas from "../calendar/ThouCanvas.vue";
enum CanvasState {
Idle,
Drawing,
Dragging,
}
@Options({
components: {
CalendarDoor,
SVGRect,
ThouCanvas,
},
props: {
doors: Array,
},
})
export default class extends Vue {
private readonly min_rect_area = 300;
private state = CanvasState.Idle;
public preview_rect = new Rectangle();
private drag_door?: Door;
private drag_origin = new Vector2D();
public doors!: Door[];
public get preview_visible(): boolean {
return this.state !== CanvasState.Idle;
}
private pop_door(point: Vector2D): Door | undefined {
const idx = this.doors.findIndex((rect) => rect.position.contains(point));
if (idx === -1) {
return;
}
return this.doors.splice(idx, 1)[0];
}
public draw_start(event: MouseEvent, point: Vector2D) {
if (this.preview_visible) {
return;
}
this.state = CanvasState.Drawing;
this.preview_rect = new Rectangle(point, point);
}
public draw_finish() {
if (this.state !== CanvasState.Drawing || this.preview_rect === undefined) {
return;
}
this.state = CanvasState.Idle;
if (this.preview_rect.area < this.min_rect_area) {
return;
}
this.doors.push(new Door(this.preview_rect));
}
public drag_start(event: MouseEvent, point: Vector2D) {
if (this.preview_visible) {
return;
}
this.drag_door = this.pop_door(point);
if (this.drag_door === undefined) {
return;
}
this.state = CanvasState.Dragging;
this.drag_origin = point;
this.preview_rect = this.drag_door.position;
}
public drag_finish() {
if (
this.state !== CanvasState.Dragging ||
this.preview_rect === undefined
) {
return;
}
this.state = CanvasState.Idle;
this.doors.push(new Door(this.preview_rect, this.drag_door!.day));
}
public on_mousemove(event: MouseEvent, point: Vector2D) {
if (this.preview_rect === undefined) {
return;
}
if (this.state === CanvasState.Drawing) {
this.preview_rect = this.preview_rect.update(undefined, point);
} else if (this.state === CanvasState.Dragging && this.drag_door) {
const movement = point.minus(this.drag_origin);
this.preview_rect = this.drag_door.position.move(movement);
}
}
public remove_rect(event: MouseEvent, point: Vector2D) {
if (this.preview_visible) {
return;
}
this.pop_door(point);
}
}
</script>
<style lang="scss" scoped>
svg {
cursor: crosshair;
* {
pointer-events: none;
}
}
</style>