2019-05-22 14:12:11 +02:00
|
|
|
<template>
|
2022-07-12 10:55:28 +02:00
|
|
|
<div class="flex items-center">
|
2021-04-12 10:43:04 +02:00
|
|
|
<figure class="image" v-if="imageSrc && !imagePreviewLoadingError">
|
|
|
|
<img :src="imageSrc" @error="showImageLoadingError" />
|
2019-10-12 19:23:32 +02:00
|
|
|
</figure>
|
|
|
|
<figure class="image is-128x128" v-else>
|
2021-04-12 10:43:04 +02:00
|
|
|
<div
|
|
|
|
class="image-placeholder"
|
|
|
|
:class="{ error: imagePreviewLoadingError }"
|
|
|
|
>
|
|
|
|
<span class="has-text-centered" v-if="imagePreviewLoadingError">{{
|
|
|
|
$t("Error while loading the preview")
|
|
|
|
}}</span>
|
2022-07-12 10:55:28 +02:00
|
|
|
<span class="has-text-centered" v-else>{{
|
|
|
|
textFallbackWithDefault
|
|
|
|
}}</span>
|
2019-10-12 19:23:32 +02:00
|
|
|
</div>
|
2019-06-17 17:15:27 +02:00
|
|
|
</figure>
|
|
|
|
|
2022-07-12 10:55:28 +02:00
|
|
|
<div class="flex flex-col">
|
|
|
|
<p v-if="modelValue" class="inline-flex">
|
|
|
|
<span class="block truncate max-w-[200px]" :title="modelValue.name">{{
|
|
|
|
modelValue.name
|
2021-04-12 10:43:04 +02:00
|
|
|
}}</span>
|
2022-07-12 10:55:28 +02:00
|
|
|
<span>({{ formatBytes(modelValue.size) }})</span>
|
2021-04-12 10:43:04 +02:00
|
|
|
</p>
|
2022-07-12 10:55:28 +02:00
|
|
|
<p v-if="pictureTooBig" class="text-mbz-danger">
|
2021-04-12 10:43:04 +02:00
|
|
|
{{
|
|
|
|
$t(
|
|
|
|
"The selected picture is too heavy. You need to select a file smaller than {size}.",
|
|
|
|
{ size: formatBytes(maxSize) }
|
|
|
|
)
|
|
|
|
}}
|
|
|
|
</p>
|
2022-07-12 10:55:28 +02:00
|
|
|
<o-field class="justify-center" variant="primary">
|
|
|
|
<o-upload @update:modelValue="onFileChanged" :accept="accept" drag-drop>
|
|
|
|
<span>
|
|
|
|
<Upload />
|
2020-11-20 18:34:13 +01:00
|
|
|
<span>{{ $t("Click to upload") }}</span>
|
|
|
|
</span>
|
2022-07-12 10:55:28 +02:00
|
|
|
</o-upload>
|
|
|
|
</o-field>
|
|
|
|
<o-button
|
2021-10-10 16:24:12 +02:00
|
|
|
type="is-text"
|
|
|
|
v-if="imageSrc"
|
|
|
|
@click="removeOrClearPicture"
|
|
|
|
@keyup.enter="removeOrClearPicture"
|
|
|
|
>
|
2020-11-20 18:34:13 +01:00
|
|
|
{{ $t("Clear") }}
|
2022-07-12 10:55:28 +02:00
|
|
|
</o-button>
|
2020-11-20 18:34:13 +01:00
|
|
|
</div>
|
2019-06-17 17:15:27 +02:00
|
|
|
</div>
|
2019-05-22 14:12:11 +02:00
|
|
|
</template>
|
|
|
|
|
2019-10-12 19:23:32 +02:00
|
|
|
<style scoped lang="scss">
|
2021-11-04 18:14:36 +01:00
|
|
|
@use "@/styles/_mixins" as *;
|
2020-02-18 08:57:00 +01:00
|
|
|
figure.image {
|
2022-07-12 10:55:28 +02:00
|
|
|
// @include margin-right(30px);
|
2020-02-18 08:57:00 +01:00
|
|
|
max-height: 200px;
|
|
|
|
max-width: 200px;
|
|
|
|
overflow: hidden;
|
|
|
|
}
|
2019-06-17 17:15:27 +02:00
|
|
|
|
2020-02-18 08:57:00 +01:00
|
|
|
.image-placeholder {
|
|
|
|
background-color: grey;
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
border-radius: 100%;
|
|
|
|
display: flex;
|
|
|
|
justify-content: center;
|
|
|
|
align-items: center;
|
|
|
|
|
2021-04-12 10:43:04 +02:00
|
|
|
&.error {
|
|
|
|
border: 2px solid red;
|
|
|
|
}
|
|
|
|
|
2020-02-18 08:57:00 +01:00
|
|
|
span {
|
|
|
|
flex: 1;
|
|
|
|
color: #eee;
|
2019-06-17 17:15:27 +02:00
|
|
|
}
|
2020-02-18 08:57:00 +01:00
|
|
|
}
|
2019-06-17 17:15:27 +02:00
|
|
|
</style>
|
|
|
|
|
2022-07-12 10:55:28 +02:00
|
|
|
<script lang="ts" setup>
|
2020-11-26 11:41:13 +01:00
|
|
|
import { IMedia } from "@/types/media.model";
|
2022-07-12 10:55:28 +02:00
|
|
|
import { computed, ref, watch } from "vue";
|
|
|
|
import { useI18n } from "vue-i18n";
|
|
|
|
import Upload from "vue-material-design-icons/Upload.vue";
|
|
|
|
|
|
|
|
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,
|
2019-06-17 17:15:27 +02:00
|
|
|
}
|
2022-07-12 10:55:28 +02:00
|
|
|
);
|
2019-05-22 14:12:11 +02:00
|
|
|
|
2022-07-12 10:55:28 +02:00
|
|
|
const textFallbackWithDefault = props.textFallback ?? t("Avatar");
|
2020-11-20 18:34:13 +01:00
|
|
|
|
2022-07-12 10:55:28 +02:00
|
|
|
const emit = defineEmits(["update:modelValue"]);
|
2019-06-17 17:15:27 +02:00
|
|
|
|
2022-07-12 10:55:28 +02:00
|
|
|
const imagePreviewLoadingError = ref(false);
|
2021-04-12 10:43:04 +02:00
|
|
|
|
2022-07-12 10:55:28 +02:00
|
|
|
const pictureTooBig = computed((): boolean => {
|
|
|
|
return props.modelValue != null && props.modelValue?.size > props.maxSize;
|
|
|
|
});
|
2019-06-17 17:15:27 +02:00
|
|
|
|
2022-07-12 10:55:28 +02:00
|
|
|
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);
|
|
|
|
}
|
2019-05-22 14:12:11 +02:00
|
|
|
}
|
2022-07-12 10:55:28 +02:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
|
|
|
// https://gist.github.com/zentala/1e6f72438796d74531803cc3833c039c
|
|
|
|
const formatBytes = (bytes: number, decimals?: number): string => {
|
|
|
|
if (bytes == 0) return "0 Bytes";
|
|
|
|
const k = 1024,
|
|
|
|
dm = decimals || 2,
|
|
|
|
sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"],
|
|
|
|
i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
|
|
|
|
};
|
2019-05-22 14:12:11 +02:00
|
|
|
</script>
|