gradio / js /tabs /shared /Tabs.svelte
mindmime's picture
Upload folder using huggingface_hub
a03b3ba verified
<script context="module">
export const TABS = {};
</script>
<script lang="ts">
import { setContext, createEventDispatcher } from "svelte";
import { writable } from "svelte/store";
import type { SelectData } from "@gradio/utils";
interface Tab {
name: string;
id: object;
elem_id: string | undefined;
visible: boolean;
interactive: boolean;
}
export let visible = true;
export let elem_id = "id";
export let elem_classes: string[] = [];
export let selected: number | string | object;
let tabs: Tab[] = [];
const selected_tab = writable<false | object | number | string>(false);
const selected_tab_index = writable<number>(0);
const dispatch = createEventDispatcher<{
change: undefined;
select: SelectData;
}>();
setContext(TABS, {
register_tab: (tab: Tab) => {
let existingTab = tabs.find((t) => t.id === tab.id);
if (existingTab) {
// update existing tab with newer values
let i = tabs.findIndex((t) => t.id === tab.id);
tabs[i] = { ...tabs[i], ...tab };
} else {
tabs.push({
name: tab.name,
id: tab.id,
elem_id: tab.elem_id,
visible: tab.visible,
interactive: tab.interactive
});
}
selected_tab.update((current) => {
if (current === false && tab.visible && tab.interactive) {
return tab.id;
}
let nextTab = tabs.find((t) => t.visible && t.interactive);
return nextTab ? nextTab.id : current;
});
tabs = tabs;
return tabs.length - 1;
},
unregister_tab: (tab: Tab) => {
const i = tabs.findIndex((t) => t.id === tab.id);
tabs.splice(i, 1);
selected_tab.update((current) =>
current === tab.id ? tabs[i]?.id || tabs[tabs.length - 1]?.id : current
);
},
selected_tab,
selected_tab_index
});
function change_tab(id: object | string | number): void {
const tab_to_activate = tabs.find((t) => t.id === id);
if (
tab_to_activate &&
tab_to_activate.interactive &&
tab_to_activate.visible
) {
selected = id;
$selected_tab = id;
$selected_tab_index = tabs.findIndex((t) => t.id === id);
dispatch("change");
} else {
console.warn("Attempted to select a non-interactive or hidden tab.");
}
}
$: tabs, selected !== null && change_tab(selected);
</script>
<div class="tabs {elem_classes.join(' ')}" class:hide={!visible} id={elem_id}>
<div class="tab-nav scroll-hide" role="tablist">
{#each tabs as t, i (t.id)}
{#if t.visible}
{#if t.id === $selected_tab}
<button
role="tab"
class="selected"
aria-selected={true}
aria-controls={t.elem_id}
id={t.elem_id ? t.elem_id + "-button" : null}
>
{t.name}
</button>
{:else}
<button
role="tab"
aria-selected={false}
aria-controls={t.elem_id}
disabled={!t.interactive}
aria-disabled={!t.interactive}
id={t.elem_id ? t.elem_id + "-button" : null}
on:click={() => {
change_tab(t.id);
dispatch("select", { value: t.name, index: i });
}}
>
{t.name}
</button>
{/if}
{/if}
{/each}
</div>
<slot />
</div>
<style>
.tabs {
position: relative;
}
.hide {
display: none;
}
.tab-nav {
display: flex;
position: relative;
flex-wrap: wrap;
border-bottom: 1px solid var(--border-color-primary);
}
button {
margin-bottom: -1px;
border: 1px solid transparent;
border-color: transparent;
border-bottom: none;
border-top-right-radius: var(--container-radius);
border-top-left-radius: var(--container-radius);
padding: var(--size-1) var(--size-4);
color: var(--body-text-color-subdued);
font-weight: var(--section-header-text-weight);
font-size: var(--section-header-text-size);
}
button:disabled {
color: var(--body-text-color-subdued);
opacity: 0.5;
cursor: not-allowed;
}
button:hover {
color: var(--body-text-color);
}
.selected {
border-color: var(--border-color-primary);
background: var(--background-fill-primary);
color: var(--body-text-color);
}
.bar {
display: block;
position: absolute;
bottom: -2px;
left: 0;
z-index: 999;
background: var(--background-fill-primary);
width: 100%;
height: 2px;
content: "";
}
</style>