Spaces:
Running
Running
<script lang="ts"> | |
import type { FileData } from "@gradio/client"; | |
import { prettyBytes } from "./utils"; | |
import { createEventDispatcher } from "svelte"; | |
import type { I18nFormatter, SelectData } from "@gradio/utils"; | |
import { DownloadLink } from "@gradio/wasm/svelte"; | |
const dispatch = createEventDispatcher<{ | |
select: SelectData; | |
change: FileData[] | FileData; | |
delete: FileData; | |
}>(); | |
export let value: FileData | FileData[]; | |
export let selectable = false; | |
export let height: number | undefined = undefined; | |
export let i18n: I18nFormatter; | |
function split_filename(filename: string): [string, string] { | |
const last_dot = filename.lastIndexOf("."); | |
if (last_dot === -1) { | |
return [filename, ""]; | |
} | |
return [filename.slice(0, last_dot), filename.slice(last_dot)]; | |
} | |
$: normalized_files = (Array.isArray(value) ? value : [value]).map((file) => { | |
const [filename_stem, filename_ext] = split_filename(file.orig_name ?? ""); | |
return { | |
...file, | |
filename_stem, | |
filename_ext | |
}; | |
}); | |
function handle_row_click( | |
event: MouseEvent & { currentTarget: HTMLTableRowElement }, | |
index: number | |
): void { | |
const tr = event.currentTarget; | |
const should_select = | |
event.target === tr || // Only select if the click is on the row itself | |
(tr && | |
tr.firstElementChild && | |
event.composedPath().includes(tr.firstElementChild)); // Or if the click is on the name column | |
if (should_select) { | |
dispatch("select", { value: normalized_files[index].orig_name, index }); | |
} | |
} | |
function remove_file(index: number): void { | |
const removed = normalized_files.splice(index, 1); | |
normalized_files = [...normalized_files]; | |
value = normalized_files; | |
dispatch("delete", removed[0]); | |
dispatch("change", normalized_files); | |
} | |
</script> | |
<div | |
class="file-preview-holder" | |
style="max-height: {typeof height === undefined ? 'auto' : height + 'px'};" | |
> | |
<table class="file-preview"> | |
<tbody> | |
{#each normalized_files as file, i (file)} | |
<tr | |
class="file" | |
class:selectable | |
on:click={(event) => { | |
handle_row_click(event, i); | |
}} | |
> | |
<td class="filename" aria-label={file.orig_name}> | |
<span class="stem">{file.filename_stem}</span> | |
<span class="ext">{file.filename_ext}</span> | |
</td> | |
<td class="download"> | |
{#if file.url} | |
<DownloadLink | |
href={file.url} | |
download={window.__is_colab__ ? null : file.orig_name} | |
> | |
{@html file.size != null | |
? prettyBytes(file.size) | |
: "(size unknown)"} ⇣ | |
</DownloadLink> | |
{:else} | |
{i18n("file.uploading")} | |
{/if} | |
</td> | |
{#if normalized_files.length > 1} | |
<td> | |
<button | |
class="label-clear-button" | |
aria-label="Remove this file" | |
on:click={() => { | |
remove_file(i); | |
}} | |
on:keydown={(event) => { | |
if (event.key === "Enter") { | |
remove_file(i); | |
} | |
}} | |
>× | |
</button> | |
</td> | |
{/if} | |
</tr> | |
{/each} | |
</tbody> | |
</table> | |
</div> | |
<style> | |
.label-clear-button { | |
color: var(--body-text-color-subdued); | |
position: relative; | |
left: -3px; | |
} | |
.label-clear-button:hover { | |
color: var(--body-text-color); | |
} | |
.file-preview { | |
table-layout: fixed; | |
width: var(--size-full); | |
max-height: var(--size-60); | |
overflow-y: auto; | |
margin-top: var(--size-1); | |
color: var(--body-text-color); | |
} | |
.file-preview-holder { | |
overflow: auto; | |
} | |
.file { | |
display: flex; | |
width: var(--size-full); | |
} | |
.file > * { | |
padding: var(--size-1) var(--size-2-5); | |
} | |
.filename { | |
flex-grow: 1; | |
display: flex; | |
overflow: hidden; | |
} | |
.filename .stem { | |
overflow: hidden; | |
text-overflow: ellipsis; | |
white-space: nowrap; | |
} | |
.filename .ext { | |
white-space: nowrap; | |
} | |
.download { | |
min-width: 8rem; | |
width: 10%; | |
white-space: nowrap; | |
text-align: right; | |
} | |
.download:hover { | |
text-decoration: underline; | |
} | |
.download > :global(a) { | |
color: var(--link-text-color); | |
} | |
.download > :global(a:hover) { | |
color: var(--link-text-color-hover); | |
} | |
.download > :global(a:visited) { | |
color: var(--link-text-color-visited); | |
} | |
.download > :global(a:active) { | |
color: var(--link-text-color-active); | |
} | |
.selectable { | |
cursor: pointer; | |
} | |
tbody > tr:nth-child(even) { | |
background: var(--block-background-fill); | |
} | |
tbody > tr:nth-child(odd) { | |
background: var(--table-odd-background-fill); | |
} | |
</style> | |