🚧 wip on ui: rework for vue 3 composition API

This commit is contained in:
Jörn-Michael Miehe 2025-11-30 18:09:08 +00:00
parent 4f603d3cb1
commit f482fd4ec1
10 changed files with 140 additions and 171 deletions

View file

@ -45,14 +45,13 @@
</figure> </figure>
</template> </template>
<script 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, Loading, 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 { Options, Vue } from "vue-class-component";
import MultiModal from "./MultiModal.vue"; import MultiModal from "./MultiModal.vue";
import BulmaButton from "./bulma/Button.vue"; import BulmaButton from "./bulma/Button.vue";

View file

@ -4,19 +4,8 @@
<DoorMapEditor /> <DoorMapEditor />
</template> </template>
<script lang="ts"> <script setup lang="ts">
import { Options, Vue } from "vue-class-component";
import CalendarAssistant from "./CalendarAssistant.vue"; import CalendarAssistant from "./CalendarAssistant.vue";
import ConfigView from "./ConfigView.vue"; import ConfigView from "./ConfigView.vue";
import DoorMapEditor from "./DoorMapEditor.vue"; import DoorMapEditor from "./DoorMapEditor.vue";
@Options({
components: {
ConfigView,
CalendarAssistant,
DoorMapEditor,
},
})
export default class extends Vue {}
</script> </script>

View file

@ -36,7 +36,7 @@
: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="fa-solid fa-door-open"
:text="day" :text="day.toString()"
@click.left="door_click(day)" @click.left="door_click(day)"
/> />
</div> </div>
@ -45,74 +45,64 @@
</BulmaDrawer> </BulmaDrawer>
</template> </template>
<script lang="ts"> <script setup lang="ts">
import { API } from "@/lib/api"; import { API } from "@/lib/api";
import { name_door, objForEach } from "@/lib/helpers"; import { name_door, objForEach } from "@/lib/helpers";
import { ImageData, NumStrDict } from "@/lib/model"; import { ImageData, NumStrDict } from "@/lib/model";
import { Options, Vue } from "vue-class-component";
import MultiModal from "../MultiModal.vue"; import MultiModal 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";
@Options({ const day_data: {
components: {
BulmaButton,
BulmaDrawer,
MultiModal,
},
})
export default class extends Vue {
public day_data: {
[day: number]: { [day: number]: {
part: string; part: string;
image_name: string; image_name: string;
}; };
} = {}; } = {};
private multi_modal?: MultiModal; let multi_modal: MultiModal | undefined;
public modal_handle(modal: MultiModal) { function modal_handle(modal: MultiModal) {
this.multi_modal = modal; multi_modal = modal;
} }
public on_open(ready: () => void, fail: () => void): void { function on_open(ready: () => void, fail: () => void) {
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]) => { .then(([day_parts, day_image_names]) => {
const _ensure_day_in_data = (day: number) => { const _ensure_day_in_data = (day: number) => {
if (!(day in this.day_data)) { if (!(day in day_data)) {
this.day_data[day] = { part: "", image_name: "" }; day_data[day] = { part: "", image_name: "" };
} }
}; };
objForEach(day_parts, (day, part) => { objForEach(day_parts, (day, part) => {
_ensure_day_in_data(day); _ensure_day_in_data(day);
this.day_data[day].part = part; day_data[day].part = part;
}); });
objForEach(day_image_names, (day, image_name) => { objForEach(day_image_names, (day, image_name) => {
_ensure_day_in_data(day); _ensure_day_in_data(day);
this.day_data[day].image_name = image_name; day_data[day].image_name = image_name;
}); });
ready(); ready();
}) })
.catch(fail); .catch(fail);
} }
public async door_click(day: number) { async function door_click(day: number) {
if (this.multi_modal === undefined) return; if (multi_modal === undefined) return;
this.multi_modal.show_progress(); multi_modal.show_progress();
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)); multi_modal!.show_image(day_image.data_url, name_door(day));
} catch (error) { } catch (error) {
this.multi_modal!.hide(); multi_modal!.hide();
}
} }
} }
</script> </script>

View file

