|
<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 || |
|
(tr && |
|
tr.firstElementChild && |
|
event.composedPath().includes(tr.firstElementChild)); |
|
|
|
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); |
|
} |
|
|
|
const is_browser = typeof window !== "undefined"; |
|
</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={is_browser && 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> |
|
|