gradio / js /fileexplorer /shared /FileTree.svelte
mindmime's picture
Upload folder using huggingface_hub
a03b3ba verified
<script lang="ts">
import type { Node } from "./utils";
import { createEventDispatcher, tick } from "svelte";
import Arrow from "./ArrowIcon.svelte";
import Checkbox from "./Checkbox.svelte";
import FileIcon from "../icons/light-file.svg";
import FolderIcon from "../icons/light-folder.svg";
export let interactive: boolean;
export let tree: Node[] = [];
export let icons: any = {};
export let node_indices: number[] = [];
export let file_count: "single" | "multiple" = "multiple";
const dispatch = createEventDispatcher<{
check: { node_indices: number[]; checked: boolean };
}>();
async function dispatch_change(i: number): Promise<void> {
await tick();
dispatch("check", {
node_indices: [...node_indices, i],
checked: !tree[i].checked
});
}
</script>
<ul>
{#each tree as { type, path, children, children_visible, checked }, i}
<li>
<span class="wrap">
<Checkbox
disabled={!interactive ||
(type === "folder" && file_count === "single")}
bind:value={checked}
on:change={() => dispatch_change(i)}
/>
{#if type === "folder"}
<span
class="icon"
class:hidden={!tree[i].children_visible}
on:click|stopPropagation={() =>
(tree[i].children_visible = !tree[i].children_visible)}
role="button"
aria-label="expand directory"
tabindex="0"
on:keydown={({ key }) =>
(key === " " || key === "Enter") &&
(tree[i].children_visible = !tree[i].children_visible)}
><Arrow /></span
>
{:else if path === ""}
<span class="file-icon">
<img src={FolderIcon} alt="folder icon" />
</span>
{:else}
<span class="file-icon">
<img src={FileIcon} alt="file icon" />
</span>
{/if}
{path ? path : "."}
</span>
{#if children && children_visible}
<svelte:self
tree={children}
{icons}
on:check
node_indices={[...node_indices, i]}
{interactive}
{file_count}
/>
{/if}
</li>
{/each}
</ul>
<style>
.icon {
display: inline-block;
width: 18px;
height: 18px;
padding: 3px 2px 3px 3px;
margin: 0;
flex-grow: 0;
display: inline-flex;
justify-content: center;
align-items: center;
border-radius: 2px;
cursor: pointer;
transition: 0.1s;
flex-shrink: 0;
}
.file-icon {
display: inline-block;
height: 20px;
margin-left: -1px;
margin: 0;
flex-grow: 0;
display: inline-flex;
justify-content: center;
align-items: center;
transition: 0.1s;
}
.file-icon img {
width: 100%;
height: 100%;
}
.icon:hover {
background: #eee;
}
.icon:hover :global(> *) {
color: var(--block-info-text-color);
}
.icon :global(> *) {
transform: rotate(90deg);
transform-origin: 40% 50%;
transition: 0.2s;
color: var(--color-accent);
}
.hidden :global(> *) {
transform: rotate(0);
color: var(--body-text-color-subdued);
}
ul {
margin-left: 26px;
padding-left: 0;
list-style: none;
}
li {
margin-left: 0;
padding-left: 0;
align-items: center;
margin: 8px 0;
font-family: var(--font-mono);
font-size: var(--scale-00);
overflow-wrap: anywhere;
word-break: break-word;
}
.wrap {
display: flex;
gap: 8px;
align-items: center;
}
</style>