Compare commits
6 commits
7643f35ec5
...
3d42713e69
| Author | SHA1 | Date | |
|---|---|---|---|
| 3d42713e69 | |||
| 24696fe44f | |||
| 630cdcd6a0 | |||
| 956d373b28 | |||
| 4bab74b852 | |||
| 804ad3f92f |
17 changed files with 98 additions and 119 deletions
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
<BulmaButton
|
<BulmaButton
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
:icon="'fa-solid fa-toggle-' + (store.is_admin ? 'on' : 'off')"
|
:icon="['fas', store.is_admin ? 'fa-toggle-on' : 'fa-toggle-off']"
|
||||||
:busy="is_busy"
|
:busy="is_busy"
|
||||||
text="Admin"
|
text="Admin"
|
||||||
@click.left="on_click"
|
@click.left="on_click"
|
||||||
|
|
@ -23,7 +23,7 @@ const modal_visible = ref(false);
|
||||||
const is_busy = ref(false);
|
const is_busy = ref(false);
|
||||||
const store = advent22Store();
|
const store = advent22Store();
|
||||||
|
|
||||||
function on_click() {
|
function on_click(): void {
|
||||||
if (store.is_admin) {
|
if (store.is_admin) {
|
||||||
store.logout();
|
store.logout();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -33,16 +33,19 @@ function on_click() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function on_submit(creds: Credentials) {
|
async function on_submit(creds: Credentials): Promise<void> {
|
||||||
modal_visible.value = false;
|
modal_visible.value = false;
|
||||||
|
|
||||||
store
|
try {
|
||||||
.login(creds)
|
await store.login(creds);
|
||||||
.catch((error) => APIError.alert(error))
|
} catch (error) {
|
||||||
.finally(() => (is_busy.value = false));
|
APIError.alert(error);
|
||||||
|
} finally {
|
||||||
|
is_busy.value = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function on_cancel() {
|
function on_cancel(): void {
|
||||||
modal_visible.value = false;
|
modal_visible.value = false;
|
||||||
is_busy.value = false;
|
is_busy.value = false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,11 +71,11 @@ let modal: HMultiModal | undefined;
|
||||||
let toast: HBulmaToast | undefined;
|
let toast: HBulmaToast | undefined;
|
||||||
let toast_timeout: number | undefined;
|
let toast_timeout: number | undefined;
|
||||||
|
|
||||||
function on_modal_handle(handle: HMultiModal) {
|
function on_modal_handle(handle: HMultiModal): void {
|
||||||
modal = handle;
|
modal = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
function on_toast_handle(handle: HBulmaToast) {
|
function on_toast_handle(handle: HBulmaToast): void {
|
||||||
toast = handle;
|
toast = handle;
|
||||||
|
|
||||||
if (store.is_touch_device) return;
|
if (store.is_touch_device) return;
|
||||||
|
|
@ -90,7 +90,7 @@ function on_toast_handle(handle: HBulmaToast) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function door_click(day: number) {
|
async function door_click(day: number): Promise<void> {
|
||||||
window.clearTimeout(toast_timeout);
|
window.clearTimeout(toast_timeout);
|
||||||
toast?.hide();
|
toast?.hide();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
ref="username_input"
|
ref="username_input"
|
||||||
class="input"
|
class="input"
|
||||||
type="text"
|
type="text"
|
||||||
v-model="username"
|
v-model="creds[0]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Passwort</label>
|
<label class="label">Passwort</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input class="input" type="password" v-model="password" />
|
<input class="input" type="password" v-model="creds[1]" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -33,13 +33,13 @@
|
||||||
<BulmaButton
|
<BulmaButton
|
||||||
class="is-success"
|
class="is-success"
|
||||||
@click.left="submit"
|
@click.left="submit"
|
||||||
icon="fa-solid fa-unlock"
|
:icon="['fas', 'fa-unlock']"
|
||||||
text="Login"
|
text="Login"
|
||||||
/>
|
/>
|
||||||
<BulmaButton
|
<BulmaButton
|
||||||
class="is-danger"
|
class="is-danger"
|
||||||
@click.left="cancel"
|
@click.left="cancel"
|
||||||
icon="fa-solid fa-circle-xmark"
|
:icon="['fas', 'fa-circle-xmark']"
|
||||||
text="Abbrechen"
|
text="Abbrechen"
|
||||||
/>
|
/>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
@ -60,11 +60,10 @@ const emit = defineEmits<{
|
||||||
(event: "cancel"): void;
|
(event: "cancel"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const username = ref("");
|
const creds = ref<Credentials>(["", ""]);
|
||||||
const password = ref("");
|
|
||||||
|
|
||||||
function submit(): void {
|
function submit(): void {
|
||||||
emit("submit", [username.value, password.value]);
|
emit("submit", creds.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancel(): void {
|
function cancel(): void {
|
||||||
|
|
|
||||||
|
|
@ -39,18 +39,17 @@ export type HMultiModal = {
|
||||||
show_image(src: string, caption: string): void;
|
show_image(src: string, caption: string): void;
|
||||||
show_loading(): void;
|
show_loading(): void;
|
||||||
hide(): void;
|
hide(): void;
|
||||||
dismiss(): void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(event: "handle", handle: HMultiModal): void;
|
(event: "handle", handle: HMultiModal): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
function hide() {
|
function hide(): void {
|
||||||
state.value = { show: "none" };
|
state.value = { show: "none" };
|
||||||
}
|
}
|
||||||
|
|
||||||
function dismiss() {
|
function dismiss(): void {
|
||||||
if (state.value.show !== "loading") {
|
if (state.value.show !== "loading") {
|
||||||
hide();
|
hide();
|
||||||
}
|
}
|
||||||
|
|
@ -58,14 +57,13 @@ function dismiss() {
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
emit("handle", {
|
emit("handle", {
|
||||||
show_image(src: string, caption: string = "") {
|
show_image(src: string, caption: string = ""): void {
|
||||||
state.value = { show: "image", src: src, caption: caption };
|
state.value = { show: "image", src: src, caption: caption };
|
||||||
},
|
},
|
||||||
show_loading() {
|
show_loading(): void {
|
||||||
state.value = { show: "loading" };
|
state.value = { show: "loading" };
|
||||||
},
|
},
|
||||||
hide,
|
hide,
|
||||||
dismiss,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const on_keydown = (e: KeyboardEvent) => {
|
const on_keydown = (e: KeyboardEvent) => {
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,7 @@
|
||||||
<span>Eingabemodus: </span>
|
<span>Eingabemodus: </span>
|
||||||
<BulmaButton
|
<BulmaButton
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
:icon="
|
:icon="['fas', store.is_touch_device ? 'hand-pointer' : 'arrow-pointer']"
|
||||||
'fa-solid fa-' +
|
|
||||||
(store.is_touch_device ? 'hand-pointer' : 'arrow-pointer')
|
|
||||||
"
|
|
||||||
:text="store.is_touch_device ? 'Touch' : 'Desktop'"
|
:text="store.is_touch_device ? 'Touch' : 'Desktop'"
|
||||||
@click.left="store.toggle_touch_device"
|
@click.left="store.toggle_touch_device"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
v-for="(data, day) in day_data"
|
v-for="(data, day) in day_data"
|
||||||
:key="`btn-${day}`"
|
:key="`btn-${day}`"
|
||||||
:class="'tag is-' + (data.part === '' ? 'warning' : 'info')"
|
:class="'tag is-' + (data.part === '' ? 'warning' : 'info')"
|
||||||
icon="fa-solid fa-door-open"
|
:icon="['fas', 'fa-door-open']"
|
||||||
:text="day.toString()"
|
:text="day.toString()"
|
||||||
@click.left="door_click(Number(day))"
|
@click.left="door_click(Number(day))"
|
||||||
/>
|
/>
|
||||||
|
|
@ -59,11 +59,11 @@ const day_data = ref<Record<number, { part: string; image_name: string }>>({});
|
||||||
|
|
||||||
let modal: HMultiModal | undefined;
|
let modal: HMultiModal | undefined;
|
||||||
|
|
||||||
function on_modal_handle(handle: HMultiModal) {
|
function on_modal_handle(handle: HMultiModal): void {
|
||||||
modal = handle;
|
modal = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function on_open() {
|
async function on_open(): Promise<void> {
|
||||||
const [day_parts, day_image_names] = await Promise.all([
|
const [day_parts, day_image_names] = await 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"),
|
||||||
|
|
@ -86,7 +86,7 @@ async function on_open() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function door_click(day: number) {
|
async function door_click(day: number): Promise<void> {
|
||||||
if (modal === undefined) return;
|
if (modal === undefined) return;
|
||||||
modal.show_loading();
|
modal.show_loading();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -247,19 +247,19 @@ 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() {
|
async function on_open(): Promise<void> {
|
||||||
const [store_update, new_admin_config_model, new_doors] = await Promise.all([
|
const [store_update, new_admin_config_model, new_doors] = await 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"),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
void store_update;
|
void store_update; // discard value
|
||||||
admin_config_model.value = new_admin_config_model;
|
admin_config_model.value = new_admin_config_model;
|
||||||
doors.value = new_doors;
|
doors.value = new_doors;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function load_dav_credentials() {
|
async function load_dav_credentials(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
dav_credentials.value = await API.request<Credentials>(
|
dav_credentials.value = await API.request<Credentials>(
|
||||||
"admin/dav_credentials",
|
"admin/dav_credentials",
|
||||||
|
|
@ -267,7 +267,7 @@ async function load_dav_credentials() {
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function load_ui_credentials() {
|
async function load_ui_credentials(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
ui_credentials.value = await API.request<Credentials>(
|
ui_credentials.value = await API.request<Credentials>(
|
||||||
"admin/ui_credentials",
|
"admin/ui_credentials",
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
:disabled="current_step === 0"
|
:disabled="current_step === 0"
|
||||||
class="level-item is-link"
|
class="level-item is-link"
|
||||||
@click="current_step--"
|
@click="current_step--"
|
||||||
icon="fa-solid fa-backward"
|
:icon="['fas', 'fa-backward']"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<BulmaBreadcrumbs
|
<BulmaBreadcrumbs
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
:disabled="current_step === 2"
|
:disabled="current_step === 2"
|
||||||
class="level-item is-link"
|
class="level-item is-link"
|
||||||
@click="current_step++"
|
@click="current_step++"
|
||||||
icon="fa-solid fa-forward"
|
:icon="['fas', 'fa-forward']"
|
||||||
/>
|
/>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|
@ -47,20 +47,20 @@
|
||||||
<BulmaButton
|
<BulmaButton
|
||||||
class="card-footer-item is-danger"
|
class="card-footer-item is-danger"
|
||||||
@click="on_download"
|
@click="on_download"
|
||||||
icon="fa-solid fa-cloud-arrow-down"
|
:icon="['fas', 'fa-cloud-arrow-down']"
|
||||||
:busy="loading_doors"
|
:busy="loading_doors"
|
||||||
text="Laden"
|
text="Laden"
|
||||||
/>
|
/>
|
||||||
<BulmaButton
|
<BulmaButton
|
||||||
class="card-footer-item is-warning"
|
class="card-footer-item is-warning"
|
||||||
@click="on_discard"
|
@click="on_discard"
|
||||||
icon="fa-solid fa-trash"
|
:icon="['fas', 'fa-trash']"
|
||||||
text="Löschen"
|
text="Löschen"
|
||||||
/>
|
/>
|
||||||
<BulmaButton
|
<BulmaButton
|
||||||
class="card-footer-item is-success"
|
class="card-footer-item is-success"
|
||||||
@click="on_upload"
|
@click="on_upload"
|
||||||
icon="fa-solid fa-cloud-arrow-up"
|
:icon="['fas', 'fa-cloud-arrow-up']"
|
||||||
:busy="saving_doors"
|
:busy="saving_doors"
|
||||||
text="Speichern"
|
text="Speichern"
|
||||||
/>
|
/>
|
||||||
|
|
@ -85,9 +85,9 @@ import DoorChooser from "../editor/DoorChooser.vue";
|
||||||
import DoorPlacer from "../editor/DoorPlacer.vue";
|
import DoorPlacer from "../editor/DoorPlacer.vue";
|
||||||
|
|
||||||
const steps: BCStep[] = [
|
const steps: BCStep[] = [
|
||||||
{ label: "Platzieren", icon: "fa-solid fa-crosshairs" },
|
{ label: "Platzieren", icon: ["fas", "fa-crosshairs"] },
|
||||||
{ label: "Ordnen", icon: "fa-solid fa-list-ol" },
|
{ label: "Ordnen", icon: ["fas", "fa-list-ol"] },
|
||||||
{ label: "Vorschau", icon: "fa-solid fa-magnifying-glass" },
|
{ label: "Vorschau", icon: ["fas", "fa-magnifying-glass"] },
|
||||||
];
|
];
|
||||||
|
|
||||||
const doors = ref<Door[]>([]);
|
const doors = ref<Door[]>([]);
|
||||||
|
|
@ -95,7 +95,7 @@ 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() {
|
async function load_doors(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const data = await API.request<DoorSaved[]>("admin/doors");
|
const data = await API.request<DoorSaved[]>("admin/doors");
|
||||||
|
|
||||||
|
|
@ -109,7 +109,7 @@ async function load_doors() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function save_doors() {
|
async function save_doors(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const data: DoorSaved[] = [];
|
const data: DoorSaved[] = [];
|
||||||
|
|
||||||
|
|
@ -128,7 +128,7 @@ async function save_doors() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function on_download() {
|
async function on_download(): Promise<void> {
|
||||||
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;
|
||||||
|
|
||||||
|
|
@ -146,14 +146,14 @@ async function on_download() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function on_discard() {
|
function on_discard(): void {
|
||||||
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.value.length = 0;
|
doors.value.length = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function on_upload() {
|
async function on_upload(): Promise<void> {
|
||||||
if (confirm("Aktuelle Änderungen an den Server schicken?")) {
|
if (confirm("Aktuelle Änderungen an den Server schicken?")) {
|
||||||
saving_doors.value = true;
|
saving_doors.value = true;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,20 +22,21 @@
|
||||||
</button>
|
</button>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<template v-if="is_open">
|
<slot v-if="state === 'loading'" name="loading">
|
||||||
<div v-if="state === 'loading'" class="card-content">
|
<div class="card-content">
|
||||||
<progress class="progress is-primary" />
|
<progress class="progress is-primary" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
</slot>
|
||||||
v-else-if="state === 'err'"
|
|
||||||
class="card-content has-text-danger has-text-centered"
|
<slot v-else-if="state === 'err'" name="error">
|
||||||
>
|
<div class="card-content has-text-danger has-text-centered">
|
||||||
<span class="icon is-large">
|
<span class="icon is-large">
|
||||||
<FontAwesomeIcon :icon="['fas', 'ban']" size="3x" />
|
<FontAwesomeIcon :icon="['fas', 'ban']" size="3x" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<slot v-else name="default" />
|
</slot>
|
||||||
</template>
|
|
||||||
|
<slot v-else-if="state === 'ok'" name="default" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -56,7 +57,7 @@ const props = withDefaults(
|
||||||
const state = ref<"closed" | "loading" | "ok" | "err">("closed");
|
const state = ref<"closed" | "loading" | "ok" | "err">("closed");
|
||||||
const is_open = computed(() => state.value !== "closed");
|
const is_open = computed(() => state.value !== "closed");
|
||||||
|
|
||||||
async function toggle() {
|
async function toggle(): Promise<void> {
|
||||||
if (is_open.value) {
|
if (is_open.value) {
|
||||||
state.value = "closed";
|
state.value = "closed";
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -64,7 +65,7 @@ async function toggle() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function load() {
|
async function load(): Promise<void> {
|
||||||
state.value = "loading";
|
state.value = "loading";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -20,29 +20,27 @@ const emit = defineEmits<{
|
||||||
(event: "handle", handle: HBulmaToast): void;
|
(event: "handle", handle: HBulmaToast): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const message = useTemplateRef("message");
|
const message_div = useTemplateRef("message");
|
||||||
|
|
||||||
onMounted(() =>
|
onMounted(() =>
|
||||||
emit("handle", {
|
emit("handle", {
|
||||||
show(options: ToastOptions = {}) {
|
show(options: ToastOptions = {}): void {
|
||||||
if (message.value === null) return;
|
if (message_div.value === null) return;
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
...options,
|
...options,
|
||||||
single: true,
|
single: true,
|
||||||
message: message.value,
|
message: message_div.value,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
hide() {
|
hide(): void {
|
||||||
if (message.value === null) return;
|
// using "toast" detaches "message" from the invisible "div"
|
||||||
|
// => toast_div is not part of this component!
|
||||||
|
const toast_div = message_div.value?.parentElement;
|
||||||
|
const delete_button = toast_div?.querySelector("button.delete");
|
||||||
|
if (!(delete_button instanceof HTMLButtonElement)) return;
|
||||||
|
|
||||||
const toast_div = message.value.parentElement;
|
delete_button.click();
|
||||||
if (toast_div === null) return;
|
|
||||||
|
|
||||||
const dbutton = toast_div.querySelector("button.delete");
|
|
||||||
if (!(dbutton instanceof HTMLButtonElement)) return;
|
|
||||||
|
|
||||||
dbutton.click();
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ const emit = defineEmits<{
|
||||||
(event: TCEventType, e: MouseEvent, point: Vector2D): void;
|
(event: TCEventType, e: MouseEvent, point: Vector2D): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
function transform_mouse_event(event: MouseEvent) {
|
function transform_mouse_event(event: MouseEvent): void {
|
||||||
if (!is_tceventtype(event.type)) return;
|
if (!is_tceventtype(event.type)) return;
|
||||||
|
|
||||||
emit(event.type, event, get_event_thous(event));
|
emit(event.type, event, get_event_thous(event));
|
||||||
|
|
|
||||||
|
|
@ -50,26 +50,20 @@ const preview_visible = computed(() => state.value.kind !== "idle");
|
||||||
function pop_door(point: Vector2D): VueLike<Door> | undefined {
|
function pop_door(point: Vector2D): VueLike<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) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return model.value.splice(idx, 1)[0];
|
return model.value.splice(idx, 1)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
function draw_start(event: MouseEvent, point: Vector2D) {
|
function draw_start(event: MouseEvent, point: Vector2D): void {
|
||||||
if (preview_visible.value) {
|
if (preview_visible.value) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
preview.value = new Rectangle(point, point);
|
preview.value = new Rectangle(point, point);
|
||||||
state.value = { kind: "drawing" };
|
state.value = { kind: "drawing" };
|
||||||
}
|
}
|
||||||
|
|
||||||
function draw_finish() {
|
function draw_finish(): void {
|
||||||
if (state.value.kind !== "drawing") {
|
if (state.value.kind !== "drawing") return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preview.value.area >= MIN_RECT_AREA) {
|
if (preview.value.area >= MIN_RECT_AREA) {
|
||||||
model.value.push(new Door(preview.value));
|
model.value.push(new Door(preview.value));
|
||||||
|
|
@ -78,33 +72,27 @@ function draw_finish() {
|
||||||
state.value = { kind: "idle" };
|
state.value = { kind: "idle" };
|
||||||
}
|
}
|
||||||
|
|
||||||
function drag_start(event: MouseEvent, point: Vector2D) {
|
function drag_start(event: MouseEvent, point: Vector2D): void {
|
||||||
if (preview_visible.value) {
|
if (preview_visible.value) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const drag_door = pop_door(point);
|
const drag_door = pop_door(point);
|
||||||
|
|
||||||
if (drag_door === undefined) {
|
if (drag_door === undefined) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
preview.value = drag_door.position;
|
preview.value = drag_door.position;
|
||||||
|
|
||||||
state.value = { kind: "dragging", door: drag_door, origin: point };
|
state.value = { kind: "dragging", door: drag_door, origin: point };
|
||||||
}
|
}
|
||||||
|
|
||||||
function drag_finish() {
|
function drag_finish(): void {
|
||||||
if (state.value.kind !== "dragging") {
|
if (state.value.kind !== "dragging") return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
model.value.push(new Door(preview.value, state.value.door.day));
|
model.value.push(new Door(preview.value, state.value.door.day));
|
||||||
|
|
||||||
state.value = { kind: "idle" };
|
state.value = { kind: "idle" };
|
||||||
}
|
}
|
||||||
|
|
||||||
function on_mousemove(event: MouseEvent, point: Vector2D) {
|
function on_mousemove(event: MouseEvent, point: Vector2D): void {
|
||||||
if (state.value.kind === "drawing") {
|
if (state.value.kind === "drawing") {
|
||||||
preview.value = preview.value.update(undefined, point);
|
preview.value = preview.value.update(undefined, point);
|
||||||
} else if (state.value.kind === "dragging") {
|
} else if (state.value.kind === "dragging") {
|
||||||
|
|
@ -113,10 +101,8 @@ function on_mousemove(event: MouseEvent, point: Vector2D) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function remove_rect(event: MouseEvent, point: Vector2D) {
|
function remove_rect(event: MouseEvent, point: Vector2D): void {
|
||||||
if (preview_visible.value) {
|
if (preview_visible.value) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pop_door(point);
|
pop_door(point);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,15 +35,13 @@ const day_input = useTemplateRef("day_input");
|
||||||
const day_str = ref("");
|
const day_str = ref("");
|
||||||
const editing = ref(false);
|
const editing = ref(false);
|
||||||
|
|
||||||
function toggle_editing() {
|
function toggle_editing(): void {
|
||||||
day_str.value = String(model.value.day);
|
day_str.value = String(model.value.day);
|
||||||
editing.value = !editing.value;
|
editing.value = !editing.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function on_click(event: MouseEvent) {
|
function on_click(event: MouseEvent): void {
|
||||||
if (!(event.target instanceof HTMLDivElement)) {
|
if (!(event.target instanceof HTMLDivElement)) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (editing.value) {
|
if (editing.value) {
|
||||||
unwrap_vuelike(model.value).day = day_str.value;
|
unwrap_vuelike(model.value).day = day_str.value;
|
||||||
|
|
@ -57,10 +55,8 @@ function on_click(event: MouseEvent) {
|
||||||
toggle_editing();
|
toggle_editing();
|
||||||
}
|
}
|
||||||
|
|
||||||
function on_keydown(event: KeyboardEvent) {
|
function on_keydown(event: KeyboardEvent): void {
|
||||||
if (!editing.value) {
|
if (!editing.value) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.key === "Enter") {
|
if (event.key === "Enter") {
|
||||||
unwrap_vuelike(model.value).day = day_str.value;
|
unwrap_vuelike(model.value).day = day_str.value;
|
||||||
|
|
|
||||||
|
|
@ -62,14 +62,14 @@ export class APIError extends Error {
|
||||||
return result();
|
return result();
|
||||||
}
|
}
|
||||||
|
|
||||||
public alert() {
|
public alert(): void {
|
||||||
toast({
|
toast({
|
||||||
message: this.format(),
|
message: this.format(),
|
||||||
type: "is-danger",
|
type: "is-danger",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static alert(error: unknown) {
|
public static alert(error: unknown): void {
|
||||||
new APIError(error, "").alert();
|
new APIError(error, "").alert();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ export function unwrap_loading<T>(o: Loading<T>): T {
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function wait_for(condition: () => boolean, action: () => void) {
|
export function wait_for(condition: () => boolean, action: () => void): void {
|
||||||
const enqueue_action = () => {
|
const enqueue_action = () => {
|
||||||
if (!condition()) {
|
if (!condition()) {
|
||||||
nextTick(enqueue_action);
|
nextTick(enqueue_action);
|
||||||
|
|
@ -38,7 +38,7 @@ export function wait_for(condition: () => boolean, action: () => void) {
|
||||||
enqueue_action();
|
enqueue_action();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handle_error(error: unknown) {
|
export function handle_error(error: unknown): void {
|
||||||
if (error instanceof APIError) {
|
if (error instanceof APIError) {
|
||||||
error.alert();
|
error.alert();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ export const advent22Store = defineStore({
|
||||||
API.request<DoorSaved[]>("user/doors"),
|
API.request<DoorSaved[]>("user/doors"),
|
||||||
API.request<number | null>("user/next_door"),
|
API.request<number | null>("user/next_door"),
|
||||||
]);
|
]);
|
||||||
is_admin; // discard value
|
void is_admin; // discard value
|
||||||
|
|
||||||
document.title = site_config.title;
|
document.title = site_config.title;
|
||||||
|
|
||||||
|
|
@ -110,13 +110,14 @@ export const advent22Store = defineStore({
|
||||||
return this.is_admin;
|
return this.is_admin;
|
||||||
},
|
},
|
||||||
|
|
||||||
login(creds: Credentials): Promise<boolean> {
|
async login(creds: Credentials): Promise<boolean> {
|
||||||
API.creds = { username: creds[0], password: creds[1] };
|
API.creds = { username: creds[0], password: creds[1] };
|
||||||
return this.update_is_admin();
|
return await this.update_is_admin();
|
||||||
},
|
},
|
||||||
|
|
||||||
logout(): Promise<boolean> {
|
logout() {
|
||||||
return this.login(["", ""]);
|
API.creds = { username: "", password: "" };
|
||||||
|
this.is_admin = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
toggle_touch_device(): void {
|
toggle_touch_device(): void {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ describe("Rectangle Tests", () => {
|
||||||
top: number,
|
top: number,
|
||||||
width: number,
|
width: number,
|
||||||
height: number,
|
height: number,
|
||||||
) {
|
): void {
|
||||||
expect(r.left).to.equal(left);
|
expect(r.left).to.equal(left);
|
||||||
expect(r.top).to.equal(top);
|
expect(r.top).to.equal(top);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue