adf0d17497
publish / version_or_publish (push) Has been cancelled
storybook-build / changes (push) Has been cancelled
storybook-build / :storybook-build (push) Has been cancelled
Sync Gradio Skills to Hugging Face / sync-skills (push) Has been cancelled
functional / changes (push) Has been cancelled
functional / build-frontend (push) Has been cancelled
functional / functional-test-SSR=false (push) Has been cancelled
functional / functional-reload (push) Has been cancelled
js / changes (push) Has been cancelled
js / js-test (push) Has been cancelled
docs-build / changes (push) Has been cancelled
docs-build / docs-build (push) Has been cancelled
docs-build / website-build (push) Has been cancelled
functional / functional-test-SSR=true (push) Has been cancelled
hygiene / hygiene-test (push) Has been cancelled
python / changes (push) Has been cancelled
python / build (push) Has been cancelled
python / test-ubuntu-latest-flaky (push) Has been cancelled
python / test-ubuntu-latest-not-flaky (push) Has been cancelled
python / test-windows-latest-flaky (push) Has been cancelled
python / test-windows-latest-not-flaky (push) Has been cancelled
159 lines
3.5 KiB
Svelte
159 lines
3.5 KiB
Svelte
<script lang="ts">
|
|
import { tick } from "svelte";
|
|
import { BaseButton } from "@gradio/button";
|
|
import { prepare_files, type FileData, type Client } from "@gradio/client";
|
|
import { is_valid_mimetype } from "@gradio/upload";
|
|
|
|
let {
|
|
elem_id = "",
|
|
elem_classes = [],
|
|
visible = true,
|
|
label,
|
|
value,
|
|
file_count,
|
|
file_types = [],
|
|
root,
|
|
size = "lg",
|
|
icon = null,
|
|
scale = null,
|
|
min_width = undefined,
|
|
variant = "secondary",
|
|
disabled = false,
|
|
max_file_size = null,
|
|
upload,
|
|
onclick,
|
|
onchange,
|
|
onupload,
|
|
onerror,
|
|
children
|
|
}: {
|
|
elem_id?: string;
|
|
elem_classes?: string[];
|
|
visible?: boolean | "hidden";
|
|
label: string | null;
|
|
value?: null | FileData | FileData[];
|
|
file_count: string;
|
|
file_types?: string[];
|
|
root: string;
|
|
size?: "sm" | "md" | "lg";
|
|
icon?: FileData | null;
|
|
scale?: number | null;
|
|
min_width?: number | undefined;
|
|
variant?: "primary" | "secondary" | "stop";
|
|
disabled?: boolean;
|
|
max_file_size?: number | null;
|
|
upload: Client["upload"];
|
|
onclick?: () => void;
|
|
onchange?: (value: null | FileData | FileData[]) => void;
|
|
onupload?: (value: null | FileData | FileData[]) => void;
|
|
onerror?: (message: string) => void;
|
|
children?: import("svelte").Snippet;
|
|
} = $props();
|
|
|
|
let hidden_upload: HTMLInputElement;
|
|
|
|
let accept_file_types = $derived.by(() => {
|
|
if (file_types == null) {
|
|
return null;
|
|
}
|
|
const mapped = file_types.map((x) => {
|
|
if (x.startsWith(".")) {
|
|
return x;
|
|
}
|
|
return x + "/*";
|
|
});
|
|
return mapped.join(", ");
|
|
});
|
|
|
|
function open_file_upload(): void {
|
|
onclick?.();
|
|
hidden_upload.click();
|
|
}
|
|
|
|
async function load_files(files: FileList): Promise<void> {
|
|
let _files: File[] = Array.from(files).filter((file) => {
|
|
const ext = "." + file.name.toLowerCase().split(".").pop();
|
|
if (is_valid_mimetype(accept_file_types, ext, file.type)) {
|
|
return true;
|
|
}
|
|
onerror?.(`Invalid file type only ${file_types?.join(", ")} allowed.`);
|
|
return false;
|
|
});
|
|
|
|
if (!_files.length) {
|
|
return;
|
|
}
|
|
if (file_count === "single") {
|
|
_files = [_files[0]];
|
|
}
|
|
let all_file_data = await prepare_files(_files);
|
|
await tick();
|
|
|
|
try {
|
|
all_file_data = (
|
|
await upload(all_file_data, root, undefined, max_file_size ?? Infinity)
|
|
)?.filter((x) => x !== null) as FileData[];
|
|
} catch (e) {
|
|
onerror?.((e as Error).message);
|
|
return;
|
|
}
|
|
const new_value =
|
|
file_count === "single" ? all_file_data?.[0] : all_file_data;
|
|
onchange?.(new_value);
|
|
onupload?.(new_value);
|
|
}
|
|
|
|
async function load_files_from_upload(e: Event): Promise<void> {
|
|
const target = e.target as HTMLInputElement;
|
|
|
|
if (!target.files) return;
|
|
await load_files(target.files);
|
|
}
|
|
|
|
function clear_input_value(e: Event): void {
|
|
const target = e.target as HTMLInputElement;
|
|
if (target.value) target.value = "";
|
|
}
|
|
</script>
|
|
|
|
<input
|
|
class="hide"
|
|
accept={accept_file_types}
|
|
type="file"
|
|
bind:this={hidden_upload}
|
|
onchange={load_files_from_upload}
|
|
onclick={clear_input_value}
|
|
multiple={file_count === "multiple" || undefined}
|
|
webkitdirectory={file_count === "directory" || undefined}
|
|
mozdirectory={file_count === "directory" || undefined}
|
|
data-testid="{label}-upload-button"
|
|
/>
|
|
|
|
<BaseButton
|
|
{size}
|
|
{variant}
|
|
{elem_id}
|
|
{elem_classes}
|
|
{visible}
|
|
onclick={open_file_upload}
|
|
{scale}
|
|
{min_width}
|
|
{disabled}
|
|
>
|
|
{#if icon}
|
|
<img class="button-icon" src={icon.url} alt={`${value} icon`} />
|
|
{/if}
|
|
{#if children}{@render children()}{/if}
|
|
</BaseButton>
|
|
|
|
<style>
|
|
.hide {
|
|
display: none;
|
|
}
|
|
.button-icon {
|
|
width: var(--text-xl);
|
|
height: var(--text-xl);
|
|
margin-right: var(--spacing-xl);
|
|
}
|
|
</style>
|