react-native-etf-table
Version:
A highly customizable, performant ETF table component for React Native with horizontal and vertical scrolling
449 lines (362 loc) • 13.6 kB
Markdown
# react-native-etf-table
A highly customizable, performant ETF table component for React Native with horizontal and vertical scrolling support.
## Features
- ✅ Horizontal and vertical scrolling
- ✅ Fixed first column with scrollable data columns
- ✅ Fully customizable styling (colors, sizes, themes)
- ✅ Auto-sizing columns based on content
- ✅ Light and dark theme support
- ✅ Custom cell rendering
- ✅ Column sorting with customizable icons
- ✅ TypeScript support
- ✅ Zero dependencies (only React Native peer dependencies)
## Installation
```bash
npm install react-native-etf-table
```
or
```bash
yarn add react-native-etf-table
```
## Basic Usage
```jsx
import React from 'react';
import ETFTableNative from 'react-native-etf-table';
const App = () => {
const data = [
{ name: 'SPY', '1d': 0.45, '1w': 1.23, '1m': 3.45, volume: '85M' },
{ name: 'QQQ', '1d': 0.82, '1w': 2.15, '1m': 5.67, volume: '52M' },
];
const columns = [
{ key: 'name', label: 'ETF', width: 80 },
{ key: '1d', label: '1D %', width: 80, isReturn: true },
{ key: '1w', label: '1W %', width: 80, isReturn: true },
{ key: '1m', label: '1M %', width: 80, isReturn: true },
{ key: 'volume', label: 'Volume', width: 80 },
];
return (
<ETFTableNative
data={data}
columns={columns}
/>
);
};
export default App;
```
## Props
### Data Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `data` | `array` | `[]` | Array of row data objects |
| `columns` | `array` | `[]` | Array of column configuration objects |
### Column Configuration
Each column object should have:
- `key` (string, required): The key to access data from row objects
- `label` (string, required): The header label for the column
- `width` (number, optional): Minimum width for the column
- `isReturn` (boolean, optional): If true, values will be formatted as percentages with color coding
- `sortable` (boolean, optional): If false, column won't be sortable (default: true)
### Header Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `headerTitle` | `string` | `'ETF Performance'` | Main header title |
| `headerSubtitle` | `string \| null` | `null` | Header subtitle (null = auto-generated from data length) |
| `showHeader` | `boolean` | `true` | Show/hide the header section |
| `headerHeight` | `number \| null` | `null` | Custom header height (null = auto) |
| `headerBackgroundColor` | `string` | `'#2563eb'` | Header background color |
| `headerTitleColor` | `string` | `'#ffffff'` | Header title text color |
| `headerSubtitleColor` | `string` | `'#bfdbfe'` | Header subtitle text color |
| `headerTitleSize` | `number` | `20` | Header title font size |
| `headerSubtitleSize` | `number` | `14` | Header subtitle font size |
### Table Header Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `showTableHeader` | `boolean` | `true` | Show/hide the table header row |
| `tableHeaderHeight` | `number` | `50` | Height of table header cells |
| `tableHeaderBackgroundColor` | `string` | `'#1f2937'` | Table header background color |
| `tableHeaderTextColor` | `string` | `'#ffffff'` | Table header text color |
| `tableHeaderTextSize` | `number` | `11` | Table header font size |
| `tableHeaderFontWeight` | `string` | `'600'` | Table header font weight |
| `tableHeaderBorderColor` | `string` | `'#4b5563'` | Table header border color |
### Cell Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `cellHeight` | `number` | `50` | Height of data cells |
| `cellMinWidth` | `number` | `80` | Minimum width for cells |
| `cellPadding` | `number` | `12` | Padding inside cells |
| `cellBackgroundColor` | `string` | `'#ffffff'` | Cell background color |
| `cellTextColor` | `string` | `'#374151'` | Cell text color |
| `cellTextSize` | `number` | `14` | Cell text font size |
| `cellBorderColor` | `string` | `'#e5e7eb'` | Cell border color |
### Fixed Column Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `fixedColumnBackgroundColor` | `string` | `'#1f2937'` | Fixed column background color |
| `fixedColumnTextColor` | `string` | `'#111827'` | Fixed column text color |
| `fixedColumnTextSize` | `number` | `14` | Fixed column font size |
| `fixedColumnFontWeight` | `string` | `'600'` | Fixed column font weight |
| `fixedColumnBorderColor` | `string` | `'#e5e7eb'` | Fixed column border color |
### Return Value Colors
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `positiveReturnColor` | `string` | `'#16a34a'` | Color for positive return values |
| `negativeReturnColor` | `string` | `'#dc2626'` | Color for negative return values |
| `neutralReturnColor` | `string` | `'#4b5563'` | Color for zero/neutral return values |
### Theme Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `theme` | `'light' \| 'dark'` | `'light'` | Theme preset (overrides individual colors) |
| `backgroundColor` | `string` | `'#f9fafb'` | Main container background color |
### Footer Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `showFooter` | `boolean` | `true` | Show/hide the footer |
| `footerText` | `string` | `'↕️ Scroll vertically • ↔️ Scroll horizontally'` | Footer text |
| `footerBackgroundColor` | `string` | `'#f3f4f6'` | Footer background color |
| `footerTextColor` | `string` | `'#6b7280'` | Footer text color |
| `footerTextSize` | `number` | `11` | Footer font size |
| `footerBorderColor` | `string` | `'#e5e7eb'` | Footer border color |
### Status Bar Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `statusBarStyle` | `'default' \| 'light-content' \| 'dark-content'` | `'light-content'` | Status bar style |
| `statusBarHeight` | `number \| null` | `null` | Custom status bar height (null = auto) |
### Auto-sizing Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `autoSizeColumns` | `boolean` | `true` | Enable automatic column width adjustment |
| `minColumnWidth` | `number` | `80` | Minimum column width when auto-sizing |
### Scroll Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `showsHorizontalScrollIndicator` | `boolean` | `false` | Show horizontal scroll indicator |
| `showsVerticalScrollIndicator` | `boolean` | `true` | Show vertical scroll indicator |
| `scrollEventThrottle` | `number` | `16` | Scroll event throttle (ms) |
| `bounces` | `boolean` | `false` | Enable scroll bouncing |
### Custom Styles
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `containerStyle` | `ViewStyle` | `{}` | Custom container styles |
| `headerStyle` | `ViewStyle` | `{}` | Custom header styles |
| `tableHeaderStyle` | `ViewStyle` | `{}` | Custom table header styles |
| `tableBodyStyle` | `ViewStyle` | `{}` | Custom table body styles |
| `footerStyle` | `ViewStyle` | `{}` | Custom footer styles |
| `cellStyle` | `ViewStyle` | `{}` | Custom cell styles |
| `headerCellStyle` | `ViewStyle` | `{}` | Custom header cell styles |
| `fixedColumnStyle` | `ViewStyle` | `{}` | Custom fixed column styles |
### Formatting Functions
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `formatValue` | `(value: any) => string` | `null` | Custom formatter for non-return values |
| `formatReturn` | `(value: number) => string` | `null` | Custom formatter for return values |
### Custom Render Functions
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `renderCell` | `(value, column, rowData) => ReactNode` | `null` | Custom cell renderer |
| `renderHeaderCell` | `(column, isSorted, sortDirection) => ReactNode` | `null` | Custom header cell renderer (receives sort state) |
| `renderFixedCell` | `(value, column, rowData) => ReactNode` | `null` | Custom fixed column cell renderer |
### Sorting Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `enableSorting` | `boolean` | `true` | Enable/disable sorting functionality |
| `initialSortColumn` | `string \| null` | `null` | Column key to sort by initially |
| `initialSortDirection` | `'asc' \| 'desc'` | `'asc'` | Initial sort direction |
| `onSort` | `(columnKey, direction) => void` | `null` | Callback fired when column is sorted |
| `customSortFunction` | `(a, b, columnKey, direction, column) => number` | `null` | Custom sort function (returns -1, 0, or 1) |
### Sort Icon Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `sortAscIcon` | `ReactNode \| (props) => ReactNode` | `null` | Custom ascending sort icon (default: ▲) |
| `sortDescIcon` | `ReactNode \| (props) => ReactNode` | `null` | Custom descending sort icon (default: ▼) |
| `sortUnsortedIcon` | `ReactNode \| (props) => ReactNode` | `null` | Custom unsorted icon (default: ⇅) |
| `sortIconSize` | `number` | `12` | Size of sort icons |
| `sortIconColor` | `string \| null` | `null` | Color of sort icons (null = uses tableHeaderTextColor) |
| `sortIconPosition` | `'left' \| 'right'` | `'right'` | Position of sort icon relative to label |
| `showSortIcon` | `boolean` | `true` | Show/hide sort icons |
## Examples
### Dark Theme
```jsx
<ETFTableNative
data={data}
columns={columns}
theme="dark"
/>
```
### Custom Colors
```jsx
<ETFTableNative
data={data}
columns={columns}
headerBackgroundColor="#8b5cf6"
cellBackgroundColor="#faf5ff"
positiveReturnColor="#10b981"
negativeReturnColor="#ef4444"
/>
```
### Custom Cell Sizes
```jsx
<ETFTableNative
data={data}
columns={columns}
cellHeight={60}
cellMinWidth={100}
cellPadding={16}
tableHeaderHeight={60}
/>
```
### Custom Cell Rendering
```jsx
<ETFTableNative
data={data}
columns={columns}
renderCell={(value, column, rowData) => {
if (column.key === 'volume') {
return (
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<Icon name="chart" size={16} />
<Text>{value}</Text>
</View>
);
}
return <Text>{value}</Text>;
}}
/>
```
### Custom Formatting
```jsx
<ETFTableNative
data={data}
columns={columns}
formatReturn={(value) => {
return `${value >= 0 ? '↑' : '↓'} ${Math.abs(value).toFixed(2)}%`;
}}
formatValue={(value) => {
if (typeof value === 'number') {
return value.toLocaleString();
}
return value;
}}
/>
```
### Disable Auto-sizing
```jsx
<ETFTableNative
data={data}
columns={columns}
autoSizeColumns={false}
/>
```
### Sorting
#### Basic Sorting (Enabled by Default)
```jsx
<ETFTableNative
data={data}
columns={columns}
// Sorting is enabled by default
/>
```
#### Initial Sort
```jsx
<ETFTableNative
data={data}
columns={columns}
initialSortColumn="1d"
initialSortDirection="desc"
/>
```
#### Disable Sorting for Specific Columns
```jsx
const columns = [
{ key: 'name', label: 'ETF', width: 80, sortable: false }, // Not sortable
{ key: '1d', label: '1D %', width: 80, isReturn: true }, // Sortable (default)
{ key: 'volume', label: 'Volume', width: 80, sortable: false }, // Not sortable
];
```
#### Custom Sort Callback
```jsx
<ETFTableNative
data={data}
columns={columns}
onSort={(columnKey, direction) => {
console.log(`Sorted by ${columnKey} in ${direction} order`);
// You can handle external sorting here
}}
/>
```
#### Custom Sort Function
```jsx
<ETFTableNative
data={data}
columns={columns}
customSortFunction={(a, b, columnKey, direction, column) => {
// Custom sorting logic
if (columnKey === 'volume') {
// Parse volume strings like "85M" to numbers
const parseVolume = (val) => {
if (typeof val === 'string') {
const num = parseFloat(val);
if (val.includes('M')) return num * 1000000;
if (val.includes('K')) return num * 1000;
return num;
}
return val;
};
const aVal = parseVolume(a[columnKey]);
const bVal = parseVolume(b[columnKey]);
return direction === 'asc' ? aVal - bVal : bVal - aVal;
}
// Default sorting for other columns
return 0;
}}
/>
```
#### Custom Sort Icons
```jsx
import { Icon } from 'react-native-vector-icons/Ionicons';
<ETFTableNative
data={data}
columns={columns}
sortAscIcon={<Icon name="arrow-up" size={12} color="#fff" />}
sortDescIcon={<Icon name="arrow-down" size={12} color="#fff" />}
sortUnsortedIcon={<Icon name="swap-vertical" size={12} color="#fff" />}
/>
```
#### Custom Sort Icons with Function
```jsx
<ETFTableNative
data={data}
columns={columns}
sortAscIcon={({ size, color }) => (
<Icon name="arrow-up" size={size} color={color} />
)}
sortDescIcon={({ size, color }) => (
<Icon name="arrow-down" size={size} color={color} />
)}
sortUnsortedIcon={({ size, color }) => (
<Icon name="swap-vertical" size={size} color={color} />
)}
/>
```
#### Customize Sort Icon Position and Style
```jsx
<ETFTableNative
data={data}
columns={columns}
sortIconPosition="left" // Icons appear on the left side
sortIconSize={14}
sortIconColor="#10b981" // Custom icon color
showSortIcon={true}
/>
```
#### Disable Sorting
```jsx
<ETFTableNative
data={data}
columns={columns}
enableSorting={false}
/>
```
## License
MIT
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.