mirror of
https://code.lenaisten.de/Lenaisten/advent22.git
synced 2025-12-14 11:43:01 +00:00
🚧 wip on ui: rework for vue 3 composition API
This commit is contained in:
parent
9079fca30f
commit
39b56d8bd0
18 changed files with 118 additions and 115 deletions
|
|
@ -33,10 +33,10 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="level-right">
|
<div class="level-right">
|
||||||
<div class="level-item">
|
<div class="level-item">
|
||||||
<TouchButton class="tag is-warning" />
|
<TouchButton class="is-small is-warning" />
|
||||||
</div>
|
</div>
|
||||||
<div class="level-item">
|
<div class="level-item">
|
||||||
<AdminButton class="tag is-link is-outlined" />
|
<AdminButton class="is-small is-link is-outlined" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,13 @@
|
||||||
import { APIError } from "@/lib/api_error";
|
import { APIError } from "@/lib/api_error";
|
||||||
import { Credentials } from "@/lib/model";
|
import { Credentials } from "@/lib/model";
|
||||||
import { advent22Store } from "@/lib/store";
|
import { advent22Store } from "@/lib/store";
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import BulmaButton from "./bulma/Button.vue";
|
import BulmaButton from "./bulma/Button.vue";
|
||||||
import LoginModal from "./LoginModal.vue";
|
import LoginModal from "./LoginModal.vue";
|
||||||
|
|
||||||
let modal_visible = false;
|
const modal_visible = ref(false);
|
||||||
let is_busy = false;
|
const is_busy = ref(false);
|
||||||
const store = advent22Store();
|
const store = advent22Store();
|
||||||
|
|
||||||
function on_click() {
|
function on_click() {
|
||||||
|
|
@ -27,22 +28,22 @@ function on_click() {
|
||||||
store.logout();
|
store.logout();
|
||||||
} else {
|
} else {
|
||||||
// show login modal
|
// show login modal
|
||||||
is_busy = true;
|
is_busy.value = true;
|
||||||
modal_visible = true;
|
modal_visible.value = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function on_submit(creds: Credentials) {
|
function on_submit(creds: Credentials) {
|
||||||
modal_visible = false;
|
modal_visible.value = false;
|
||||||
|
|
||||||
store
|
store
|
||||||
.login(creds)
|
.login(creds)
|
||||||
.catch((error) => APIError.alert(error))
|
.catch((error) => APIError.alert(error))
|
||||||
.finally(() => (is_busy = false));
|
.finally(() => (is_busy.value = false));
|
||||||
}
|
}
|
||||||
|
|
||||||
function on_cancel() {
|
function on_cancel() {
|
||||||
modal_visible = false;
|
modal_visible.value = false;
|
||||||
is_busy = false;
|
is_busy.value = false;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ import { API } from "@/lib/api";
|
||||||
import { APIError } from "@/lib/api_error";
|
import { APIError } from "@/lib/api_error";
|
||||||
import { ensure_loaded, name_door } from "@/lib/helpers";
|
import { ensure_loaded, name_door } from "@/lib/helpers";
|
||||||
import { ImageData } from "@/lib/model";
|
import { ImageData } from "@/lib/model";
|
||||||
import { Door } from "@/lib/rects/door";
|
import { VueDoor } from "@/lib/rects/door";
|
||||||
import { advent22Store } from "@/lib/store";
|
import { advent22Store } from "@/lib/store";
|
||||||
|
|
||||||
import { onBeforeUnmount } from "vue";
|
import { onBeforeUnmount } from "vue";
|
||||||
|
|
@ -62,7 +62,7 @@ import CalendarDoor from "./calendar/CalendarDoor.vue";
|
||||||
import ThouCanvas from "./calendar/ThouCanvas.vue";
|
import ThouCanvas from "./calendar/ThouCanvas.vue";
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
doors: Door[];
|
doors: VueDoor[];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const store = advent22Store();
|
const store = advent22Store();
|
||||||
|
|
@ -81,7 +81,7 @@ function on_toast_handle(handle: HBulmaToast) {
|
||||||
if (store.is_touch_device) return;
|
if (store.is_touch_device) return;
|
||||||
|
|
||||||
store.when_initialized(() => {
|
store.when_initialized(() => {
|
||||||
toast_timeout = setTimeout(() => {
|
toast_timeout = window.setTimeout(() => {
|
||||||
if (store.user_doors.length === 0) return;
|
if (store.user_doors.length === 0) return;
|
||||||
if (store.is_touch_device) return;
|
if (store.is_touch_device) return;
|
||||||
|
|
||||||
|
|
@ -91,7 +91,7 @@ function on_toast_handle(handle: HBulmaToast) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function door_click(day: number) {
|
async function door_click(day: number) {
|
||||||
if (toast_timeout !== undefined) clearTimeout(toast_timeout);
|
window.clearTimeout(toast_timeout);
|
||||||
toast?.hide();
|
toast?.hide();
|
||||||
|
|
||||||
if (modal === undefined) return;
|
if (modal === undefined) return;
|
||||||
|
|
|
||||||
|
|
@ -4,46 +4,45 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Duration } from "luxon";
|
import { Duration } from "luxon";
|
||||||
import { onBeforeUnmount, onMounted } from "vue";
|
import { onBeforeUnmount, onMounted, ref } from "vue";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
until: number;
|
until: number;
|
||||||
tick_time: number;
|
tick_time?: number;
|
||||||
}>(),
|
}>(),
|
||||||
{ tick_time: 200 },
|
{ tick_time: 200 },
|
||||||
);
|
);
|
||||||
|
|
||||||
let interval_id: number | null = null;
|
let interval_id: number | undefined;
|
||||||
let string_repr = "";
|
const string_repr = ref("");
|
||||||
|
|
||||||
function tick(): void {
|
|
||||||
const distance_ms = props.until - Date.now();
|
|
||||||
|
|
||||||
if (distance_ms <= 0) {
|
|
||||||
string_repr = "Jetzt!";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const distance = Duration.fromMillis(distance_ms);
|
|
||||||
const d_days = distance.shiftTo("day").mapUnits(Math.floor);
|
|
||||||
const d_hms = distance.minus(d_days).shiftTo("hour", "minute", "second");
|
|
||||||
|
|
||||||
if (d_days.days > 0) {
|
|
||||||
string_repr = d_days.toHuman() + " ";
|
|
||||||
} else {
|
|
||||||
string_repr = "";
|
|
||||||
}
|
|
||||||
string_repr += d_hms.toFormat("hh:mm:ss");
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
function tick(): void {
|
||||||
|
const distance_ms = props.until - Date.now();
|
||||||
|
|
||||||
|
if (distance_ms <= 0) {
|
||||||
|
string_repr.value = "Jetzt!";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const distance = Duration.fromMillis(distance_ms);
|
||||||
|
const d_days = distance.shiftTo("day").mapUnits(Math.floor);
|
||||||
|
const d_hms = distance.minus(d_days).shiftTo("hour", "minute", "second");
|
||||||
|
|
||||||
|
if (d_days.days > 0) {
|
||||||
|
string_repr.value = d_days.toHuman() + " ";
|
||||||
|
} else {
|
||||||
|
string_repr.value = "";
|
||||||
|
}
|
||||||
|
string_repr.value += d_hms.toFormat("hh:mm:ss");
|
||||||
|
}
|
||||||
|
|
||||||
tick();
|
tick();
|
||||||
interval_id = window.setInterval(tick, props.tick_time);
|
interval_id = window.setInterval(tick, props.tick_time);
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
if (interval_id === null) return;
|
|
||||||
window.clearInterval(interval_id);
|
window.clearInterval(interval_id);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,6 @@ import { Credentials } from "@/lib/model";
|
||||||
import { nextTick, onBeforeUnmount, onMounted, ref, useTemplateRef } from "vue";
|
import { nextTick, onBeforeUnmount, onMounted, ref, useTemplateRef } from "vue";
|
||||||
import BulmaButton from "./bulma/Button.vue";
|
import BulmaButton from "./bulma/Button.vue";
|
||||||
|
|
||||||
defineProps<{ visible: boolean }>();
|
|
||||||
const username_input = useTemplateRef("username_input");
|
const username_input = useTemplateRef("username_input");
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<Calendar :doors="doors" />
|
<Calendar :doors="store.user_doors" />
|
||||||
<hr />
|
<hr />
|
||||||
<div class="content" v-html="store.site_config.content" />
|
<div class="content" v-html="store.site_config.content" />
|
||||||
<div class="content has-text-primary">
|
<div class="content has-text-primary">
|
||||||
|
|
@ -20,12 +20,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Door } from "@/lib/rects/door";
|
|
||||||
import { advent22Store } from "@/lib/store";
|
import { advent22Store } from "@/lib/store";
|
||||||
|
|
||||||
import Calendar from "./Calendar.vue";
|
import Calendar from "./Calendar.vue";
|
||||||
import CountDown from "./CountDown.vue";
|
import CountDown from "./CountDown.vue";
|
||||||
|
|
||||||
const store = advent22Store();
|
const store = advent22Store();
|
||||||
const doors = store.user_doors as Door[];
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -49,17 +49,18 @@
|
||||||
import { API } from "@/lib/api";
|
import { API } from "@/lib/api";
|
||||||
import { name_door, objForEach } from "@/lib/helpers";
|
import { name_door, objForEach } from "@/lib/helpers";
|
||||||
import { ImageData, NumStrDict } from "@/lib/model";
|
import { ImageData, NumStrDict } from "@/lib/model";
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import MultiModal, { HMultiModal } from "../MultiModal.vue";
|
import MultiModal, { HMultiModal } from "../MultiModal.vue";
|
||||||
import BulmaButton from "../bulma/Button.vue";
|
import BulmaButton from "../bulma/Button.vue";
|
||||||
import BulmaDrawer from "../bulma/Drawer.vue";
|
import BulmaDrawer from "../bulma/Drawer.vue";
|
||||||
|
|
||||||
const day_data: {
|
const day_data = ref<{
|
||||||
[day: number]: {
|
[day: number]: {
|
||||||
part: string;
|
part: string;
|
||||||
image_name: string;
|
image_name: string;
|
||||||
};
|
};
|
||||||
} = {};
|
}>({});
|
||||||
|
|
||||||
let modal: HMultiModal | undefined;
|
let modal: HMultiModal | undefined;
|
||||||
|
|
||||||
|
|
@ -74,19 +75,19 @@ function on_open(ready: () => void, fail: () => void) {
|
||||||
])
|
])
|
||||||
.then(([day_parts, day_image_names]) => {
|
.then(([day_parts, day_image_names]) => {
|
||||||
const _ensure_day_in_data = (day: number) => {
|
const _ensure_day_in_data = (day: number) => {
|
||||||
if (!(day in day_data)) {
|
if (!(day in day_data.value)) {
|
||||||
day_data[day] = { part: "", image_name: "" };
|
day_data.value[day] = { part: "", image_name: "" };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
objForEach(day_parts, (day, part) => {
|
objForEach(day_parts, (day, part) => {
|
||||||
_ensure_day_in_data(day);
|
_ensure_day_in_data(day);
|
||||||
day_data[day].part = part;
|
day_data.value[day].part = part;
|
||||||
});
|
});
|
||||||
|
|
||||||
objForEach(day_image_names, (day, image_name) => {
|
objForEach(day_image_names, (day, image_name) => {
|
||||||
_ensure_day_in_data(day);
|
_ensure_day_in_data(day);
|
||||||
day_data[day].image_name = image_name;
|
day_data.value[day].image_name = image_name;
|
||||||
});
|
});
|
||||||
|
|
||||||
ready();
|
ready();
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,7 @@ import { API } from "@/lib/api";
|
||||||
import { AdminConfigModel, Credentials, DoorSaved } from "@/lib/model";
|
import { AdminConfigModel, Credentials, DoorSaved } from "@/lib/model";
|
||||||
import { advent22Store } from "@/lib/store";
|
import { advent22Store } from "@/lib/store";
|
||||||
import { DateTime } from "luxon";
|
import { DateTime } from "luxon";
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import BulmaDrawer from "../bulma/Drawer.vue";
|
import BulmaDrawer from "../bulma/Drawer.vue";
|
||||||
import BulmaSecret from "../bulma/Secret.vue";
|
import BulmaSecret from "../bulma/Secret.vue";
|
||||||
|
|
@ -195,7 +196,7 @@ import CountDown from "../CountDown.vue";
|
||||||
|
|
||||||
const store = advent22Store();
|
const store = advent22Store();
|
||||||
|
|
||||||
let admin_config_model: AdminConfigModel = {
|
const admin_config_model = ref<AdminConfigModel>({
|
||||||
solution: {
|
solution: {
|
||||||
value: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
value: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
||||||
whitespace: "KEEP",
|
whitespace: "KEEP",
|
||||||
|
|
@ -233,14 +234,14 @@ let admin_config_model: AdminConfigModel = {
|
||||||
cache_ttl: 0,
|
cache_ttl: 0,
|
||||||
config_file: "sed diam nonumy",
|
config_file: "sed diam nonumy",
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
|
|
||||||
let doors: DoorSaved[] = [];
|
const doors = ref<DoorSaved[]>([]);
|
||||||
let dav_credentials: Credentials = ["", ""];
|
const dav_credentials = ref<Credentials>(["", ""]);
|
||||||
let ui_credentials: Credentials = ["", ""];
|
const ui_credentials = ref<Credentials>(["", ""]);
|
||||||
|
|
||||||
function fmt_puzzle_date(name: keyof AdminConfigModel["puzzle"]): string {
|
function fmt_puzzle_date(name: keyof AdminConfigModel["puzzle"]): string {
|
||||||
const iso_date = admin_config_model.puzzle[name];
|
const iso_date = admin_config_model.value.puzzle[name];
|
||||||
if (!(typeof iso_date === "string")) return "-";
|
if (!(typeof iso_date === "string")) return "-";
|
||||||
|
|
||||||
return DateTime.fromISO(iso_date).toLocaleString(DateTime.DATE_SHORT);
|
return DateTime.fromISO(iso_date).toLocaleString(DateTime.DATE_SHORT);
|
||||||
|
|
@ -255,8 +256,8 @@ function on_open(ready: () => void, fail: () => void): void {
|
||||||
.then(([store_update, new_admin_config_model, new_doors]) => {
|
.then(([store_update, new_admin_config_model, new_doors]) => {
|
||||||
store_update; // discard value
|
store_update; // discard value
|
||||||
|
|
||||||
admin_config_model = new_admin_config_model;
|
admin_config_model.value = new_admin_config_model;
|
||||||
doors = new_doors;
|
doors.value = new_doors;
|
||||||
|
|
||||||
ready();
|
ready();
|
||||||
})
|
})
|
||||||
|
|
@ -265,13 +266,13 @@ function on_open(ready: () => void, fail: () => void): void {
|
||||||
|
|
||||||
function load_dav_credentials(): void {
|
function load_dav_credentials(): void {
|
||||||
API.request<Credentials>("admin/dav_credentials")
|
API.request<Credentials>("admin/dav_credentials")
|
||||||
.then((creds) => (dav_credentials = creds))
|
.then((creds) => (dav_credentials.value = creds))
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
function load_ui_credentials(): void {
|
function load_ui_credentials(): void {
|
||||||
API.request<Credentials>("admin/ui_credentials")
|
API.request<Credentials>("admin/ui_credentials")
|
||||||
.then((creds) => (ui_credentials = creds))
|
.then((creds) => (ui_credentials.value = creds))
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DoorPlacer v-if="current_step === 0" v-model="doors" />
|
<DoorPlacer v-if="current_step === 0" v-model="(doors as Door[])" />
|
||||||
<DoorChooser v-if="current_step === 1" v-model="doors" />
|
<DoorChooser v-if="current_step === 1" v-model="(doors as Door[])" />
|
||||||
<div v-if="current_step === 2" class="card-content">
|
<div v-if="current_step === 2" class="card-content">
|
||||||
<Calendar :doors="doors" />
|
<Calendar :doors="(doors as Door[])" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer class="card-footer is-flex is-justify-content-space-around">
|
<footer class="card-footer is-flex is-justify-content-space-around">
|
||||||
|
|
@ -71,11 +71,11 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { API } from "@/lib/api";
|
import { API } from "@/lib/api";
|
||||||
import { APIError } from "@/lib/api_error";
|
import { APIError } from "@/lib/api_error";
|
||||||
import { Step } from "@/lib/helpers";
|
|
||||||
import { DoorSaved } from "@/lib/model";
|
import { DoorSaved } from "@/lib/model";
|
||||||
import { Door } from "@/lib/rects/door";
|
import { Door } from "@/lib/rects/door";
|
||||||
import { toast } from "bulma-toast";
|
import { toast } from "bulma-toast";
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
import { BCStep } from "../bulma/Breadcrumbs.vue";
|
||||||
|
|
||||||
import Calendar from "../Calendar.vue";
|
import Calendar from "../Calendar.vue";
|
||||||
import BulmaBreadcrumbs from "../bulma/Breadcrumbs.vue";
|
import BulmaBreadcrumbs from "../bulma/Breadcrumbs.vue";
|
||||||
|
|
@ -84,26 +84,25 @@ import BulmaDrawer from "../bulma/Drawer.vue";
|
||||||
import DoorChooser from "../editor/DoorChooser.vue";
|
import DoorChooser from "../editor/DoorChooser.vue";
|
||||||
import DoorPlacer from "../editor/DoorPlacer.vue";
|
import DoorPlacer from "../editor/DoorPlacer.vue";
|
||||||
|
|
||||||
const steps: Step[] = [
|
const steps: BCStep[] = [
|
||||||
{ label: "Platzieren", icon: "fa-solid fa-crosshairs" },
|
{ label: "Platzieren", icon: "fa-solid fa-crosshairs" },
|
||||||
{ label: "Ordnen", icon: "fa-solid fa-list-ol" },
|
{ label: "Ordnen", icon: "fa-solid fa-list-ol" },
|
||||||
{ label: "Vorschau", icon: "fa-solid fa-magnifying-glass" },
|
{ label: "Vorschau", icon: "fa-solid fa-magnifying-glass" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const doors: Door[] = [];
|
const doors = ref<Door[]>([]);
|
||||||
|
|
||||||
const current_step = ref(0);
|
const current_step = ref(0);
|
||||||
let loading_doors = false;
|
const loading_doors = ref(false);
|
||||||
let saving_doors = false;
|
const saving_doors = ref(false);
|
||||||
|
|
||||||
function load_doors(): Promise<void> {
|
function load_doors(): Promise<void> {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
API.request<DoorSaved[]>("admin/doors")
|
API.request<DoorSaved[]>("admin/doors")
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
doors.length = 0;
|
doors.value.length = 0;
|
||||||
|
|
||||||
for (const value of data) {
|
for (const value of data) {
|
||||||
doors.push(Door.load(value));
|
doors.value.push(Door.load(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve();
|
resolve();
|
||||||
|
|
@ -119,7 +118,7 @@ function save_doors(): Promise<void> {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const data: DoorSaved[] = [];
|
const data: DoorSaved[] = [];
|
||||||
|
|
||||||
for (const door of doors) {
|
for (const door of doors.value) {
|
||||||
data.push(door.save());
|
data.push(door.save());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -138,7 +137,7 @@ function on_open(ready: () => void, fail: () => void): void {
|
||||||
|
|
||||||
function on_download() {
|
function on_download() {
|
||||||
if (confirm("Aktuelle Änderungen verwerfen und Status vom Server laden?")) {
|
if (confirm("Aktuelle Änderungen verwerfen und Status vom Server laden?")) {
|
||||||
loading_doors = true;
|
loading_doors.value = true;
|
||||||
|
|
||||||
load_doors()
|
load_doors()
|
||||||
.then(() =>
|
.then(() =>
|
||||||
|
|
@ -149,20 +148,20 @@ function on_download() {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.catch(() => {})
|
.catch(() => {})
|
||||||
.finally(() => (loading_doors = false));
|
.finally(() => (loading_doors.value = false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function on_discard() {
|
function on_discard() {
|
||||||
if (confirm("Alle Türchen löschen? (nur lokal)")) {
|
if (confirm("Alle Türchen löschen? (nur lokal)")) {
|
||||||
// empty `doors` array
|
// empty `doors` array
|
||||||
doors.length = 0;
|
doors.value.length = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function on_upload() {
|
function on_upload() {
|
||||||
if (confirm("Aktuelle Änderungen an den Server schicken?")) {
|
if (confirm("Aktuelle Änderungen an den Server schicken?")) {
|
||||||
saving_doors = true;
|
saving_doors.value = true;
|
||||||
|
|
||||||
save_doors()
|
save_doors()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
@ -175,9 +174,9 @@ function on_upload() {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.catch(() => {})
|
.catch(() => {})
|
||||||
.finally(() => (saving_doors = false));
|
.finally(() => (saving_doors.value = false));
|
||||||
})
|
})
|
||||||
.catch(() => (saving_doors = false));
|
.catch(() => (saving_doors.value = false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,14 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Step } from "@/lib/helpers";
|
export interface BCStep {
|
||||||
|
label: string;
|
||||||
|
icon: string | string[];
|
||||||
|
}
|
||||||
|
|
||||||
const model = defineModel<number>({ required: true });
|
const model = defineModel<number>({ required: true });
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
steps: Step[];
|
steps: BCStep[];
|
||||||
}>();
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Options as ToastOptions, toast } from "bulma-toast";
|
import { Options as ToastOptions, toast } from "bulma-toast";
|
||||||
import { onMounted, ref } from "vue";
|
import { onMounted, useTemplateRef } from "vue";
|
||||||
|
|
||||||
export type HBulmaToast = {
|
export type HBulmaToast = {
|
||||||
show(options: ToastOptions): void;
|
show(options: ToastOptions): void;
|
||||||
|
|
@ -20,7 +20,7 @@ const emit = defineEmits<{
|
||||||
(event: "handle", handle: HBulmaToast): void;
|
(event: "handle", handle: HBulmaToast): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const message = ref<HTMLDivElement | null>(null);
|
const message = useTemplateRef("message");
|
||||||
|
|
||||||
onMounted(() =>
|
onMounted(() =>
|
||||||
emit("handle", {
|
emit("handle", {
|
||||||
|
|
@ -34,10 +34,10 @@ onMounted(() =>
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
hide() {
|
hide() {
|
||||||
if (!(message.value instanceof HTMLElement)) return;
|
if (message.value === null) return;
|
||||||
|
|
||||||
const toast_div = message.value.parentElement;
|
const toast_div = message.value.parentElement;
|
||||||
if (!(toast_div instanceof HTMLDivElement)) return;
|
if (toast_div === null) return;
|
||||||
|
|
||||||
const dbutton = toast_div.querySelector("button.delete");
|
const dbutton = toast_div.querySelector("button.delete");
|
||||||
if (!(dbutton instanceof HTMLButtonElement)) return;
|
if (!(dbutton instanceof HTMLButtonElement)) return;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Door } from "@/lib/rects/door";
|
import { VueDoor } from "@/lib/rects/door";
|
||||||
import { advent22Store } from "@/lib/store";
|
import { advent22Store } from "@/lib/store";
|
||||||
|
|
||||||
import SVGRect from "./SVGRect.vue";
|
import SVGRect from "./SVGRect.vue";
|
||||||
|
|
@ -23,8 +23,8 @@ const store = advent22Store();
|
||||||
|
|
||||||
withDefaults(
|
withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
door: Door;
|
door: VueDoor;
|
||||||
force_visible: boolean;
|
force_visible?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
force_visible: false,
|
force_visible: false,
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { loading_success } from "@/lib/helpers";
|
import { loading_success } from "@/lib/helpers";
|
||||||
import { Rectangle } from "@/lib/rects/rectangle";
|
import { VueRectangle } from "@/lib/rects/rectangle";
|
||||||
import { advent22Store } from "@/lib/store";
|
import { advent22Store } from "@/lib/store";
|
||||||
|
|
||||||
const store = advent22Store();
|
const store = advent22Store();
|
||||||
|
|
@ -38,7 +38,7 @@ withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
variant: BulmaVariant;
|
variant: BulmaVariant;
|
||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
rectangle: Rectangle;
|
rectangle: VueRectangle;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
visible: true,
|
visible: true,
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
import { Door } from "@/lib/rects/door";
|
import { Door } from "@/lib/rects/door";
|
||||||
import { Rectangle } from "@/lib/rects/rectangle";
|
import { Rectangle } from "@/lib/rects/rectangle";
|
||||||
import { Vector2D } from "@/lib/rects/vector2d";
|
import { Vector2D } from "@/lib/rects/vector2d";
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import CalendarDoor from "../calendar/CalendarDoor.vue";
|
import CalendarDoor from "../calendar/CalendarDoor.vue";
|
||||||
import SVGRect from "../calendar/SVGRect.vue";
|
import SVGRect from "../calendar/SVGRect.vue";
|
||||||
|
|
@ -40,11 +41,11 @@ type CanvasState =
|
||||||
const model = defineModel<Door[]>({ required: true });
|
const model = defineModel<Door[]>({ required: true });
|
||||||
|
|
||||||
const MIN_RECT_AREA = 300;
|
const MIN_RECT_AREA = 300;
|
||||||
let state: CanvasState = { kind: "idle" };
|
const state = ref<CanvasState>({ kind: "idle" });
|
||||||
let preview = new Rectangle();
|
const preview = ref(new Rectangle());
|
||||||
|
|
||||||
function preview_visible(): boolean {
|
function preview_visible(): boolean {
|
||||||
return state.kind !== "idle";
|
return state.value.kind !== "idle";
|
||||||
}
|
}
|
||||||
|
|
||||||
function pop_door(point: Vector2D): Door | undefined {
|
function pop_door(point: Vector2D): Door | undefined {
|
||||||
|
|
@ -62,18 +63,18 @@ function draw_start(event: MouseEvent, point: Vector2D) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
preview = new Rectangle(point, point);
|
preview.value = new Rectangle(point, point);
|
||||||
state = { kind: "drawing" };
|
state.value = { kind: "drawing" };
|
||||||
}
|
}
|
||||||
|
|
||||||
function draw_finish() {
|
function draw_finish() {
|
||||||
if (state.kind !== "drawing" || preview.area < MIN_RECT_AREA) {
|
if (state.value.kind !== "drawing" || preview.value.area < MIN_RECT_AREA) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
model.value.push(new Door(preview));
|
model.value.push(new Door(preview.value as Rectangle));
|
||||||
|
|
||||||
state = { kind: "idle" };
|
state.value = { kind: "idle" };
|
||||||
}
|
}
|
||||||
|
|
||||||
function drag_start(event: MouseEvent, point: Vector2D) {
|
function drag_start(event: MouseEvent, point: Vector2D) {
|
||||||
|
|
@ -87,27 +88,27 @@ function drag_start(event: MouseEvent, point: Vector2D) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
preview = drag_door.position;
|
preview.value = drag_door.position;
|
||||||
|
|
||||||
state = { kind: "dragging", door: drag_door, origin: point };
|
state.value = { kind: "dragging", door: drag_door, origin: point };
|
||||||
}
|
}
|
||||||
|
|
||||||
function drag_finish() {
|
function drag_finish() {
|
||||||
if (state.kind !== "dragging") {
|
if (state.value.kind !== "dragging") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
model.value.push(new Door(preview, state.door.day));
|
model.value.push(new Door(preview.value as Rectangle, state.value.door.day));
|
||||||
|
|
||||||
state = { kind: "idle" };
|
state.value = { kind: "idle" };
|
||||||
}
|
}
|
||||||
|
|
||||||
function on_mousemove(event: MouseEvent, point: Vector2D) {
|
function on_mousemove(event: MouseEvent, point: Vector2D) {
|
||||||
if (state.kind === "drawing") {
|
if (state.value.kind === "drawing") {
|
||||||
preview = preview.update(undefined, point);
|
preview.value = preview.value.update(undefined, point);
|
||||||
} else if (state.kind === "dragging") {
|
} else if (state.value.kind === "dragging") {
|
||||||
const movement = point.minus(state.origin);
|
const movement = point.minus(state.value.origin);
|
||||||
preview = state.door.position.move(movement);
|
preview.value = state.value.door.position.move(movement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,11 +32,11 @@ const model = defineModel<Door>({ required: true });
|
||||||
const day_input = useTemplateRef("day_input");
|
const day_input = useTemplateRef("day_input");
|
||||||
|
|
||||||
const day_str = ref("");
|
const day_str = ref("");
|
||||||
let editing = false;
|
const editing = ref(false);
|
||||||
|
|
||||||
function toggle_editing() {
|
function toggle_editing() {
|
||||||
day_str.value = String(model.value.day);
|
day_str.value = String(model.value.day);
|
||||||
editing = !editing;
|
editing.value = !editing.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function on_click(event: MouseEvent) {
|
function on_click(event: MouseEvent) {
|
||||||
|
|
@ -44,7 +44,7 @@ function on_click(event: MouseEvent) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!editing) {
|
if (!editing.value) {
|
||||||
const day_input_focus = () => {
|
const day_input_focus = () => {
|
||||||
if (day_input.value === null) {
|
if (day_input.value === null) {
|
||||||
nextTick(day_input_focus);
|
nextTick(day_input_focus);
|
||||||
|
|
@ -62,7 +62,7 @@ function on_click(event: MouseEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function on_keydown(event: KeyboardEvent) {
|
function on_keydown(event: KeyboardEvent) {
|
||||||
if (!editing) {
|
if (!editing.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,3 @@ export function handle_error(error: unknown) {
|
||||||
export function name_door(day: number): string {
|
export function name_door(day: number): string {
|
||||||
return `Türchen ${day}`;
|
return `Türchen ${day}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Step {
|
|
||||||
label: string;
|
|
||||||
icon: string | string[];
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { UnwrapRef } from "vue";
|
||||||
import { DoorSaved } from "../model";
|
import { DoorSaved } from "../model";
|
||||||
import { Rectangle } from "./rectangle";
|
import { Rectangle } from "./rectangle";
|
||||||
import { Vector2D } from "./vector2d";
|
import { Vector2D } from "./vector2d";
|
||||||
|
|
@ -50,3 +51,5 @@ export class Door {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type VueDoor = UnwrapRef<Door>;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { UnwrapRef } from "vue";
|
||||||
import { Vector2D } from "./vector2d";
|
import { Vector2D } from "./vector2d";
|
||||||
|
|
||||||
export class Rectangle {
|
export class Rectangle {
|
||||||
|
|
@ -77,3 +78,5 @@ export class Rectangle {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type VueRectangle = UnwrapRef<Rectangle>;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue