Spaces:
Sleeping
Sleeping
Download frontend/src/components/SchemaView.tsx from Sid-WC121/visual-notebook: direct link, hf CLI and curl.
- Browser
- Download file 6.05 kB
-
https://huggingface.co/spaces/Sid-WC121/visual-notebook/resolve/main/frontend/src/components/SchemaView.tsx
- Command line
-
hf download hf://spaces/Sid-WC121/visual-notebook/frontend/src/components/SchemaView.tsx
-
curl -L -o SchemaView.tsx https://huggingface.co/spaces/Sid-WC121/visual-notebook/resolve/main/frontend/src/components/SchemaView.tsx
6.05 kB
| import React, { useEffect, useState } from 'react'; | |
| import { useSchema } from '../api/hooks'; | |
| import { useSessionStore } from '../store/session'; | |
| import { formatNumber, typeBadgeClass, typeLabel } from '../lib/format'; | |
| import type { ColumnInfo } from '../api/types'; | |
| interface Props { | |
| compact?: boolean; | |
| } | |
| export function SchemaView({ compact = false }: Props) { | |
| const { columns, rowCount } = useSessionStore(); | |
| const { fetch, loading } = useSchema(); | |
| const [localColumns, setLocalColumns] = useState<ColumnInfo[]>([]); | |
| // Re-fetch schema when columns from the store change (e.g. after operations) | |
| useEffect(() => { | |
| loadSchema(); | |
| }, [columns.length, columns.map(c => c.name).join(',')]); | |
| const loadSchema = async () => { | |
| const data = await fetch(); | |
| if (data) { | |
| setLocalColumns(data.columns); | |
| useSessionStore.getState().setSchema(data.columns, data.row_count); | |
| } | |
| }; | |
| const displayColumns = localColumns.length > 0 ? localColumns : columns; | |
| // Only show numeric stat columns if any column has numeric stats | |
| const anyHasNumericStats = displayColumns.some( | |
| (col) => col.has_numeric_stats === true || col.column_type === 'numeric' | |
| ); | |
| const renderStatCell = (value: number | string | null | undefined, col: ColumnInfo) => { | |
| const isNumeric = col.has_numeric_stats === true || col.column_type === 'numeric'; | |
| // Non-numeric column: show subtle dash | |
| if (!isNumeric) { | |
| return <span className="stat-na">—</span>; | |
| } | |
| // Numeric column but value is null/undefined | |
| if (value == null) { | |
| return <span className="stat-na">N/A</span>; | |
| } | |
| // Numeric column with actual value: show the formatted value | |
| const numVal = typeof value === 'number' ? value : Number(value); | |
| if (isNaN(numVal)) { | |
| return <span className="stat-na">N/A</span>; | |
| } | |
| return <span className="text-gray-700 font-mono text-xs">{formatNumber(numVal)}</span>; | |
| }; | |
| if (loading && displayColumns.length === 0) { | |
| return <div className="text-center py-6 text-gray-400">Loading schema...</div>; | |
| } | |
| // Compact mode: card-based layout for sidebar (avoids wide table overflow) | |
| if (compact) { | |
| return ( | |
| <div className="space-y-1.5"> | |
| {displayColumns.map((col) => { | |
| const isNumeric = col.has_numeric_stats === true || col.column_type === 'numeric'; | |
| return ( | |
| <div | |
| key={col.name} | |
| className="bg-gray-50 rounded-lg px-3 py-2 border border-gray-100" | |
| > | |
| <div className="flex items-center justify-between mb-1"> | |
| <span className="text-xs font-medium text-gray-900 truncate max-w-[140px]" title={col.name}> | |
| {col.name} | |
| </span> | |
| <span className={`badge ${typeBadgeClass(col.column_type)} text-[10px]`}> | |
| {typeLabel(col.column_type)} | |
| </span> | |
| </div> | |
| <div className="flex items-center gap-3 text-[10px] text-gray-500"> | |
| <span>{formatNumber(col.count - col.null_count)} filled</span> | |
| <span>{formatNumber(col.unique_count)} unique</span> | |
| {col.null_count > 0 && ( | |
| <span className="text-amber-600">{formatNumber(col.null_count)} null</span> | |
| )} | |
| </div> | |
| {isNumeric && ( | |
| <div className="flex items-center gap-3 mt-1 text-[10px] text-gray-400 font-mono"> | |
| {col.min != null && <span>min {formatNumber(col.min)}</span>} | |
| {col.max != null && <span>max {formatNumber(col.max)}</span>} | |
| {col.mean != null && <span>avg {formatNumber(col.mean)}</span>} | |
| {col.std != null && <span>std {formatNumber(col.std)}</span>} | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| })} | |
| {!anyHasNumericStats && ( | |
| <p className="text-[10px] text-gray-400 italic mt-2"> | |
| No numeric columns — statistical summaries require numeric data. | |
| </p> | |
| )} | |
| </div> | |
| ); | |
| } | |
| // Full table mode for cell view | |
| return ( | |
| <div className="overflow-x-auto -mx-4 px-4"> | |
| <table className="data-table w-full text-xs"> | |
| <thead> | |
| <tr> | |
| <th>Column</th> | |
| <th>Type</th> | |
| <th>Non-Null</th> | |
| <th>Unique</th> | |
| {anyHasNumericStats && <th>Min</th>} | |
| {anyHasNumericStats && <th>Max</th>} | |
| {anyHasNumericStats && <th>Mean</th>} | |
| {anyHasNumericStats && <th>Std</th>} | |
| {anyHasNumericStats && <th>Median</th>} | |
| {anyHasNumericStats && <th>IQR</th>} | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {displayColumns.map((col) => { | |
| return ( | |
| <tr key={col.name}> | |
| <td className="font-medium text-gray-900 whitespace-normal break-all max-w-[120px]">{col.name}</td> | |
| <td> | |
| <span className={`badge ${typeBadgeClass(col.column_type)}`}> | |
| {typeLabel(col.column_type)} {col.column_type} | |
| </span> | |
| </td> | |
| <td>{formatNumber(col.count - col.null_count)}</td> | |
| <td>{formatNumber(col.unique_count)}</td> | |
| {anyHasNumericStats && <td>{renderStatCell(col.min, col)}</td>} | |
| {anyHasNumericStats && <td>{renderStatCell(col.max, col)}</td>} | |
| {anyHasNumericStats && <td>{renderStatCell(col.mean, col)}</td>} | |
| {anyHasNumericStats && <td>{renderStatCell(col.std, col)}</td>} | |
| {anyHasNumericStats && <td>{renderStatCell(col.median, col)}</td>} | |
| {anyHasNumericStats && <td>{renderStatCell(col.iqr, col)}</td>} | |
| </tr> | |
| ); | |
| })} | |
| </tbody> | |
| </table> | |
| {!anyHasNumericStats && ( | |
| <p className="text-xs text-gray-400 mt-3 italic">No numeric columns found — statistical summaries require numeric data.</p> | |
| )} | |
| </div> | |
| ); | |
| } | |