KQ-1656 · GHOST/ROWS

Ghost Rows

An empty table with three ways in — import a file, connect a source, or start from a template — whose templates preview themselves in place: pointing at one, or focusing it, re-heads the table with its columns and pours its sample rows in one by one, each dropping from 12px above on springs.snap a cascade apart and settling faint with dashed rules, and moving on drains them last row first on the exit ease. Choosing the template turns the ghosts solid in a wave down the rows while the ways in fold away on springs.glide; an import in progress is a progress row and a failed one explains the file problem in plain words. The options are one tab stop — arrows move across all three groups, Home and End jump, Enter chooses — and focus is carried to Undo and back so it never falls to the page.

KQ-1656 · GHOST/ROWS

Inventory

Fernworks · new table

Import a file
Connect a source
Start from a template
Inventory0 items
ItemQuantity
No items yetPick a way in to start.

empty · point at a template to preview it

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/ghost-rows

Props

ghostOpacity
number
default 0.42
How faint the poured preview rows are, 0.2 to 0.7.
layout
"split" | "stack"
default "split"
Ways in beside the table, or as three columns above it. Narrow containers always stack.
accent
string
default "var(--accent-bright)"
Focus and hover wash, the solid wave and the progress bar, any CSS colour.
templates
GhostTemplate[]
default defaultGhostTemplates
Templates a table can start from: { id, label, blurb, columns: { key, label, weight?, align?, mono? }[], rows }.
sources
GhostSource[]
default defaultGhostSources
Sources a table can be connected to: { id, label, detail, kind: "erp" | "sheet" | "feed" }.
value / defaultValue
string | null
default — / null
The chosen template id, controlled or initial; null while the table is empty.
onValueChange
(id: string | null) => void
default —
Fires from the press or key that chose a template, or from Undo.
preview
string | null
default null
A template previewed without pointing at it (a guided tour, an onboarding hint); pointing or focusing takes over.
onPreviewChange
(id: string | null) => void
default —
Fires when pointing or focusing changes the previewed template, with its id, or null when it leaves.
onTemplate
(template: GhostTemplate) => void
default —
A template was chosen.
onImport
(file: File) => void
default —
A file was picked or dropped on the table. Drive importState from here.
onConnect
(source: GhostSource) => void | Promise<unknown>
default —
A source was chosen. Return a promise to show connecting; a rejection says so on the option.
importState
GhostImportState | null
default null
An import in progress ({ phase: "reading", file, progress, rows }) shown as a progress row, or one that failed ({ phase: "failed", file, error }) shown as a row that explains.
accept
string
default ".csv,.tsv,.xlsx"
File types the picker offers.
ghostRows
number
default 6
How many sample rows pour in; the phone preview shows two.
title
string
default "Inventory"
The table's name and the surface's accessible name.
workspace
string
default "Fernworks"
The workspace the table belongs to.
height
number
default 540
Height of the surface in px; its body scrolls inside it.
disabled
boolean
default false
Nothing can be chosen, imported or connected.
className
string
default —
Classes for the outer surface.