@idealyst/datagrid 1.0.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,217 @@
1
+ # @idealyst/datagrid
2
+
3
+ High-performance, cross-platform datagrid component for React and React Native, built on top of @idealyst/components.
4
+
5
+ ## Features
6
+
7
+ - 🚀 **Virtualized Rendering** - Powered by react-window for web, native FlatList for mobile
8
+ - 📱 **Cross-Platform** - Works seamlessly on React and React Native
9
+ - 🎨 **Theme Integration** - Built with @idealyst/theme and Unistyles
10
+ - 📊 **Rich Features** - Sorting, selection, custom rendering, and more
11
+ - âš¡ **Performance** - Optimized for large datasets with virtualization
12
+ - 🔧 **Customizable** - Flexible column configuration and styling
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @idealyst/datagrid
18
+ # or
19
+ yarn add @idealyst/datagrid
20
+ ```
21
+
22
+ ### Peer Dependencies
23
+
24
+ This package requires the following peer dependencies:
25
+
26
+ - `@idealyst/components` - Core component library
27
+ - `@idealyst/theme` - Theme system
28
+ - `react` >= 16.8.0
29
+ - `react-window` (for web virtualization)
30
+ - `react-native` >= 0.60.0 (for native)
31
+ - `react-native-unistyles` - Styling system
32
+
33
+ ## Usage
34
+
35
+ ### Basic Example
36
+
37
+ ```tsx
38
+ import { DataGrid } from '@idealyst/datagrid';
39
+
40
+ const data = [
41
+ { id: 1, name: 'John Doe', email: 'john@example.com', age: 30 },
42
+ { id: 2, name: 'Jane Smith', email: 'jane@example.com', age: 25 },
43
+ // ... more data
44
+ ];
45
+
46
+ const columns = [
47
+ { key: 'id', header: 'ID', width: 60 },
48
+ { key: 'name', header: 'Name', width: 150 },
49
+ { key: 'email', header: 'Email', width: 200 },
50
+ { key: 'age', header: 'Age', width: 80, sortable: true },
51
+ ];
52
+
53
+ function MyDataGrid() {
54
+ return (
55
+ <DataGrid
56
+ data={data}
57
+ columns={columns}
58
+ height={400}
59
+ virtualized={true}
60
+ />
61
+ );
62
+ }
63
+ ```
64
+
65
+ ### Advanced Features
66
+
67
+ #### Custom Cell Rendering
68
+
69
+ ```tsx
70
+ const columns = [
71
+ {
72
+ key: 'status',
73
+ header: 'Status',
74
+ render: (value) => (
75
+ <Badge variant="filled" intent={value === 'active' ? 'success' : 'neutral'}>
76
+ {value}
77
+ </Badge>
78
+ ),
79
+ },
80
+ {
81
+ key: 'actions',
82
+ header: 'Actions',
83
+ render: (value, row) => (
84
+ <Button size="small" onPress={() => handleEdit(row)}>
85
+ Edit
86
+ </Button>
87
+ ),
88
+ },
89
+ ];
90
+ ```
91
+
92
+ #### Row Selection
93
+
94
+ ```tsx
95
+ function SelectableDataGrid() {
96
+ const [selectedRows, setSelectedRows] = useState<number[]>([]);
97
+
98
+ return (
99
+ <DataGrid
100
+ data={data}
101
+ columns={columns}
102
+ selectedRows={selectedRows}
103
+ onSelectionChange={setSelectedRows}
104
+ multiSelect={true}
105
+ onRowClick={(row, index) => console.log('Clicked:', row)}
106
+ />
107
+ );
108
+ }
109
+ ```
110
+
111
+ #### Sorting
112
+
113
+ ```tsx
114
+ function SortableDataGrid() {
115
+ const [sortedData, setSortedData] = useState(data);
116
+
117
+ const handleSort = (column, direction) => {
118
+ const sorted = [...data].sort((a, b) => {
119
+ const aVal = a[column.key];
120
+ const bVal = b[column.key];
121
+ return direction === 'asc'
122
+ ? aVal > bVal ? 1 : -1
123
+ : aVal < bVal ? 1 : -1;
124
+ });
125
+ setSortedData(sorted);
126
+ };
127
+
128
+ return (
129
+ <DataGrid
130
+ data={sortedData}
131
+ columns={columns}
132
+ onSort={handleSort}
133
+ />
134
+ );
135
+ }
136
+ ```
137
+
138
+ #### Dynamic Styling
139
+
140
+ ```tsx
141
+ <DataGrid
142
+ data={data}
143
+ columns={columns}
144
+ rowStyle={(row, index) => ({
145
+ backgroundColor: index % 2 === 0 ? '#f5f5f5' : 'white',
146
+ })}
147
+ headerStyle={{
148
+ backgroundColor: '#e0e0e0',
149
+ fontWeight: 'bold',
150
+ }}
151
+ />
152
+ ```
153
+
154
+ ## API Reference
155
+
156
+ ### DataGrid Props
157
+
158
+ | Prop | Type | Default | Description |
159
+ |------|------|---------|-------------|
160
+ | `data` | `T[]` | Required | Array of data objects |
161
+ | `columns` | `Column<T>[]` | Required | Column configuration |
162
+ | `height` | `number \| string` | `400` | Height of the grid |
163
+ | `width` | `number \| string` | `'100%'` | Width of the grid |
164
+ | `rowHeight` | `number` | `48` | Height of each row |
165
+ | `headerHeight` | `number` | `56` | Height of the header |
166
+ | `virtualized` | `boolean` | `true` | Enable virtualization for large datasets |
167
+ | `stickyHeader` | `boolean` | `true` | Keep header visible when scrolling |
168
+ | `selectedRows` | `number[]` | `[]` | Array of selected row indices |
169
+ | `multiSelect` | `boolean` | `false` | Allow multiple row selection |
170
+ | `onRowClick` | `(row: T, index: number) => void` | - | Row click handler |
171
+ | `onSelectionChange` | `(rows: number[]) => void` | - | Selection change handler |
172
+ | `onSort` | `(column: Column, direction: 'asc' \| 'desc') => void` | - | Sort handler |
173
+ | `style` | `ViewStyle` | - | Container style |
174
+ | `headerStyle` | `ViewStyle` | - | Header style |
175
+ | `rowStyle` | `ViewStyle \| ((row: T, index: number) => ViewStyle)` | - | Row style |
176
+
177
+ ### Column Configuration
178
+
179
+ | Property | Type | Description |
180
+ |----------|------|-------------|
181
+ | `key` | `string` | Unique column identifier |
182
+ | `header` | `string` | Column header text |
183
+ | `width` | `number` | Fixed column width |
184
+ | `minWidth` | `number` | Minimum column width |
185
+ | `maxWidth` | `number` | Maximum column width |
186
+ | `sortable` | `boolean` | Enable sorting for this column |
187
+ | `resizable` | `boolean` | Enable resizing (coming soon) |
188
+ | `accessor` | `(row: T) => any` | Custom value accessor |
189
+ | `render` | `(value: any, row: T, index: number) => ReactNode` | Custom cell renderer |
190
+ | `headerStyle` | `ViewStyle` | Header cell style |
191
+ | `cellStyle` | `ViewStyle \| ((value: any, row: T) => ViewStyle)` | Cell style |
192
+
193
+ ## Architecture
194
+
195
+ The DataGrid is built using a layered architecture:
196
+
197
+ 1. **Platform Primitives** - Low-level components that handle platform differences:
198
+ - `ScrollView` - Native scrolling for each platform
199
+ - `VirtualizedList` - react-window for web, FlatList for native
200
+
201
+ 2. **DataGrid Component** - Unified component that uses primitives:
202
+ - Single implementation works on all platforms
203
+ - Composes @idealyst/components for UI elements
204
+ - Uses Unistyles for theming
205
+
206
+ This approach ensures maximum code reuse while optimizing for each platform's strengths.
207
+
208
+ ## Performance Tips
209
+
210
+ 1. **Use Virtualization** - Enable `virtualized={true}` for datasets > 100 rows
211
+ 2. **Set Fixed Heights** - Provide `rowHeight` for better performance
212
+ 3. **Optimize Renders** - Use `React.memo` for custom cell renderers
213
+ 4. **Limit Columns** - Consider horizontal scrolling for many columns
214
+
215
+ ## License
216
+
217
+ MIT
package/package.json ADDED
@@ -0,0 +1,81 @@
1
+ {
2
+ "name": "@idealyst/datagrid",
3
+ "version": "1.0.40",
4
+ "description": "High-performance datagrid component for React and React Native",
5
+ "documentation": "https://github.com/your-username/idealyst-framework/tree/main/packages/datagrid#readme",
6
+ "main": "src/index.ts",
7
+ "module": "src/index.ts",
8
+ "types": "src/index.ts",
9
+ "react-native": "src/index.native.ts",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/your-username/idealyst-framework.git",
13
+ "directory": "packages/datagrid"
14
+ },
15
+ "author": "Your Name <your.email@example.com>",
16
+ "license": "MIT",
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "react-native": "./src/index.native.ts",
23
+ "import": "./src/index.ts",
24
+ "require": "./src/index.ts",
25
+ "types": "./src/index.ts"
26
+ },
27
+ "./examples": {
28
+ "import": "./src/examples/index.ts",
29
+ "require": "./src/examples/index.ts",
30
+ "types": "./src/examples/index.ts"
31
+ }
32
+ },
33
+ "scripts": {
34
+ "prepublishOnly": "echo 'Publishing TypeScript source directly'",
35
+ "publish:npm": "npm publish"
36
+ },
37
+ "peerDependencies": {
38
+ "@idealyst/components": "^1.0.40",
39
+ "@idealyst/theme": "^1.0.40",
40
+ "react": ">=16.8.0",
41
+ "react-native": ">=0.60.0",
42
+ "react-native-unistyles": "^3.0.4",
43
+ "react-window": "^1.8.10"
44
+ },
45
+ "peerDependenciesMeta": {
46
+ "@idealyst/components": {
47
+ "optional": false
48
+ },
49
+ "@idealyst/theme": {
50
+ "optional": false
51
+ },
52
+ "react-native": {
53
+ "optional": true
54
+ },
55
+ "react-native-unistyles": {
56
+ "optional": true
57
+ },
58
+ "react-window": {
59
+ "optional": true
60
+ }
61
+ },
62
+ "devDependencies": {
63
+ "@types/react": "^19.1.0",
64
+ "@types/react-window": "^1.8.8",
65
+ "typescript": "^5.0.0"
66
+ },
67
+ "files": [
68
+ "src",
69
+ "README.md"
70
+ ],
71
+ "keywords": [
72
+ "react",
73
+ "react-native",
74
+ "datagrid",
75
+ "table",
76
+ "grid",
77
+ "cross-platform",
78
+ "react-window",
79
+ "virtualization"
80
+ ]
81
+ }
@@ -0,0 +1,196 @@
1
+ import React, { useState, useMemo, useCallback } from 'react';
2
+ import { View, Text, Button } from '@idealyst/components';
3
+ import { createStyleSheet, useStyles } from 'react-native-unistyles';
4
+ import { ScrollView } from '../primitives/ScrollView';
5
+ import { VirtualizedList } from '../primitives/VirtualizedList';
6
+ import type { DataGridProps, Column } from './types';
7
+
8
+ export function DataGrid<T extends Record<string, any>>({
9
+ data,
10
+ columns,
11
+ rowHeight = 48,
12
+ headerHeight = 56,
13
+ onRowClick,
14
+ onSort,
15
+ virtualized = true,
16
+ height = 400,
17
+ width = '100%',
18
+ style,
19
+ headerStyle,
20
+ rowStyle,
21
+ selectedRows = [],
22
+ onSelectionChange,
23
+ multiSelect = false,
24
+ stickyHeader = true,
25
+ }: DataGridProps<T>) {
26
+ const { styles } = useStyles(stylesheet);
27
+ const [sortColumn, setSortColumn] = useState<string | null>(null);
28
+ const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc');
29
+
30
+ const handleSort = useCallback((column: Column<T>) => {
31
+ if (!column.sortable) return;
32
+
33
+ const newDirection = sortColumn === column.key && sortDirection === 'asc' ? 'desc' : 'asc';
34
+ setSortColumn(column.key);
35
+ setSortDirection(newDirection);
36
+ onSort?.(column, newDirection);
37
+ }, [sortColumn, sortDirection, onSort]);
38
+
39
+ const handleRowClick = useCallback((row: T, index: number) => {
40
+ if (onSelectionChange) {
41
+ let newSelection: number[];
42
+ if (multiSelect) {
43
+ if (selectedRows.includes(index)) {
44
+ newSelection = selectedRows.filter(i => i !== index);
45
+ } else {
46
+ newSelection = [...selectedRows, index];
47
+ }
48
+ } else {
49
+ newSelection = selectedRows.includes(index) ? [] : [index];
50
+ }
51
+ onSelectionChange(newSelection);
52
+ }
53
+ onRowClick?.(row, index);
54
+ }, [selectedRows, onSelectionChange, multiSelect, onRowClick]);
55
+
56
+ const renderHeader = () => (
57
+ <View style={[styles.header, headerStyle]}>
58
+ {columns.map((column) => (
59
+ <View
60
+ key={column.key}
61
+ style={[
62
+ styles.headerCell,
63
+ { width: column.width || 'auto', minWidth: column.minWidth, maxWidth: column.maxWidth },
64
+ column.headerStyle,
65
+ ]}
66
+ >
67
+ <Text weight="bold" style={styles.headerText}>
68
+ {column.header}
69
+ </Text>
70
+ {column.sortable && (
71
+ <Text style={styles.sortIndicator}>
72
+ {sortColumn === column.key ? (sortDirection === 'asc' ? 'â–²' : 'â–¼') : ''}
73
+ </Text>
74
+ )}
75
+ </View>
76
+ ))}
77
+ </View>
78
+ );
79
+
80
+ const renderRow = ({ item, index }: { item: T; index: number }) => {
81
+ const isSelected = selectedRows.includes(index);
82
+ const computedRowStyle = typeof rowStyle === 'function' ? rowStyle(item, index) : rowStyle;
83
+
84
+ return (
85
+ <View
86
+ style={[
87
+ styles.row,
88
+ isSelected && styles.selectedRow,
89
+ computedRowStyle,
90
+ ]}
91
+ onPress={() => handleRowClick(item, index)}
92
+ >
93
+ {columns.map((column) => {
94
+ const value = column.accessor ? column.accessor(item) : item[column.key];
95
+ const cellContent = column.render ? column.render(value, item, index) : value;
96
+ const computedCellStyle = typeof column.cellStyle === 'function'
97
+ ? column.cellStyle(value, item)
98
+ : column.cellStyle;
99
+
100
+ return (
101
+ <View
102
+ key={column.key}
103
+ style={[
104
+ styles.cell,
105
+ { width: column.width || 'auto', minWidth: column.minWidth, maxWidth: column.maxWidth },
106
+ computedCellStyle,
107
+ ]}
108
+ >
109
+ {typeof cellContent === 'string' || typeof cellContent === 'number' ? (
110
+ <Text>{cellContent}</Text>
111
+ ) : (
112
+ cellContent
113
+ )}
114
+ </View>
115
+ );
116
+ })}
117
+ </View>
118
+ );
119
+ };
120
+
121
+ const containerHeight = typeof height === 'number' ? height : undefined;
122
+
123
+ return (
124
+ <View style={[styles.container, { width, height }, style]}>
125
+ {stickyHeader && renderHeader()}
126
+ {virtualized && containerHeight ? (
127
+ <VirtualizedList
128
+ data={data}
129
+ renderItem={renderRow}
130
+ itemHeight={rowHeight}
131
+ height={containerHeight - (stickyHeader ? headerHeight : 0)}
132
+ width={width}
133
+ />
134
+ ) : (
135
+ <ScrollView
136
+ style={styles.scrollView}
137
+ showsVerticalScrollIndicator={true}
138
+ >
139
+ {!stickyHeader && renderHeader()}
140
+ {data.map((item, index) => renderRow({ item, index }))}
141
+ </ScrollView>
142
+ )}
143
+ </View>
144
+ );
145
+ }
146
+
147
+ const stylesheet = createStyleSheet((theme) => ({
148
+ container: {
149
+ backgroundColor: theme.colors.background,
150
+ borderWidth: 1,
151
+ borderColor: theme.colors.neutral[200],
152
+ borderRadius: theme.radius.md,
153
+ overflow: 'hidden',
154
+ },
155
+ header: {
156
+ flexDirection: 'row',
157
+ backgroundColor: theme.colors.neutral[50],
158
+ borderBottomWidth: 2,
159
+ borderBottomColor: theme.colors.neutral[200],
160
+ },
161
+ headerCell: {
162
+ flexDirection: 'row',
163
+ alignItems: 'center',
164
+ justifyContent: 'space-between',
165
+ padding: theme.spacing.sm,
166
+ borderRightWidth: 1,
167
+ borderRightColor: theme.colors.neutral[200],
168
+ },
169
+ headerText: {
170
+ fontSize: 14,
171
+ color: theme.colors.neutral[700],
172
+ },
173
+ sortIndicator: {
174
+ fontSize: 10,
175
+ marginLeft: theme.spacing.xs,
176
+ color: theme.colors.primary[500],
177
+ },
178
+ row: {
179
+ flexDirection: 'row',
180
+ borderBottomWidth: 1,
181
+ borderBottomColor: theme.colors.neutral[100],
182
+ backgroundColor: theme.colors.background,
183
+ },
184
+ selectedRow: {
185
+ backgroundColor: theme.colors.primary[50],
186
+ },
187
+ cell: {
188
+ padding: theme.spacing.sm,
189
+ borderRightWidth: 1,
190
+ borderRightColor: theme.colors.neutral[100],
191
+ justifyContent: 'center',
192
+ },
193
+ scrollView: {
194
+ flex: 1,
195
+ },
196
+ }));
@@ -0,0 +1,2 @@
1
+ export { DataGrid } from './DataGrid';
2
+ export type { DataGridProps, Column, CellProps, RowProps, HeaderCellProps } from './types';
@@ -0,0 +1,59 @@
1
+ import { ViewStyle, TextStyle } from 'react-native';
2
+
3
+ export interface Column<T = any> {
4
+ key: string;
5
+ header: string;
6
+ width?: number;
7
+ minWidth?: number;
8
+ maxWidth?: number;
9
+ resizable?: boolean;
10
+ sortable?: boolean;
11
+ accessor?: (row: T) => any;
12
+ render?: (value: any, row: T, index: number) => React.ReactNode;
13
+ headerStyle?: ViewStyle;
14
+ cellStyle?: ViewStyle | ((value: any, row: T) => ViewStyle);
15
+ }
16
+
17
+ export interface DataGridProps<T = any> {
18
+ data: T[];
19
+ columns: Column<T>[];
20
+ rowHeight?: number;
21
+ headerHeight?: number;
22
+ onRowClick?: (row: T, index: number) => void;
23
+ onSort?: (column: Column<T>, direction: 'asc' | 'desc') => void;
24
+ virtualized?: boolean;
25
+ height?: number | string;
26
+ width?: number | string;
27
+ style?: ViewStyle;
28
+ headerStyle?: ViewStyle;
29
+ rowStyle?: ViewStyle | ((row: T, index: number) => ViewStyle);
30
+ selectedRows?: number[];
31
+ onSelectionChange?: (selectedRows: number[]) => void;
32
+ multiSelect?: boolean;
33
+ stickyHeader?: boolean;
34
+ }
35
+
36
+ export interface CellProps {
37
+ children: React.ReactNode;
38
+ style?: ViewStyle;
39
+ width?: number;
40
+ onPress?: () => void;
41
+ }
42
+
43
+ export interface RowProps {
44
+ children: React.ReactNode;
45
+ style?: ViewStyle;
46
+ onPress?: () => void;
47
+ selected?: boolean;
48
+ }
49
+
50
+ export interface HeaderCellProps {
51
+ children: React.ReactNode;
52
+ style?: ViewStyle;
53
+ width?: number;
54
+ sortable?: boolean;
55
+ sortDirection?: 'asc' | 'desc' | null;
56
+ onSort?: () => void;
57
+ resizable?: boolean;
58
+ onResize?: (width: number) => void;
59
+ }
@@ -0,0 +1,136 @@
1
+ import React, { useState } from 'react';
2
+ import { View, Text, Button } from '@idealyst/components';
3
+ import { DataGrid } from '../DataGrid';
4
+ import type { Column } from '../DataGrid/types';
5
+
6
+ interface User {
7
+ id: number;
8
+ name: string;
9
+ email: string;
10
+ role: string;
11
+ status: 'active' | 'inactive';
12
+ joinDate: string;
13
+ }
14
+
15
+ const sampleData: User[] = [
16
+ { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin', status: 'active', joinDate: '2023-01-15' },
17
+ { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User', status: 'active', joinDate: '2023-02-20' },
18
+ { id: 3, name: 'Bob Johnson', email: 'bob@example.com', role: 'Moderator', status: 'inactive', joinDate: '2023-03-10' },
19
+ { id: 4, name: 'Alice Brown', email: 'alice@example.com', role: 'User', status: 'active', joinDate: '2023-04-05' },
20
+ { id: 5, name: 'Charlie Wilson', email: 'charlie@example.com', role: 'User', status: 'active', joinDate: '2023-05-12' },
21
+ // Add more sample data for testing virtualization
22
+ ...Array.from({ length: 95 }, (_, i) => ({
23
+ id: i + 6,
24
+ name: `User ${i + 6}`,
25
+ email: `user${i + 6}@example.com`,
26
+ role: 'User',
27
+ status: (i % 2 === 0 ? 'active' : 'inactive') as 'active' | 'inactive',
28
+ joinDate: `2023-${String((i % 12) + 1).padStart(2, '0')}-${String((i % 28) + 1).padStart(2, '0')}`,
29
+ })),
30
+ ];
31
+
32
+ export function BasicExample() {
33
+ const [selectedRows, setSelectedRows] = useState<number[]>([]);
34
+ const [data, setData] = useState(sampleData);
35
+
36
+ const columns: Column<User>[] = [
37
+ {
38
+ key: 'id',
39
+ header: 'ID',
40
+ width: 60,
41
+ sortable: true,
42
+ },
43
+ {
44
+ key: 'name',
45
+ header: 'Name',
46
+ width: 150,
47
+ sortable: true,
48
+ },
49
+ {
50
+ key: 'email',
51
+ header: 'Email',
52
+ width: 200,
53
+ },
54
+ {
55
+ key: 'role',
56
+ header: 'Role',
57
+ width: 100,
58
+ render: (value) => (
59
+ <View style={{ paddingHorizontal: 8, paddingVertical: 4, backgroundColor: '#f0f0f0', borderRadius: 4 }}>
60
+ <Text size="small">{value}</Text>
61
+ </View>
62
+ ),
63
+ },
64
+ {
65
+ key: 'status',
66
+ header: 'Status',
67
+ width: 100,
68
+ render: (value) => (
69
+ <Text
70
+ size="small"
71
+ weight="medium"
72
+ style={{ color: value === 'active' ? '#22c55e' : '#ef4444' }}
73
+ >
74
+ {value}
75
+ </Text>
76
+ ),
77
+ },
78
+ {
79
+ key: 'joinDate',
80
+ header: 'Join Date',
81
+ width: 120,
82
+ sortable: true,
83
+ },
84
+ ];
85
+
86
+ const handleSort = (column: Column<User>, direction: 'asc' | 'desc') => {
87
+ const sorted = [...data].sort((a, b) => {
88
+ const aVal = a[column.key as keyof User];
89
+ const bVal = b[column.key as keyof User];
90
+
91
+ if (typeof aVal === 'number' && typeof bVal === 'number') {
92
+ return direction === 'asc' ? aVal - bVal : bVal - aVal;
93
+ }
94
+
95
+ const aStr = String(aVal);
96
+ const bStr = String(bVal);
97
+ return direction === 'asc'
98
+ ? aStr.localeCompare(bStr)
99
+ : bStr.localeCompare(aStr);
100
+ });
101
+ setData(sorted);
102
+ };
103
+
104
+ return (
105
+ <View spacing="lg" style={{ padding: 20 }}>
106
+ <Text size="xlarge" weight="bold">DataGrid Example</Text>
107
+
108
+ <View spacing="sm">
109
+ <Text>Selected rows: {selectedRows.length}</Text>
110
+ {selectedRows.length > 0 && (
111
+ <Button
112
+ size="small"
113
+ variant="outlined"
114
+ onPress={() => setSelectedRows([])}
115
+ >
116
+ Clear Selection
117
+ </Button>
118
+ )}
119
+ </View>
120
+
121
+ <DataGrid
122
+ data={data}
123
+ columns={columns}
124
+ height={500}
125
+ virtualized={true}
126
+ selectedRows={selectedRows}
127
+ onSelectionChange={setSelectedRows}
128
+ multiSelect={true}
129
+ onSort={handleSort}
130
+ rowStyle={(row, index) => ({
131
+ backgroundColor: index % 2 === 0 ? '#fafafa' : '#ffffff',
132
+ })}
133
+ />
134
+ </View>
135
+ );
136
+ }
@@ -0,0 +1 @@
1
+ export { BasicExample } from './BasicExample';
@@ -0,0 +1,2 @@
1
+ export { DataGrid } from './DataGrid';
2
+ export type { DataGridProps, Column, CellProps, RowProps, HeaderCellProps } from './DataGrid/types';
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { DataGrid } from './DataGrid';
2
+ export type { DataGridProps, Column, CellProps, RowProps, HeaderCellProps } from './DataGrid/types';
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { ScrollView as RNScrollView, ScrollViewProps as RNScrollViewProps } from 'react-native';
3
+
4
+ interface ScrollViewProps extends RNScrollViewProps {
5
+ children: React.ReactNode;
6
+ }
7
+
8
+ export const ScrollView: React.FC<ScrollViewProps> = (props) => {
9
+ return <RNScrollView {...props} />;
10
+ };
@@ -0,0 +1,45 @@
1
+ import React from 'react';
2
+ import { View } from '@idealyst/components';
3
+
4
+ interface ScrollViewProps {
5
+ children: React.ReactNode;
6
+ horizontal?: boolean;
7
+ style?: any;
8
+ contentContainerStyle?: any;
9
+ onScroll?: (event: any) => void;
10
+ scrollEventThrottle?: number;
11
+ showsHorizontalScrollIndicator?: boolean;
12
+ showsVerticalScrollIndicator?: boolean;
13
+ }
14
+
15
+ export const ScrollView: React.FC<ScrollViewProps> = ({
16
+ children,
17
+ horizontal = false,
18
+ style,
19
+ contentContainerStyle,
20
+ onScroll,
21
+ showsHorizontalScrollIndicator = true,
22
+ showsVerticalScrollIndicator = true,
23
+ ...props
24
+ }) => {
25
+ const scrollStyle = {
26
+ ...style,
27
+ overflow: 'auto',
28
+ WebkitOverflowScrolling: 'touch',
29
+ ...(horizontal && { overflowY: 'hidden', overflowX: 'auto' }),
30
+ ...(!showsHorizontalScrollIndicator && { scrollbarWidth: 'none', msOverflowStyle: 'none' }),
31
+ ...(!showsVerticalScrollIndicator && { scrollbarWidth: 'none', msOverflowStyle: 'none' }),
32
+ };
33
+
34
+ return (
35
+ <div
36
+ style={scrollStyle}
37
+ onScroll={onScroll}
38
+ {...props}
39
+ >
40
+ <View style={contentContainerStyle}>
41
+ {children}
42
+ </View>
43
+ </div>
44
+ );
45
+ };
@@ -0,0 +1 @@
1
+ export { ScrollView } from './ScrollView.native';
@@ -0,0 +1 @@
1
+ export { ScrollView } from './ScrollView.web';
@@ -0,0 +1,42 @@
1
+ import React from 'react';
2
+ import { FlatList, VirtualizedList as RNVirtualizedList } from 'react-native';
3
+
4
+ interface VirtualizedListProps {
5
+ data: any[];
6
+ renderItem: ({ item, index }: { item: any; index: number }) => React.ReactElement;
7
+ itemHeight: number | ((index: number) => number);
8
+ height: number;
9
+ width?: number | string;
10
+ horizontal?: boolean;
11
+ onScroll?: (event: any) => void;
12
+ }
13
+
14
+ export const VirtualizedList: React.FC<VirtualizedListProps> = ({
15
+ data,
16
+ renderItem,
17
+ itemHeight,
18
+ height,
19
+ horizontal = false,
20
+ onScroll,
21
+ }) => {
22
+ const getItemLayout = typeof itemHeight === 'number'
23
+ ? (data: any, index: number) => ({
24
+ length: itemHeight,
25
+ offset: itemHeight * index,
26
+ index,
27
+ })
28
+ : undefined;
29
+
30
+ return (
31
+ <FlatList
32
+ data={data}
33
+ renderItem={renderItem}
34
+ getItemLayout={getItemLayout}
35
+ horizontal={horizontal}
36
+ onScroll={onScroll}
37
+ style={{ height, width }}
38
+ showsHorizontalScrollIndicator={false}
39
+ showsVerticalScrollIndicator={false}
40
+ />
41
+ );
42
+ };
@@ -0,0 +1,44 @@
1
+ import React from 'react';
2
+ import { VariableSizeList, FixedSizeList } from 'react-window';
3
+
4
+ interface VirtualizedListProps {
5
+ data: any[];
6
+ renderItem: ({ item, index }: { item: any; index: number }) => React.ReactElement;
7
+ itemHeight: number | ((index: number) => number);
8
+ height: number;
9
+ width?: number | string;
10
+ horizontal?: boolean;
11
+ onScroll?: (event: any) => void;
12
+ }
13
+
14
+ export const VirtualizedList: React.FC<VirtualizedListProps> = ({
15
+ data,
16
+ renderItem,
17
+ itemHeight,
18
+ height,
19
+ width = '100%',
20
+ horizontal = false,
21
+ onScroll,
22
+ }) => {
23
+ const isVariableSize = typeof itemHeight === 'function';
24
+ const List = isVariableSize ? VariableSizeList : FixedSizeList;
25
+
26
+ const listProps = {
27
+ height: horizontal ? '100%' : height,
28
+ width: horizontal ? height : width,
29
+ itemCount: data.length,
30
+ itemSize: itemHeight,
31
+ layout: horizontal ? 'horizontal' : 'vertical',
32
+ onScroll,
33
+ };
34
+
35
+ return (
36
+ <List {...listProps}>
37
+ {({ index, style }) => (
38
+ <div style={style}>
39
+ {renderItem({ item: data[index], index })}
40
+ </div>
41
+ )}
42
+ </List>
43
+ );
44
+ };
@@ -0,0 +1 @@
1
+ export { VirtualizedList } from './VirtualizedList.native';
@@ -0,0 +1 @@
1
+ export { VirtualizedList } from './VirtualizedList.web';