Data Display


Card

A surface container with optional header, body, and footer slots.

PropTypeDefaultDescription
variant?'elevated' | 'outlined' | 'filled''elevated'Visual style
header?React.ReactNodeContent rendered in the header slot
footer?React.ReactNodeContent rendered in the footer slot
children?React.ReactNodeCard body content
size?'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Controls padding scale
accessibilityLabel?stringAccessibility label
import { Card, Text, Stack } from '@objectifthunes/limestone-sdk';

<Card variant="elevated" size="md">
  <Text variant="heading">Profile</Text>
  <Text variant="body" color="mutedForeground">Manage your account settings.</Text>
</Card>

Avatar

A user avatar that renders a remote image or falls back to initials derived from the name prop.

PropTypeDefaultDescription
src?stringRemote image URL
name?stringFull name; initials are extracted when image is absent or fails to load
size?'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Avatar diameter
variant?'circle' | 'rounded''circle'Shape of the avatar
fallbackColor?ColorPropBackground color for the initials fallback
accessibilityLabel?stringAccessibility label
import { Avatar, Stack } from '@objectifthunes/limestone-sdk';

// Image with fallback to initials
<Avatar src="https://example.com/user.jpg" name="Alice Martin" size="lg" />

// Initials only
<Avatar name="Bob Chen" size="md" variant="rounded" fallbackColor="primary" />

Tag

A compact label used to categorise or annotate content. Supports an optional dismiss button.

PropTypeDefaultDescription
labelstringTag text
variant?'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'error''default'Color scheme
size?'xs' | 'sm' | 'md' | 'lg' | 'xl''sm'Height and font size
icon?stringIcon name rendered to the left of the label
onDismiss?() => voidWhen provided, a dismiss button is rendered
accessibilityLabel?stringAccessibility label
import { Tag, Stack } from '@objectifthunes/limestone-sdk';

<Stack direction="horizontal" gap="sm">
  <Tag label="React Native" variant="primary" />
  <Tag label="TypeScript" variant="secondary" />
  <Tag label="Bug" variant="error" onDismiss={() => removeTag('bug')} />
</Stack>

List

A data list backed by React Native FlatList or SectionList. Supports pull-to-refresh, infinite scroll, and sticky section headers.

PropTypeDefaultDescription
data?T[]Flat array of items (use with renderItem + keyExtractor)
sections?ListSection<T>[]Sectioned data; each section has title and data
renderItem(item: T, index: number) => React.ReactNodeRow renderer
keyExtractor(item: T, index: number) => stringUnique key per row
emptyComponent?React.ReactNodeRendered when the list is empty
refreshing?booleanfalseControls pull-to-refresh spinner
onRefresh?() => voidCalled when the user pulls to refresh
onEndReached?() => voidCalled when the list scrolls near the end
onEndReachedThreshold?numberDistance from end that triggers onEndReached
stickyHeaders?booleanfalsePin section headers while scrolling
separatorColor?ColorPropRow separator line color
import { List, Text, Box } from '@objectifthunes/limestone-sdk';

interface User { id: string; name: string; }

<List
  data={users}
  keyExtractor={(u) => u.id}
  renderItem={(u) => (
    <Box py="sm" px="md">
      <Text variant="body">{u.name}</Text>
    </Box>
  )}
  refreshing={isRefreshing}
  onRefresh={reload}
  onEndReached={loadMore}
/>

Accordion

A vertically stacked list of collapsible sections. Supports single or multiple open panels simultaneously.

PropTypeDefaultDescription
itemsAccordionItem[]Each item: { id, title, content, disabled? }
defaultOpenIds?string[][]IDs of panels open on first render
allowMultiple?booleanfalseWhen true, multiple panels can be open at once
variant?'default' | 'separated''default'separated adds gaps between panels
accessibilityLabel?stringAccessibility label for the accordion container
import { Accordion } from '@objectifthunes/limestone-sdk';
import { Text } from '@objectifthunes/limestone-sdk';

const items = [
  { id: 'faq-1', title: 'What is limestone-sdk?', content: <Text variant="body">A mobile toolkit for React Native + Expo.</Text> },
  { id: 'faq-2', title: 'Does it support dark mode?', content: <Text variant="body">Yes — define a darkTheme in defineConfig.</Text> },
];

<Accordion items={items} allowMultiple defaultOpenIds={['faq-1']} />

EmptyState

A centred placeholder shown when a list or page has no content. Supports a title, message, icon, and a single action button.

PropTypeDefaultDescription
titlestringPrimary heading
message?stringExplanatory text below the heading
icon?stringIcon name rendered above the heading
action?{ label: string; onPress: () => void }Optional call-to-action button
size?'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Controls icon size and padding
accessibilityLabel?stringAccessibility label
import { EmptyState } from '@objectifthunes/limestone-sdk';

