import { For, createSignal } from 'solid-js' import { JsonTree } from '@tanstack/devtools-ui' import { useTableDevtoolsContext } from '../TableContextProvider' import { useTableStore } from '../useTableStore' import { useStyles } from '../styles/use-styles' import { ResizableSplit } from './ResizableSplit' export function StatePanel() { const styles = useStyles() const { table } = useTableDevtoolsContext() const [initialStateCopied, setInitialStateCopied] = createSignal(false) const [stateCopied, setStateCopied] = createSignal(false) const [pasteError, setPasteError] = createSignal(null) const tableInstance = table() const tableState = useTableStore( tableInstance ? tableInstance.store : undefined, (state) => state, ) const getInitialState = (): unknown => { tableState?.() if (!tableInstance) { return { message: 'No table instance is connected. Pass a table instance to TableDevtoolsPanel.', } } return tableInstance.initialState as unknown } const getState = (): unknown => { tableState?.() if (!tableInstance) { return undefined } return tableInstance.store.state as unknown } const copyToClipboard = async ( value: unknown, setCopied: (v: boolean) => void, ) => { try { await navigator.clipboard.writeText(JSON.stringify(value, null, 2)) setCopied(true) setTimeout(() => setCopied(false), 1500) } catch { // Clipboard API may fail in some contexts } } const handlePaste = async () => { if (!tableInstance) return setPasteError(null) try { const text = await navigator.clipboard.readText() const parsed = JSON.parse(text) if ( typeof parsed !== 'object' || parsed === null || Array.isArray(parsed) ) { setPasteError('Invalid state: must be a JSON object') return } tableInstance.baseStore.setState(() => ({ ...tableInstance.store.state, ...parsed, })) } catch (e) { setPasteError( e instanceof Error ? e.message : 'Failed to parse clipboard', ) } } const handleReset = () => { tableInstance?.reset() } const getStateSummaries = (): Array<{ key: string; summary: string }> => { const state = getState() as Record | undefined if (!state || typeof state !== 'object') return [] const summaries: Array<{ key: string; summary: string }> = [] if (Array.isArray(state.sorting) && state.sorting.length > 0) { const parts = (state.sorting as Array<{ id: string; desc: boolean }>).map( (s) => `${s.id} (${s.desc ? 'desc' : 'asc'})`, ) summaries.push({ key: 'sorting', summary: `${state.sorting.length} column(s) sorted: ${parts.join(', ')}`, }) } if (Array.isArray(state.columnFilters) && state.columnFilters.length > 0) { const parts = ( state.columnFilters as Array<{ id: string; value: unknown }> ).map((f) => `${f.id}=${JSON.stringify(f.value)}`) summaries.push({ key: 'columnFilters', summary: `${state.columnFilters.length} filter(s): ${parts.join(', ')}`, }) } if ( state.rowSelection && typeof state.rowSelection === 'object' && !Array.isArray(state.rowSelection) ) { const count = Object.keys( state.rowSelection as Record, ).filter((k) => (state.rowSelection as Record)[k]).length summaries.push({ key: 'rowSelection', summary: `${count} row(s) selected`, }) } if ( state.pagination && typeof state.pagination === 'object' && !Array.isArray(state.pagination) ) { const p = state.pagination as { pageIndex?: number; pageSize?: number } summaries.push({ key: 'pagination', summary: `Page ${(p.pageIndex ?? 0) + 1}, size ${p.pageSize ?? 10}`, }) } if (Array.isArray(state.grouping) && state.grouping.length > 0) { summaries.push({ key: 'grouping', summary: `Grouped by: ${(state.grouping as Array).join(', ')}`, }) } if ( state.columnPinning && typeof state.columnPinning === 'object' && !Array.isArray(state.columnPinning) ) { const p = state.columnPinning as { left?: Array right?: Array } const left = p.left?.length ?? 0 const right = p.right?.length ?? 0 summaries.push({ key: 'columnPinning', summary: `${left} left, ${right} right pinned`, }) } if (state.expanded !== undefined && state.expanded !== null) { if (state.expanded === true) { summaries.push({ key: 'expanded', summary: 'All expanded' }) } else if ( typeof state.expanded === 'object' && !Array.isArray(state.expanded) ) { const count = Object.keys( state.expanded as Record, ).filter((k) => (state.expanded as Record)[k]).length summaries.push({ key: 'expanded', summary: `${count} row(s) expanded`, }) } } if ('globalFilter' in state) { const val = state.globalFilter const str = val === undefined || val === null ? 'Not set' : typeof val === 'string' ? val ? `"${val}"` : '(empty)' : JSON.stringify(val) summaries.push({ key: 'globalFilter', summary: str }) } if ( state.columnVisibility && typeof state.columnVisibility === 'object' && !Array.isArray(state.columnVisibility) ) { const hidden = Object.entries( state.columnVisibility as Record, ).filter(([, v]) => v === false).length if (hidden > 0) { summaries.push({ key: 'columnVisibility', summary: `${hidden} column(s) hidden`, }) } } if (Array.isArray(state.columnOrder) && state.columnOrder.length > 0) { summaries.push({ key: 'columnOrder', summary: `Custom order (${state.columnOrder.length} columns)`, }) } return summaries } const stateSummaries = getStateSummaries() return (
initialState
} right={ <>
State
{pasteError() && (
{pasteError()}
)} {stateSummaries.length > 0 && (
State Summary {(item) => (
{item.key}: {item.summary}
)}
)} } />
) }