mobilizon/src/components/PictureUpload.vue
Thomas Citharel 2e72f6faf4
build: switch from yarn to npm to manage js dependencies and move js contents to root
yarn v1 is being deprecated and starts to have some issues

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2023-11-14 17:24:42 +01:00

127 lines
3.1 KiB
Vue

<template>
<div>
<o-upload
rootClass="!flex"
v-if="!imageSrc || imagePreviewLoadingError || pictureTooBig"
@update:modelValue="onFileChanged"
:accept="accept"
drag-drop
>
<div
class="w-100 rounded text-center p-4 rounded-xl border-dashed border-2 border-gray-600"
>
<span class="mx-auto flex w-fit">
<Upload />
<span class="capitalize"
>{{ $t("Click to upload") }} {{ textFallbackWithDefault }}</span
>
</span>
<p v-if="pictureTooBig" class="text-mbz-danger">
{{
$t(
"The selected picture is too heavy. You need to select a file smaller than {size}.",
{ size: formatBytes(maxSize) }
)
}}
</p>
<span
class="has-text-centered text-mbz-danger"
v-if="imagePreviewLoadingError"
>{{ $t("Error while loading the preview") }}</span
>
</div>
</o-upload>
</div>
<div
v-if="
imageSrc &&
!imagePreviewLoadingError &&
!pictureTooBig &&
!imagePreviewLoadingError
"
>
<figure
class="w-fit relative image mx-auto my-4"
v-if="imageSrc && !imagePreviewLoadingError"
>
<img
class="max-h-52 rounded-xl"
:src="imageSrc"
@error="showImageLoadingError"
/>
<o-button
class="!absolute right-1 bottom-1"
variant="danger"
v-if="imageSrc"
@click="removeOrClearPicture"
@keyup.enter="removeOrClearPicture"
>
{{ $t("Clear") }}
</o-button>
</figure>
</div>
</template>
<script lang="ts" setup>
import { IMedia } from "@/types/media.model";
import { computed, ref, watch } from "vue";
import { useI18n } from "vue-i18n";
import Upload from "vue-material-design-icons/Upload.vue";
import { formatBytes } from "@/utils/datetime";
const { t } = useI18n({ useScope: "global" });
const props = withDefaults(
defineProps<{
modelValue: File | null;
defaultImage?: IMedia | null;
accept?: string;
textFallback?: string;
maxSize?: number;
}>(),
{
accept: "image/gif,image/png,image/jpeg,image/webp",
maxSize: 10_485_760,
}
);
const textFallbackWithDefault = props.textFallback ?? t("Avatar");
const emit = defineEmits(["update:modelValue"]);
const imagePreviewLoadingError = ref(false);
const pictureTooBig = computed((): boolean => {
return props.modelValue != null && props.modelValue?.size > props.maxSize;
});
const imageSrc = computed((): string | null | undefined => {
if (props.modelValue !== undefined) {
if (props.modelValue === null) return null;
try {
return URL.createObjectURL(props.modelValue);
} catch (e) {
console.error(e, props.modelValue);
}
}
return props.defaultImage?.url;
});
const onFileChanged = (file: File | null): void => {
emit("update:modelValue", file);
};
const removeOrClearPicture = async (): Promise<void> => {
onFileChanged(null);
};
watch(imageSrc, () => {
imagePreviewLoadingError.value = false;
});
const showImageLoadingError = (): void => {
imagePreviewLoadingError.value = true;
};
</script>