We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
validate file "mime type"
ui/packages/components-vue/src/components/input/File.vue
Line 254 in 50ab5f5
<template> <div :class="[classes, getClassesString([modifiersClasses, stateClasses, themeClasses])]" class="box --button flx --flxColumn --flx-start-stretch --gap-10 --width" > <div v-if="minAmount !== maxAmount && thumbnails.length" class="flx --flxRow --flx-start-center --gap-10" > <ul class="flx --flxRow-wrap --flx-start-center --gap-10"> <li v-for="(thumb, thumb_index) in thumbnails" :key="thumb_index" class="flx --flxRow --flx-start-center --gap-5" > <BaseAction class="avatar --index --bdr" :tooltip="t('file_delete_files', 1)" tooltip-position="bottom" @click.prevent="removeFile(thumb_index)" > <div class="back"> <BaseImg :src="thumb" :alt="t('file_thumb')" /> </div> <ActionLink :theme="eColors.LIGHT" class="--shadow"> <IconFa name="xmark" size="20" /> </ActionLink> </BaseAction> </li> </ul> <span class="--txtWrap-nowrap"> {{ t("file_one_of_amount", { count: modelValue.length, amount: maxAmount, }) }} </span> </div> <BaseInput v-slot="{ id }" ref="fileInput" style="display: none" v-bind="{ ...$attrs, type: 'file', accept: (accept ?? ['image/*']).join(','), multiple: maxAmount > 1, disabled, }" @change="handleInputChange" > <template v-if="!isLoading"> <label v-if="modelValue.length < maxAmount" :for="id" :class="[...themeClasses, { '--bgColor-none': !isDragover }]" class="box --bdr-dashed --size-xs flx --flxColumn --flx-center --minHeight-90" @drag="prevent" @dragstart="prevent" @dragend="handleMouseOut" @dragleave="handleMouseOut" @drop="handleFileDrop" @dragover="handleMouseOver" @dragenter="handleMouseOver" > <div class="txt --txtAlignFlx-center"> <template v-if="!isDragover"> <p> <b>{{ t("file_choose_file", maxAmount) }}</b> {{ (isAdvancedUpload && !isDragover && t("file_or_drop_files_here", maxAmount)) || "" }} </p> <p class="--txtSize-xs"> {{ t("file_max_file_size_mb", { size: maxFileSize / 1e6 }) }} </p> </template> <p v-else> <b>{{ t("file_drop_files_here", maxAmount) }}</b> </p> </div> </label> <div v-else :class="themeClasses" class="box --bdr-solid --size-xs --bgColor-none flx --flxRow --flx-center" > <p>{{ t("file_completed") }}</p> <ActionButton :theme="theme" :aria-label="t('file_delete_files', maxAmount)" @click.prevent="setFiles()" > {{ t("file_delete_files", maxAmount) }} </ActionButton> </div> </template> <div v-else :class="themeClasses" class="box --bdr-solid --size-xs --bgColor-none flx --flxRow --flx-center" > {{ t("file_loading_files", maxAmount) }} </div> </BaseInput> </div> </template> <script setup lang="ts"> import { ref, computed } from "vue"; import _ from "lodash"; import type { tProps } from "@open-xamu-co/ui-common-types"; import { eColors } from "@open-xamu-co/ui-common-enums"; import { fileMatchesMimeTypes, standardImageMimeTypes, renameFile, getBase64FromImageFile, useUtils, useSwal, useI18n, } from "@open-xamu-co/ui-common-helpers"; import BaseImg from "../base/Img.vue"; import BaseAction from "../base/Action.vue"; import BaseInput from "../base/Input.vue"; import IconFa from "../icon/Fa.vue"; import ActionButton from "../action/Button.vue"; import ActionLink from "../action/Link.vue"; import type { iUseModifiersProps, iUseStateProps, iUseThemeProps, iInputProps, } from "../../types/props"; import useModifiers from "../../composables/modifiers"; import useState from "../../composables/state"; import useTheme from "../../composables/theme"; import useHelpers from "../../composables/helpers"; interface iInputFileProps extends iInputProps, iUseModifiersProps, iUseStateProps, iUseThemeProps { filePrefix?: string; min?: number; max?: number; /** * max file size in bytes */ maxSize?: number; /** * Match files type * * default: all image types */ accept?: string[]; // PRIVATE modelValue: File[]; /** * Content clasess */ classes?: tProps<string>; } interface iDropEvent extends DragEvent { originalEvent: { dataTransfer: DataTransfer; }; } /** * File Input element * TODO: Support more than images, add dinamyc file types (mapping mimes) * * input value is not required * * @component */ defineOptions({ name: "InputFile", inheritAttrs: false }); const props = defineProps<iInputFileProps>(); const emit = defineEmits(["update:model-value"]); const { t } = useHelpers(useI18n); const { getClassesString, isBrowser } = useHelpers(useUtils); const Swal = useHelpers(useSwal); const { modifiersClasses } = useModifiers(props); const { stateClasses } = useState(props); const { themeClasses } = useTheme(props); const fileInput = ref<HTMLInputElement>(); const thumbnails = ref<string[]>([]); const isAdvancedUpload = ref(false); const isLoading = ref(false); const isDragover = ref(false); const minAmount = computed(() => props.min ?? 1); const maxAmount = computed(() => props.max ?? 100); const maxFileSize = computed(() => props.maxSize ?? 1e7); /** * setFiles */ function setFiles(files: File[] = [], thumbs: string[] = []) { thumbnails.value = thumbs; emit("update:model-value", files); } /** * check support for drag and drop */ function checkAdvancedUploadSupport() { const div = document.createElement("div"); return ( ("draggable" in div || ("ondragstart" in div && "ondrop" in div)) && "FormData" in window && "FileReader" in window ); } /** * stores the files */ async function storeFiles(files: FileList) { isLoading.value = true; // copy the files const savedFiles = [...props.modelValue]; const savedThumbs = [...thumbnails.value]; try { for (let i = 0; i < files.length; i++) { // omit if max file reached if (savedFiles.length >= maxAmount.value) { Swal.fire({ title: t("swal.file_limit"), text: t("swal.file_limit_text", { count: maxAmount.value, amount: maxAmount.value, }), icon: "warning", }); break; } // TODO: Allow for multiple file types // validate file "mime type" const isImage = await fileMatchesMimeTypes(files[i], standardImageMimeTypes); // 50MB max file size if (isImage) { // is image file if (files[i].size < maxFileSize.value) { const fileName = `${props.filePrefix ?? "image"}_${i}`; savedFiles.push(renameFile(files[i], fileName)); savedThumbs.push(await getBase64FromImageFile(files[i])); } else { // file too big Swal.fire({ title: t("swal.file_too_big"), text: t("swal.file_too_big_text"), icon: "warning", }); } } else { // not image Swal.fire({ title: t("swal.file_wrong_format_image"), text: t("swal.file_wrong_format_image_text"), icon: "warning", }); } } // last one, save all. setFiles(savedFiles, savedThumbs); } catch (error) { console.log(error); Swal.fire({ title: t("swal.file_unknown_error"), text: t("swal.file_unknown_error_text"), icon: "error", timer: undefined, showConfirmButton: true, }); } isLoading.value = false; } /** * remove the given file in the given key */ const removeFile = _.debounce((index: number) => { // modify and set again setFiles(props.modelValue.toSpliced(index, 1), thumbnails.value.toSpliced(index, 1)); }); /** * just a prevent * * @listener */ function prevent(e: Event) { if (!isAdvancedUpload.value) return; e.preventDefault(); e.stopPropagation(); } /** * drag event is over * * @listener */ function handleMouseOver(e: Event) { prevent(e); isDragover.value = true; } /** * cursor is out of bounds * * @listener */ function handleMouseOut(e: Event) { prevent(e); isDragover.value = false; } /** * file was droped * * @listener */ function handleFileDrop(e: Event) { const { dataTransfer, originalEvent } = e as iDropEvent; handleMouseOut(e); storeFiles(dataTransfer?.files || originalEvent.dataTransfer.files); } /** * file was selected from file explorer * process files on explorer search * * @listenerOverride files require specific event handling */ function handleInputChange(e: Event) { const { target } = e as Event & { target: HTMLInputElement }; if (!target.files) return; prevent(e); storeFiles(target.files); } // lifecycle if (isBrowser) isAdvancedUpload.value = checkAdvancedUploadSupport(); </script>
The text was updated successfully, but these errors were encountered:
No branches or pull requests
validate file "mime type"
ui/packages/components-vue/src/components/input/File.vue
Line 254 in 50ab5f5
The text was updated successfully, but these errors were encountered: