mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
180 lines
6.4 KiB
Svelte
180 lines
6.4 KiB
Svelte
<script lang="ts">
|
|
import { Trim } from '$lib/components';
|
|
import { humanFileSize } from '$lib/helpers/sizeConvertion';
|
|
import { onMount } from 'svelte';
|
|
import { Helper, Label } from '.';
|
|
|
|
export let label: string = null;
|
|
export let files: FileList;
|
|
|
|
export let allowedFileExtensions: string[] = [];
|
|
export let maxSize: number = null;
|
|
export let required = false;
|
|
export let optionalText: string = null;
|
|
export let tooltip: string = null;
|
|
export let error: string = null;
|
|
|
|
let input: HTMLInputElement;
|
|
let hovering = false;
|
|
|
|
function setFiles(value: FileList) {
|
|
if (!value) return;
|
|
|
|
const hasInvalidExt = Array.from(value).some((file) => {
|
|
const fileExtension = file.name.split('.').pop();
|
|
return allowedFileExtensions?.length
|
|
? !allowedFileExtensions.includes(fileExtension)
|
|
: false;
|
|
});
|
|
if (hasInvalidExt) {
|
|
error = 'Invalid file extension';
|
|
return;
|
|
}
|
|
|
|
files = value;
|
|
input.files = value;
|
|
}
|
|
|
|
function resetFiles() {
|
|
setFiles(new DataTransfer().files);
|
|
}
|
|
|
|
function dropHandler(ev: DragEvent) {
|
|
ev.dataTransfer.dropEffect = 'move';
|
|
hovering = false;
|
|
if (!ev.dataTransfer.items) return;
|
|
for (let i = 0; i < ev.dataTransfer.items.length; i++) {
|
|
if (ev.dataTransfer.items[i].kind === 'file') {
|
|
const dataTransfer = new DataTransfer();
|
|
dataTransfer.items.add(ev.dataTransfer.items[i].getAsFile());
|
|
setFiles(dataTransfer.files);
|
|
}
|
|
}
|
|
}
|
|
|
|
function dragOverHandler() {
|
|
hovering = true;
|
|
}
|
|
|
|
const handleInvalid = (event: Event) => {
|
|
event.preventDefault();
|
|
|
|
if (input.validity.valueMissing) {
|
|
error = 'This field is required';
|
|
return;
|
|
}
|
|
error = input.validationMessage;
|
|
};
|
|
|
|
$: if (files) {
|
|
error = null;
|
|
}
|
|
|
|
$: fileArray = files?.length ? Array.from(files) : [];
|
|
|
|
onMount(() => {
|
|
setFiles(files);
|
|
});
|
|
|
|
const handleChange = (event: Event) => {
|
|
const target = event.currentTarget as HTMLInputElement;
|
|
setFiles(target.files);
|
|
};
|
|
</script>
|
|
|
|
<input
|
|
on:change={handleChange}
|
|
bind:this={input}
|
|
accept={allowedFileExtensions.map((n) => `.${n}`).join(',')}
|
|
type="file"
|
|
style="display: none"
|
|
{required}
|
|
on:invalid={handleInvalid} />
|
|
|
|
<div>
|
|
{#if label}
|
|
<Label {required} {optionalText} {tooltip} hide={!label}>
|
|
{label}
|
|
</Label>
|
|
{/if}
|
|
<div
|
|
role="region"
|
|
class="box is-no-shadow u-padding-24"
|
|
style="--box-border-radius:var(--border-radius-xsmall); z-index: 1"
|
|
class:u-margin-block-start-8={!!label}
|
|
class:is-border-dashed={!hovering}
|
|
class:is-hover-with-file={hovering}
|
|
on:drop|preventDefault={dropHandler}
|
|
on:dragover|preventDefault={dragOverHandler}
|
|
on:dragenter|preventDefault
|
|
on:dragleave|preventDefault={() => (hovering = false)}>
|
|
<div class="upload-file-box">
|
|
<div class="upload-file-box-image">
|
|
<span class="icon-upload" aria-hidden="true" />
|
|
</div>
|
|
<div class="u-min-width-0 u-text-center">
|
|
<h5 class="upload-file-box-title heading-level-7 u-inline">
|
|
<span class="is-only-desktop">Drag and drop a file here to upload</span>
|
|
<span class="is-not-desktop">Upload a File</span>
|
|
</h5>
|
|
{#if allowedFileExtensions?.length}
|
|
<button
|
|
class="tooltip u-inline u-margin-inline-start-4"
|
|
aria-label="variables info">
|
|
<span class="icon-info" aria-hidden="true" />
|
|
<span class="tooltip-popup" role="tooltip"
|
|
>Only {allowedFileExtensions.join(', ')} accepted.</span>
|
|
</button>
|
|
{/if}
|
|
</div>
|
|
<div class="u-flex u-main-center u-cross-center u-gap-16 u-flex-vertical-mobile">
|
|
{#if maxSize}
|
|
{@const readableMaxSize = humanFileSize(maxSize)}
|
|
<p class="upload-file-box-info body-text-2">
|
|
Max file size: {readableMaxSize.value + readableMaxSize.unit}
|
|
</p>
|
|
{/if}
|
|
<button
|
|
class="button is-secondary is-full-width-mobile"
|
|
type="button"
|
|
on:click={() => input.click()}>
|
|
<span class="text">Choose a file</span>
|
|
</button>
|
|
</div>
|
|
|
|
{#if files?.length}
|
|
<ul class="upload-file-box-list u-min-width-0">
|
|
{#each fileArray as file}
|
|
{@const fileName = file.name.split('.')}
|
|
{@const fileSize = humanFileSize(file.size)}
|
|
<li class="u-flex u-cross-center u-min-width-0">
|
|
<span class="icon-document" aria-hidden="true" />
|
|
<span class="upload-file-box-name u-trim u-min-width-0">
|
|
<Trim>{fileName[0]}</Trim>
|
|
</span>
|
|
<span class="upload-file-box-name u-min-width-0 u-flex-shrink-0">
|
|
.{fileName[1]}
|
|
</span>
|
|
<span
|
|
class="upload-file-box-size u-margin-inline-start-4 u-margin-inline-end-16">
|
|
{fileSize.value + fileSize.unit}
|
|
</span>
|
|
<button
|
|
on:click|preventDefault={resetFiles}
|
|
type="button"
|
|
class="button is-text is-only-icon u-margin-inline-start-auto"
|
|
aria-label="remove file"
|
|
style="--button-size:1.5rem;">
|
|
<span class="icon-x" aria-hidden="true" />
|
|
</button>
|
|
</li>
|
|
{/each}
|
|
</ul>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
{#if error}
|
|
<Helper type="warning">{error}</Helper>
|
|
{/if}
|
|
</div>
|