🚧 wip on ui: rework for vue 3 composition API

This commit is contained in:
Jörn-Michael Miehe 2025-12-05 02:12:33 +00:00
parent 835bf0345a
commit 029b361e2d
17 changed files with 436 additions and 511 deletions

View file

@ -38,7 +38,7 @@
"sass": "^1.77.8", "sass": "^1.77.8",
"sass-loader": "^16.0.0", "sass-loader": "^16.0.0",
"typescript": "~5.5.4", "typescript": "~5.5.4",
"vue": "^3.4.37", "vue": "^3.5.25",
"vue-class-component": "^8.0.0-0" "vue-class-component": "^8.0.0-0"
}, },
"dependencies": {}, "dependencies": {},

View file

@ -43,8 +43,7 @@
</footer> </footer>
</template> </template>
<script lang="ts"> <script setup lang="ts">
import { Options, Vue } from "vue-class-component";
import { advent22Store } from "./lib/store"; import { advent22Store } from "./lib/store";
import AdminView from "./components/admin/AdminView.vue"; import AdminView from "./components/admin/AdminView.vue";
@ -52,17 +51,7 @@ import AdminButton from "./components/AdminButton.vue";
import TouchButton from "./components/TouchButton.vue"; import TouchButton from "./components/TouchButton.vue";
import UserView from "./components/UserView.vue"; import UserView from "./components/UserView.vue";
@Options({ const store = advent22Store();
components: {
AdminView,
AdminButton,
TouchButton,
UserView,
},
})
export default class extends Vue {
public readonly store = advent22Store();
}
</script> </script>
<style> <style>

View file

@ -10,48 +10,39 @@
/> />
</template> </template>
<script lang="ts"> <script setup lang="ts">
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 { Options, Vue } from "vue-class-component";
import BulmaButton from "./bulma/Button.vue"; import BulmaButton from "./bulma/Button.vue";
import LoginModal from "./LoginModal.vue"; import LoginModal from "./LoginModal.vue";
@Options({ let modal_visible = false;
components: { let is_busy = false;
BulmaButton, const store = advent22Store();
LoginModal,
},
})
export default class extends Vue {
public modal_visible = false;
public is_busy = false;
public readonly store = advent22Store();
public on_click() { function on_click() {
if (this.store.is_admin) { if (store.is_admin) {
this.store.logout(); store.logout();
} else { } else {
// show login modal // show login modal
this.is_busy = true; is_busy = true;
this.modal_visible = true; modal_visible = true;
}
}
public on_submit(creds: Credentials) {
this.modal_visible = false;
this.store
.login(creds)
.catch((error) => APIError.alert(error))
.finally(() => (this.is_busy = false));
}
public on_cancel() {
this.modal_visible = false;
this.is_busy = false;
} }
} }
function on_submit(creds: Credentials) {
modal_visible = false;
store
.login(creds)
.catch((error) => APIError.alert(error))
.finally(() => (is_busy = false));
}
function on_cancel() {
modal_visible = false;
is_busy = false;
}
</script> </script>

View file

