gandi-ink

πŸ“‹ Components

The three list components and two helpers exported for building your own Gandi screens.

GandiBody is built from three presentation-only list components. They take props and render rows: no data fetching, no input handling. Use them to build a different layout, or to place one list in a pane of a larger dashboard. Types come from @kud/gandi.

All three share the same shape of props:

PropTypeDefaultPurpose
selectednumber-1Index of the highlighted row. -1 highlights nothing.
rowsnumberrequiredHow many rows the list may use. Longer lists scroll.
emptyTextstringper componentShown when the list is empty.

The parent owns the selection index; the lists only render it, keeping the selected row in view.

DomainList

import { DomainList } from "@kud/gandi-ink"

<DomainList domains={domains} selected={2} rows={10} />

Takes domains: Domain[]. Each row shows the name, the time left until the registry expiry as a countdown (41d, or expired 3d ago), and an autorenew marker (↻ auto or Β· manual). The countdown turns amber at 30 days or fewer and red at 7 or fewer. Default emptyText is "No domains".

DnsList

Takes records: DnsRecord[]. One row per rrset: name, type, TTL in seconds and the values joined with commas. A record is the unit you act on, so values are not split across rows. Default emptyText is "No DNS records".

RedirectList

Takes redirects: WebRedir[]. Each row shows the host, the redirect type and the target URL (β€” when there is none). The host is rendered exactly as the API returns it, already fully qualified, so do not append the domain yourself. Default emptyText is "No web redirects".

Helpers

windowSlice

windowSlice<T>(items: T[], selected: number, rows: number): { items: T[]; offset: number }

Returns the visible slice of items that keeps selected in view, centred when it can be, plus the offset of the first visible item. It is pure arithmetic with no state. Returns an empty slice when rows is zero or negative or items is empty, and the whole list when it already fits.

fit

fit(text: string, width: number): string

Pads or truncates text to exactly width characters, so fixed-width columns line up. Text that is too long is cut and ends with …. A width of zero or less returns an empty string.

Both are exported so a host composing its own lists scrolls and pads identically to the built-in ones.