<EmptyState
  icon="inbox"
  title="No messages"
  message="When you receive a message it will appear here."
  action={{ label: 'Compose', onPress: openComposer }}
/>

StepIndicator

A horizontal progress indicator that visualises the steps of a multi-step flow.

PropTypeDefaultDescription
stepsStep[]Each step: { label? }
currentStepnumberZero-based index of the active step
variant?'default' | 'numbered''default'numbered shows the step number inside the circle
size?'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Circle diameter
accessibilityLabel?stringAccessibility label

Each step resolves to a StepStatus of completed, active, or upcoming.

import { StepIndicator } from '@objectifthunes/limestone-sdk';

const steps = [
  { label: 'Account' },
  { label: 'Profile' },
  { label: 'Payment' },
  { label: 'Review' },
];

<StepIndicator steps={steps} currentStep={1} variant="numbered" size="md" />

Timeline

A vertical list of timestamped events, each with an optional icon and severity variant.

PropTypeDefaultDescription
eventsTimelineEvent[]Ordered array of events
size?'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Node size
accessibilityLabel?stringAccessibility label

Each TimelineEvent has: id, title, description?, timestamp?, icon?, and variant? (default | success | warning | error).

import { Timeline } from '@objectifthunes/limestone-sdk';

const events = [
  {
    id: 'ev-1',
    title: 'Order placed',
    description: 'Your order #1042 has been received.',
    timestamp: '10:00 AM',
    variant: 'success',
  },
  {
    id: 'ev-2',
    title: 'Payment failed',
    description: 'Card ending in 4242 was declined.',
    timestamp: '10:05 AM',
    variant: 'error',
  },
  {
    id: 'ev-3',
    title: 'Retrying payment',
    timestamp: '10:06 AM',
    variant: 'warning',
  },
];

<Timeline events={events} size="md" />

---

## ListItem

A single row for use inside a `List` or standalone. Renders a leading element (icon or avatar), a title+subtitle text block, and a trailing element (icon, badge, or custom content).

| Prop | Type | Default | Description |
|---|---|---|---|
| `title` | `string` || Primary row text |
| `subtitle?` | `string` || Secondary text below the title |
| `leading?` | `React.ReactNode` || Content on the left (avatar, icon, image) |
| `trailing?` | `React.ReactNode` || Content on the right (icon, badge, text) |
| `onPress?` | `() => void` || Makes the row pressable |
| `disabled?` | `boolean` | `false` | Disables press interaction |
| `showChevron?` | `boolean` | `false` | Render a right-pointing chevron in the trailing slot |
| `separator?` | `boolean` | `true` | Render a bottom border |
| `size?` | `'sm' \| 'md' \| 'lg'` | `'md'` | Row height and font size |
| `accessibilityLabel?` | `string` || Accessibility label |
| `testID?` | `string` || Test identifier |

