Skip to content

Commit

Permalink
Fixed input file style bugs and added multiple props
Browse files Browse the repository at this point in the history
  • Loading branch information
Explicit12 committed May 24, 2024
1 parent d8b6315 commit 9dd47eb
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 89 deletions.
1 change: 0 additions & 1 deletion src/assets/icons/upload_file.svg

This file was deleted.

33 changes: 23 additions & 10 deletions src/ui.form-input-file/composables/attrs.composable.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import defaultConfig from "../configs/default.config";

export function useAttrs(props) {
const { config, getAttrs } = useUI(defaultConfig, () => props.config);
const { dropzoneWrapper, placeholder } = config.value;
const { dropzoneWrapper, placeholder, selectedItem } = config.value;

const cvaDropzoneWrapper = cva({
base: dropzoneWrapper.base,
Expand All @@ -20,6 +20,12 @@ export function useAttrs(props) {
compoundVariants: placeholder.compoundVariants,
});

const cvaSelectedItem = cva({
base: selectedItem.base,
variants: selectedItem.variants,
compoundVariants: selectedItem.compoundVariants,
});

const dropzoneWrapperClasses = computed(() =>
cvaDropzoneWrapper({
error: Boolean(props.error),
Expand All @@ -35,20 +41,25 @@ export function useAttrs(props) {
}),
);

const selectedItemClasses = computed(() =>
cvaSelectedItem({
size: props.size,
}),
);

const labelAttrs = getAttrs("label", { isComponent: true });
const buttonAttrs = getAttrs("button", { isComponent: true });
const dropzoneWrapperAttrs = getAttrs("dropzoneWrapper", { classes: dropzoneWrapperClasses });
const descriptionAttrs = getAttrs("description", { isComponent: true });
const contentWrapperAttrs = getAttrs("contentWrapper");
const buttonWrapperAttrs = getAttrs("buttonWrapper");
const placeholderWrapperAttrs = getAttrs("placeholderWrapper");
const placeholderAttrs = getAttrs("placeholder", {
classes: placeholderClasses,
isComponent: true,
});
const placeholderIconAttrs = getAttrs("placeholderIcon", { isComponent: true });
const placeholderAttrs = getAttrs("placeholder", { classes: placeholderClasses });
const clearIconAttrs = getAttrs("clearIcon", { isComponent: true });
const chooseFileIconAttrs = getAttrs("chooseFileIcon", { isComponent: true });
const chooseFileIconNameAttrs = getAttrs("chooseFileIconName", { isComponent: true });
const inputAttrs = getAttrs("input");
const fileListAttrs = getAttrs("fileList");
const selectedItemAttrs = getAttrs("selectedItem", { classes: selectedItemClasses });

return {
config,
Expand All @@ -57,11 +68,13 @@ export function useAttrs(props) {
buttonAttrs,
dropzoneWrapperAttrs,
descriptionAttrs,
buttonWrapperAttrs,
placeholderWrapperAttrs,
placeholderIconAttrs,
contentWrapperAttrs,
clearIconAttrs,
chooseFileIconNameAttrs,
placeholderAttrs,
fileListAttrs,
buttonWrapperAttrs,
selectedItemAttrs,
chooseFileIconAttrs,
};
}
28 changes: 19 additions & 9 deletions src/ui.form-input-file/configs/default.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,20 @@ export default /*tw*/ {
],
},
description: "text-gray-700",
buttonWrapper: "relative mt-3 flex w-full gap-52 justify-between rounded bg-brand-50 p-4",
placeholderWrapper: "flex items-center gap-2",
contentWrapper: "relative mt-3 flex w-full gap-6 justify-between items-start rounded bg-brand-50 p-4",
fileList: "pr-4 shrink-0 text-gray-700 flex-grow flex flex-col gap-4",
placeholder: {
base: "pr-4 shrink-0 text-ellipsis overflow-hidden text-gray-700 text-nowrap",
base: "pr-4 shrink-0 text-gray-700 flex-grow",
variants: {
size: {
sm: "text-sm",
md: "text-base",
lg: "text-lg",
},
},
},
selectedItem: {
base: "pr-4 shrink-0 text-gray-700 flex-grow",
variants: {
size: {
sm: "text-sm",
Expand All @@ -39,25 +49,25 @@ export default /*tw*/ {
},
},
button: "hover:cursor-pointer",
placeholderIcon: "-rotate-45",
placeholderIconName: "attach_file",
chooseFileIcon: "",
chooseFileIconName: "upload_file",
chooseFileIconName: "attach_file",
clearIcon: "",
clearIconName: "close",
dropzoneWrapperHover: "border-gray-400 bg-gray-50",
dropzoneWrapperHover: "border-gray-400",
dropzoneWrapperError: "hover:border-red-400 border-red-300",
input: "sr-only pointer-events-none size-0 opacity-0",
buttonWrapper: "flex gap-4 items-center",
i18n: {
sizeError: "File size is too big.",
formatError: "Format is not supported.",
noFile: "No file selected",
uploadFile: "Upload file",
uploadFile: "Choose file",
},
defaultVariants: {
size: "md",
labelAlign: "topInside",
allowedFileTypes: [],
maxFileSize: 100,
multiple: false,
maxFileSize: 0,
},
};
152 changes: 89 additions & 63 deletions src/ui.form-input-file/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,58 +3,64 @@
<div ref="dropZoneRef" :ondrop="onDrop" v-bind="dropzoneWrapperAttrs">
<UText :size="nestedComponentSize" v-bind="descriptionAttrs" :html="description" />

<div v-bind="buttonWrapperAttrs">
<div v-bind="placeholderWrapperAttrs">
<div v-bind="contentWrapperAttrs">
<slot name="left" />
<span v-if="!isValue" v-bind="placeholderAttrs" v-text="currentLocale.noFile" />
<div v-else v-bind="fileListAttrs">
<template v-if="props.multiple">
<span
v-for="(file, idx) in currentFiles"
v-bind="selectedItemAttrs"
:key="idx"
v-text="file.name"
/>
</template>
<span v-else v-bind="selectedItemAttrs" v-text="currentFiles.name" />
</div>

<div v-bind="buttonWrapperAttrs">
<template v-if="Array.isArray(currentFiles) || !currentFiles">
<UButton
class="hover:cursor-pointer"
:label="currentLocale.uploadFile"
variant="thirdary"
filled
:size="nestedComponentSize"
v-bind="buttonAttrs"
tag="label"
:for="id"
>
<template #right>
<UIcon
internal
:size="nestedComponentSize"
:name="config.chooseFileIconName"
v-bind="chooseFileIconAttrs"
/>
</template>
</UButton>

<input
:id="id"
ref="fileInputRef"
:multiple="multiple"
type="file"
:accept="accept"
v-bind="inputAttrs"
@change="onChangeFile"
/>
</template>
<UIcon
pill
v-if="isValue"
interactive
internal
:size="nestedComponentSize"
:name="config.placeholderIconName"
v-bind="placeholderIconAttrs"
:name="config.clearIconName"
pill
v-bind="clearIconAttrs"
@click="onClickResetFiles"
/>
<span v-bind="placeholderAttrs" v-text="placeholder" />
</div>

<template v-if="!currentFiles.length">
<UButton
class="hover:cursor-pointer"
:label="currentLocale.uploadFile"
variant="thirdary"
filled
:size="nestedComponentSize"
v-bind="buttonAttrs"
tag="label"
:for="id"
>
<template #right>
<UIcon
internal
:size="nestedComponentSize"
:name="config.chooseFileIconName"
v-bind="chooseFileIconAttrs"
/>
</template>
</UButton>

<input
:id="id"
ref="fileInputRef"
type="file"
:accept="accept"
v-bind="inputAttrs"
@change="onChangeFile"
/>
</template>
<UIcon
v-else
interactive
internal
:size="nestedComponentSize"
:name="config.clearIconName"
pill
v-bind="clearIconAttrs"
@click="onClickResetFiles"
/>
</div>
</div>
</ULabel>
Expand Down Expand Up @@ -87,15 +93,15 @@ const props = defineProps({
*/
label: {
type: String,
default: "Label",
default: "",
},
/**
* Set description.
*/
description: {
type: String,
default: "Some description here",
default: "",
},
/**
Expand All @@ -108,8 +114,16 @@ const props = defineProps({
},
modelValue: {
type: Array,
default: () => [],
type: [Array, File],
default: null,
},
/**
* Allow select multiple files.
*/
multiple: {
type: Boolean,
default: UIService.get(defaultConfig, UInputFile).default.multiple,
},
/**
Expand Down Expand Up @@ -175,21 +189,26 @@ const {
buttonAttrs,
dropzoneWrapperAttrs,
descriptionAttrs,
buttonWrapperAttrs,
placeholderWrapperAttrs,
placeholderIconAttrs,
contentWrapperAttrs,
clearIconAttrs,
chooseFileIconAttrs,
placeholderAttrs,
inputAttrs,
fileListAttrs,
buttonWrapperAttrs,
selectedItemAttrs,
} = useAttrs(props);
const i18nGlobal = tm(UInputFile);
const currentLocale = computed(() => merge(defaultConfig.i18n, i18nGlobal, props.config.i18n));
const currentFiles = computed({
get: () => props.modelValue,
set: (newValue) => emit("update:modelValue", newValue),
set: (newValue) => {
const fallbackValue = props.multiple ? [] : null;
emit("update:modelValue", newValue || fallbackValue);
},
});
const currentError = computed({
Expand All @@ -205,8 +224,11 @@ const extensionNames = computed(() => {
return props.allowedFileTypes.map((type) => type.replace(".", ""));
});
const placeholder = computed(() => {
return currentFiles.value.length ? currentFiles.value[0].name : currentLocale.value.noFile;
const isValue = computed(() => {
return (
(Array.isArray(currentFiles.value) && currentFiles.value.length) ||
(!Array.isArray(currentFiles.value) && currentFiles.value)
);
});
const nestedComponentSize = computed(() => {
Expand Down Expand Up @@ -238,7 +260,7 @@ function validate(file) {
const isValidSize = targetFileSize <= props.maxFileSize;
if (!isValidSize) {
if (!isValidSize && props.maxFileSize) {
currentError.value = currentLocale.value.sizeError;
}
Expand All @@ -256,25 +278,29 @@ function onChangeFile(event) {
return;
}
currentFiles.value = Array.from(event.target.files);
currentFiles.value = props.multiple
? [...currentFiles.value, Array.from(event.target.files).at(0)]
: Array.from(event.target.files).at(0);
if (fileInputRef.value) fileInputRef.value.value = "";
}
function onClickResetFiles() {
currentFiles.value = [];
currentFiles.value = null;
if (fileInputRef.value) fileInputRef.value.value = "";
}
function onDragOver(event) {
event.preventDefault();
dropZoneRef.value.classList.add(config.value.dropzoneWrapperHover.split(" "));
dropZoneRef.value.classList.add(...config.value.dropzoneWrapperHover.split(" "));
}
function onDragLeave(event) {
event.preventDefault();
dropZoneRef.value.classList.remove(config.value.dropzoneWrapperHover.split(" "));
dropZoneRef.value.classList.remove(...config.value.dropzoneWrapperHover.split(" "));
}
function onDrop(event) {
Expand All @@ -297,7 +323,7 @@ function onDrop(event) {
return;
}
currentFiles.value = [targetFile];
currentFiles.value = props.multiple ? [...currentFiles.value, targetFile] : targetFile;
});
}
</script>
Loading

0 comments on commit 9dd47eb

Please sign in to comment.