@ -1,7 +1,8 @@
<!-- eslint-disable vue/multi-word-component-names -->
<template> <template>
<MultiModal @handle="modal_handle" /> <MultiModal @handle="on_modal_handle" />
<BulmaToast @handle="toast_handle" class="content"> <BulmaToast @handle="on_toast_handle" class="content">
<p> <p>
Du hast noch keine Türchen geöffnet, vielleicht gibt es ein Anzeigeproblem Du hast noch keine Türchen geöffnet, vielleicht gibt es ein Anzeigeproblem
in Deinem Webbrowser? in Deinem Webbrowser?
@ -29,14 +30,14 @@
<figure> <figure>
<div class="image is-unselectable"> <div class="image is-unselectable">
<img :src="_ensure_loaded(store.background_image).data_url" /> <img :src="ensure_loaded(store.background_image).data_url" />
<ThouCanvas> <ThouCanvas>
<CalendarDoor <CalendarDoor
v-for="(door, index) in doors" v-for="(door, index) in doors"
:key="`door-${index}`" :key="`door-${index}`"
:door="door" :door="door"
:visible="store.is_touch_device" :visible="store.is_touch_device"
:title="_name_door(door.day)" :title="name_door(door.day)"
@click="door_click(door.day)" @click="door_click(door.day)"
style="cursor: pointer" style="cursor: pointer"
/> />
@ -48,83 +49,62 @@
<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 { ensure_loaded, Loading, 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 { Door } from "@/lib/rects/door";
import { advent22Store } from "@/lib/store"; import { advent22Store } from "@/lib/store";
import MultiModal from "./MultiModal.vue"; import { onBeforeUnmount } from "vue";
import MultiModal, { HMultiModal } from "./MultiModal.vue";
import BulmaButton from "./bulma/Button.vue"; import BulmaButton from "./bulma/Button.vue";
import BulmaToast from "./bulma/Toast.vue"; import BulmaToast, { HBulmaToast } from "./bulma/Toast.vue";
import CalendarDoor from "./calendar/CalendarDoor.vue"; import CalendarDoor from "./calendar/CalendarDoor.vue";
import ThouCanvas from "./calendar/ThouCanvas.vue"; import ThouCanvas from "./calendar/ThouCanvas.vue";
@Options({ defineProps<{
components: { doors: Door[];
MultiModal, }>();
BulmaButton,
BulmaToast,
ThouCanvas,
CalendarDoor,
},
props: {
doors: Array,
},
})
export default class extends Vue {
public readonly doors!: Door[];
public readonly store = advent22Store();
private multi_modal?: MultiModal; const store = advent22Store();
public toast?: typeof BulmaToast; let modal: HMultiModal | undefined;
private toast_timeout?: number; let toast: HBulmaToast | undefined;
let toast_timeout: number | undefined;
public modal_handle(modal: MultiModal) { function on_modal_handle(handle: HMultiModal) {
this.multi_modal = modal; modal = handle;
} }
public toast_handle(toast: typeof BulmaToast) { function on_toast_handle(handle: HBulmaToast) {
this.toast = toast; toast = handle;
if (this.store.is_touch_device) return; if (store.is_touch_device) return;
this.store.when_initialized(() => { store.when_initialized(() => {
this.toast_timeout = setTimeout(() => { toast_timeout = setTimeout(() => {
if (this.store.user_doors.length === 0) return; if (store.user_doors.length === 0) return;
if (this.store.is_touch_device) return; if (store.is_touch_device) return;
this.toast!.show({ duration: 600000, type: "is-warning" }); toast!.show({ duration: 600000, type: "is-warning" });
}, 10e3); }, 10e3);
}); });
} }
public async door_click(day: number) { async function door_click(day: number) {
if (this.toast_timeout !== undefined) clearTimeout(this.toast_timeout); if (toast_timeout !== undefined) clearTimeout(toast_timeout);
this.toast?.hide(); toast?.hide();
if (this.multi_modal === undefined) return; if (modal === undefined) return;
this.multi_modal.show_progress(); modal.show_loading();
try { try {
const day_image = await API.request<ImageData>(`user/image_${day}`); const day_image = await API.request<ImageData>(`user/image_${day}`);
this.multi_modal!.show_image(day_image.data_url, name_door(day)); modal.show_image(day_image.data_url, name_door(day));
} catch (error) { } catch (error) {
APIError.alert(error); APIError.alert(error);
this.multi_modal!.hide(); modal.hide();
}
}
public beforeUnmount(): void {
this.toast?.hide();
}
public _ensure_loaded<T>(o: Loading<T>): T {
return ensure_loaded(o);
}
public _name_door(day: number): string {
return name_door(day);
} }
} }
onBeforeUnmount(() => toast?.hide());
</script> </script>

View file

@ -2,31 +2,26 @@
{{ string_repr }} {{ string_repr }}
</template> </template>
<script lang="ts"> <script setup lang="ts">
import { Duration } from "luxon"; import { Duration } from "luxon";
import { Options, Vue } from "vue-class-component"; import { onBeforeUnmount, onMounted } from "vue";
@Options({ const props = withDefaults(
props: { defineProps<{
until: Number, until: number;
tick_time: { tick_time: number;
type: Number, }>(),
default: 200, { tick_time: 200 },
}, );
},
})
export default class extends Vue {
private until!: number;
private tick_time!: number;
private interval_id: number | null = null; let interval_id: number | null = null;
public string_repr = ""; let string_repr = "";
private tick(): void { function tick(): void {
const distance_ms = this.until - Date.now(); const distance_ms = props.until - Date.now();
if (distance_ms <= 0) { if (distance_ms <= 0) {
this.string_repr = "Jetzt!"; string_repr = "Jetzt!";
return; return;
} }
@ -35,21 +30,20 @@ export default class extends Vue {
const d_hms = distance.minus(d_days).shiftTo("hour", "minute", "second"); const d_hms = distance.minus(d_days).shiftTo("hour", "minute", "second");
if (d_days.days > 0) { if (d_days.days > 0) {
this.string_repr = d_days.toHuman() + " "; string_repr = d_days.toHuman() + " ";
} else { } else {
this.string_repr = ""; string_repr = "";
}
this.string_repr += d_hms.toFormat("hh:mm:ss");
}
public mounted(): void {
this.tick();
this.interval_id = window.setInterval(this.tick, this.tick_time);
}
public beforeUnmount(): void {
if (this.interval_id === null) return;
window.clearInterval(this.interval_id);
} }
string_repr += d_hms.toFormat("hh:mm:ss");
} }
onMounted(() => {
tick();
interval_id = window.setInterval(tick, props.tick_time);
});
onBeforeUnmount(() => {
if (interval_id === null) return;
window.clearInterval(interval_id);
});
</script> </script>

View file

@ -47,48 +47,44 @@
</div> </div>
</template> </template>
<script lang="ts"> <script setup lang="ts">
import { Options, Vue } from "vue-class-component"; import { Credentials } from "@/lib/model";
import { nextTick, onBeforeUnmount, onMounted, ref, useTemplateRef } from "vue";
import BulmaButton from "./bulma/Button.vue"; import BulmaButton from "./bulma/Button.vue";
@Options({ defineProps<{ visible: boolean }>();
components: { const username_input = useTemplateRef("username_input");
BulmaButton,
},
props: {
visible: Boolean,
},
emits: ["cancel", "submit"],
})
export default class extends Vue {
public username = "";
public password = "";
private on_keydown(e: KeyboardEvent) { const emit = defineEmits<{
if (e.key === "Enter") this.submit(); (event: "submit", creds: Credentials): void;
else if (e.key === "Escape") this.cancel(); (event: "cancel"): void;
} }>();
public mounted(): void { const username = ref("");
window.addEventListener("keydown", this.on_keydown); const password = ref("");
this.$nextTick(() => { function submit(): void {
if (!(this.$refs.username_input instanceof HTMLElement)) return; emit("submit", [username.value, password.value]);
this.$refs.username_input.focus();
});
}
public beforeUnmount(): void {
window.removeEventListener("keydown", this.on_keydown);
}
public submit(): void {
this.$emit("submit", [this.username, this.password]);
}
public cancel(): void {
this.$emit("cancel");
}
} }
function cancel(): void {
emit("cancel");
}
onMounted(() => {
const on_keydown = (e: KeyboardEvent) => {
if (e.key === "Enter") submit();
else if (e.key === "Escape") cancel();
};
window.addEventListener("keydown", on_keydown);
nextTick(() => {
username_input.value?.focus();
});
onBeforeUnmount(() => {
window.removeEventListener("keydown", on_keydown);
});
});
</script> </script>

View file

@ -1,83 +1,81 @@
<template> <template>
<div class="modal is-active" v-if="active" @click="dismiss()"> <div v-if="state.show !== 'none'" class="modal is-active" @click="dismiss()">
<div class="modal-background" /> <div class="modal-background" />
<div class="modal-content" style="max-height: 100vh; max-width: 95vw"> <div class="modal-content" style="max-height: 100vh; max-width: 95vw">
<template v-if="progress"> <template v-if="state.show === 'loading'">
<progress class="progress is-primary" max="100" /> <progress class="progress is-primary" max="100" />
</template> </template>
<template v-else> <template v-else-if="state.show === 'image'">
<figure> <figure>
<figcaption class="tag is-primary"> <figcaption class="tag is-primary">
{{ caption }} {{ state.caption }}
</figcaption> </figcaption>
<div class="image is-square"> <div class="image is-square">
<img :src="image_src" alt="Kalender-Bild" /> <img :src="state.src" alt="Kalender-Bild" />
</div> </div>
</figure> </figure>
</template> </template>
</div> </div>
<button <button
v-if="!progress" v-if="state.show !== 'loading'"
class="modal-close is-large has-background-primary" class="modal-close is-large has-background-primary"
/> />
</div> </div>
</template> </template>
<script lang="ts"> <script setup lang="ts">
import { Options, Vue } from "vue-class-component"; import { onBeforeUnmount, onMounted, ref } from "vue";
@Options({ type ModalState =
emits: ["handle"], | { show: "none" }
}) | { show: "loading" }
export default class extends Vue { | { show: "image"; src: string; caption: string };
public active = false;
public progress = false;
public image_src = "";
public caption = "";
private on_keydown(e: KeyboardEvent) { const state = ref<ModalState>({ show: "none" });
if (e.key === "Escape") this.dismiss();
}
public created(): void { export type HMultiModal = {
this.$emit("handle", this); show_image(src: string, caption: string): void;
} show_loading(): void;
hide(): void;
dismiss(): void;
};
public mounted(): void { const emit = defineEmits<{
window.addEventListener("keydown", this.on_keydown); (event: "handle", handle: HMultiModal): void;
} }>();
public beforeUnmount(): void { function hide() {
window.removeEventListener("keydown", this.on_keydown); state.value = { show: "none" };
} }
public show() { function dismiss() {
this.active = true; if (state.value.show !== "loading") {
} hide();
public hide() {
this.active = false;
}
public dismiss() {
// Cannot dismiss the "loading" screen
if (this.active && this.progress) return;
this.active = false;
}
public show_image(src: string, caption: string = "") {
this.progress = false;
this.image_src = src;
this.caption = caption;
this.show();
}
public show_progress() {
this.progress = true;
this.show();
} }
} }
onMounted(() => {
emit("handle", {
show_image(src: string, caption: string = "") {
state.value = { show: "image", src: src, caption: caption };
},
show_loading() {
state.value = { show: "loading" };
},
hide,
dismiss,
});
const on_keydown = (e: KeyboardEvent) => {
if (e.key === "Escape") dismiss();
};
window.addEventListener("keydown", on_keydown);
onBeforeUnmount(() => {
window.removeEventListener("keydown", on_keydown);
});
});
</script> </script>

View file

@ -11,18 +11,10 @@
/> />
</template> </template>
<script lang="ts"> <script setup lang="ts">
import { advent22Store } from "@/lib/store"; import { advent22Store } from "@/lib/store";
import { Options, Vue } from "vue-class-component";
import BulmaButton from "./bulma/Button.vue"; import BulmaButton from "./bulma/Button.vue";
@Options({ const store = advent22Store();
components: {
BulmaButton,
},
})
export default class extends Vue {
public readonly store = advent22Store();
}
</script> </script>

View file

@ -1,5 +1,5 @@
<template> <template>
<Calendar :doors="store.user_doors" /> <Calendar :doors="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">
@ -19,20 +19,13 @@
</div> </div>
</template> </template>
<script lang="ts"> <script setup lang="ts">
import { Door } from "@/lib/rects/door";
import { advent22Store } from "@/lib/store"; import { advent22Store } from "@/lib/store";
import { Options, Vue } from "vue-class-component";
import Calendar from "./Calendar.vue"; import Calendar from "./Calendar.vue";
import CountDown from "./CountDown.vue"; import CountDown from "./CountDown.vue";
@Options({ const store = advent22Store();
components: { const doors = store.user_doors as Door[];
Calendar,
CountDown,
},
})
export default class extends Vue {
public readonly store = advent22Store();
}
</script> </script>

View file

@ -1,5 +1,5 @@
<template> <template>
<MultiModal @handle="modal_handle" /> <MultiModal @handle="on_modal_handle" />
<BulmaDrawer header="Kalender-Assistent" @open="on_open" refreshable> <BulmaDrawer header="Kalender-Assistent" @open="on_open" refreshable>
<div class="card-content"> <div class="card-content">
@ -50,7 +50,7 @@ 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 MultiModal 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";
@ -61,10 +61,10 @@ const day_data: {
}; };
} = {}; } = {};
let multi_modal: MultiModal | undefined; let modal: HMultiModal | undefined;
function modal_handle(modal: MultiModal) { function on_modal_handle(handle: HMultiModal) {
multi_modal = modal; modal = handle;
} }
function on_open(ready: () => void, fail: () => void) { function on_open(ready: () => void, fail: () => void) {
@ -95,14 +95,14 @@ function on_open(ready: () => void, fail: () => void) {
} }
async function door_click(day: number) { async function door_click(day: number) {
if (multi_modal === undefined) return; if (modal === undefined) return;
multi_modal.show_progress(); modal.show_loading();
try { try {
const day_image = await API.request<ImageData>(`user/image_${day}`); const day_image = await API.request<ImageData>(`user/image_${day}`);
multi_modal!.show_image(day_image.data_url, name_door(day)); modal.show_image(day_image.data_url, name_door(day));
} catch (error) { } catch (error) {
multi_modal!.hide(); modal.hide();
} }
} }
</script> </script>

View file

@ -37,8 +37,8 @@
</div> </div>
</div> </div>
<DoorPlacer v-if="current_step === 0" :doors="doors" /> <DoorPlacer v-if="current_step === 0" v-model="doors" />
<DoorChooser v-if="current_step === 1" :doors="doors" /> <DoorChooser v-if="current_step === 1" v-model="doors" />
<div v-if="current_step === 2" class="card-content"> <div v-if="current_step === 2" class="card-content">
<Calendar :doors="doors" /> <Calendar :doors="doors" />
</div> </div>
@ -75,6 +75,7 @@ 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 Calendar from "../Calendar.vue"; import Calendar from "../Calendar.vue";
import BulmaBreadcrumbs from "../bulma/Breadcrumbs.vue"; import BulmaBreadcrumbs from "../bulma/Breadcrumbs.vue";
@ -91,8 +92,7 @@ const steps: Step[] = [
const doors: Door[] = []; const doors: Door[] = [];
// eslint-disable-next-line prefer-const const current_step = ref(0);
let current_step = 0; // write access in template
let loading_doors = false; let loading_doors = false;
let saving_doors = false; let saving_doors = false;

View file

@ -8,32 +8,32 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import * as bulmaToast from "bulma-toast"; import { Options as ToastOptions, toast } from "bulma-toast";
import { onMounted, ref } from "vue"; import { onMounted, ref } from "vue";
export type HBulmaToast = {
show(options: ToastOptions): void;
hide(): void;
};
const emit = defineEmits<{ const emit = defineEmits<{
( (event: "handle", handle: HBulmaToast): void;
event: "handle",
show: (options: bulmaToast.Options) => void,
hide: () => void,
): void;
}>(); }>();
const message = ref<HTMLDivElement | null>(null); const message = ref<HTMLDivElement | null>(null);
onMounted(() => onMounted(() =>
emit( emit("handle", {
"handle", show(options: ToastOptions = {}) {
(options: bulmaToast.Options = {}) => {
if (!(message.value instanceof HTMLElement)) return; if (!(message.value instanceof HTMLElement)) return;
bulmaToast.toast({ toast({
...options, ...options,
single: true, single: true,
message: message.value, message: message.value,
}); });
}, },
() => { hide() {
if (!(message.value instanceof HTMLElement)) return; if (!(message.value instanceof HTMLElement)) return;
const toast_div = message.value.parentElement; const toast_div = message.value.parentElement;
@ -44,6 +44,6 @@ onMounted(() =>
dbutton.click(); dbutton.click();
}, },
), }),
); );
</script> </script>

View file

@ -15,7 +15,7 @@
force_visible force_visible
/> />
<SVGRect <SVGRect
v-if="state.kind !== 'idle'" v-if="preview_visible()"
variant="success" variant="success"
:rectangle="preview" :rectangle="preview"
visible visible
@ -43,6 +43,10 @@ const MIN_RECT_AREA = 300;
let state: CanvasState = { kind: "idle" }; let state: CanvasState = { kind: "idle" };
let preview = new Rectangle(); let preview = new Rectangle();
function preview_visible(): boolean {
return state.kind !== "idle";
}
function pop_door(point: Vector2D): Door | undefined { function pop_door(point: Vector2D): Door | undefined {
const idx = model.value.findIndex((rect) => rect.position.contains(point)); const idx = model.value.findIndex((rect) => rect.position.contains(point));
@ -54,7 +58,7 @@ function pop_door(point: Vector2D): Door | undefined {
} }
function draw_start(event: MouseEvent, point: Vector2D) { function draw_start(event: MouseEvent, point: Vector2D) {
if (state.kind !== "idle") { if (preview_visible()) {
return; return;
} }
@ -73,7 +77,7 @@ function draw_finish() {
} }
function drag_start(event: MouseEvent, point: Vector2D) { function drag_start(event: MouseEvent, point: Vector2D) {
if (state.kind !== "idle") { if (preview_visible()) {
return; return;
} }
@ -108,7 +112,7 @@ function on_mousemove(event: MouseEvent, point: Vector2D) {
} }
function remove_rect(event: MouseEvent, point: Vector2D) { function remove_rect(event: MouseEvent, point: Vector2D) {
if (state.kind !== "idle") { if (preview_visible()) {
return; return;
} }

View file

@ -11,42 +11,26 @@
</ul> </ul>
</div> </div>
<figure class="image is-unselectable"> <figure class="image is-unselectable">
<img :src="_ensure_loaded(store.background_image).data_url" /> <img :src="ensure_loaded(store.background_image).data_url" />
<ThouCanvas> <ThouCanvas>
<PreviewDoor <PreviewDoor
v-for="(door, index) in doors" v-for="(_, index) in model"
:key="`door-${index}`" :key="`door-${index}`"
:door="door" v-model="model[index]"
/> />
</ThouCanvas> </ThouCanvas>
</figure> </figure>
</div> </div>
</template> </template>
<script lang="ts"> <script setup lang="ts">
import { ensure_loaded, Loading } from "@/lib/helpers"; import { ensure_loaded } from "@/lib/helpers";
import { Door } from "@/lib/rects/door"; import { Door } from "@/lib/rects/door";
import { advent22Store } from "@/lib/store"; import { advent22Store } from "@/lib/store";
import { Options, Vue } from "vue-class-component";
import ThouCanvas from "../calendar/ThouCanvas.vue"; import ThouCanvas from "../calendar/ThouCanvas.vue";
import PreviewDoor from "./PreviewDoor.vue"; import PreviewDoor from "./PreviewDoor.vue";
@Options({ const model = defineModel<Door[]>({ required: true });
components: { const store = advent22Store();
ThouCanvas,
PreviewDoor,
},
props: {
doors: Array,
},
})
export default class extends Vue {
public doors!: Door[];
public readonly store = advent22Store();
public _ensure_loaded<T>(o: Loading<T>): T {
return ensure_loaded(o);
}
}
</script> </script>

View file

@ -9,34 +9,19 @@
</ul> </ul>
</div> </div>
<figure class="image is-unselectable"> <figure class="image is-unselectable">
<img :src="_ensure_loaded(store.background_image).data_url" /> <img :src="ensure_loaded(store.background_image).data_url" />
<DoorCanvas :doors="doors" /> <DoorCanvas v-model="model" />
</figure> </figure>
</div> </div>
</template> </template>
<script lang="ts"> <script setup lang="ts">
import { ensure_loaded, Loading } from "@/lib/helpers"; import { ensure_loaded } from "@/lib/helpers";
import { Door } from "@/lib/rects/door"; import { Door } from "@/lib/rects/door";
import { advent22Store } from "@/lib/store"; import { advent22Store } from "@/lib/store";
import { Options, Vue } from "vue-class-component";
import DoorCanvas from "./DoorCanvas.vue"; import DoorCanvas from "./DoorCanvas.vue";
@Options({ const model = defineModel<Door[]>({ required: true });
components: { const store = advent22Store();
DoorCanvas,
},
props: {
doors: Array,
},
})
export default class extends Vue {
public doors!: Door[];
public readonly store = advent22Store();
public _ensure_loaded<T>(o: Loading<T>): T {
return ensure_loaded(o);
}
}
</script> </script>

View file

@ -1,7 +1,7 @@
<template> <template>
<SVGRect <SVGRect
style="cursor: text" style="cursor: text"
:rectangle="door.position" :rectangle="model.position"
:variant="editing ? 'success' : 'primary'" :variant="editing ? 'success' : 'primary'"
@click.left="on_click" @click.left="on_click"
visible visible
@ -12,78 +12,68 @@
ref="day_input" ref="day_input"
class="input is-large" class="input is-large"
type="number" type="number"
:min="MIN_DAY" :min="Door.MIN_DAY"
placeholder="Tag" placeholder="Tag"
@keydown="on_keydown" @keydown="on_keydown"
/> />
<div v-else class="has-text-danger"> <div v-else class="has-text-danger">
{{ door.day > 0 ? door.day : "*" }} {{ model.day > 0 ? model.day : "*" }}
</div> </div>
</SVGRect> </SVGRect>
</template> </template>
<script lang="ts"> <script setup lang="ts">
import { Door } from "@/lib/rects/door"; import { Door } from "@/lib/rects/door";
import { Options, Vue } from "vue-class-component"; import { nextTick, ref, useTemplateRef } from "vue";
import SVGRect from "../calendar/SVGRect.vue"; import SVGRect from "../calendar/SVGRect.vue";
@Options({ const model = defineModel<Door>({ required: true });
components: { const day_input = useTemplateRef("day_input");
SVGRect,
},
props: {
door: Door,
},
})
export default class extends Vue {
public door!: Door;
public readonly MIN_DAY = Door.MIN_DAY;
public day_str = ""; const day_str = ref("");
public editing = false; let editing = false;
private toggle_editing() { function toggle_editing() {
this.day_str = String(this.door.day); day_str.value = String(model.value.day);
this.editing = !this.editing; editing = !editing;
} }
public on_click(event: MouseEvent) { function on_click(event: MouseEvent) {
if (!(event.target instanceof HTMLDivElement)) { if (!(event.target instanceof HTMLDivElement)) {
return; return;
} }
if (!this.editing) { if (!editing) {
const day_input_focus = () => { const day_input_focus = () => {
if (this.$refs.day_input instanceof HTMLInputElement) { if (day_input.value === null) {
this.$refs.day_input.select(); nextTick(day_input_focus);
return; return;
} }
this.$nextTick(day_input_focus); day_input.value.select();
}; };
day_input_focus(); day_input_focus();
} else { } else {
this.door.day = this.day_str; model.value.day = day_str.value;
} }
this.toggle_editing(); toggle_editing();
} }
public on_keydown(event: KeyboardEvent) { function on_keydown(event: KeyboardEvent) {
if (!this.editing) { if (!editing) {
return; return;
} }
if (event.key === "Enter") { if (event.key === "Enter") {
this.door.day = this.day_str; model.value.day = day_str.value;
this.toggle_editing(); toggle_editing();
} else if (event.key === "Delete") { } else if (event.key === "Delete") {
this.door.day = -1; model.value.day = -1;
this.toggle_editing(); toggle_editing();
} else if (event.key === "Escape") { } else if (event.key === "Escape") {
this.toggle_editing(); toggle_editing();
}
} }
} }
</script> </script>

View file

@ -229,20 +229,20 @@
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83"
integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==
"@babel/helper-string-parser@^7.24.8": "@babel/helper-string-parser@^7.27.1":
version "7.24.8" version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz#5b3329c9a58803d5df425e5785865881a81ca48d" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687"
integrity sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ== integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
"@babel/helper-validator-identifier@^7.22.20": "@babel/helper-validator-identifier@^7.22.20":
version "7.22.20" version "7.22.20"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0"
integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
"@babel/helper-validator-identifier@^7.24.7": "@babel/helper-validator-identifier@^7.28.5":
version "7.24.7" version "7.28.5"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4"
integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==
"@babel/helper-validator-option@^7.22.15": "@babel/helper-validator-option@^7.22.15":
version "7.22.15" version "7.22.15"
@ -281,12 +281,12 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.4.tgz#409fbe690c333bb70187e2de4021e1e47a026661" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.4.tgz#409fbe690c333bb70187e2de4021e1e47a026661"
integrity sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ== integrity sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ==
"@babel/parser@^7.24.7": "@babel/parser@^7.28.5":
version "7.25.3" version "7.28.5"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.25.3.tgz#91fb126768d944966263f0657ab222a642b82065" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.5.tgz#0b0225ee90362f030efd644e8034c99468893b08"
integrity sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw== integrity sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==
dependencies: dependencies:
"@babel/types" "^7.25.2" "@babel/types" "^7.28.5"
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.23.3": "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.23.3":
version "7.23.3" version "7.23.3"
@ -1026,14 +1026,13 @@
"@babel/helper-validator-identifier" "^7.22.20" "@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0" to-fast-properties "^2.0.0"
"@babel/types@^7.25.2": "@babel/types@^7.28.5":
version "7.25.2" version "7.28.5"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.25.2.tgz#55fb231f7dc958cd69ea141a4c2997e819646125" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.5.tgz#10fc405f60897c35f07e85493c932c7b5ca0592b"
integrity sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q== integrity sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==
dependencies: dependencies:
"@babel/helper-string-parser" "^7.24.8" "@babel/helper-string-parser" "^7.27.1"
"@babel/helper-validator-identifier" "^7.24.7" "@babel/helper-validator-identifier" "^7.28.5"
to-fast-properties "^2.0.0"
"@discoveryjs/json-ext@0.5.7": "@discoveryjs/json-ext@0.5.7":
version "0.5.7" version "0.5.7"
@ -1171,10 +1170,10 @@
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
"@jridgewell/sourcemap-codec@^1.5.0": "@jridgewell/sourcemap-codec@^1.5.5":
version "1.5.0" version "1.5.5"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba"
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==
"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9":
version "0.3.20" version "0.3.20"
@ -1860,47 +1859,47 @@
semver "^7.3.4" semver "^7.3.4"
strip-ansi "^6.0.0" strip-ansi "^6.0.0"
"@vue/compiler-core@3.4.37": "@vue/compiler-core@3.5.25":
version "3.4.37" version "3.5.25"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.37.tgz#55db3900e09424c65c39111a05a3c6e698f371e3" resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.25.tgz#7ffb658d7919348baad8c491eb5b948ee8e44108"
integrity sha512-ZDDT/KiLKuCRXyzWecNzC5vTcubGz4LECAtfGPENpo0nrmqJHwuWtRLxk/Sb9RAKtR9iFflFycbkjkY+W/PZUQ== integrity sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==
dependencies: dependencies:
"@babel/parser" "^7.24.7" "@babel/parser" "^7.28.5"
"@vue/shared" "3.4.37" "@vue/shared" "3.5.25"
entities "^5.0.0" entities "^4.5.0"
estree-walker "^2.0.2" estree-walker "^2.0.2"
source-map-js "^1.2.0" source-map-js "^1.2.1"
"@vue/compiler-dom@3.4.37": "@vue/compiler-dom@3.5.25":
version "3.4.37" version "3.5.25"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.37.tgz#a1fcf79e287cb828545082ff1afa8630480a3044" resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.25.tgz#dd799ac2474cda54303039310b8994f0cfb40957"
integrity sha512-rIiSmL3YrntvgYV84rekAtU/xfogMUJIclUMeIKEtVBFngOL3IeZHhsH3UaFEgB5iFGpj6IW+8YuM/2Up+vVag== integrity sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==
dependencies: dependencies:
"@vue/compiler-core" "3.4.37" "@vue/compiler-core" "3.5.25"
"@vue/shared" "3.4.37" "@vue/shared" "3.5.25"
"@vue/compiler-sfc@3.4.37": "@vue/compiler-sfc@3.5.25":
version "3.4.37" version "3.5.25"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.37.tgz#8afaf1a86cb849422c765d4369ba1e85fffe0234" resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.25.tgz#30377920c3869c3bb32111aa4aefad53921831ad"
integrity sha512-vCfetdas40Wk9aK/WWf8XcVESffsbNkBQwS5t13Y/PcfqKfIwJX2gF+82th6dOpnpbptNMlMjAny80li7TaCIg== integrity sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==
dependencies: dependencies:
"@babel/parser" "^7.24.7" "@babel/parser" "^7.28.5"
"@vue/compiler-core" "3.4.37" "@vue/compiler-core" "3.5.25"
"@vue/compiler-dom" "3.4.37" "@vue/compiler-dom" "3.5.25"
"@vue/compiler-ssr" "3.4.37" "@vue/compiler-ssr" "3.5.25"
"@vue/shared" "3.4.37" "@vue/shared" "3.5.25"
estree-walker "^2.0.2" estree-walker "^2.0.2"
magic-string "^0.30.10" magic-string "^0.30.21"
postcss "^8.4.40" postcss "^8.5.6"
source-map-js "^1.2.0" source-map-js "^1.2.1"
"@vue/compiler-ssr@3.4.37": "@vue/compiler-ssr@3.5.25":
version "3.4.37" version "3.5.25"
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.4.37.tgz#b75e1c76c3184f86fa9f0ba4d61d13bc6afcbf8a" resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.25.tgz#51dd89b88a1e044d1beab158c91a29963d28eb96"
integrity sha512-TyAgYBWrHlFrt4qpdACh8e9Ms6C/AZQ6A6xLJaWrCL8GCX5DxMzxyeFAEMfU/VFr4tylHm+a2NpfJpcd7+20XA== integrity sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==
dependencies: dependencies:
"@vue/compiler-dom" "3.4.37" "@vue/compiler-dom" "3.5.25"
"@vue/shared" "3.4.37" "@vue/shared" "3.5.25"
"@vue/component-compiler-utils@^3.1.0", "@vue/component-compiler-utils@^3.3.0": "@vue/component-compiler-utils@^3.1.0", "@vue/component-compiler-utils@^3.3.0":
version "3.3.0" version "3.3.0"
@ -1932,43 +1931,43 @@
"@typescript-eslint/parser" "^7.1.1" "@typescript-eslint/parser" "^7.1.1"
vue-eslint-parser "^9.3.1" vue-eslint-parser "^9.3.1"
"@vue/reactivity@3.4.37": "@vue/reactivity@3.5.25":
version "3.4.37" version "3.5.25"
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.4.37.tgz#5a199563eaab51ed9f94ddf0a82f9179bcc01676" resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.25.tgz#2420fa02022dab3373033c955802b9cdab5435ad"
integrity sha512-UmdKXGx0BZ5kkxPqQr3PK3tElz6adTey4307NzZ3whZu19i5VavYal7u2FfOmAzlcDVgE8+X0HZ2LxLb/jgbYw== integrity sha512-5xfAypCQepv4Jog1U4zn8cZIcbKKFka3AgWHEFQeK65OW+Ys4XybP6z2kKgws4YB43KGpqp5D/K3go2UPPunLA==
dependencies: dependencies:
"@vue/shared" "3.4.37" "@vue/shared" "3.5.25"
"@vue/runtime-core@3.4.37": "@vue/runtime-core@3.5.25":
version "3.4.37" version "3.5.25"
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.4.37.tgz#3fe734a666db7842bea4185a13f7697a2102b719" resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.25.tgz#5e524db201b419db6f091db440452fe4e49efdee"
integrity sha512-MNjrVoLV/sirHZoD7QAilU1Ifs7m/KJv4/84QVbE6nyAZGQNVOa1HGxaOzp9YqCG+GpLt1hNDC4RbH+KtanV7w== integrity sha512-Z751v203YWwYzy460bzsYQISDfPjHTl+6Zzwo/a3CsAf+0ccEjQ8c+0CdX1WsumRTHeywvyUFtW6KvNukT/smA==
dependencies: dependencies:
"@vue/reactivity" "3.4.37" "@vue/reactivity" "3.5.25"
"@vue/shared" "3.4.37" "@vue/shared" "3.5.25"
"@vue/runtime-dom@3.4.37": "@vue/runtime-dom@3.5.25":
version "3.4.37" version "3.5.25"
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.4.37.tgz#219f84577027103de6ddc71351d8237c7c16adac" resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.25.tgz#ebd9815f39ee70fe32698c615cc09bda604e4e06"
integrity sha512-Mg2EwgGZqtwKrqdL/FKMF2NEaOHuH+Ks9TQn3DHKyX//hQTYOun+7Tqp1eo0P4Ds+SjltZshOSRq6VsU0baaNg== integrity sha512-a4WrkYFbb19i9pjkz38zJBg8wa/rboNERq3+hRRb0dHiJh13c+6kAbgqCPfMaJ2gg4weWD3APZswASOfmKwamA==
dependencies: dependencies:
"@vue/reactivity" "3.4.37" "@vue/reactivity" "3.5.25"
"@vue/runtime-core" "3.4.37" "@vue/runtime-core" "3.5.25"
"@vue/shared" "3.4.37" "@vue/shared" "3.5.25"
csstype "^3.1.3" csstype "^3.1.3"
"@vue/server-renderer@3.4.37": "@vue/server-renderer@3.5.25":
version "3.4.37" version "3.5.25"
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.4.37.tgz#d341425bb5395a3f6ed70572ea5c3edefab92f28" resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.25.tgz#ca67ac93cb84dd3c3bc2f89c046a18ab04f7cc96"
integrity sha512-jZ5FAHDR2KBq2FsRUJW6GKDOAG9lUTX8aBEGq4Vf6B/35I9fPce66BornuwmqmKgfiSlecwuOb6oeoamYMohkg== integrity sha512-UJaXR54vMG61i8XNIzTSf2Q7MOqZHpp8+x3XLGtE3+fL+nQd+k7O5+X3D/uWrnQXOdMw5VPih+Uremcw+u1woQ==
dependencies: dependencies:
"@vue/compiler-ssr" "3.4.37" "@vue/compiler-ssr" "3.5.25"
"@vue/shared" "3.4.37" "@vue/shared" "3.5.25"
"@vue/shared@3.4.37": "@vue/shared@3.5.25":
version "3.4.37" version "3.5.25"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.37.tgz#4f4c08a2e73da512a77b47165cf59ffbc1b5ade8" resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.25.tgz#21edcff133a5a04f72c4e4c6142260963fe5afbe"
integrity sha512-nIh8P2fc3DflG8+5Uw8PT/1i17ccFn0xxN/5oE9RfV5SVnd7G0XEFRwakrnNFE/jlS95fpGXDVG5zDETS26nmg== integrity sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==
"@vue/test-utils@^2.4.6": "@vue/test-utils@^2.4.6":
version "2.4.6" version "2.4.6"
@ -3450,10 +3449,10 @@ entities@^2.0.0:
resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
entities@^5.0.0: entities@^4.5.0:
version "5.0.0" version "4.5.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-5.0.0.tgz#b2ab51fe40d995817979ec79dd621154c3c0f62b" resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
integrity sha512-BeJFvFRJddxobhvEdm5GqHzRV/X+ACeuw0/BuuxsCh1EUZcAIz8+kYmBp/LrQuloy6K1f3a0M7+IhmZ7QnkISA== integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
errno@^0.1.3: errno@^0.1.3:
version "0.1.8" version "0.1.8"
@ -4947,12 +4946,12 @@ luxon@^3.5.0:
resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.5.0.tgz#6b6f65c5cd1d61d1fd19dbf07ee87a50bf4b8e20" resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.5.0.tgz#6b6f65c5cd1d61d1fd19dbf07ee87a50bf4b8e20"
integrity sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ== integrity sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==
magic-string@^0.30.10: magic-string@^0.30.21:
version "0.30.11" version "0.30.21"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.11.tgz#301a6f93b3e8c2cb13ac1a7a673492c0dfd12954" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.21.tgz#56763ec09a0fa8091df27879fd94d19078c00d91"
integrity sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A== integrity sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==
dependencies: dependencies:
"@jridgewell/sourcemap-codec" "^1.5.0" "@jridgewell/sourcemap-codec" "^1.5.5"
make-dir@^3.0.2, make-dir@^3.1.0: make-dir@^3.0.2, make-dir@^3.1.0:
version "3.1.0" version "3.1.0"
@ -5213,7 +5212,12 @@ nanoid@3.1.20:
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788"
integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==
nanoid@^3.3.6, nanoid@^3.3.7: nanoid@^3.3.11:
version "3.3.11"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b"
integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==
nanoid@^3.3.6:
version "3.3.7" version "3.3.7"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
@ -5606,10 +5610,10 @@ picocolors@^1.0.0:
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
picocolors@^1.0.1: picocolors@^1.1.1:
version "1.0.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
version "2.3.1" version "2.3.1"
@ -5912,14 +5916,14 @@ postcss@^8.2.6, postcss@^8.3.5, postcss@^8.4.21:
picocolors "^1.0.0" picocolors "^1.0.0"
source-map-js "^1.0.2" source-map-js "^1.0.2"
postcss@^8.4.40: postcss@^8.5.6:
version "8.4.41" version "8.5.6"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.41.tgz#d6104d3ba272d882fe18fc07d15dc2da62fa2681" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c"
integrity sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ== integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==
dependencies: dependencies:
nanoid "^3.3.7" nanoid "^3.3.11"
picocolors "^1.0.1" picocolors "^1.1.1"
source-map-js "^1.2.0" source-map-js "^1.2.1"
prelude-ls@^1.2.1: prelude-ls@^1.2.1:
version "1.2.1" version "1.2.1"
@ -6507,10 +6511,10 @@ sockjs@^0.3.24:
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
source-map-js@^1.2.0: source-map-js@^1.2.1:
version "1.2.0" version "1.2.1"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
source-map-support@^0.5.13, source-map-support@~0.5.20: source-map-support@^0.5.13, source-map-support@~0.5.20:
version "0.5.21" version "0.5.21"
@ -6606,7 +6610,7 @@ statuses@2.0.1:
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: "string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3" version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@ -6632,6 +6636,15 @@ string-width@^3.0.0, string-width@^3.1.0:
is-fullwidth-code-point "^2.0.0" is-fullwidth-code-point "^2.0.0"
strip-ansi "^5.1.0" strip-ansi "^5.1.0"
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^5.0.1, string-width@^5.1.2: string-width@^5.0.1, string-width@^5.1.2:
version "5.1.2" version "5.1.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
@ -6655,7 +6668,7 @@ string_decoder@~1.1.1:
dependencies: dependencies:
safe-buffer "~5.1.0" safe-buffer "~5.1.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: "strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1" version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@ -6676,6 +6689,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
dependencies: dependencies:
ansi-regex "^4.1.0" ansi-regex "^4.1.0"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^7.0.1: strip-ansi@^7.0.1:
version "7.1.0" version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@ -7098,16 +7118,16 @@ vue-template-es2015-compiler@^1.9.0:
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825" resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw== integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
vue@^3.4.37: vue@^3.5.25:
version "3.4.37" version "3.5.25"
resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.37.tgz#64ce0eeb8de16a29fb74e504777ee8c0c1cf229e" resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.25.tgz#b68b5092b617c57a0a36e8e640fd2c09aa2a374d"
integrity sha512-3vXvNfkKTBsSJ7JP+LyR7GBuwQuckbWvuwAid3xbqK9ppsKt/DUvfqgZ48fgOLEfpy1IacL5f8QhUVl77RaI7A== integrity sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==
dependencies: dependencies:
"@vue/compiler-dom" "3.4.37" "@vue/compiler-dom" "3.5.25"
"@vue/compiler-sfc" "3.4.37" "@vue/compiler-sfc" "3.5.25"
"@vue/runtime-dom" "3.4.37" "@vue/runtime-dom" "3.5.25"
"@vue/server-renderer" "3.4.37" "@vue/server-renderer" "3.5.25"
"@vue/shared" "3.4.37" "@vue/shared" "3.5.25"
w3c-hr-time@^1.0.2: w3c-hr-time@^1.0.2:
version "1.0.2" version "1.0.2"
@ -7369,7 +7389,7 @@ workerpool@6.1.0:
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.0.tgz#a8e038b4c94569596852de7a8ea4228eefdeb37b" resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.0.tgz#a8e038b4c94569596852de7a8ea4228eefdeb37b"
integrity sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg== integrity sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0" version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@ -7395,6 +7415,15 @@ wrap-ansi@^5.1.0:
string-width "^3.0.0" string-width "^3.0.0"
strip-ansi "^5.0.0" strip-ansi "^5.0.0"
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^8.1.0: wrap-ansi@^8.1.0:
version "8.1.0" version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"