Input File v1.0
The <e-input-file> component is a component that can be used for .
Usage
An e-input-file has attributes that must be provided,
<e-input-file
id="input_id"
/>
Attributes
| Value | Type | Optional | Default |
|---|---|---|---|
| id | String | no | |
| value | String | yes | '' |
| placeholder | String | yes | '' |
| label | String | yes | '' |
| required | Boolean | yes | false |
| disabled | Boolean | yes | false |
| meta | EInputMeta_File | yes | 'default' |
Meta Attributes
| Value | Type | Optional | Default |
|---|---|---|---|
| accept | [ String, String[] ] | yes | '' |
| override_name | String | yes | '' |
| autoComplete | Boolean | yes | '' |
| disabled | Boolean | yes | '' |
| multiple | Boolean | yes | '' |
| files | File[] | yes | 'default' |
Events
| Value | Optional | params |
|---|---|---|
| blur | no | [ event: FocusEvent ] |
| keyup.enter | no | [ event: KeyboardEvent ] |
| update:value | no | [ value: FileList ] |
Examples
Below a few interactive examples of e-input-file can be found.
Default
<e-input-file
id="input_id"
/>
Labelled, label='FileInput with Label'
<e-input-file
id="input_with_label_id"
/>
Source Code
e-input-file.vue
<script setup lang="ts">
import { computed, ref, unref } from 'vue';
import { isEmpty, join } from 'lodash-es';
import { useElementHover } from '@vueuse/core';
import { EInput_File } from 'types/e-input/interfaces/EInput_File';
import EIcon from '../e-icon/e-icon.vue';
export type Props = EInput_File;
const props = withDefaults(defineProps<Props>(), {
value: '',
label: '',
placeholder: '',
required: false,
disabled: false,
meta: () => {
return {
autoComplete: false,
disabled: false,
accept: '',
files: [],
multiple: false,
override_name: ''
};
}
});
const emit = defineEmits<{
(e: 'blur', event: FocusEvent): void;
(e: 'keyup.enter', event: KeyboardEvent): void;
(e: 'update:value', value: FileList): void;
}>();
const input = ref<HTMLInputElement>();
const inputContainer = ref<HTMLDivElement>();
const isHovered = useElementHover(inputContainer);
const isFocused = ref(false);
const isDisabled = computed(() => props.disabled ?? props.meta?.disabled);
const showPlaceholder = computed(() => isEmpty(props.label) && isEmpty(props.value));
const displayText = computed(() => props.label || props.value || props.placeholder || '');
const stateClasses = computed(() => {
return {
disabled: isDisabled.value,
focused: isFocused.value,
placeholder: showPlaceholder.value
};
});
const accept = computed(() => {
switch (typeof props.meta?.accept) {
case 'string':
return props.meta?.accept ?? '';
case 'object':
return props.meta?.accept?.join(', ');
default:
return null;
}
});
const multiple = computed(() => props.meta?.multiple);
const iconState = computed(() => {
const states: string[] = [];
if (isDisabled.value) {
return 'disabled';
}
if (isFocused.value) {
states.push('focused');
}
if (unref(isHovered)) {
states.push('hovered');
}
if (states.length > 0) {
return join(states, '-');
} else {
return 'default';
}
});
async function updateValue(event: Event) {
const target = event.target as HTMLInputElement;
const files = target.files;
emitUpdate(files);
}
function emitUpdate(files: FileList) {
emit('update:value', files);
}
function setFocus(newValue: boolean) {
isFocused.value = newValue;
}
</script>
<template>
<label
ref="inputContainer"
class="e-input-file"
:class="stateClasses"
:for="id"
:title="displayText"
tabindex="0"
@focusin="setFocus(true)"
@focusout="setFocus(false)"
>
<e-icon
:icon="['upload']"
:icon-state="iconState"
style-key="input"
/>
<span v-text="displayText" />
<input
:id="id"
ref="input"
type="file"
:name="id"
:accept="accept"
:disabled="isDisabled"
:multiple="multiple"
:required="required"
@input="updateValue"
@blur="emit('blur', $event)"
@keyup.enter="emit('keyup.enter', $event)"
/>
</label>
</template>
<style scoped lang="scss">
input[type='file'] {
display: none;
}
.e-input-file {
background-color: var(--e-input-background);
border: 2px solid transparent;
border-radius: 15px;
padding: 14px;
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
cursor: pointer;
.e-icon {
max-width: 24px;
}
span {
color: var(--e-text-input-font-color);
font-family: var(--e-text-input-font-family);
font-size: var(--e-text-input-font-size);
font-weight: var(--e-text-input-font-weight);
line-height: var(--e-text-input-font-line-height);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&:hover,
&:focus,
&.focused {
background-color: var(--e-input-focus-background);
span {
color: inherit;
}
}
&:focus,
&.focused {
border-color: var(--e-secondary);
span {
color: var(--e-secondary);
}
}
&:hover {
border-color: var(--e-navigation-button-font-hover-color);
span {
color: var(--e-navigation-button-font-hover-color);
}
}
&.placeholder {
span {
color: var(--e-gray-300);
}
}
&:disabled {
border-color: var(--e-disabled);
span {
color: var(--e-disabled);
}
}
&[required] {
+ label::after {
content: '*';
padding-left: 2px;
color: var(--e-required);
font-weight: bold;
}
}
}
</style>