Skip to content
kerfjsGitHub

ViewportRail

A thin map of a document, showing where each person is looking.

SourcePackage @kerf-js/veneer

Preview

tsx
import { ViewportRail } from '@kerf-js/veneer'import type { Peer, ViewportPeer } from '@kerf-js/veneer'export function Document() {  return (  <RailHeight>    <ViewportRail      peers={VIEWPORT_PEERS}      self={{ top: 0.02, height: 0.3 }}    />  </RailHeight>  )}const PEOPLE = [  {    id: 'mara',    name: 'Mara Lindqvist',    email: 'mara@kerf.dev',    role: 'Owner',    team: 'Platform',    initials: 'ML',  },  {    id: 'jun',    name: 'Jun Tanaka',    email: 'jun@kerf.dev',    role: 'Admin',    team: 'Platform',    initials: 'JT',  },  {    id: 'priya',    name: 'Priya Raman',    email: 'priya@kerf.dev',    role: 'Member',    team: 'Web',    initials: 'PR',  },  {    id: 'tomas',    name: 'Tomás Oliveira',    email: 'tomas@kerf.dev',    role: 'Member',    team: 'Web',    initials: 'TO',  },  {    id: 'lena',    name: 'Lena Fischer',    email: 'lena@kerf.dev',    role: 'Member',    team: 'Data',    initials: 'LF',  },  {    id: 'noor',    name: 'Noor Haddad',    email: 'noor@kerf.dev',    role: 'Viewer',    team: 'Data',    initials: 'NH',  },  {    id: 'sam',    name: 'Sam Okafor',    email: 'sam@kerf.dev',    role: 'Member',    team: 'Mobile',    initials: 'SO',  },  {    id: 'ines',    name: 'Inés Vidal',    email: 'ines@kerf.dev',    role: 'Viewer',    team: 'Mobile',    initials: 'IV',  },]const PEERS: Peer[] = PEOPLE.map((p, i) => ({  id: p.id,  name: p.name,  color: i + 1,}))const [  MARA,  JUN,  PRIYA,  TOMAS,  LENA,  NOOR,  SAM,  INES,] = PEERS as [  Peer,  Peer,  Peer,  Peer,  Peer,  Peer,  Peer,  Peer,]const VIEWPORT_PEERS: ViewportPeer[] = [  { ...JUN, top: 0.12, height: 0.3 },  { ...LENA, top: 0.55, height: 0.3 },]const RailHeight = ({  children,}: {  children: unknown}) => (  <div style="display:grid;block-size:180px">    {children}  </div>)

Installation

npm install @kerf-js/veneer

Usage

tsx
import { ViewportRail } from '@kerf-js/veneer'<RailHeight>  <ViewportRail    peers={VIEWPORT_PEERS}    self={{ top: 0.02, height: 0.3 }}  /></RailHeight>

Jump to someone

onJump turns each box into a button.

tsx
import { state } from '@kerf-js/core'import { Row, Text, ViewportRail } from '@kerf-js/veneer'import type { Peer, ViewportPeer } from '@kerf-js/veneer'export function Jump() {  const seen = state(    'Press a box to jump to that person.',  )  return (    <Row gap="group">      <RailHeight>        <ViewportRail          peers={VIEWPORT_PEERS}          self={{ top: 0.02, height: 0.3 }}          onJump={(p) =>            (seen.value = `Jumped to ${p.name}.`)          }        />      </RailHeight>      <Text subtle>{seen.value}</Text>    </Row>  )}const PEOPLE = [  {    id: 'mara',    name: 'Mara Lindqvist',    email: 'mara@kerf.dev',    role: 'Owner',    team: 'Platform',    initials: 'ML',  },  {    id: 'jun',    name: 'Jun Tanaka',    email: 'jun@kerf.dev',    role: 'Admin',    team: 'Platform',    initials: 'JT',  },  {    id: 'priya',    name: 'Priya Raman',    email: 'priya@kerf.dev',    role: 'Member',    team: 'Web',    initials: 'PR',  },  {    id: 'tomas',    name: 'Tomás Oliveira',    email: 'tomas@kerf.dev',    role: 'Member',    team: 'Web',    initials: 'TO',  },  {    id: 'lena',    name: 'Lena Fischer',    email: 'lena@kerf.dev',    role: 'Member',    team: 'Data',    initials: 'LF',  },  {    id: 'noor',    name: 'Noor Haddad',    email: 'noor@kerf.dev',    role: 'Viewer',    team: 'Data',    initials: 'NH',  },  {    id: 'sam',    name: 'Sam Okafor',    email: 'sam@kerf.dev',    role: 'Member',    team: 'Mobile',    initials: 'SO',  },  {    id: 'ines',    name: 'Inés Vidal',    email: 'ines@kerf.dev',    role: 'Viewer',    team: 'Mobile',    initials: 'IV',  },]const PEERS: Peer[] = PEOPLE.map((p, i) => ({  id: p.id,  name: p.name,  color: i + 1,}))const [  MARA,  JUN,  PRIYA,  TOMAS,  LENA,  NOOR,  SAM,  INES,] = PEERS as [  Peer,  Peer,  Peer,  Peer,  Peer,  Peer,  Peer,  Peer,]const VIEWPORT_PEERS: ViewportPeer[] = [  { ...JUN, top: 0.12, height: 0.3 },  { ...LENA, top: 0.55, height: 0.3 },]const RailHeight = ({  children,}: {  children: unknown}) => (  <div style="display:grid;block-size:180px">    {children}  </div>)

Accessibility

  • This has no shortcuts of its own: controls inside it use Tab, Enter and Space as usual, and veneer's parts keep their roles and labels.

API Reference

PropTypeDefaultDescription
peersLive<ViewportPeer[]>—People and their viewports, from 0 to 1 of the document.
self{ top: number; height: number; }—Your own viewport.
labelstring'Where people are looking'Names the rail.
EventTypeDefaultDescription
onJump((p: ViewportPeer) => void)—Makes each box a button.

RelatedSection peersFollowing