dataframe-sandbox / src /App.svelte
hmb's picture
hmb HF Staff
Initial commit: Dataframe demo with Vite + Svelte (no SvelteKit routing)
951c359
raw
history blame
43.8 kB
<script lang="ts">
import Dataframe from "@gradio/dataframe";
import { onMount } from "svelte";
import ConfigPanel from "./lib/ConfigPanel.svelte";
import SyntaxHighlighter from "./lib/SyntaxHighlighter.svelte";
import { sampleDatasets } from "./lib/sampleData";
// Configuration state
let selectedDataset = "basic";
let value = {
data: sampleDatasets.basic.data,
headers: sampleDatasets.basic.headers,
metadata: null
};
let datatype: string[] = sampleDatasets.basic.datatypes;
let editable = sampleDatasets.basic.config.editable;
let show_row_numbers = sampleDatasets.basic.config.show_row_numbers;
let show_search: "none" | "search" | "filter" = sampleDatasets.basic.config.show_search;
let show_copy_button = sampleDatasets.basic.config.show_copy_button;
let show_fullscreen_button = sampleDatasets.basic.config.show_fullscreen_button;
let show_label = sampleDatasets.basic.config.show_label;
let label = sampleDatasets.basic.config.label;
let max_height = sampleDatasets.basic.config.max_height;
let line_breaks = sampleDatasets.basic.config.line_breaks;
let wrap = sampleDatasets.basic.config.wrap;
let column_widths: string[] = [];
let max_chars: number | undefined = sampleDatasets.basic.config.max_chars;
// Theme state
let currentTheme = "default";
// Active view state
let activeView = "canvas"; // "canvas", "docs", "events", "source"
// Mobile responsive state
let showLeftPanel = false;
let showRightPanel = true; // Show controls by default
// Resizable controls panel
let controlsHeight = 220; // Default height
let isDragging = false;
let dragStartY = 0;
let dragStartHeight = 0;
// Event handlers
function handleChange(e: CustomEvent) {
console.log("Data changed:", e.detail);
value = e.detail;
}
function handleSelect(e: CustomEvent) {
console.log("Cell selected:", e.detail);
}
function handleInput(e: CustomEvent) {
console.log("Input event:", e.detail);
}
function handleDatasetChange(e: CustomEvent) {
const dataset = e.detail.dataset;
selectedDataset = dataset;
const data = sampleDatasets[dataset as keyof typeof sampleDatasets];
value = {
data: data.data,
headers: data.headers,
metadata: null
};
datatype = data.datatypes;
}
function handleThemeChange(e: CustomEvent) {
const theme = e.detail.theme;
currentTheme = theme;
const body = document.body;
body.className = body.className.replace(/\b(dark|purple-theme|green-theme|orange-theme)\b/g, '');
if (theme !== 'default') {
body.classList.add(theme);
}
}
function switchDataset(dataset: string) {
selectedDataset = dataset;
const data = sampleDatasets[dataset as keyof typeof sampleDatasets];
value = {
data: data.data,
headers: data.headers,
metadata: null
};
datatype = data.datatypes;
// Apply configuration from the dataset
const config = data.config;
editable = config.editable;
show_row_numbers = config.show_row_numbers;
show_search = config.show_search;
show_copy_button = config.show_copy_button;
show_fullscreen_button = config.show_fullscreen_button;
show_label = config.show_label;
label = config.label;
max_height = config.max_height;
line_breaks = config.line_breaks;
wrap = config.wrap;
max_chars = config.max_chars;
// Switch to canvas view to show the selected example
activeView = 'canvas';
}
function applyTheme(theme: string) {
currentTheme = theme;
const body = document.body;
body.className = body.className.replace(/\b(dark|purple-theme|green-theme|orange-theme)\b/g, '');
if (theme !== 'default') {
body.classList.add(theme);
}
}
function setActiveView(view: string) {
activeView = view;
}
function getExampleIcon(key: string): string {
const icons: Record<string, string> = {
basic: 'πŸ“‹',
compact: 'πŸ“Š',
filterable: 'πŸ”',
readonly: 'πŸ”’',
wrapped: 'πŸ“',
large: 'πŸ“ˆ',
mixed: '🎨',
minimal: '⚑'
};
return icons[key] || 'πŸ“„';
}
function getExampleTitle(key: string): string {
const titles: Record<string, string> = {
basic: 'Basic Example',
compact: 'Compact View',
filterable: 'Filter & Search',
readonly: 'Read-Only',
wrapped: 'Text Wrapping',
large: 'Large Dataset',
mixed: 'Rich Content',
minimal: 'Minimal UI'
};
return titles[key] || key.charAt(0).toUpperCase() + key.slice(1);
}
function getExampleDescription(key: string): string {
const descriptions: Record<string, string> = {
basic: 'Standard configuration with all features',
compact: 'Minimal UI, no controls',
filterable: 'Advanced filtering capabilities',
readonly: 'Non-editable data display',
wrapped: 'Long text with wrapping',
large: 'Performance with 50 rows',
mixed: 'HTML & Markdown support',
minimal: 'Bare minimum interface'
};
return descriptions[key] || 'Example configuration';
}
function handleAddColumn() {
const newHeaders = [...value.headers, `Column ${value.headers.length + 1}`];
const newData = value.data.map((row: any) => [...row, ""]);
value = {
...value,
headers: newHeaders,
data: newData
};
datatype = [...datatype, "str"];
}
function handleAddRow() {
const newRow = Array(value.headers.length).fill("");
value = {
...value,
data: [...value.data, newRow]
};
}
// Resizable controls panel handlers
function handleDragStart(e: MouseEvent) {
isDragging = true;
dragStartY = e.clientY;
dragStartHeight = controlsHeight;
document.body.classList.add('dragging');
document.addEventListener('mousemove', handleDragMove);
document.addEventListener('mouseup', handleDragEnd);
e.preventDefault();
}
function handleDragMove(e: MouseEvent) {
if (!isDragging) return;
const deltaY = dragStartY - e.clientY; // Inverted because we want up movement to increase height
const newHeight = Math.max(180, Math.min(500, dragStartHeight + deltaY)); // Min 180px, max 500px
controlsHeight = newHeight;
}
function handleDragEnd() {
isDragging = false;
document.body.classList.remove('dragging');
document.removeEventListener('mousemove', handleDragMove);
document.removeEventListener('mouseup', handleDragEnd);
}
onMount(() => {
if (currentTheme !== 'default') {
document.body.classList.add(currentTheme);
}
});
</script>
<!-- Storybook-style layout -->
<div class="min-h-screen bg-[#f6f9fc] flex flex-col">
<!-- Storybook Header -->
<header class="h-16 bg-white border-b-2 border-[#d1d5db] flex items-center px-4 md:px-6 relative z-20">
<!-- Mobile Menu Buttons -->
<div class="flex items-center space-x-2 md:hidden">
<button
on:click={() => currentTheme === 'dark' ? applyTheme('default') : applyTheme('dark')}
class="p-2 text-[#73828c] hover:text-[#2e3438] hover:bg-[#f6f9fc] rounded-lg transition-all duration-200"
aria-label="Toggle dark mode"
title="Toggle dark/light mode"
>
{#if currentTheme === 'dark'}
<span class="text-lg">β˜€οΈ</span>
{:else}
<span class="text-lg">πŸŒ™</span>
{/if}
</button>
<button
on:click={() => showLeftPanel = !showLeftPanel}
class="p-2 text-[#73828c] hover:text-[#2e3438] hover:bg-[#f6f9fc] rounded-lg transition-all duration-200"
aria-label="Toggle navigation menu"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</button>
<button
on:click={() => showRightPanel = !showRightPanel}
class="p-2 text-[#73828c] hover:text-[#2e3438] hover:bg-[#f6f9fc] rounded-lg transition-all duration-200"
aria-label="Toggle controls panel"
>
<svg class="w-5 h-5" viewBox="0 0 24 24" stroke-width="1.5" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.0503 10.6066L2.97923 17.6777C2.19818 18.4587 2.19818 19.725 2.97923 20.5061V20.5061C3.76027 21.2871 5.0266 21.2871 5.80765 20.5061L12.8787 13.435" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
<path d="M10.0502 10.6066C9.20638 8.45358 9.37134 5.6286 11.1109 3.88909C12.8504 2.14957 16.0606 1.76777 17.8284 2.82843L14.7877 5.8691L14.5051 8.98014L17.6161 8.69753L20.6568 5.65685C21.7175 7.42462 21.3357 10.6349 19.5961 12.3744C17.8566 14.1139 15.0316 14.2789 12.8786 13.435" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
</button>
</div>
<div class="flex items-center space-x-3 md:space-x-4 flex-1 md:flex-initial justify-center md:justify-start">
<!-- Gradio Logo -->
<div class="w-8 h-8 md:w-10 md:h-10 flex items-center justify-center">
<img src="./gradio-logo.svg" alt="Gradio Logo" class="w-full h-full object-contain" />
</div>
<div class="flex flex-col">
<h1 class="text-sm md:text-base font-bold text-[#2e3438] leading-tight tracking-tight">
@gradio/dataframe demo
</h1>
</div>
</div>
<div class="hidden md:flex ml-auto items-center space-x-4">
<button
on:click={() => currentTheme === 'dark' ? applyTheme('default') : applyTheme('dark')}
class="px-3 py-2 !mr-2 text-sm font-medium text-[#73828c] rounded-lg flex items-center"
aria-label="Toggle dark mode"
title="Toggle dark/light mode"
>
{#if currentTheme === 'dark'}
<span class="mr-2">β˜€οΈ</span>
{:else}
<span class="mr-2">πŸŒ™</span>
{/if}
Toggle Theme
</button>
<button
on:click={() => showRightPanel = !showRightPanel}
class="px-4 py-2 !mr-2 text-sm font-medium text-[#73828c] hover:text-[#2e3438] hover:bg-[#f6f9fc] rounded-lg transition-all duration-200 flex items-center border border-[#e0e0e0]"
aria-label="Toggle controls panel"
>
<span class="mr-2">πŸ› οΈ</span>
{showRightPanel ? 'Hide' : 'Show'} Controls
</button>
<button
on:click={() => setActiveView('docs')}
class="px-4 py-2 text-sm font-medium text-[#73828c] hover:text-[#2e3438] hover:bg-[#f6f9fc] rounded-lg transition-all duration-200 flex items-center"
>
<span class="mr-2">πŸ“–</span>
Docs
</button>
<a
href="https://www.npmjs.com/package/@gradio/dataframe"
target="_blank"
rel="noopener noreferrer"
class="px-4 py-2 text-sm font-semibold text-white rounded-lg transition-all duration-200 flex items-center no-underline"
>
<span class="mr-2">πŸ“¦</span>
npm
</a>
</div>
</header>
<div class="flex flex-1 overflow-hidden relative">
<!-- Mobile Overlay -->
{#if showLeftPanel || showRightPanel}
<div
class="fixed inset-0 bg-black bg-opacity-50 z-10 md:hidden"
role="button"
tabindex="0"
on:click={() => { showLeftPanel = false; showRightPanel = false; }}
on:keydown={(e) => { if (e.key === 'Escape') { showLeftPanel = false; showRightPanel = false; } }}
></div>
{/if}
<!-- Left Panel: Examples & Documentation -->
<div class="
{showLeftPanel ? 'translate-x-0' : '-translate-x-full'}
md:translate-x-0
fixed md:static
left-0 top-16 bottom-0
w-64 md:w-[200px] lg:w-[220px] xl:w-[240px]
bg-white border-r-1 border-[#d1d5db] [border-right-width:1px!important]
overflow-y-auto
z-20
transition-transform duration-300 ease-in-out
">
<!-- Documentation Content -->
<div class="p-4 space-y-4">
<div>
<h3 class="text-xs font-semibold text-[#2e3438] mb-2">@gradio/dataframe</h3>
<p class="text-[10px] text-[#73828c] leading-relaxed">
A standalone dataframe component from Gradio that provides interactive table functionality with editing, searching, and data manipulation capabilities.
</p>
</div>
</div>
<!-- Documentation Navigation -->
<div class="p-4 border-b border-[#e0e0e0]">
<h2 class="text-[8px] font-bold text-[#2e3438] uppercase tracking-wider mb-3">Navigation</h2>
<div class="flex flex-col space-y-2">
<button
on:click={() => setActiveView('canvas')}
class="nav-button {activeView === 'canvas' ? 'nav-button-active' : 'nav-button-inactive'}"
>
<div class="flex items-center justify-center mr-3">
<span class="text-xs">🎯</span>
</div>
Canvas
</button>
<button
on:click={() => setActiveView('docs')}
class="nav-button {activeView === 'docs' ? 'nav-button-active' : 'nav-button-inactive'}"
>
<div class="flex items-center justify-center mr-3">
<span class="text-xs">πŸ“–</span>
</div>
Documentation
</button>
<button
on:click={() => setActiveView('events')}
class="nav-button {activeView === 'events' ? 'nav-button-active' : 'nav-button-inactive'}"
>
<div class="flex items-center justify-center mr-3">
<span class="text-xs">πŸ”</span>
</div>
Events
</button>
<button
on:click={() => setActiveView('source')}
class="nav-button {activeView === 'source' ? 'nav-button-active' : 'nav-button-inactive'}"
>
<div class="flex items-center justify-center mr-3">
<span class="text-xs">πŸ’»</span>
</div>
Source Code
</button>
</div>
</div>
<!-- Examples Navigation -->
<div class="p-4 border-b border-[#e0e0e0]">
<h2 class="text-[8px] font-bold text-[#2e3438] uppercase tracking-wider mb-3">Examples</h2>
<!-- Example Cards -->
<div class="space-y-2">
{#each Object.entries(sampleDatasets) as [key, dataset]}
<button
on:click={() => switchDataset(key)}
class="w-full group"
>
<div class="example-card {selectedDataset === key ? 'example-card-active' : 'example-card-inactive'}">
<div class="flex items-center justify-between p-2">
<div class="flex items-center">
<div class="example-icon {selectedDataset === key ? 'example-icon-active' : 'example-icon-inactive'}">
<span class="text-xs">{getExampleIcon(key)}</span>
</div>
<div class="text-left">
<h4 class="text-[10px] font-semibold {selectedDataset === key ? 'text-[#1e293b]' : 'text-[#374151]'} group-hover:text-[#1e293b] transition-colors duration-200">{getExampleTitle(key)}</h4>
<p class="text-[8px] {selectedDataset === key ? 'text-[#64748b]' : 'text-[#9ca3af]'} mt-0.5">{getExampleDescription(key)}</p>
</div>
</div>
</div>
</div>
</button>
{/each}
</div>
</div>
</div>
<!-- Main Content Area -->
<div class="flex-1 bg-[#f6f9fc] overflow-y-auto border-l border-r border-[#e0e0e0] md:border-l-0" style="padding-bottom: {activeView === 'canvas' ? controlsHeight + 'px' : '16px'}">
<!-- Canvas Area -->
{#if activeView === 'canvas'}
<div class="flex-1 bg-[#f6f9fc] p-2 sm:p-4 md:p-4 lg:p-6 xl:p-8 overflow-auto">
<div class="main-container">
<div class="content-card">
<div class="border border-[#e2e8f0] rounded-lg md:rounded-xl p-4 md:p-8 bg-[#f8fafc] overflow-x-auto">
<Dataframe
bind:value
datatype={datatype as any}
{editable}
{show_row_numbers}
{show_search}
{show_copy_button}
{show_fullscreen_button}
{show_label}
{label}
{max_height}
{line_breaks}
{wrap}
{column_widths}
{max_chars}
on:change={handleChange}
on:select={handleSelect}
on:input={handleInput}
/>
</div>
</div>
</div>
</div>
{:else if activeView === 'docs'}
<!-- Documentation Page -->
<div class="page-wrapper">
<div class="main-container">
<div class="content-card">
<div class="mb-8 md:mb-12">
<h1 class="page-title">@gradio/dataframe</h1>
<p class="description-text">
A standalone dataframe component from Gradio that provides interactive table functionality with editing, searching, and data manipulation capabilities.
</p>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12 mb-12">
<div>
<h2 class="section-title">Installation</h2>
<div class="bg-[#f8fafc] border border-[#e2e8f0] rounded-xl p-6 mb-8">
<code class="text-sm text-[#2e3438] font-mono font-semibold">npm install @gradio/dataframe</code>
</div>
</div>
<div>
<h2 class="section-title">Key Features</h2>
<ul class="space-y-4">
<li class="flex items-start">
<span class="text-[#10b981] mr-4 mt-1 text-lg">βœ“</span>
<span class="feature-text">Interactive editing and data manipulation</span>
</li>
<li class="flex items-start">
<span class="text-[#10b981] mr-4 mt-1 text-lg">βœ“</span>
<span class="feature-text">Search and filter capabilities</span>
</li>
<li class="flex items-start">
<span class="text-[#10b981] mr-4 mt-1 text-lg">βœ“</span>
<span class="feature-text">Customizable appearance and behavior</span>
</li>
<li class="flex items-start">
<span class="text-[#10b981] mr-4 mt-1 text-lg">βœ“</span>
<span class="feature-text">Support for multiple data types</span>
</li>
<li class="flex items-start">
<span class="text-[#10b981] mr-4 mt-1 text-lg">βœ“</span>
<span class="feature-text">Copy to clipboard functionality</span>
</li>
<li class="flex items-start">
<span class="text-[#10b981] mr-4 mt-1 text-lg">βœ“</span>
<span class="feature-text">Fullscreen mode support</span>
</li>
</ul>
</div>
</div>
<div class="mb-12">
<h3 class="subsection-title">Quick Start</h3>
<SyntaxHighlighter
language="typescript"
code={`<script>
import Dataframe from "@gradio/dataframe";
let value = {
data: [["Alice", 25], ["Bob", 30]],
headers: ["Name", "Age"],
};
</script>
<Dataframe bind:value />`}
/>
</div>
<div class="mb-12">
<h2 class="text-lg font-bold text-[#2e3438] mb-4">API Reference</h2>
<div class="overflow-x-auto border border-[#e2e8f0] rounded-xl">
<table class="w-full">
<thead class="bg-[#f8fafc]">
<tr>
<th class="px-6 py-4 text-left font-bold text-[#2e3438] border-b border-[#e2e8f0] text-sm">Prop</th>
<th class="px-6 py-4 text-left font-bold text-[#2e3438] border-b border-[#e2e8f0] text-sm">Type</th>
<th class="px-6 py-4 text-left font-bold text-[#2e3438] border-b border-[#e2e8f0] text-sm">Default</th>
<th class="px-6 py-4 text-left font-bold text-[#2e3438] border-b border-[#e2e8f0] text-sm">Description</th>
</tr>
</thead>
<tbody>
<tr class="hover:bg-[#f8fafc] transition-colors duration-150">
<td class="px-6 py-4 border-b border-[#e2e8f0]"><code class="text-sm bg-[#e2e8f0] px-3 py-1.5 rounded-md font-mono font-semibold text-[#1e293b]">value</code></td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">DataFrame | Array | List</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">null</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] leading-relaxed">Data to display. Supports pandas, numpy, polars, and list of lists</td>
</tr>
<tr class="hover:bg-[#f8fafc] transition-colors duration-150">
<td class="px-6 py-4 border-b border-[#e2e8f0]"><code class="text-sm bg-[#e2e8f0] px-3 py-1.5 rounded-md font-mono font-semibold text-[#1e293b]">headers</code></td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">string[]</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">null</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] leading-relaxed">List of column header names</td>
</tr>
<tr class="hover:bg-[#f8fafc] transition-colors duration-150">
<td class="px-6 py-4 border-b border-[#e2e8f0]"><code class="text-sm bg-[#e2e8f0] px-3 py-1.5 rounded-md font-mono font-semibold text-[#1e293b]">datatype</code></td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">string | string[]</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">"str"</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] leading-relaxed">Data types: "str", "number", "bool", "date", "markdown", "html", "image", "auto"</td>
</tr>
<tr class="hover:bg-[#f8fafc] transition-colors duration-150">
<td class="px-6 py-4 border-b border-[#e2e8f0]"><code class="text-sm bg-[#e2e8f0] px-3 py-1.5 rounded-md font-mono font-semibold text-[#1e293b]">interactive</code></td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">boolean</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">null</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] leading-relaxed">Whether users can edit the dataframe (inferred if not provided)</td>
</tr>
<tr class="hover:bg-[#f8fafc] transition-colors duration-150">
<td class="px-6 py-4 border-b border-[#e2e8f0]"><code class="text-sm bg-[#e2e8f0] px-3 py-1.5 rounded-md font-mono font-semibold text-[#1e293b]">label</code></td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">string</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">null</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] leading-relaxed">Label text that appears above the component</td>
</tr>
<tr class="hover:bg-[#f8fafc] transition-colors duration-150">
<td class="px-6 py-4 border-b border-[#e2e8f0]"><code class="text-sm bg-[#e2e8f0] px-3 py-1.5 rounded-md font-mono font-semibold text-[#1e293b]">show_label</code></td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">boolean</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">null</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] leading-relaxed">Whether to display the label</td>
</tr>
<tr class="hover:bg-[#f8fafc] transition-colors duration-150">
<td class="px-6 py-4 border-b border-[#e2e8f0]"><code class="text-sm bg-[#e2e8f0] px-3 py-1.5 rounded-md font-mono font-semibold text-[#1e293b]">max_height</code></td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">number | string</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">500</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] leading-relaxed">Maximum height in pixels or CSS units</td>
</tr>
<tr class="hover:bg-[#f8fafc] transition-colors duration-150">
<td class="px-6 py-4 border-b border-[#e2e8f0]"><code class="text-sm bg-[#e2e8f0] px-3 py-1.5 rounded-md font-mono font-semibold text-[#1e293b]">show_row_numbers</code></td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">boolean</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">false</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] leading-relaxed">Display row numbers in a separate column</td>
</tr>
<tr class="hover:bg-[#f8fafc] transition-colors duration-150">
<td class="px-6 py-4 border-b border-[#e2e8f0]"><code class="text-sm bg-[#e2e8f0] px-3 py-1.5 rounded-md font-mono font-semibold text-[#1e293b]">show_search</code></td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">"none" | "search" | "filter"</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">"none"</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] leading-relaxed">Show search input and filter functionality</td>
</tr>
<tr class="hover:bg-[#f8fafc] transition-colors duration-150">
<td class="px-6 py-4 border-b border-[#e2e8f0]"><code class="text-sm bg-[#e2e8f0] px-3 py-1.5 rounded-md font-mono font-semibold text-[#1e293b]">show_copy_button</code></td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">boolean</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">false</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] leading-relaxed">Show button to copy table data to clipboard</td>
</tr>
<tr class="hover:bg-[#f8fafc] transition-colors duration-150">
<td class="px-6 py-4 border-b border-[#e2e8f0]"><code class="text-sm bg-[#e2e8f0] px-3 py-1.5 rounded-md font-mono font-semibold text-[#1e293b]">show_fullscreen_button</code></td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">boolean</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">false</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] leading-relaxed">Show button to view table in fullscreen mode</td>
</tr>
<tr class="hover:bg-[#f8fafc] transition-colors duration-150">
<td class="px-6 py-4 border-b border-[#e2e8f0]"><code class="text-sm bg-[#e2e8f0] px-3 py-1.5 rounded-md font-mono font-semibold text-[#1e293b]">wrap</code></td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">boolean</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">false</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] leading-relaxed">Enable text wrapping in table cells</td>
</tr>
<tr class="hover:bg-[#f8fafc] transition-colors duration-150">
<td class="px-6 py-4 border-b border-[#e2e8f0]"><code class="text-sm bg-[#e2e8f0] px-3 py-1.5 rounded-md font-mono font-semibold text-[#1e293b]">line_breaks</code></td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">boolean</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">true</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] leading-relaxed">Enable GitHub-flavored Markdown line breaks (for markdown columns)</td>
</tr>
<tr class="hover:bg-[#f8fafc] transition-colors duration-150">
<td class="px-6 py-4 border-b border-[#e2e8f0]"><code class="text-sm bg-[#e2e8f0] px-3 py-1.5 rounded-md font-mono font-semibold text-[#1e293b]">column_widths</code></td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">(string | number)[]</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">null</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] leading-relaxed">Width of each column in pixels ("100px") or percentage ("10%")</td>
</tr>
<tr class="hover:bg-[#f8fafc] transition-colors duration-150">
<td class="px-6 py-4 border-b border-[#e2e8f0]"><code class="text-sm bg-[#e2e8f0] px-3 py-1.5 rounded-md font-mono font-semibold text-[#1e293b]">max_chars</code></td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">number</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">null</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] leading-relaxed">Maximum characters to display per cell before truncating</td>
</tr>
<tr class="hover:bg-[#f8fafc] transition-colors duration-150">
<td class="px-6 py-4 border-b border-[#e2e8f0]"><code class="text-sm bg-[#e2e8f0] px-3 py-1.5 rounded-md font-mono font-semibold text-[#1e293b]">row_count</code></td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">number | [number, "fixed" | "dynamic"]</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">[1, "dynamic"]</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] leading-relaxed">Limit number of rows and whether users can add/delete rows</td>
</tr>
<tr class="hover:bg-[#f8fafc] transition-colors duration-150">
<td class="px-6 py-4 border-b border-[#e2e8f0]"><code class="text-sm bg-[#e2e8f0] px-3 py-1.5 rounded-md font-mono font-semibold text-[#1e293b]">col_count</code></td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">number | [number, "fixed" | "dynamic"]</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">null</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] leading-relaxed">Limit number of columns and whether users can add/delete columns</td>
</tr>
<tr class="hover:bg-[#f8fafc] transition-colors duration-150">
<td class="px-6 py-4 border-b border-[#e2e8f0]"><code class="text-sm bg-[#e2e8f0] px-3 py-1.5 rounded-md font-mono font-semibold text-[#1e293b]">pinned_columns</code></td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">number</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">null</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] leading-relaxed">Number of columns to pin from the left</td>
</tr>
<tr class="hover:bg-[#f8fafc] transition-colors duration-150">
<td class="px-6 py-4 border-b border-[#e2e8f0]"><code class="text-sm bg-[#e2e8f0] px-3 py-1.5 rounded-md font-mono font-semibold text-[#1e293b]">static_columns</code></td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">number[]</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] font-medium">null</td>
<td class="px-6 py-4 border-b border-[#e2e8f0] text-[#64748b] leading-relaxed">Column indices that should not be editable</td>
</tr>
<tr class="hover:bg-[#f8fafc] transition-colors duration-150">
<td class="px-6 py-4"><code class="text-sm bg-[#e2e8f0] px-3 py-1.5 rounded-md font-mono font-semibold text-[#1e293b]">type</code></td>
<td class="px-6 py-4 text-[#64748b] font-medium">"pandas" | "numpy" | "array" | "polars"</td>
<td class="px-6 py-4 text-[#64748b] font-medium">"pandas"</td>
<td class="px-6 py-4 text-[#64748b] leading-relaxed">Type of value returned by the component</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{:else if activeView === 'events'}
<!-- Events Page -->
<div class="page-wrapper">
<div class="main-container">
<div class="content-card">
<div class="mb-8 md:mb-12">
<h1 class="page-title">Events</h1>
<p class="description-text">
The dataframe component fires several events that you can listen to for user interactions and data changes.
</p>
</div>
<div class="space-y-10">
<div class="event-section">
<div class="flex items-start mb-6">
<code class="bg-[#F87701] text-white px-4 py-2 rounded-lg text-[#2e3438] mr-6 font-mono font-bold text-sm border shadow-sm">change</code>
<div class="flex-1">
<h3 class="subsection-title">Data Change Event</h3>
<p class="text-sm text-[#64748b] mb-4 leading-relaxed font-medium">Fired when the table data is modified by the user.</p>
<SyntaxHighlighter
language="typescript"
code={`<Dataframe
bind:value
on:change={(e) => {
console.log("Data changed:", e.detail);
}}
/>`}
/>
</div>
</div>
</div>
<div class="event-section">
<div class="flex items-start mb-6">
<code class="bg-[#10b981] text-white px-4 py-2 rounded-lg text-[#2e3438] mr-6 font-mono font-bold text-sm border shadow-sm">select</code>
<div class="flex-1">
<h3 class="subsection-title">Cell Selection Event</h3>
<p class="text-[#64748b] mb-6 leading-relaxed font-medium">Fired when a cell is selected or focused.</p>
<SyntaxHighlighter
language="typescript"
code={`<Dataframe
bind:value
on:select={(e) => {
console.log("Cell selected:", e.detail);
// e.detail contains: { row: number, col: number }
}}
/>`}
/>
</div>
</div>
</div>
<div class="event-section">
<div class="flex items-start mb-6">
<code class="bg-[#f59e0b] text-white px-4 py-2 rounded-lg text-[#2e3438] mr-6 font-mono font-bold text-sm border shadow-sm">input</code>
<div class="flex-1">
<h3 class="subsection-title">User Input Event</h3>
<p class="text-[#64748b] mb-6 leading-relaxed font-medium">Fired during user input, useful for real-time validation.</p>
<SyntaxHighlighter
language="typescript"
code={`<Dataframe
bind:value
on:input={(e) => {
console.log("User input:", e.detail);
// Real-time validation logic here
}}
/>`}
/>
</div>
</div>
</div>
</div>
<div class="mt-12 p-8 bg-gradient-to-r from-[#f8fafc] to-[#e0f2fe] border border-[#e2e8f0] rounded-xl">
<h3 class="text-2xl font-bold text-[#2e3438] mb-4">Event Data Structure</h3>
<p class="text-[#64748b] mb-6 leading-relaxed font-medium">All events provide data through the <code class="bg-white px-3 py-1.5 rounded-md border font-mono text-sm text-[#1e293b] font-semibold">e.detail</code> property:</p>
<SyntaxHighlighter
language="typescript"
code={`// Change event detail
{
data: (string | number)[][],
headers: string[],
metadata: any
}
// Select event detail
{
row: number,
col: number,
value: string | number
}`}
/>
</div>
</div>
</div>
</div>
{:else if activeView === 'source'}
<!-- Source Code Page -->
<div class="page-wrapper overflow-y-scroll">
<div class="main-container">
<div class="content-card">
<div class="mb-8 md:mb-12">
<h1 class="page-title">Source Code</h1>
<p class="description-text">
Complete example showing how to implement the dataframe component with all features.
</p>
</div>
<div class="mb-10">
<h2 class="text-lg font-bold text-[#2e3438] mb-4">Current Configuration</h2>
<SyntaxHighlighter
language="typescript"
code={`<script lang="ts">
import Dataframe from "@gradio/dataframe";
let value = ` + JSON.stringify({
data: value.data.slice(0, 3),
headers: value.headers,
metadata: null
}, null, 2) + `;
let datatype = ` + JSON.stringify(datatype) + `;
function handleChange(e: any) {
console.log("Data changed:", e.detail);
}
function handleSelect(e: any) {
console.log("Cell selected:", e.detail);
}
function handleInput(e: any) {
console.log("Input event:", e.detail);
}
</script>
<Dataframe
bind:value
datatype={datatype}
editable={` + editable + `}
show_row_numbers={` + show_row_numbers + `}
show_search="` + show_search + `"
show_copy_button={` + show_copy_button + `}
show_fullscreen_button={` + show_fullscreen_button + `}
show_label={` + show_label + `}
label="` + label + `"
max_height={` + max_height + `}
line_breaks={` + line_breaks + `}
wrap={` + wrap + `}` + (max_chars ? `
max_chars={` + max_chars + `}` : '') + `
on:change={handleChange}
on:select={handleSelect}
on:input={handleInput}
/>`}
/>
</div>
<div class="mb-10">
<h2 class="text-lg font-bold text-[#2e3438] mb-4">TypeScript Definitions</h2>
<SyntaxHighlighter
language="typescript"
code={`interface DataframeValue {
data: (string | number)[][];
headers: string[];
metadata: any;
}
interface DataframeProps {
value: DataframeValue;
datatype?: string[];
editable?: boolean;
show_row_numbers?: boolean;
show_search?: "none" | "search" | "filter";
show_copy_button?: boolean;
show_fullscreen_button?: boolean;
show_label?: boolean;
label?: string;
max_height?: number;
line_breaks?: boolean;
wrap?: boolean;
max_chars?: number;
column_widths?: string[];
}`}
/>
</div>
<div>
<h2 class="text-lg font-bold text-[#2e3438] mb-4">Installation</h2>
<div class="bg-[#f8fafc] border border-[#e2e8f0] rounded-xl p-8">
<h3 class="text-lg font-semibold text-[#2e3438] mb-4">Package.json</h3>
<SyntaxHighlighter
language="json"
code={`{
"dependencies": {
"@gradio/dataframe": "^latest"
}
}`}
/>
</div>
</div>
</div>
</div>
</div>
{/if}
<!-- Bottom Controls Panel (Horizontal) -->
<div class="
{showRightPanel && activeView === 'canvas' ? 'translate-y-0' : 'translate-y-full'}
fixed
left-0 md:left-[200px] lg:left-[220px] xl:left-[240px]
right-0 bottom-0
bg-white border-t border-[#d1d5db]
z-20
transition-transform duration-300 ease-in-out
" style="height: {controlsHeight}px">
<!-- Drag handle for resizing -->
<div
class="flex justify-center border-b border-[#e0e0e0] cursor-ns-resize hover:bg-gray-50 transition-colors duration-200 {isDragging ? 'bg-gray-100' : ''}"
on:mousedown={handleDragStart}
role="slider"
tabindex="0"
aria-label="Resize controls panel"
aria-valuenow={controlsHeight}
aria-valuemin="120"
aria-valuemax="400"
title="Drag to resize controls panel"
>
</div>
<!-- Mobile bottom sheet handle -->
<div class="md:hidden flex justify-center py-2 border-b border-[#e0e0e0]">
<div class="w-10 h-1 bg-[#d1d5db] rounded-full"></div>
</div>
<ConfigPanel
bind:selectedDataset
bind:currentTheme
bind:editable
bind:show_row_numbers
bind:show_search
bind:show_copy_button
bind:show_fullscreen_button
bind:show_label
bind:label
bind:max_height
bind:line_breaks
bind:wrap
bind:max_chars
on:datasetChange={handleDatasetChange}
on:themeChange={handleThemeChange}
on:addColumn={handleAddColumn}
on:addRow={handleAddRow}
/>
</div>
</div>
</div>
</div>