@ -183,28 +183,19 @@
</BulmaDrawer> </BulmaDrawer>
</template> </template>
<script lang="ts"> <script setup lang="ts">
import { API } from "@/lib/api"; import { API } from "@/lib/api";
import { AdminConfigModel, Credentials, DoorSaved } from "@/lib/model"; import { AdminConfigModel, Credentials, DoorSaved } from "@/lib/model";
import { advent22Store } from "@/lib/store"; import { advent22Store } from "@/lib/store";
import { DateTime } from "luxon"; import { DateTime } from "luxon";
import { Options, Vue } from "vue-class-component";
import BulmaDrawer from "../bulma/Drawer.vue"; import BulmaDrawer from "../bulma/Drawer.vue";
import BulmaSecret from "../bulma/Secret.vue"; import BulmaSecret from "../bulma/Secret.vue";
import CountDown from "../CountDown.vue"; import CountDown from "../CountDown.vue";
@Options({ const store = advent22Store();
components: {
BulmaDrawer,
BulmaSecret,
CountDown,
},
})
export default class extends Vue {
public readonly store = advent22Store();
public admin_config_model: AdminConfigModel = { let admin_config_model: AdminConfigModel = {
solution: { solution: {
value: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", value: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
whitespace: "KEEP", whitespace: "KEEP",
@ -242,46 +233,46 @@ export default class extends Vue {
cache_ttl: 0, cache_ttl: 0,
config_file: "sed diam nonumy", config_file: "sed diam nonumy",
}, },
}; };
public doors: DoorSaved[] = [];
public dav_credentials: Credentials = ["", ""];
public ui_credentials: Credentials = ["", ""];
public fmt_puzzle_date(name: keyof AdminConfigModel["puzzle"]): string { let doors: DoorSaved[] = [];
const iso_date = this.admin_config_model.puzzle[name]; let dav_credentials: Credentials = ["", ""];
let ui_credentials: Credentials = ["", ""];
function fmt_puzzle_date(name: keyof AdminConfigModel["puzzle"]): string {
const iso_date = admin_config_model.puzzle[name];
if (!(typeof iso_date == "string")) return "-"; if (!(typeof iso_date == "string")) return "-";
return DateTime.fromISO(iso_date).toLocaleString(DateTime.DATE_SHORT); return DateTime.fromISO(iso_date).toLocaleString(DateTime.DATE_SHORT);
} }
public on_open(ready: () => void, fail: () => void): void { function on_open(ready: () => void, fail: () => void): void {
Promise.all([ Promise.all([
this.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, admin_config_model, doors]) => { .then(([store_update, new_admin_config_model, new_doors]) => {
store_update; // discard value store_update; // discard value
this.admin_config_model = admin_config_model; admin_config_model = new_admin_config_model;
this.doors = doors; doors = new_doors;
ready(); ready();
}) })
.catch(fail); .catch(fail);
} }
public load_dav_credentials(): void { function load_dav_credentials(): void {
API.request<Credentials>("admin/dav_credentials") API.request<Credentials>("admin/dav_credentials")
.then((creds) => (this.dav_credentials = creds)) .then((creds) => (dav_credentials = creds))
.catch(() => {}); .catch(() => {});
} }
public load_ui_credentials(): void { function load_ui_credentials(): void {
API.request<Credentials>("admin/ui_credentials") API.request<Credentials>("admin/ui_credentials")
.then((creds) => (this.ui_credentials = creds)) .then((creds) => (ui_credentials = creds))
.catch(() => {}); .catch(() => {});
}
} }
</script> </script>

View file

@ -28,7 +28,7 @@ const props = defineProps<{
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
"update:modelValue": [number]; (event: "update:modelValue", value: number): void;
}>(); }>();
function change_step(next_step: number) { function change_step(next_step: number) {

View file

@ -53,12 +53,7 @@ withDefaults(
); );
const emit = defineEmits<{ const emit = defineEmits<{
open: [ (event: "open", ready: () => void, fail: () => void): void;
{
ready(): void;
fail(): void;
},
];
}>(); }>();
const is_open = ref(false); const is_open = ref(false);
@ -70,10 +65,11 @@ function toggle() {
if (is_open.value) { if (is_open.value) {
state.value = "loading"; state.value = "loading";
emit("open", { emit(
ready: () => (state.value = "ready"), "open",
fail: () => (state.value = "failed"), () => (state.value = "ready"),
}); () => (state.value = "failed"),
);
} }
} }

View file

@ -16,7 +16,7 @@ import { ref } from "vue";
import BulmaButton from "./Button.vue"; import BulmaButton from "./Button.vue";
const emit = defineEmits<{ const emit = defineEmits<{
load: []; (event: "load"): void;
}>(); }>();
const state = ref<"start" | "click" | "show">("start"); const state = ref<"start" | "click" | "show">("start");

View file

@ -12,19 +12,19 @@ import * as bulmaToast from "bulma-toast";
import { onMounted, ref } from "vue"; import { onMounted, ref } from "vue";
const emit = defineEmits<{ const emit = defineEmits<{
handle: [ (
{ event: "handle",
show(options: bulmaToast.Options): void; show: (options: bulmaToast.Options) => void,
hide(): void; hide: () => void,
}, ): void;
];
}>(); }>();
const message = ref<HTMLDivElement | null>(null); const message = ref<HTMLDivElement | null>(null);
onMounted(() => onMounted(() =>
emit("handle", { emit(
show(options: bulmaToast.Options = {}) { "handle",
(options: bulmaToast.Options = {}) => {
if (!(message.value instanceof HTMLElement)) return; if (!(message.value instanceof HTMLElement)) return;
bulmaToast.toast({ bulmaToast.toast({
@ -33,7 +33,7 @@ onMounted(() =>
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

@ -49,6 +49,9 @@ function get_bg_aspect_ratio(): number {
if (!loading_success(store.background_image)) return 1; if (!loading_success(store.background_image)) return 1;
return store.background_image.height / store.background_image.width; return store.background_image.height / store.background_image.width;
// aspect_ratio is width/height!
// return store.background_image.aspect_ratio;
} }
</script> </script>

View file

@ -58,8 +58,9 @@ export interface DoorSaved {
} }
export interface ImageData { export interface ImageData {
height: number;
width: number; width: number;
height: number;
aspect_ratio: number;
data_url: string; data_url: string;
} }