# rn-awesome-stats URL: /docs/native/docyrus/awesome-stats KPI stat cards with number formatting, period comparison, SVG mini charts, pressable cards, card menus, reordering and flex / grid / tabs layouts. ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/rn-awesome-stats ``` **Dependencies:** - [@shopify/flash-list](https://www.npmjs.com/package/@shopify/flash-list) - [react-native-svg](https://www.npmjs.com/package/react-native-svg) ## Usage ```tsx import { AwesomeStats, type AwesomeStatItem } from '@/components/docyrus-native/awesome-stats'; const items: AwesomeStatItem[] = [ { id: 'revenue', title: 'Revenue', value: 48500, icon: 'fal dollar-sign', color: 'emerald-500', format: { style: 'currency', currency: 'USD', notation: 'compact' }, comparison: { previousValue: 42100, period: 'last-month' }, miniChart: { type: 'area', data: [32, 35, 31, 38, 42, 48] }, }, { id: 'users', title: 'Users', value: 1250, comparison: { previousValue: 1100, period: 'last-week' }, }, ]; ``` ### Layouts ```tsx {/* horizontal FlashList */} ``` ### Pressable cards `item.onClick` takes precedence over the global `onItemClick`. `clickTarget="title"` makes only the title pressable (underlined). ```tsx ## API Reference ### AwesomeStatsProps | Prop | Type | Default | Description | |------|------|---------|-------------| | `items` | `AwesomeStatItem[]` | - | Stat items (required) | | `layout` | `AwesomeStatsLayout` | - | Layout configuration (required) | | `cardVariant` | `'default' \| 'awesome'` | `'default'` | Plain card or `AwesomeCard` chrome | | `awesomeCardProps` | `AwesomeStatsAwesomeCardProps` | - | AwesomeCard options when `cardVariant="awesome"` | | `sortable` | `boolean` | `false` | Reorder via long-press → Move left / Move right | | `onItemsOrderChange` | `(items: AwesomeStatItem[]) => void` | - | Called with the reordered items | | `getCardMenuItems` | `(item: AwesomeStatItem) => AwesomeStatsCardMenuItem[]` | - | Menu items per card (`item.menuItems` wins) | | `onItemClick` | `(item: AwesomeStatItem) => void` | - | Global press handler (`item.onClick` wins) | | `clickTarget` | `'title' \| 'card'` | `'card'` | Which part of the card is pressable | | `ref` | `Ref` | - | Ref to the root View | | `className` | `string` | - | Classes for the root View | ### AwesomeStatsLayout **Flex** — `{ type: 'flex' }` | Field | Type | Default | Description | |-------|------|---------|-------------| | `cardWidth` | `number \| string` | - | Card width: dp number or `'px'` / `'%'` (of the container) / `'rem'` | | `behavior` | `'wrap' \| 'scroll'` | - | Wrap into rows, or scroll horizontally (FlashList) | | `gap` | `number \| string` | `16` | Gap between cards | **Grid** — `{ type: 'grid' }` | Field | Type | Default | Description | |-------|------|---------|-------------| | `columns` | `number` | - | Number of columns | | `maxCardWidth` | `number \| string` | - | Max card width | | `gap` | `number \| string` | `16` | Gap between cards | **Tabs** — `{ type: 'tabs' }` | Field | Type | Default | Description | |-------|------|---------|-------------| | `defaultTabId` | `string` | first item | Initially selected tab | **Scroll (deprecated)** — `{ type: 'scroll', cardWidth?, behavior?, gap? }`, an alias of `flex` (`behavior` defaults to `'scroll'`, `cardWidth` to `240`). ### AwesomeStatItem | Field | Type | Default | Description | |-------|------|---------|-------------| | `id` | `string` | - | Unique identifier | | `eyebrow` | `ReactNode` | - | Small uppercase caption above the title | | `title` | `ReactNode` | - | Title | | `subtitle` | `ReactNode` | - | Text below the title | | `icon` | `ReactNode \| string` | - | DocyrusIcon string or node, shown in an accent-tinted box | | `color` | `string` | theme primary | Accent colour: Tailwind name (`'indigo-500'`, `'blue'`), hex, rgb or hsl | | `value` | `number` | - | Value | | `format` | `AwesomeStatValueFormat` | - | Number formatting | | `unitLabel` | `ReactNode` | - | Unit shown after the value | | `comparison` | `AwesomeStatComparison` | - | Comparison with a previous period | | `miniChart` | `AwesomeStatMiniChart` | - | Mini chart | | `menuItems` | `AwesomeStatsCardMenuItem[]` | - | Card menu items | | `onClick` | `(item: AwesomeStatItem) => void` | - | Per-item press handler | ### AwesomeStatValueFormat | Field | Type | Default | Description | |-------|------|---------|-------------| | `locale` | `string` | device | Intl locale | | `style` | `'number' \| 'currency' \| 'percent'` | `'number'` | Format style | | `currency` | `string` | `'USD'` | Currency code | | `currencyDisplay` | `'symbol' \| 'code' \| 'name' \| 'narrowSymbol'` | `'symbol'` | Currency display | | `notation` | `'standard' \| 'scientific' \| 'engineering' \| 'compact'` | - | Notation | | `minimumFractionDigits` | `number` | - | Minimum fraction digits | | `maximumFractionDigits` | `number` | - | Maximum fraction digits | | `signDisplay` | `Intl.NumberFormatOptions['signDisplay']` | - | Sign display | | `percentScale` | `'whole' \| 'fraction'` | `'whole'` | `'whole'` treats `12` as 12% | ### AwesomeStatComparison | Field | Type | Default | Description | |-------|------|---------|-------------| | `previousValue` | `number` | - | Previous period value | | `period` | `'yesterday' \| 'last-week' \| 'last-month' \| 'last-quarter' \| 'last-year'` | - | Comparison period | | `positiveDirection` | `'up' \| 'down'` | `'up'` | Which direction is good | ### AwesomeStatMiniChart | Field | Type | Default | Description | |-------|------|---------|-------------| | `type` | `'sparkline' \| 'bar' \| 'area'` | - | Chart type (SVG paths) | | `data` | `number[] \| Array>` | - | Values or rows | | `dataKey` | `string` | `'value'` | Row key to plot | | `position` | `'right' \| 'bottom'` | `'right'` | Beside the value (96×80) or full-width below (96 high) | ### AwesomeStatsCardMenuItem | Field | Type | Default | Description | |-------|------|---------|-------------| | `id` | `string` | - | Identifier | | `label` | `ReactNode` | - | Row label (flattened to text) | | `icon` | `ReactNode` | - | DocyrusIcon string; non-string icons are omitted | | `shortcut` | `ReactNode` | - | Web parity; not shown | | `disabled` | `boolean` | - | Disabled row | | `variant` | `'default' \| 'destructive'` | `'default'` | Destructive styling | | `onSelect` | `(item: AwesomeStatItem) => void` | - | Called with the stat | ### AwesomeStatsAwesomeCardProps | Field | Type | Default | Description | |-------|------|---------|-------------| | `pattern` | `boolean` | `true` | Pattern background | | `patternStyle` | `PatternStyle` | `'stripes'` | Pattern motif | | `collapsible` | `boolean` | `false` | Collapsible card | | `collapsed` | `boolean` | `false` | Initially collapsed | | `chevronPosition` | `'left' \| 'right'` | `'left'` | Chevron position | | `className` | `string` | - | Card classes | ## Type Exports | Type | Description | |------|-------------| | `AwesomeStatsProps` | Component props | | `AwesomeStatItem` | Stat item | | `AwesomeStatValueFormat` | Number formatting options | | `AwesomeStatComparison` | Comparison configuration | | `AwesomeStatComparisonPeriod` | Comparison period union | | `AwesomeStatMiniChart` | Mini chart configuration | | `AwesomeStatsCardMenuItem` | Card menu item | | `AwesomeStatsAwesomeCardProps` | AwesomeCard options | | `AwesomeStatsClickTarget` | `'title' \| 'card'` | | `AwesomeStatsLayout` | Layout union |