Files
wehub-resource-sync adf0d17497
publish / version_or_publish (push) Has been cancelled
storybook-build / changes (push) Has been cancelled
storybook-build / :storybook-build (push) Has been cancelled
Sync Gradio Skills to Hugging Face / sync-skills (push) Has been cancelled
functional / changes (push) Has been cancelled
functional / build-frontend (push) Has been cancelled
functional / functional-test-SSR=false (push) Has been cancelled
functional / functional-reload (push) Has been cancelled
js / changes (push) Has been cancelled
js / js-test (push) Has been cancelled
docs-build / changes (push) Has been cancelled
docs-build / docs-build (push) Has been cancelled
docs-build / website-build (push) Has been cancelled
functional / functional-test-SSR=true (push) Has been cancelled
hygiene / hygiene-test (push) Has been cancelled
python / changes (push) Has been cancelled
python / build (push) Has been cancelled
python / test-ubuntu-latest-flaky (push) Has been cancelled
python / test-ubuntu-latest-not-flaky (push) Has been cancelled
python / test-windows-latest-flaky (push) Has been cancelled
python / test-windows-latest-not-flaky (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:17:32 +08:00

209 lines
4.8 KiB
Svelte

<script lang="ts">
import { Copy, Check } from "@gradio/icons";
import { FullscreenButton } from "@gradio/atoms";
import { IconButton } from "@gradio/atoms";
let {
show_fullscreen_button = false,
show_copy_button = false,
show_search = "none",
fullscreen = false,
on_copy,
on_commit_filter,
current_search_query = null,
onsearch,
onfullscreen
}: {
show_fullscreen_button?: boolean;
show_copy_button?: boolean;
show_search?: "none" | "search" | "filter";
fullscreen?: boolean;
on_copy: () => Promise<void>;
on_commit_filter: () => void;
current_search_query?: string | null;
onsearch?: (query: string | null) => void;
onfullscreen?: () => void;
} = $props();
let copied = $state(false);
let timer: ReturnType<typeof setTimeout>;
let input_value = $state("");
function handle_search_input(e: Event): void {
const target = e.target as HTMLTextAreaElement;
input_value = target.value;
const new_query = input_value || null;
if (current_search_query !== new_query) {
current_search_query = new_query;
onsearch?.(current_search_query);
}
}
function copy_feedback(): void {
copied = true;
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
copied = false;
}, 2000);
}
async function handle_copy(): Promise<void> {
await on_copy();
copy_feedback();
}
$effect(() => {
return () => {
if (timer) clearTimeout(timer);
};
});
</script>
<div class="toolbar" role="toolbar" aria-label="Table actions">
<div class="toolbar-buttons">
{#if show_search !== "none"}
<div class="search-container">
<input
type="text"
value={current_search_query || ""}
oninput={handle_search_input}
placeholder={show_search === "filter" ? "Filter..." : "Search..."}
class="search-input"
class:filter-mode={show_search === "filter"}
title={`Enter text to ${show_search} the table`}
/>
{#if current_search_query && show_search === "filter"}
<button
class="toolbar-button check-button"
onclick={on_commit_filter}
aria-label="Apply filter and update dataframe values"
title="Apply filter and update dataframe values"
>
<Check />
</button>
{/if}
</div>
{/if}
{#if show_copy_button}
<IconButton
Icon={copied ? Check : Copy}
label={copied ? "Copied to clipboard" : "Copy table data"}
onclick={handle_copy}
/>
{/if}
{#if show_fullscreen_button}
<FullscreenButton {fullscreen} onclick={(_fs) => onfullscreen?.()} />
{/if}
</div>
</div>
<style>
.toolbar {
display: flex;
align-items: center;
gap: var(--size-2);
flex: 0 0 auto;
font-family: var(--font-sans);
}
.toolbar-buttons {
display: flex;
gap: var(--size-1);
flex-wrap: nowrap;
align-items: center;
}
.toolbar-button {
display: flex;
align-items: center;
justify-content: center;
width: var(--size-6);
height: var(--size-6);
padding: var(--size-1);
border: none;
border-radius: var(--radius-sm);
background: transparent;
color: var(--body-text-color-subdued);
cursor: pointer;
transition: all 0.2s;
}
.toolbar-button:hover {
background: var(--background-fill-secondary);
color: var(--body-text-color);
}
.toolbar-button :global(svg) {
width: var(--size-4);
height: var(--size-4);
}
.search-container {
position: relative;
}
.search-input {
width: var(--size-full);
height: var(--size-6);
padding: var(--size-2);
padding-right: var(--size-8);
border: 1px solid var(--border-color-primary);
border-radius: var(--table-radius);
font-size: var(--text-sm);
color: var(--body-text-color);
background: var(--background-fill-secondary);
transition: all 0.2s ease;
}
.search-input:hover {
border-color: var(--border-color-secondary);
background: var(--background-fill-primary);
}
.search-input:focus {
outline: none;
border-color: var(--color-accent);
background: var(--background-fill-primary);
box-shadow: 0 0 0 1px var(--color-accent);
}
.check-button {
position: absolute;
right: var(--size-1);
top: 50%;
transform: translateY(-50%);
background: var(--color-accent);
color: white;
border: none;
width: var(--size-4);
height: var(--size-4);
border-radius: var(--radius-sm);
display: flex;
align-items: center;
justify-content: center;
padding: var(--size-1);
}
.check-button :global(svg) {
width: var(--size-3);
height: var(--size-3);
}
.check-button:hover {
background: var(--color-accent-soft);
}
.toolbar-buttons :global(.icon-button) {
background: transparent !important;
height: var(--size-6);
width: var(--size-6);
}
.toolbar-buttons :global(.icon-button:hover) {
background: var(--background-fill-secondary) !important;
color: var(--body-text-color) !important;
border: 1px solid var(--border-color-primary);
border-radius: var(--radius-sm) !important;
}
</style>