Compare commits
No commits in common. "7cca8c4825c959b1b46dee1c2bf81947f339b857" and "ac3773483f6a2ca1a399bf1558b97df1b6e1c9dd" have entirely different histories.
7cca8c4825
...
ac3773483f
14 changed files with 162 additions and 141 deletions
|
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<div class="image is-unselectable">
|
<div class="image is-unselectable">
|
||||||
<img :src="unwrap_loading(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"
|
||||||
|
|
@ -49,7 +49,7 @@
|
||||||
<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 { VueLike, name_door, unwrap_loading } from "@/lib/helpers";
|
import { ensure_loaded, Like, 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";
|
||||||
|
|
@ -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: VueLike<Door>[];
|
doors: Like<Door>[];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const store = advent22Store();
|
const store = advent22Store();
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<ConfigView />
|
<ConfigView />
|
||||||
<CalendarAssistant />
|
<CalendarAssistant />
|
||||||
<DoorMapEditor />
|
<DoorMapEditor />
|
||||||
<BulmaDrawer header="Vorschau">
|
<BulmaDrawer header="Vorschau" @open="(ready) => ready()">
|
||||||
<UserView />
|
<UserView />
|
||||||
</BulmaDrawer>
|
</BulmaDrawer>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MultiModal @handle="on_modal_handle" />
|
<MultiModal @handle="on_modal_handle" />
|
||||||
|
|
||||||
<BulmaDrawer header="Kalender-Assistent" :opening="on_open" refreshable>
|
<BulmaDrawer header="Kalender-Assistent" @open="on_open" refreshable>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<p>Hervorgehobenen Tagen wurde kein Buchstabe zugewiesen.</p>
|
<p>Hervorgehobenen Tagen wurde kein Buchstabe zugewiesen.</p>
|
||||||
|
|
@ -37,7 +37,7 @@
|
||||||
:class="'tag is-' + (data.part === '' ? 'warning' : 'info')"
|
:class="'tag is-' + (data.part === '' ? 'warning' : 'info')"
|
||||||
icon="fa-solid fa-door-open"
|
icon="fa-solid fa-door-open"
|
||||||
:text="day.toString()"
|
:text="day.toString()"
|
||||||
@click.left="door_click(Number(day))"
|
@click.left="door_click(day)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -55,7 +55,12 @@ 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 = ref<Record<number, { part: string; image_name: string }>>({});
|
const day_data = ref<{
|
||||||
|
[day: number]: {
|
||||||
|
part: string;
|
||||||
|
image_name: string;
|
||||||
|
};
|
||||||
|
}>({});
|
||||||
|
|
||||||
let modal: HMultiModal | undefined;
|
let modal: HMultiModal | undefined;
|
||||||
|
|
||||||
|
|
@ -63,27 +68,31 @@ function on_modal_handle(handle: HMultiModal) {
|
||||||
modal = handle;
|
modal = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function on_open() {
|
function on_open(ready: () => void, fail: () => void) {
|
||||||
const [day_parts, day_image_names] = await Promise.all([
|
Promise.all([
|
||||||
API.request<NumStrDict>("admin/day_parts"),
|
API.request<NumStrDict>("admin/day_parts"),
|
||||||
API.request<NumStrDict>("admin/day_image_names"),
|
API.request<NumStrDict>("admin/day_image_names"),
|
||||||
]);
|
])
|
||||||
|
.then(([day_parts, day_image_names]) => {
|
||||||
|
const _ensure_day_in_data = (day: number) => {
|
||||||
|
if (!(day in day_data.value)) {
|
||||||
|
day_data.value[day] = { part: "", image_name: "" };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const _ensure_day_in_data = (day: number) => {
|
objForEach(day_parts, (day, part) => {
|
||||||
if (!(day in day_data.value)) {
|
_ensure_day_in_data(day);
|
||||||
day_data.value[day] = { part: "", image_name: "" };
|
day_data.value[day].part = part;
|
||||||
}
|
});
|
||||||
};
|
|
||||||
|
|
||||||
objForEach(day_parts, (day, part) => {
|
objForEach(day_image_names, (day, image_name) => {
|
||||||
_ensure_day_in_data(day);
|
_ensure_day_in_data(day);
|
||||||
day_data.value[day].part = part;
|
day_data.value[day].image_name = image_name;
|
||||||
});
|
});
|
||||||
|
|
||||||
objForEach(day_image_names, (day, image_name) => {
|
ready();
|
||||||
_ensure_day_in_data(day);
|
})
|
||||||
day_data.value[day].image_name = image_name;
|
.catch(fail);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function door_click(day: number) {
|
async function door_click(day: number) {
|
||||||
|
|
@ -93,7 +102,7 @@ async function door_click(day: number) {
|
||||||
try {
|
try {
|
||||||
const day_image = await API.request<ImageData>(`user/image_${day}`);
|
const day_image = await API.request<ImageData>(`user/image_${day}`);
|
||||||
modal.show_image(day_image.data_url, name_door(day));
|
modal.show_image(day_image.data_url, name_door(day));
|
||||||
} catch {
|
} catch (error) {
|
||||||
modal.hide();
|
modal.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<BulmaDrawer header="Konfiguration" :opening="on_open" refreshable>
|
<BulmaDrawer header="Konfiguration" @open="on_open" refreshable>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column is-one-third">
|
<div class="column is-one-third">
|
||||||
|
|
@ -247,32 +247,33 @@ function fmt_puzzle_date(name: keyof AdminConfigModel["puzzle"]): string {
|
||||||
return DateTime.fromISO(iso_date).toLocaleString(DateTime.DATE_SHORT);
|
return DateTime.fromISO(iso_date).toLocaleString(DateTime.DATE_SHORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function on_open() {
|
function on_open(ready: () => void, fail: () => void): void {
|
||||||
const [store_update, new_admin_config_model, new_doors] = await Promise.all([
|
Promise.all([
|
||||||
store.update(),
|
store.update(),
|
||||||
API.request<AdminConfigModel>("admin/config_model"),
|
API.request<AdminConfigModel>("admin/config_model"),
|
||||||
API.request<DoorSaved[]>("admin/doors"),
|
API.request<DoorSaved[]>("admin/doors"),
|
||||||
]);
|
])
|
||||||
|
.then(([store_update, new_admin_config_model, new_doors]) => {
|
||||||
|
store_update; // discard value
|
||||||
|
|
||||||
void store_update;
|
admin_config_model.value = new_admin_config_model;
|
||||||
admin_config_model.value = new_admin_config_model;
|
doors.value = new_doors;
|
||||||
doors.value = new_doors;
|
|
||||||
|
ready();
|
||||||
|
})
|
||||||
|
.catch(fail);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function load_dav_credentials() {
|
function load_dav_credentials(): void {
|
||||||
try {
|
API.request<Credentials>("admin/dav_credentials")
|
||||||
dav_credentials.value = await API.request<Credentials>(
|
.then((creds) => (dav_credentials.value = creds))
|
||||||
"admin/dav_credentials",
|
.catch(() => {});
|
||||||
);
|
|
||||||
} catch {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function load_ui_credentials() {
|
function load_ui_credentials(): void {
|
||||||
try {
|
API.request<Credentials>("admin/ui_credentials")
|
||||||
ui_credentials.value = await API.request<Credentials>(
|
.then((creds) => (ui_credentials.value = creds))
|
||||||
"admin/ui_credentials",
|
.catch(() => {});
|
||||||
);
|
|
||||||
} catch {}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<BulmaDrawer header="Türchen bearbeiten" :opening="load_doors">
|
<BulmaDrawer header="Türchen bearbeiten" @open="on_open">
|
||||||
<nav class="level is-mobile mb-0" style="overflow-x: auto">
|
<nav class="level is-mobile mb-0" style="overflow-x: auto">
|
||||||
<BulmaButton
|
<BulmaButton
|
||||||
:disabled="current_step === 0"
|
:disabled="current_step === 0"
|
||||||
|
|
@ -95,54 +95,60 @@ const current_step = ref(0);
|
||||||
const loading_doors = ref(false);
|
const loading_doors = ref(false);
|
||||||
const saving_doors = ref(false);
|
const saving_doors = ref(false);
|
||||||
|
|
||||||
async function load_doors() {
|
function load_doors(): Promise<void> {
|
||||||
try {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const data = await API.request<DoorSaved[]>("admin/doors");
|
API.request<DoorSaved[]>("admin/doors")
|
||||||
|
.then((data) => {
|
||||||
|
doors.value.length = 0;
|
||||||
|
|
||||||
doors.value.length = 0;
|
for (const value of data) {
|
||||||
for (const value of data) {
|
doors.value.push(Door.load(value));
|
||||||
doors.value.push(Door.load(value));
|
}
|
||||||
}
|
|
||||||
} catch (error) {
|
resolve();
|
||||||
APIError.alert(error);
|
})
|
||||||
throw null;
|
.catch((error) => {
|
||||||
}
|
APIError.alert(error);
|
||||||
|
reject();
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function save_doors() {
|
function save_doors(): Promise<void> {
|
||||||
try {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const data: DoorSaved[] = [];
|
const data: DoorSaved[] = [];
|
||||||
|
|
||||||
for (const door of doors.value) {
|
for (const door of doors.value) {
|
||||||
data.push(door.save());
|
data.push(door.save());
|
||||||
}
|
}
|
||||||
|
|
||||||
await API.request<void>({
|
API.request<void>({ endpoint: "admin/doors", method: "PUT", data: data })
|
||||||
endpoint: "admin/doors",
|
.then(resolve)
|
||||||
method: "PUT",
|
.catch((error) => {
|
||||||
data: data,
|
APIError.alert(error);
|
||||||
});
|
reject();
|
||||||
} catch (error) {
|
});
|
||||||
APIError.alert(error);
|
});
|
||||||
throw null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function on_download() {
|
function on_open(ready: () => void, fail: () => void): void {
|
||||||
|
load_doors().then(ready).catch(fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.value = true;
|
loading_doors.value = true;
|
||||||
|
|
||||||
try {
|
load_doors()
|
||||||
load_doors();
|
.then(() =>
|
||||||
|
toast({
|
||||||
toast({
|
message: "Erfolgreich!",
|
||||||
message: "Erfolgreich!",
|
type: "is-success",
|
||||||
type: "is-success",
|
duration: 2e3,
|
||||||
duration: 2e3,
|
}),
|
||||||
});
|
)
|
||||||
} finally {
|
.catch(() => {})
|
||||||
loading_doors.value = false;
|
.finally(() => (loading_doors.value = false));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,22 +159,24 @@ function on_discard() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function on_upload() {
|
function on_upload() {
|
||||||
if (confirm("Aktuelle Änderungen an den Server schicken?")) {
|
if (confirm("Aktuelle Änderungen an den Server schicken?")) {
|
||||||
saving_doors.value = true;
|
saving_doors.value = true;
|
||||||
|
|
||||||
try {
|
save_doors()
|
||||||
save_doors();
|
.then(() => {
|
||||||
load_doors();
|
load_doors()
|
||||||
|
.then(() =>
|
||||||
toast({
|
toast({
|
||||||
message: "Erfolgreich!",
|
message: "Erfolgreich!",
|
||||||
type: "is-success",
|
type: "is-success",
|
||||||
duration: 2e3,
|
duration: 2e3,
|
||||||
});
|
}),
|
||||||
} finally {
|
)
|
||||||
saving_doors.value = false;
|
.catch(() => {})
|
||||||
}
|
.finally(() => (saving_doors.value = false));
|
||||||
|
})
|
||||||
|
.catch(() => (saving_doors.value = false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,11 @@
|
||||||
<header class="card-header is-unselectable" style="cursor: pointer">
|
<header class="card-header is-unselectable" style="cursor: pointer">
|
||||||
<p class="card-header-title" @click="toggle">{{ header }}</p>
|
<p class="card-header-title" @click="toggle">{{ header }}</p>
|
||||||
|
|
||||||
<p v-if="refreshable && is_open" class="card-header-icon px-0">
|
<p v-if="refreshable" class="card-header-icon px-0">
|
||||||
<BulmaButton class="is-small is-primary" @click="load">
|
<BulmaButton class="is-small is-primary" @click="refresh">
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
:icon="['fas', 'arrows-rotate']"
|
:icon="['fas', 'arrows-rotate']"
|
||||||
:spin="state === 'loading'"
|
:spin="is_open && state === 'loading'"
|
||||||
/>
|
/>
|
||||||
</BulmaButton>
|
</BulmaButton>
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
<progress class="progress is-primary" />
|
<progress class="progress is-primary" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else-if="state === 'err'"
|
v-else-if="state === 'failed'"
|
||||||
class="card-content has-text-danger has-text-centered"
|
class="card-content has-text-danger has-text-centered"
|
||||||
>
|
>
|
||||||
<span class="icon is-large">
|
<span class="icon is-large">
|
||||||
|
|
@ -40,39 +40,42 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
|
||||||
import BulmaButton from "./Button.vue";
|
import BulmaButton from "./Button.vue";
|
||||||
|
|
||||||
const props = withDefaults(
|
withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
header: string;
|
header: string;
|
||||||
opening?: () => Promise<void>;
|
|
||||||
refreshable?: boolean;
|
refreshable?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
{ opening: async () => {}, refreshable: false },
|
{ refreshable: false },
|
||||||
);
|
);
|
||||||
|
|
||||||
const state = ref<"closed" | "loading" | "ok" | "err">("closed");
|
const emit = defineEmits<{
|
||||||
const is_open = computed(() => state.value !== "closed");
|
(event: "open", ready: () => void, fail: () => void): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const is_open = ref(false);
|
||||||
|
const state = ref<"loading" | "ready" | "failed">("loading");
|
||||||
|
|
||||||
|
function toggle() {
|
||||||
|
is_open.value = !is_open.value;
|
||||||
|
|
||||||
async function toggle() {
|
|
||||||
if (is_open.value) {
|
if (is_open.value) {
|
||||||
state.value = "closed";
|
state.value = "loading";
|
||||||
} else {
|
|
||||||
await load();
|
emit(
|
||||||
|
"open",
|
||||||
|
() => (state.value = "ready"),
|
||||||
|
() => (state.value = "failed"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function load() {
|
function refresh() {
|
||||||
state.value = "loading";
|
is_open.value = false;
|
||||||
|
toggle();
|
||||||
try {
|
|
||||||
await props.opening();
|
|
||||||
state.value = "ok";
|
|
||||||
} catch {
|
|
||||||
state.value = "err";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,14 @@
|
||||||
import { Door } from "@/lib/rects/door";
|
import { Door } from "@/lib/rects/door";
|
||||||
import { advent22Store } from "@/lib/store";
|
import { advent22Store } from "@/lib/store";
|
||||||
|
|
||||||
import { VueLike } from "@/lib/helpers";
|
import { Like } from "@/lib/helpers";
|
||||||
import SVGRect from "./SVGRect.vue";
|
import SVGRect from "./SVGRect.vue";
|
||||||
|
|
||||||
const store = advent22Store();
|
const store = advent22Store();
|
||||||
|
|
||||||
withDefaults(
|
withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
door: VueLike<Door>;
|
door: Like<Door>;
|
||||||
force_visible?: boolean;
|
force_visible?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { VueLike, loading_success } from "@/lib/helpers";
|
import { Like, loading_success } from "@/lib/helpers";
|
||||||
import { Rectangle } from "@/lib/rects/rectangle";
|
import { Rectangle } from "@/lib/rects/rectangle";
|
||||||
import { advent22Store } from "@/lib/store";
|
import { advent22Store } from "@/lib/store";
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
|
|
@ -39,7 +39,7 @@ withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
variant: BulmaVariant;
|
variant: BulmaVariant;
|
||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
rectangle: VueLike<Rectangle>;
|
rectangle: Like<Rectangle>;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
visible: true,
|
visible: true,
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ import { Rectangle } from "@/lib/rects/rectangle";
|
||||||
import { Vector2D } from "@/lib/rects/vector2d";
|
import { Vector2D } from "@/lib/rects/vector2d";
|
||||||
import { computed, ref } from "vue";
|
import { computed, ref } from "vue";
|
||||||
|
|
||||||
import { VueLike } from "@/lib/helpers";
|
import { Like } from "@/lib/helpers";
|
||||||
import CalendarDoor from "../calendar/CalendarDoor.vue";
|
import CalendarDoor from "../calendar/CalendarDoor.vue";
|
||||||
import SVGRect from "../calendar/SVGRect.vue";
|
import SVGRect from "../calendar/SVGRect.vue";
|
||||||
import ThouCanvas from "../calendar/ThouCanvas.vue";
|
import ThouCanvas from "../calendar/ThouCanvas.vue";
|
||||||
|
|
@ -37,9 +37,9 @@ import ThouCanvas from "../calendar/ThouCanvas.vue";
|
||||||
type CanvasState =
|
type CanvasState =
|
||||||
| { kind: "idle" }
|
| { kind: "idle" }
|
||||||
| { kind: "drawing" }
|
| { kind: "drawing" }
|
||||||
| { kind: "dragging"; door: VueLike<Door>; origin: Vector2D };
|
| { kind: "dragging"; door: Like<Door>; origin: Vector2D };
|
||||||
|
|
||||||
const model = defineModel<VueLike<Door>[]>({ required: true });
|
const model = defineModel<Like<Door>[]>({ required: true });
|
||||||
|
|
||||||
const MIN_RECT_AREA = 300;
|
const MIN_RECT_AREA = 300;
|
||||||
const state = ref<CanvasState>({ kind: "idle" });
|
const state = ref<CanvasState>({ kind: "idle" });
|
||||||
|
|
@ -47,7 +47,7 @@ const preview = ref(new Rectangle());
|
||||||
|
|
||||||
const preview_visible = computed(() => state.value.kind !== "idle");
|
const preview_visible = computed(() => state.value.kind !== "idle");
|
||||||
|
|
||||||
function pop_door(point: Vector2D): VueLike<Door> | undefined {
|
function pop_door(point: Vector2D): Like<Door> | undefined {
|
||||||
const idx = model.value.findIndex((rect) => rect.position.contains(point));
|
const idx = model.value.findIndex((rect) => rect.position.contains(point));
|
||||||
|
|
||||||
if (idx === -1) {
|
if (idx === -1) {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<figure class="image is-unselectable">
|
<figure class="image is-unselectable">
|
||||||
<img :src="unwrap_loading(store.background_image).data_url" />
|
<img :src="ensure_loaded(store.background_image).data_url" />
|
||||||
<ThouCanvas>
|
<ThouCanvas>
|
||||||
<PreviewDoor
|
<PreviewDoor
|
||||||
v-for="(_, index) in model"
|
v-for="(_, index) in model"
|
||||||
|
|
@ -24,13 +24,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { VueLike, unwrap_loading } from "@/lib/helpers";
|
import { ensure_loaded, Like } 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 ThouCanvas from "../calendar/ThouCanvas.vue";
|
import ThouCanvas from "../calendar/ThouCanvas.vue";
|
||||||
import PreviewDoor from "./PreviewDoor.vue";
|
import PreviewDoor from "./PreviewDoor.vue";
|
||||||
|
|
||||||
const model = defineModel<VueLike<Door>[]>({ required: true });
|
const model = defineModel<Like<Door>[]>({ required: true });
|
||||||
const store = advent22Store();
|
const store = advent22Store();
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -9,19 +9,19 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<figure class="image is-unselectable">
|
<figure class="image is-unselectable">
|
||||||
<img :src="unwrap_loading(store.background_image).data_url" />
|
<img :src="ensure_loaded(store.background_image).data_url" />
|
||||||
<DoorCanvas v-model="model" />
|
<DoorCanvas v-model="model" />
|
||||||
</figure>
|
</figure>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { VueLike, unwrap_loading } from "@/lib/helpers";
|
import { ensure_loaded, Like } 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 DoorCanvas from "./DoorCanvas.vue";
|
import DoorCanvas from "./DoorCanvas.vue";
|
||||||
|
|
||||||
const model = defineModel<VueLike<Door>[]>({ required: true });
|
const model = defineModel<Like<Door>[]>({ required: true });
|
||||||
const store = advent22Store();
|
const store = advent22Store();
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,10 @@
|
||||||
import { Door } from "@/lib/rects/door";
|
import { Door } from "@/lib/rects/door";
|
||||||
import { ref, useTemplateRef } from "vue";
|
import { ref, useTemplateRef } from "vue";
|
||||||
|
|
||||||
import { VueLike, unwrap_vuelike, wait_for } from "@/lib/helpers";
|
import { Like, unwrap_like, wait_for } from "@/lib/helpers";
|
||||||
import SVGRect from "../calendar/SVGRect.vue";
|
import SVGRect from "../calendar/SVGRect.vue";
|
||||||
|
|
||||||
const model = defineModel<VueLike<Door>>({ required: true });
|
const model = defineModel<Like<Door>>({ required: true });
|
||||||
const day_input = useTemplateRef("day_input");
|
const day_input = useTemplateRef("day_input");
|
||||||
|
|
||||||
const day_str = ref("");
|
const day_str = ref("");
|
||||||
|
|
@ -46,7 +46,7 @@ function on_click(event: MouseEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (editing.value) {
|
if (editing.value) {
|
||||||
unwrap_vuelike(model.value).day = day_str.value;
|
unwrap_like(model.value).day = day_str.value;
|
||||||
} else {
|
} else {
|
||||||
wait_for(
|
wait_for(
|
||||||
() => day_input.value !== null,
|
() => day_input.value !== null,
|
||||||
|
|
@ -63,7 +63,7 @@ function on_keydown(event: KeyboardEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === "Enter") {
|
if (event.key === "Enter") {
|
||||||
unwrap_vuelike(model.value).day = day_str.value;
|
unwrap_like(model.value).day = day_str.value;
|
||||||
toggle_editing();
|
toggle_editing();
|
||||||
} else if (event.key === "Delete") {
|
} else if (event.key === "Delete") {
|
||||||
model.value.day = -1;
|
model.value.day = -1;
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,9 @@ export function objForEach<T>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type VueLike<T> = T | UnwrapRef<T>;
|
export type Like<T> = T | UnwrapRef<T>;
|
||||||
|
|
||||||
export function unwrap_vuelike<T>(value: VueLike<T>): T {
|
export function unwrap_like<T>(value: Like<T>): T {
|
||||||
return value as T;
|
return value as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -27,8 +27,8 @@ export function loading_success<T>(o: Loading<T>): o is T {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function unwrap_loading<T>(o: Loading<T>): T {
|
export function ensure_loaded<T>(o: Loading<T>): T {
|
||||||
if (!loading_success(o)) throw null;
|
if (!loading_success(o)) throw "";
|
||||||
|
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { VueLike, unwrap_vuelike } from "../helpers";
|
import { Like, unwrap_like } from "../helpers";
|
||||||
import { DoorSaved } from "../model";
|
import { DoorSaved } from "../model";
|
||||||
import { Rectangle } from "./rectangle";
|
import { Rectangle } from "./rectangle";
|
||||||
import { Vector2D } from "./vector2d";
|
import { Vector2D } from "./vector2d";
|
||||||
|
|
@ -9,11 +9,11 @@ export class Door {
|
||||||
private _day = Door.MIN_DAY;
|
private _day = Door.MIN_DAY;
|
||||||
public position: Rectangle;
|
public position: Rectangle;
|
||||||
|
|
||||||
constructor(position: VueLike<Rectangle>);
|
constructor(position: Like<Rectangle>);
|
||||||
constructor(position: VueLike<Rectangle>, day: number);
|
constructor(position: Like<Rectangle>, day: number);
|
||||||
constructor(position: VueLike<Rectangle>, day = Door.MIN_DAY) {
|
constructor(position: Like<Rectangle>, day = Door.MIN_DAY) {
|
||||||
this.day = day;
|
this.day = day;
|
||||||
this.position = unwrap_vuelike(position);
|
this.position = unwrap_like(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get day(): number {
|
public get day(): number {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue