facilmap/frontend/src/lib/components/manage-views-dialog.vue

156 wiersze
4.9 KiB
Vue

<script setup lang="ts">
import type { ID, View } from "facilmap-types";
import { displayView } from "facilmap-leaflet";
import { computed, ref, watchEffect } from "vue";
import { useToasts } from "./ui/toasts/toasts.vue";
import { showConfirm } from "./ui/alert.vue";
import ModalDialog from "./ui/modal-dialog.vue";
import { injectContextRequired, requireClientContext, requireMapContext } from "./facil-map-context-provider/facil-map-context-provider.vue";
import { getOrderedViews } from "facilmap-utils";
import Draggable from "vuedraggable";
import Icon from "./ui/icon.vue";
import { useI18n } from "../utils/i18n";
const context = injectContextRequired();
const client = requireClientContext(context);
const mapContext = requireMapContext(context);
const toasts = useToasts();
const i18n = useI18n();
const emit = defineEmits<{
hidden: [];
}>();
const isSavingDefaultView = ref<ID>();
const isMoving = ref<ID>();
const isDeleting = ref(new Set<ID>());
const isBusy = computed(() => {
return isSavingDefaultView.value != null || isDeleting.value.size > 0 || isMoving.value != null;
});
function display(view: View): void {
displayView(mapContext.value.components.map, view, { overpassLayer: mapContext.value.components.overpassLayer });
};
async function makeDefault(view: View): Promise<void> {
isSavingDefaultView.value = view.id;
toasts.hideToast(`fm${context.id}-save-view-error-default`);
try {
await client.value.editPad({ defaultViewId: view.id });
} catch (err) {
toasts.showErrorToast(`fm${context.id}-save-view-error-default`, () => i18n.t("manage-views-dialog.default-view-error"), err);
} finally {
isSavingDefaultView.value = undefined;
}
};
async function deleteView(view: View): Promise<void> {
toasts.hideToast(`fm${context.id}-save-view-error-${view.id}`);
try {
if (!await showConfirm({
title: i18n.t("manage-views-dialog.delete-view-title"),
message: i18n.t("manage-views-dialog.delete-view-message", { viewName: view.name }),
variant: "danger",
okLabel: i18n.t("manage-views-dialog.delete-view-ok")
}))
return;
isDeleting.value.add(view.id);
await client.value.deleteView({ id: view.id });
} catch (err) {
toasts.showErrorToast(`fm${context.id}-save-view-error-${view.id}`, () => i18n.t("manage-views-dialog.delete-view-error", { viewName: view.name }), err);
} finally {
isDeleting.value.delete(view.id);
}
};
const orderedViews = ref<View[]>([]);
watchEffect(() => {
if (isMoving.value == null) {
orderedViews.value = getOrderedViews(client.value.views);
}
});
const handleDrag = toasts.toastErrors(async (e: any) => {
if (e.moved) {
isMoving.value = e.moved.element.id;
try {
// This handler is called when orderedViews is already reordered
const newIdx = e.moved.newIndex === 0 ? 0 : (orderedViews.value[e.moved.newIndex - 1].idx + 1);
await client.value.editView({
id: e.moved.element.id,
idx: newIdx
});
} finally {
isMoving.value = undefined;
}
}
});
</script>
<template>
<ModalDialog
:title="i18n.t('manage-views-dialog.title')"
:isBusy="isBusy"
size="lg"
class="fm-manage-views"
@hidden="emit('hidden')"
>
<table class="table table-striped table-hover">
<Draggable
v-model="orderedViews"
tag="tbody"
handle=".fm-drag-handle"
itemKey="id"
@change="handleDrag"
>
<template #item="{ element: view }">
<tr>
<td
class="text-break align-middle"
:class="{
'font-weight-bold': client.padData?.defaultView && view.id == client.padData.defaultView.id
}"
>
<a href="javascript:" @click="display(view)">{{view.name}}</a>
</td>
<td class="td-buttons text-right align-middle">
<button
type="button"
class="btn btn-secondary"
v-show="!client.padData?.defaultView || view.id !== client.padData.defaultView.id"
@click="makeDefault(view)"
:disabled="!!isSavingDefaultView || isDeleting.has(view.id)"
>
<div v-if="isSavingDefaultView == view.id" class="spinner-border spinner-border-sm"></div>
{{i18n.t("manage-views-dialog.make-default")}}
</button>
<button
type="button"
class="btn btn-secondary"
@click="deleteView(view)"
:disabled="isDeleting.has(view.id) || isSavingDefaultView == view.id || isMoving != null"
>
<div v-if="isDeleting.has(view.id)" class="spinner-border spinner-border-sm"></div>
{{i18n.t("manage-views-dialog.delete")}}
</button>
<button
type="button"
class="btn btn-secondary fm-drag-handle"
:disabled="isDeleting.has(view.id) || isMoving != null"
>
<div v-if="isMoving === view.id" class="spinner-border spinner-border-sm"></div>
<Icon v-else icon="resize-vertical" :alt="i18n.t('manage-views-dialog.reorder-alt')"></Icon>
</button>
</td>
</tr>
</template>
</Draggable>
</table>
</ModalDialog>
</template>