hd0013's picture
Upload folder using huggingface_hub
8fdc036 verified
raw
history blame contribute delete
No virus
4.6 kB
<script lang="ts">
import type { FileNode } from "./types";
import { createEventDispatcher } 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 path: string[] = [];
export let selected_files: string[][] = [];
export let selected_folders: string[][] = [];
export let is_selected_entirely = false;
export let interactive: boolean;
export let ls_fn: (path: string[]) => Promise<FileNode[]>;
export let file_count: "single" | "multiple" = "multiple";
export let valid_for_selection: boolean;
let content: FileNode[] = [];
let opened_folders: number[] = [];
const toggle_open_folder = (i: number): void => {
if (opened_folders.includes(i)) {
opened_folders = opened_folders.filter((x) => x !== i);
} else {
opened_folders = [...opened_folders, i];
}
};
const open_folder = (i: number): void => {
if (!opened_folders.includes(i)) {
opened_folders = [...opened_folders, i];
}
};
(async () => {
content = await ls_fn(path);
if (valid_for_selection) {
content = [{ name: ".", type: "file" }, ...content];
}
opened_folders = content
.map((x, i) =>
x.type === "folder" &&
(is_selected_entirely || selected_files.some((y) => y[0] === x.name))
? i
: null
)
.filter((x): x is number => x !== null);
})();
$: if (is_selected_entirely) {
content.forEach((x) => {
dispatch("check", {
path: [...path, x.name],
checked: true,
type: x.type
});
});
}
const dispatch = createEventDispatcher<{
check: { path: string[]; checked: boolean; type: "file" | "folder" };
}>();
</script>
<ul>
{#each content as { type, name, valid }, i}
<li>
<span class="wrap">
<Checkbox
disabled={!interactive ||
(type === "folder" && file_count === "single")}
value={(type === "file" ? selected_files : selected_folders).some(
(x) => x[0] === name && x.length === 1
)}
on:change={(e) => {
let checked = e.detail;
dispatch("check", {
path: [...path, name],
checked,
type
});
if (type === "folder" && checked) {
open_folder(i);
}
}}
/>
{#if type === "folder"}
<span
class="icon"
class:hidden={!opened_folders.includes(i)}
on:click|stopPropagation={() => toggle_open_folder(i)}
role="button"
aria-label="expand directory"
tabindex="0"
on:keydown={({ key }) => {
if (key === " " || key === "Enter") {
toggle_open_folder(i);
}
}}><Arrow /></span
>
{:else}
<span class="file-icon">
<img src={name === "." ? FolderIcon : FileIcon} alt="file icon" />
</span>
{/if}
{name}
</span>
{#if type === "folder" && opened_folders.includes(i)}
<svelte:self
path={[...path, name]}
selected_files={selected_files
.filter((x) => x[0] === name)
.map((x) => x.slice(1))}
selected_folders={selected_folders
.filter((x) => x[0] === name)
.map((x) => x.slice(1))}
is_selected_entirely={selected_folders.some(
(x) => x[0] === name && x.length === 1
)}
{interactive}
{ls_fn}
{file_count}
valid_for_selection={valid}
on:check
/>
{/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>