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