```typescript
import { ListItem, Avatar, Icon, Badge } from '@objectifthunes/limestone-sdk';

// Navigation row
<ListItem
  title="Edit profile"
  subtitle="Update your name, bio, and photo"
  leading={<Icon name="user" size="md" color="mutedForeground" />}
  showChevron
  onPress={() => router.push('/profile/edit')}
/>

// Unread message row
<ListItem
  title="Alice Martin"
  subtitle="Hey, are you free tomorrow?"
  leading={<Avatar name="Alice Martin" size="sm" />}
  trailing={<Badge count={2} />}
  onPress={() => openConversation('alice')}
/>

RatingStars

A star rating display and optional interactive input. In read-only mode it renders filled/half/empty stars; in interactive mode the user can tap to set a rating.

PropTypeDefaultDescription
valuenumberCurrent rating (0–max)
max?number5Total number of stars
onValueChange?(v: number) => voidWhen provided, the component is interactive
allowHalf?booleanfalseAllow 0.5-increment ratings
size?'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Star size
color?ColorProp'warning'Filled star color
emptyColor?ColorProp'muted'Empty star color
showValue?booleanfalseRender the numeric value next to the stars
accessibilityLabel?stringAccessibility label
testID?stringTest identifier
import { RatingStars } from '@objectifthunes/limestone-sdk';
import { useState } from 'react';

// Read-only display
<RatingStars value={4.5} allowHalf showValue />

// Interactive rating input
function ReviewForm() {
  const [rating, setRating] = useState(0);

  return (
    <RatingStars
      value={rating}
      onValueChange={setRating}
      size="lg"
    />
  );
}

PriceDisplay

A formatted price block with optional original price (strike-through), discount badge, and per-period label. Resolves currency formatting from the locale.

PropTypeDefaultDescription
amountnumberCurrent price
currency?string'USD'ISO 4217 currency code
locale?stringdevice localeBCP 47 locale string for formatting
originalAmount?numberOriginal price rendered with a strike-through when provided
period?stringBilling period label, e.g. '/mo' or 'per year'
size?'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Font size scale
showDiscount?booleanfalseCompute and display the discount percentage badge
accessibilityLabel?stringAccessibility label
testID?stringTest identifier
import { PriceDisplay } from '@objectifthunes/limestone-sdk';

// Sale price with discount badge
<PriceDisplay
  amount={7.99}
  originalAmount={14.99}
  currency="USD"
  period="/mo"
  showDiscount
/>

// Annual plan
<PriceDisplay
  amount={79.99}
  currency="EUR"
  period="per year"
  size="lg"
/>

KeyValueRow

A single-line row displaying a label on the left and a value on the right. Commonly used in detail screens, receipts, and settings summaries.

PropTypeDefaultDescription
labelstringLeft-side descriptor
valuestring | React.ReactNodeRight-side content
labelVariant?'body' | 'caption' | 'label''body'Typography variant for the label
valueVariant?'body' | 'caption' | 'label''body'Typography variant for the value
labelColor?ColorProp'mutedForeground'Label text color
valueColor?ColorProp'foreground'Value text color
separator?booleantrueRender a bottom border
copyable?booleanfalseTap-to-copy the value string
accessibilityLabel?stringAccessibility label
testID?stringTest identifier
import { KeyValueRow, Stack } from '@objectifthunes/limestone-sdk';

function OrderSummary({ order }: { order: Order }) {
  return (
    <Stack>
      <KeyValueRow label="Order ID"   value={order.id}     copyable />
      <KeyValueRow label="Status"     value={order.status} />
      <KeyValueRow label="Total"      value={`$${order.total.toFixed(2)}`} />
      <KeyValueRow label="Placed on"  value={order.date}   separator={false} />
    </Stack>
  );
}

Chart

A data visualisation component supporting line, bar, and pie chart types. Backed by react-native-svg — no WebView required.

PropTypeDefaultDescription
type'line' | 'bar' | 'pie'Chart variant
dataChartDataset[]One or more datasets; each has label, values (number[]), and optional color
labels?string[]X-axis labels for line/bar charts
height?number220Chart height in pixels
showGrid?booleantrueRender background grid lines (line/bar only)
showLegend?booleantrueRender a colour legend below the chart
showValues?booleanfalseRender the numeric value on each bar or data point
animated?booleantrueAnimate on mount
onDataPointPress?(dataset: number, index: number, value: number) => voidCalled when a bar or data point is tapped
accessibilityLabel?stringAccessibility label
testID?stringTest identifier
import { Chart } from '@objectifthunes/limestone-sdk';

const revenueData = [
  { label: 'Revenue', values: [4200, 5800, 3900, 7100, 6400, 8200], color: '#7c3aed' },
];
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'];

// Line chart
<Chart type="line" data={revenueData} labels={months} showGrid showLegend />

// Bar chart with tap handler
<Chart
  type="bar"
  data={revenueData}
  labels={months}
  showValues
  onDataPointPress={(_, index, value) => showDetails(months[index], value)}
/>

// Pie chart
const spendData = [
  { label: 'Housing', values: [1800], color: '#7c3aed' },
  { label: 'Food',    values: [620],  color: '#f97316' },
  { label: 'Travel',  values: [430],  color: '#06b6d4' },
  { label: 'Other',   values: [290],  color: '#6366f1' },
];
<Chart type="pie" data={spendData} showLegend />

ActivityRing

A circular progress ring modelled after Apple Watch Activity rings. Supports one to three concentric rings, each representing a separate metric.

PropTypeDefaultDescription
ringsActivityRingData[]1–3 rings; each has value (0–100), color, and optional label
size?number120Outer diameter in pixels
strokeWidth?number12Ring stroke width in pixels
trackColor?ColorProp'muted'Unfilled track color
animated?booleantrueAnimate rings on mount
showLabels?booleanfalseRender value labels alongside each ring
centerContent?React.ReactNodeContent rendered in the centre of the ring stack
accessibilityLabel?stringAccessibility label
testID?stringTest identifier
import { ActivityRing } from '@objectifthunes/limestone-sdk';

<ActivityRing
  rings={[
    { value: 78, color: '#ef4444', label: 'Move' },
    { value: 55, color: '#22c55e', label: 'Exercise' },
    { value: 91, color: '#3b82f6', label: 'Stand' },
  ]}
  size={140}
  strokeWidth={14}
  showLabels
/>

CountdownTimer

A live countdown that ticks down to a target date/time. Renders days, hours, minutes, and seconds with configurable granularity and an animated flip or fade transition between digit changes.

PropTypeDefaultDescription
targetDate | numberTarget timestamp (Date or Unix ms)
onComplete?() => voidCalled once when the countdown reaches zero
format?Array<'days' | 'hours' | 'minutes' | 'seconds'>all fourWhich units to display
variant?'flip' | 'fade' | 'plain''plain'Digit transition animation
size?'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Digit block size
labelPosition?'above' | 'below' | 'none''below'Where to render unit labels
separatorChar?string':'Character rendered between units
accessibilityLabel?stringAccessibility label
testID?stringTest identifier
import { CountdownTimer } from '@objectifthunes/limestone-sdk';

// Flash sale ending in 2 hours
const saleEnd = new Date(Date.now() + 2 * 60 * 60 * 1000);

<CountdownTimer
  target={saleEnd}
  format={['hours', 'minutes', 'seconds']}
  variant="flip"
  size="lg"
  onComplete={() => toast.info('Sale has ended')}
/>

CreditCardDisplay

A styled credit card visualisation showing the masked card number, cardholder name, expiry, and network logo. Supports a flip animation to reveal the CVV on the back face.

PropTypeDefaultDescription
cardNumberstringFull or masked card number; last 4 digits are always shown
cardholderNamestringName printed on the card
expirystringExpiry in MM/YY format
network?'visa' | 'mastercard' | 'amex' | 'discover' | 'unknown''unknown'Card network for logo rendering
cvv?stringCVV shown on back face when flipped is true
flipped?booleanfalseShow the card back face
onFlip?() => voidCalled when the card is tapped (use to toggle flipped)
gradient?string[]theme primary gradientBackground gradient colours
width?number320Card width in pixels (height auto-calculated at 63.5% ratio)
accessibilityLabel?stringAccessibility label
testID?stringTest identifier
import { CreditCardDisplay } from '@objectifthunes/limestone-sdk';
import { useState } from 'react';

function PaymentMethod({ card }: { card: Card }) {
  const [flipped, setFlipped] = useState(false);

  return (
    <CreditCardDisplay
      cardNumber={card.number}
      cardholderName={card.holderName}
      expiry={card.expiry}
      network={card.network}
      cvv={card.cvv}
      flipped={flipped}
      onFlip={() => setFlipped((f) => !f)}
    />
  );
}

DataTable

A scrollable table with sortable columns, optional row selection, and pagination controls. Designed for dense data views such as transaction histories and admin dashboards.

PropTypeDefaultDescription
columnsDataColumn<T>[]Column definitions; each has key, header, width?, sortable?, renderCell?
dataT[]Array of row data objects
keyExtractor(row: T) => stringUnique key per row
selectable?booleanfalseShow row checkboxes and a select-all header checkbox
selectedKeys?string[]Controlled set of selected row keys
onSelectionChange?(keys: string[]) => voidCalled when selection changes
sortKey?stringCurrently sorted column key
sortDirection?'asc' | 'desc''asc'Sort direction
onSort?(key: string, direction: 'asc' | 'desc') => voidCalled when a sortable column header is tapped
pageSize?numberWhen set, renders pagination controls
page?number0Current page index (zero-based)
totalRows?numberTotal row count for pagination display
onPageChange?(page: number) => voidCalled when the user navigates to a new page
emptyComponent?React.ReactNodeRendered when data is empty
accessibilityLabel?stringAccessibility label
testID?stringTest identifier
import { DataTable } from '@objectifthunes/limestone-sdk';
import type { DataColumn } from '@objectifthunes/limestone-sdk';
import { useState } from 'react';

interface Transaction {
  id: string;
  date: string;
  description: string;
  amount: number;
  status: 'completed' | 'pending' | 'failed';
}

const columns: DataColumn<Transaction>[] = [
  { key: 'date',        header: 'Date',        width: 100, sortable: true },
  { key: 'description', header: 'Description', sortable: false },
  { key: 'amount',      header: 'Amount',      width: 90,  sortable: true,
    renderCell: (row) => <Text color={row.amount < 0 ? 'destructive' : 'success'}>{`$${Math.abs(row.amount).toFixed(2)}`}</Text> },
  { key: 'status',      header: 'Status',      width: 100 },
];

function TransactionsTable({ transactions }: { transactions: Transaction[] }) {
  const [sort, setSort] = useState<{ key: string; dir: 'asc' | 'desc' }>({ key: 'date', dir: 'desc' });

  return (
    <DataTable
      columns={columns}
      data={transactions}
      keyExtractor={(t) => t.id}
      sortKey={sort.key}
      sortDirection={sort.dir}
      onSort={(key, dir) => setSort({ key, dir })}
      pageSize={20}
      selectable
    />
  );
}