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

145 lines
3.2 KiB
Vue

<template>
<SVGRect :rectangle="door.position" :color="editing ? 'yellow' : 'green'" />
<foreignObject
:x="Math.round(parent_aspect_ratio * door.position.left)"
:y="door.position.top"
:width="Math.round(parent_aspect_ratio * door.position.width)"
:height="door.position.height"
:style="`transform: scaleX(${1 / parent_aspect_ratio})`"
>
<div
xmlns="http://www.w3.org/1999/xhtml"
class="px-4 is-flex is-align-items-center is-justify-content-center"
@click.left="on_click"
>
<input
v-if="editing"
v-model="day_str"
ref="day_input"
class="input is-large"
type="number"
:min="MIN_DAY"
placeholder="Tag"
@keydown="on_keydown"
/>
<div v-else class="is-size-1 has-text-weight-bold has-text-danger">
{{ door.day > 0 ? door.day : "*" }}
</div>
</div>
</foreignObject>
</template>
<script lang="ts">
import { Door } from "@/lib/door";
import { Options, Vue } from "vue-class-component";
import SVGRect from "../calendar/SVGRect.vue";
@Options({
components: {
SVGRect,
},
props: {
door: Door,
},
emits: ["update:door"],
})
export default class extends Vue {
public door!: Door;
public readonly MIN_DAY = Door.MIN_DAY;
public day_str = "";
public editing = false;
private refreshKey = 0;
declare $refs: {
day_input: HTMLInputElement | unknown;
};
private refresh() {
window.setTimeout(() => {
// don't loop endlessly
if (this.refreshKey < 10000) {
this.refreshKey++;
}
}, 100);
}
public get parent_aspect_ratio(): number {
this.refreshKey; // read it just to force recompute on change
if (!(this.$el instanceof Text) || this.$el.parentElement === null) {
this.refresh();
return 1;
}
const parent = this.$el.parentElement;
const result = parent.clientWidth / parent.clientHeight;
// force recompute for suspicious results
if (result === 0 || result === Infinity) {
this.refresh();
return 1;
}
return result;
}
private toggle_editing() {
this.day_str = String(this.door.day);
this.editing = !this.editing;
}
public on_click(event: MouseEvent) {
if (event.target === null || !(event.target instanceof HTMLDivElement)) {
return;
}
if (!this.editing) {
const day_input_focus = () => {
if (this.$refs.day_input instanceof HTMLInputElement) {
this.$refs.day_input.select();
return;
}
this.$nextTick(day_input_focus);
};
day_input_focus();
} else {
this.door.day = this.day_str;
}
this.toggle_editing();
}
public on_keydown(event: KeyboardEvent) {
if (!this.editing) {
return;
}
if (event.key === "Enter") {
this.door.day = this.day_str;
this.toggle_editing();
} else if (event.key === "Delete") {
this.door.day = -1;
this.toggle_editing();
} else if (event.key === "Escape") {
this.toggle_editing();
}
}
public beforeUnmount() {
this.$emit("update:door", this.door);
}
}
</script>
<style lang="scss" scoped>
foreignObject {
cursor: pointer;
> div {
height: inherit;
}
}
</style>