157 lines
3.3 KiB
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>
|