kopia lustrzana https://github.com/FacilMap/facilmap
155 wiersze
3.8 KiB
Vue
155 wiersze
3.8 KiB
Vue
<script lang="ts">
|
|
import { createApp, defineComponent, h, ref, type VNode, type VNodeArrayChildren } from "vue";
|
|
import Alert from "./alert.vue";
|
|
import type { ThemeColour } from "../../utils/bootstrap";
|
|
import ModalDialog from "./modal-dialog.vue";
|
|
import ValidatedField from "./validated-form/validated-field.vue";
|
|
|
|
export type AlertProps = {
|
|
title: string;
|
|
message: string;
|
|
type?: "alert" | "confirm";
|
|
variant?: ThemeColour;
|
|
show?: boolean;
|
|
okLabel?: string;
|
|
};
|
|
|
|
export interface AlertResult {
|
|
ok: boolean;
|
|
}
|
|
|
|
async function renderAlert({ getContent, onShown, ...props }: AlertProps & {
|
|
getContent?: () => string | VNode | VNodeArrayChildren;
|
|
onShown?: () => void;
|
|
}): Promise<AlertResult> {
|
|
return await new Promise<AlertResult>((resolve) => {
|
|
const el = document.createElement('div');
|
|
el.classList.add("fm-alert-container");
|
|
document.body.appendChild(el);
|
|
const app = createApp(defineComponent({
|
|
setup() {
|
|
return () => h(Alert, {
|
|
...props,
|
|
onShown: () => {
|
|
onShown?.();
|
|
},
|
|
onHide: (result) => {
|
|
resolve(result);
|
|
},
|
|
onHidden: () => {
|
|
app.unmount();
|
|
el.remove();
|
|
}
|
|
}, getContent);
|
|
}
|
|
}));
|
|
app.mount(el);
|
|
});
|
|
}
|
|
|
|
export async function showAlert(props: Omit<AlertProps, 'type' | 'show'>): Promise<void> {
|
|
await renderAlert({ ...props, type: 'alert' });
|
|
}
|
|
|
|
export async function showConfirm(props: Omit<AlertProps, 'type' | 'show'>): Promise<boolean> {
|
|
const result = await renderAlert({ ...props, type: 'confirm' });
|
|
return result.ok;
|
|
}
|
|
|
|
export async function showPrompt({ initialValue = "", validate, ...props }: Omit<AlertProps, 'type' | 'show' | 'message'> & {
|
|
initialValue?: string;
|
|
/** Validate the value. Return an empty string or undefined to indicate validity. */
|
|
validate?: (value: string) => string | undefined;
|
|
}): Promise<string | undefined> {
|
|
const value = ref(initialValue);
|
|
const submitted = ref(false);
|
|
const touched = ref(false);
|
|
const inputRef = ref<HTMLInputElement>();
|
|
|
|
const result = await renderAlert({
|
|
...props,
|
|
message: '',
|
|
type: 'confirm',
|
|
getContent: () => h(ValidatedField, {
|
|
class: ['position-relative', touched.value ? 'was-validated' : ''],
|
|
value: value.value,
|
|
validators: validate ? [validate] as any : []
|
|
}, {
|
|
default: (slotProps: any) => [
|
|
h('input', {
|
|
type: "text",
|
|
class: "form-control",
|
|
value: value.value,
|
|
onInput: (e: InputEvent) => {
|
|
value.value = (e.target as HTMLInputElement).value;
|
|
touched.value = true;
|
|
},
|
|
onBlur: () => {
|
|
touched.value = true;
|
|
},
|
|
ref: (el) => {
|
|
slotProps.inputRef(el);
|
|
inputRef.value = el as any;
|
|
}
|
|
}),
|
|
h('div', {
|
|
class: "invalid-tooltip"
|
|
}, slotProps.validationError)
|
|
]
|
|
}),
|
|
onShown: () => {
|
|
inputRef.value!.focus();
|
|
}
|
|
});
|
|
|
|
submitted.value = true;
|
|
|
|
return result.ok ? value.value : undefined;
|
|
}
|
|
</script>
|
|
|
|
<script setup lang="ts">
|
|
const props = withDefaults(defineProps<AlertProps>(), {
|
|
type: "alert",
|
|
okLabel: "OK"
|
|
});
|
|
|
|
const emit = defineEmits<{
|
|
shown: [];
|
|
hide: [result: AlertResult];
|
|
hidden: [result: AlertResult];
|
|
}>();
|
|
|
|
const result = ref<AlertResult>({
|
|
ok: false
|
|
});
|
|
|
|
const modalRef = ref<InstanceType<typeof ModalDialog>>();
|
|
|
|
const handleSubmit = () => {
|
|
result.value.ok = true;
|
|
modalRef.value!.modal.hide();
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<ModalDialog
|
|
:title="props.title"
|
|
:isCreate="props.type === 'confirm'"
|
|
:okLabel="props.okLabel"
|
|
:okVariant="props.variant"
|
|
@shown="emit('shown')"
|
|
@hide="emit('hide', result)"
|
|
@hidden="emit('hidden', result)"
|
|
@submit="handleSubmit"
|
|
ref="modalRef"
|
|
>
|
|
<slot>{{props.message}}</slot>
|
|
</ModalDialog>
|
|
</template>
|
|
|
|
<style lang="scss">
|
|
.fm-alert-container {
|
|
position: absolute;
|
|
}
|
|
</style> |