@idealyst/datagrid 1.0.83 → 1.0.85
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/package.json +8 -3
- package/src/DataGrid/DataGrid.native.tsx +3 -2
- package/src/DataGrid/DataGrid.styles.tsx +188 -0
- package/src/DataGrid/DataGrid.tsx +43 -92
- package/src/DataGrid/types.ts +1 -0
- package/src/examples/BasicExample.tsx +4 -4
- package/src/examples/DataGridShowcase.tsx +23 -23
- package/src/primitives/Table/Table.native.tsx +8 -6
- package/src/primitives/Table/Table.web.tsx +17 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idealyst/datagrid",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.85",
|
|
4
4
|
"description": "High-performance datagrid component for React and React Native",
|
|
5
5
|
"documentation": "https://github.com/IdealystIO/idealyst-framework/tree/main/packages/datagrid#readme",
|
|
6
6
|
"readme": "README.md",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"publish:npm": "npm publish"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"@idealyst/components": "^1.0.
|
|
40
|
-
"@idealyst/theme": "^1.0.
|
|
39
|
+
"@idealyst/components": "^1.0.85",
|
|
40
|
+
"@idealyst/theme": "^1.0.85",
|
|
41
41
|
"react": ">=16.8.0",
|
|
42
42
|
"react-native": ">=0.60.0",
|
|
43
43
|
"react-native-unistyles": "^3.0.4",
|
|
@@ -61,8 +61,13 @@
|
|
|
61
61
|
}
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
|
+
"@idealyst/components": "^1.0.85",
|
|
65
|
+
"@idealyst/theme": "^1.0.85",
|
|
64
66
|
"@types/react": "^19.1.0",
|
|
65
67
|
"@types/react-window": "^1.8.8",
|
|
68
|
+
"react": "^19.1.0",
|
|
69
|
+
"react-native": "^0.80.1",
|
|
70
|
+
"react-native-unistyles": "^3.0.10",
|
|
66
71
|
"typescript": "^5.0.0"
|
|
67
72
|
},
|
|
68
73
|
"files": [
|
|
@@ -71,6 +71,7 @@ export function DataGrid<T extends Record<string, any>>({
|
|
|
71
71
|
shadowOffset: { width: 0, height: 2 },
|
|
72
72
|
shadowOpacity: 0.1,
|
|
73
73
|
shadowRadius: 4,
|
|
74
|
+
minHeight: 300,
|
|
74
75
|
},
|
|
75
76
|
headerStyle
|
|
76
77
|
]}>
|
|
@@ -97,7 +98,7 @@ export function DataGrid<T extends Record<string, any>>({
|
|
|
97
98
|
>
|
|
98
99
|
<Text
|
|
99
100
|
weight="bold"
|
|
100
|
-
size="
|
|
101
|
+
size="sm"
|
|
101
102
|
onPress={column.sortable ? () => handleSort(column) : undefined}
|
|
102
103
|
style={{
|
|
103
104
|
color: '#374151',
|
|
@@ -163,7 +164,7 @@ export function DataGrid<T extends Record<string, any>>({
|
|
|
163
164
|
]}
|
|
164
165
|
>
|
|
165
166
|
{typeof cellContent === 'string' || typeof cellContent === 'number' ? (
|
|
166
|
-
<Text size="
|
|
167
|
+
<Text size="sm" numberOfLines={1}>
|
|
167
168
|
{cellContent}
|
|
168
169
|
</Text>
|
|
169
170
|
) : (
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native-unistyles';
|
|
2
|
+
import { Theme, StylesheetStyles } from '@idealyst/theme';
|
|
3
|
+
|
|
4
|
+
type CellAlignment = 'left' | 'center' | 'right';
|
|
5
|
+
|
|
6
|
+
type DataGridVariants = {
|
|
7
|
+
stickyHeader: boolean;
|
|
8
|
+
virtualized: boolean;
|
|
9
|
+
selected: boolean;
|
|
10
|
+
clickable: boolean;
|
|
11
|
+
alignment: CellAlignment;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type ExpandedDataGridStyles = StylesheetStyles<keyof DataGridVariants>;
|
|
15
|
+
|
|
16
|
+
export type DataGridStylesheet = {
|
|
17
|
+
container: ExpandedDataGridStyles;
|
|
18
|
+
scrollView: ExpandedDataGridStyles;
|
|
19
|
+
scrollViewContent: ExpandedDataGridStyles;
|
|
20
|
+
table: ExpandedDataGridStyles;
|
|
21
|
+
header: ExpandedDataGridStyles;
|
|
22
|
+
headerRow: ExpandedDataGridStyles;
|
|
23
|
+
headerCell: ExpandedDataGridStyles;
|
|
24
|
+
headerText: ExpandedDataGridStyles;
|
|
25
|
+
row: ExpandedDataGridStyles;
|
|
26
|
+
cell: ExpandedDataGridStyles;
|
|
27
|
+
spacerRow: ExpandedDataGridStyles;
|
|
28
|
+
spacerCell: ExpandedDataGridStyles;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Generate DataGrid styles
|
|
33
|
+
*/
|
|
34
|
+
export const dataGridStyles = StyleSheet.create((theme: Theme) => {
|
|
35
|
+
return {
|
|
36
|
+
container: {
|
|
37
|
+
borderWidth: 1,
|
|
38
|
+
borderColor: theme.colors.border.primary,
|
|
39
|
+
borderStyle: 'solid',
|
|
40
|
+
overflow: 'hidden',
|
|
41
|
+
display: 'flex',
|
|
42
|
+
flexDirection: 'column',
|
|
43
|
+
_web: {
|
|
44
|
+
boxSizing: 'border-box',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
scrollView: {
|
|
49
|
+
flex: 1,
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
scrollViewContent: {
|
|
53
|
+
// Dynamic width set in component based on column widths
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
table: {
|
|
57
|
+
// Dynamic width and height set in component
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
header: ({ stickyHeader }: DataGridVariants) => ({
|
|
61
|
+
variants: {
|
|
62
|
+
stickyHeader: {
|
|
63
|
+
true: {
|
|
64
|
+
_web: {
|
|
65
|
+
position: 'sticky',
|
|
66
|
+
top: 0,
|
|
67
|
+
zIndex: 100,
|
|
68
|
+
boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
|
|
69
|
+
},
|
|
70
|
+
_native: {
|
|
71
|
+
elevation: 4,
|
|
72
|
+
zIndex: 100,
|
|
73
|
+
shadowColor: '#000',
|
|
74
|
+
shadowOffset: { width: 0, height: 2 },
|
|
75
|
+
shadowOpacity: 0.1,
|
|
76
|
+
shadowRadius: 4,
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
false: {},
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
}),
|
|
83
|
+
|
|
84
|
+
headerRow: ({ stickyHeader }: DataGridVariants) => ({
|
|
85
|
+
backgroundColor: stickyHeader ? theme.colors.surface.primary : theme.colors.surface.secondary,
|
|
86
|
+
borderBottomWidth: 2,
|
|
87
|
+
borderBottomColor: theme.colors.border.secondary,
|
|
88
|
+
borderStyle: 'solid',
|
|
89
|
+
flexDirection: 'row',
|
|
90
|
+
variants: {
|
|
91
|
+
stickyHeader: {
|
|
92
|
+
true: {
|
|
93
|
+
backgroundColor: theme.colors.surface.primary,
|
|
94
|
+
},
|
|
95
|
+
false: {
|
|
96
|
+
backgroundColor: theme.colors.surface.secondary,
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
}),
|
|
101
|
+
|
|
102
|
+
headerCell: {
|
|
103
|
+
padding: theme.sizes.view.md.spacing,
|
|
104
|
+
borderRightWidth: 1,
|
|
105
|
+
borderRightColor: theme.colors.border.secondary,
|
|
106
|
+
borderStyle: 'solid',
|
|
107
|
+
_web: {
|
|
108
|
+
boxSizing: 'border-box',
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
headerText: ({ clickable }: DataGridVariants) => ({
|
|
113
|
+
userSelect: 'none',
|
|
114
|
+
display: 'flex',
|
|
115
|
+
alignItems: 'center',
|
|
116
|
+
variants: {
|
|
117
|
+
clickable: {
|
|
118
|
+
true: {
|
|
119
|
+
_web: {
|
|
120
|
+
cursor: 'pointer',
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
false: {
|
|
124
|
+
_web: {
|
|
125
|
+
cursor: 'default',
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
}),
|
|
131
|
+
|
|
132
|
+
row: ({ selected }: DataGridVariants) => ({
|
|
133
|
+
borderBottomWidth: 1,
|
|
134
|
+
borderBottomColor: theme.colors.border.primary,
|
|
135
|
+
borderStyle: 'solid',
|
|
136
|
+
backgroundColor: theme.colors.surface.primary,
|
|
137
|
+
flexDirection: 'row',
|
|
138
|
+
_web: {
|
|
139
|
+
transition: 'background-color 0.2s ease',
|
|
140
|
+
},
|
|
141
|
+
variants: {
|
|
142
|
+
selected: {
|
|
143
|
+
true: {
|
|
144
|
+
backgroundColor: theme.intents.primary.hover,
|
|
145
|
+
},
|
|
146
|
+
false: {
|
|
147
|
+
backgroundColor: theme.colors.surface.primary,
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
}),
|
|
152
|
+
|
|
153
|
+
cell: ({ alignment }: DataGridVariants) => ({
|
|
154
|
+
padding: theme.sizes.view.md.spacing,
|
|
155
|
+
borderRightWidth: 1,
|
|
156
|
+
borderRightColor: theme.colors.border.primary,
|
|
157
|
+
borderStyle: 'solid',
|
|
158
|
+
_web: {
|
|
159
|
+
boxSizing: 'border-box',
|
|
160
|
+
},
|
|
161
|
+
variants: {
|
|
162
|
+
alignment: {
|
|
163
|
+
left: {
|
|
164
|
+
justifyContent: 'flex-start',
|
|
165
|
+
alignItems: 'flex-start',
|
|
166
|
+
},
|
|
167
|
+
center: {
|
|
168
|
+
justifyContent: 'center',
|
|
169
|
+
alignItems: 'center',
|
|
170
|
+
},
|
|
171
|
+
right: {
|
|
172
|
+
justifyContent: 'flex-end',
|
|
173
|
+
alignItems: 'flex-end',
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
}),
|
|
178
|
+
|
|
179
|
+
spacerRow: {
|
|
180
|
+
// Dynamic height set in component
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
spacerCell: {
|
|
184
|
+
padding: 0,
|
|
185
|
+
borderWidth: 0,
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
});
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import React, { useState, useCallback, useMemo, useRef
|
|
1
|
+
import React, { useState, useCallback, useMemo, useRef } from 'react';
|
|
2
2
|
import { View, Text } from '@idealyst/components';
|
|
3
3
|
import { ScrollView } from '../primitives/ScrollView';
|
|
4
4
|
import { Table, TableRow, TableCell, TableHeader, TableBody } from '../primitives/Table';
|
|
5
5
|
import type { DataGridProps, Column } from './types';
|
|
6
|
+
import { dataGridStyles } from './DataGrid.styles';
|
|
6
7
|
|
|
7
8
|
export function DataGrid<T extends Record<string, any>>({
|
|
8
9
|
data,
|
|
@@ -25,7 +26,6 @@ export function DataGrid<T extends Record<string, any>>({
|
|
|
25
26
|
const [sortColumn, setSortColumn] = useState<string | null>(null);
|
|
26
27
|
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc');
|
|
27
28
|
const [scrollTop, setScrollTop] = useState(0);
|
|
28
|
-
const scrollRef = useRef<any>(null);
|
|
29
29
|
|
|
30
30
|
// Virtualization calculations
|
|
31
31
|
const visibleRange = useMemo(() => {
|
|
@@ -72,7 +72,7 @@ export function DataGrid<T extends Record<string, any>>({
|
|
|
72
72
|
boxSizing: 'border-box' as const,
|
|
73
73
|
flexShrink: 0,
|
|
74
74
|
};
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
if (column.width) {
|
|
77
77
|
return {
|
|
78
78
|
...baseStyle,
|
|
@@ -91,39 +91,6 @@ export function DataGrid<T extends Record<string, any>>({
|
|
|
91
91
|
}
|
|
92
92
|
};
|
|
93
93
|
|
|
94
|
-
// Helper function for consistent cell base styles
|
|
95
|
-
const getCellBaseStyle = (theme: any) => ({
|
|
96
|
-
padding: theme.spacing.sm,
|
|
97
|
-
borderRightWidth: 1,
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
// Helper function for platform-specific header styles
|
|
101
|
-
const getStickyHeaderStyle = (theme: any) => {
|
|
102
|
-
if (!stickyHeader) return {};
|
|
103
|
-
|
|
104
|
-
// Platform detection - check if we're on web or native
|
|
105
|
-
const isWeb = typeof document !== 'undefined';
|
|
106
|
-
|
|
107
|
-
if (isWeb) {
|
|
108
|
-
return {
|
|
109
|
-
position: 'sticky' as const,
|
|
110
|
-
top: 0,
|
|
111
|
-
zIndex: 100,
|
|
112
|
-
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
|
|
113
|
-
};
|
|
114
|
-
} else {
|
|
115
|
-
// React Native - use elevation instead of boxShadow
|
|
116
|
-
return {
|
|
117
|
-
elevation: 4,
|
|
118
|
-
zIndex: 100,
|
|
119
|
-
shadowColor: '#000',
|
|
120
|
-
shadowOffset: { width: 0, height: 2 },
|
|
121
|
-
shadowOpacity: 0.1,
|
|
122
|
-
shadowRadius: 4,
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
|
|
127
94
|
const handleSort = useCallback((column: Column<T>) => {
|
|
128
95
|
if (!column.sortable) return;
|
|
129
96
|
|
|
@@ -151,37 +118,28 @@ export function DataGrid<T extends Record<string, any>>({
|
|
|
151
118
|
}, [selectedRows, onSelectionChange, multiSelect, onRowClick]);
|
|
152
119
|
|
|
153
120
|
const renderHeader = () => (
|
|
154
|
-
<TableRow style={
|
|
155
|
-
|
|
156
|
-
borderBottomWidth: 2,
|
|
157
|
-
borderBottomColor: theme.colors.neutral[200],
|
|
121
|
+
<TableRow style={{
|
|
122
|
+
...dataGridStyles.headerRow({ stickyHeader }),
|
|
158
123
|
minHeight: headerHeight,
|
|
159
|
-
flexDirection: 'row',
|
|
160
|
-
...getStickyHeaderStyle(theme),
|
|
161
124
|
...headerStyle,
|
|
162
|
-
}
|
|
125
|
+
}}>
|
|
163
126
|
{columns.map((column) => (
|
|
164
127
|
<TableCell
|
|
165
128
|
key={column.key}
|
|
166
129
|
width={column.width}
|
|
167
|
-
style={
|
|
168
|
-
...
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
})}
|
|
130
|
+
style={{
|
|
131
|
+
...dataGridStyles.headerCell,
|
|
132
|
+
...getColumnStyle(column),
|
|
133
|
+
}}
|
|
134
|
+
onPress={column.sortable ? () => handleSort(column) : undefined}
|
|
173
135
|
>
|
|
174
|
-
<Text
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
136
|
+
<Text
|
|
137
|
+
weight="bold"
|
|
138
|
+
style={dataGridStyles.headerText({ clickable: column.sortable || false })}
|
|
139
|
+
>
|
|
178
140
|
{column.header}
|
|
179
141
|
{column.sortable && (
|
|
180
|
-
<Text
|
|
181
|
-
fontSize: 10,
|
|
182
|
-
marginLeft: theme.spacing.xs,
|
|
183
|
-
color: theme.colors.primary[500],
|
|
184
|
-
})}>
|
|
142
|
+
<Text style={{ marginLeft: 4 }}>
|
|
185
143
|
{sortColumn === column.key ? ` ${sortDirection === 'asc' ? '▲' : '▼'}` : ''}
|
|
186
144
|
</Text>
|
|
187
145
|
)}
|
|
@@ -195,36 +153,33 @@ export function DataGrid<T extends Record<string, any>>({
|
|
|
195
153
|
const actualIndex = virtualized ? visibleRange.start + virtualIndex : virtualIndex;
|
|
196
154
|
const isSelected = selectedRows.includes(actualIndex);
|
|
197
155
|
const computedRowStyle = typeof rowStyle === 'function' ? rowStyle(item, actualIndex) : rowStyle;
|
|
198
|
-
|
|
156
|
+
|
|
199
157
|
return (
|
|
200
158
|
<TableRow
|
|
201
159
|
key={actualIndex}
|
|
202
|
-
style={
|
|
203
|
-
|
|
204
|
-
borderBottomColor: theme.colors.neutral[100],
|
|
205
|
-
backgroundColor: isSelected ? theme.colors.primary[50] : theme.colors.background,
|
|
160
|
+
style={{
|
|
161
|
+
...dataGridStyles.row({ selected: isSelected }),
|
|
206
162
|
minHeight: rowHeight,
|
|
207
|
-
flexDirection: 'row',
|
|
208
163
|
...computedRowStyle,
|
|
209
|
-
}
|
|
164
|
+
}}
|
|
210
165
|
onPress={() => handleRowClick(item, actualIndex)}
|
|
211
166
|
>
|
|
212
167
|
{columns.map((column) => {
|
|
213
168
|
const value = column.accessor ? column.accessor(item) : item[column.key];
|
|
214
169
|
const cellContent = column.render ? column.render(value, item, actualIndex) : value;
|
|
215
|
-
const computedCellStyle = typeof column.cellStyle === 'function'
|
|
216
|
-
? column.cellStyle(value, item)
|
|
170
|
+
const computedCellStyle = typeof column.cellStyle === 'function'
|
|
171
|
+
? column.cellStyle(value, item)
|
|
217
172
|
: column.cellStyle;
|
|
218
|
-
|
|
173
|
+
|
|
219
174
|
return (
|
|
220
175
|
<TableCell
|
|
221
176
|
key={column.key}
|
|
222
177
|
width={column.width}
|
|
223
|
-
style={
|
|
224
|
-
...
|
|
225
|
-
|
|
178
|
+
style={{
|
|
179
|
+
...dataGridStyles.cell({ alignment: column.align || 'left' }),
|
|
180
|
+
...getColumnStyle(column),
|
|
226
181
|
...computedCellStyle,
|
|
227
|
-
}
|
|
182
|
+
}}
|
|
228
183
|
>
|
|
229
184
|
{typeof cellContent === 'string' || typeof cellContent === 'number' ? (
|
|
230
185
|
<Text>{cellContent}</Text>
|
|
@@ -241,57 +196,53 @@ export function DataGrid<T extends Record<string, any>>({
|
|
|
241
196
|
const containerHeight = typeof height === 'number' ? height : undefined;
|
|
242
197
|
|
|
243
198
|
return (
|
|
244
|
-
<View style={
|
|
245
|
-
|
|
246
|
-
borderWidth: 1,
|
|
247
|
-
borderColor: theme.colors.neutral[200],
|
|
248
|
-
borderRadius: theme.radius.md,
|
|
249
|
-
overflow: 'hidden',
|
|
199
|
+
<View style={{
|
|
200
|
+
...dataGridStyles.container,
|
|
250
201
|
width,
|
|
251
202
|
height,
|
|
252
|
-
display: 'flex',
|
|
253
|
-
flexDirection: 'column',
|
|
254
203
|
...style,
|
|
255
|
-
}
|
|
204
|
+
}}>
|
|
256
205
|
<ScrollView
|
|
257
|
-
style={{
|
|
258
|
-
|
|
206
|
+
style={{
|
|
207
|
+
...dataGridStyles.scrollView,
|
|
259
208
|
...(containerHeight ? { maxHeight: containerHeight } : {})
|
|
260
209
|
}}
|
|
261
210
|
contentContainerStyle={{
|
|
211
|
+
...dataGridStyles.scrollViewContent,
|
|
262
212
|
width: minTableWidth,
|
|
263
213
|
}}
|
|
264
214
|
showsVerticalScrollIndicator={true}
|
|
265
215
|
showsHorizontalScrollIndicator={true}
|
|
266
|
-
bounces={false}
|
|
267
|
-
directionalLockEnabled={false}
|
|
268
216
|
onScroll={handleScroll}
|
|
269
217
|
scrollEventThrottle={16}
|
|
270
218
|
>
|
|
271
219
|
<Table style={{
|
|
220
|
+
...dataGridStyles.table,
|
|
272
221
|
width: minTableWidth,
|
|
273
222
|
...(virtualized ? { height: totalHeight } : {})
|
|
274
223
|
}}>
|
|
275
|
-
<TableHeader>
|
|
224
|
+
<TableHeader style={dataGridStyles.header({ stickyHeader })}>
|
|
276
225
|
{renderHeader()}
|
|
277
226
|
</TableHeader>
|
|
278
227
|
<TableBody>
|
|
279
228
|
{virtualized && visibleRange.offsetY > 0 && (
|
|
280
|
-
<TableRow style={{ height: visibleRange.offsetY }}>
|
|
281
|
-
<TableCell
|
|
282
|
-
style={{
|
|
229
|
+
<TableRow style={{ ...dataGridStyles.spacerRow, height: visibleRange.offsetY }}>
|
|
230
|
+
<TableCell
|
|
231
|
+
style={{ ...dataGridStyles.spacerCell, height: visibleRange.offsetY }}
|
|
283
232
|
colSpan={columns.length}
|
|
284
233
|
>
|
|
234
|
+
<View />
|
|
285
235
|
</TableCell>
|
|
286
236
|
</TableRow>
|
|
287
237
|
)}
|
|
288
238
|
{visibleData.map((item, index) => renderRow(item, index))}
|
|
289
239
|
{virtualized && (data.length - visibleRange.end - 1) > 0 && (
|
|
290
|
-
<TableRow style={{ height: (data.length - visibleRange.end - 1) * rowHeight }}>
|
|
291
|
-
<TableCell
|
|
292
|
-
style={{
|
|
240
|
+
<TableRow style={{ ...dataGridStyles.spacerRow, height: (data.length - visibleRange.end - 1) * rowHeight }}>
|
|
241
|
+
<TableCell
|
|
242
|
+
style={{ ...dataGridStyles.spacerCell, height: (data.length - visibleRange.end - 1) * rowHeight }}
|
|
293
243
|
colSpan={columns.length}
|
|
294
244
|
>
|
|
245
|
+
<View />
|
|
295
246
|
</TableCell>
|
|
296
247
|
</TableRow>
|
|
297
248
|
)}
|
package/src/DataGrid/types.ts
CHANGED
|
@@ -8,6 +8,7 @@ export interface Column<T = any> {
|
|
|
8
8
|
maxWidth?: number;
|
|
9
9
|
resizable?: boolean;
|
|
10
10
|
sortable?: boolean;
|
|
11
|
+
align?: 'left' | 'center' | 'right';
|
|
11
12
|
accessor?: (row: T) => any;
|
|
12
13
|
render?: (value: any, row: T, index: number) => React.ReactNode;
|
|
13
14
|
headerStyle?: ViewStyle;
|
|
@@ -57,7 +57,7 @@ export function BasicExample() {
|
|
|
57
57
|
width: 100,
|
|
58
58
|
render: (value) => (
|
|
59
59
|
<View style={{ paddingHorizontal: 8, paddingVertical: 4, backgroundColor: '#f0f0f0', borderRadius: 4 }}>
|
|
60
|
-
<Text size="
|
|
60
|
+
<Text size="sm">{value}</Text>
|
|
61
61
|
</View>
|
|
62
62
|
),
|
|
63
63
|
},
|
|
@@ -67,7 +67,7 @@ export function BasicExample() {
|
|
|
67
67
|
width: 100,
|
|
68
68
|
render: (value) => (
|
|
69
69
|
<Text
|
|
70
|
-
size="
|
|
70
|
+
size="sm"
|
|
71
71
|
weight="medium"
|
|
72
72
|
style={{ color: value === 'active' ? '#22c55e' : '#ef4444' }}
|
|
73
73
|
>
|
|
@@ -103,13 +103,13 @@ export function BasicExample() {
|
|
|
103
103
|
|
|
104
104
|
return (
|
|
105
105
|
<View spacing="lg" style={{ padding: 20 }}>
|
|
106
|
-
<Text size="
|
|
106
|
+
<Text size="xl" weight="bold">DataGrid Example</Text>
|
|
107
107
|
|
|
108
108
|
<View spacing="sm">
|
|
109
109
|
<Text>Selected rows: {selectedRows.length}</Text>
|
|
110
110
|
{selectedRows.length > 0 && (
|
|
111
111
|
<Button
|
|
112
|
-
size="
|
|
112
|
+
size="sm"
|
|
113
113
|
variant="outlined"
|
|
114
114
|
onPress={() => setSelectedRows([])}
|
|
115
115
|
>
|
|
@@ -73,7 +73,7 @@ export function DataGridShowcase({
|
|
|
73
73
|
width: showAllColumns ? 120 : 100,
|
|
74
74
|
sortable: true,
|
|
75
75
|
render: (value) => (
|
|
76
|
-
<Badge variant="outlined" size="
|
|
76
|
+
<Badge variant="outlined" size="sm">
|
|
77
77
|
{value}
|
|
78
78
|
</Badge>
|
|
79
79
|
),
|
|
@@ -84,7 +84,7 @@ export function DataGridShowcase({
|
|
|
84
84
|
width: showAllColumns ? 100 : 80,
|
|
85
85
|
sortable: true,
|
|
86
86
|
render: (value) => (
|
|
87
|
-
<Text weight="semibold" size={showAllColumns ? '
|
|
87
|
+
<Text weight="semibold" size={showAllColumns ? 'md' : 'sm'}>
|
|
88
88
|
${value.toFixed(2)}
|
|
89
89
|
</Text>
|
|
90
90
|
),
|
|
@@ -96,7 +96,7 @@ export function DataGridShowcase({
|
|
|
96
96
|
sortable: true,
|
|
97
97
|
render: (value, row) => (
|
|
98
98
|
<Text
|
|
99
|
-
size="
|
|
99
|
+
size="sm"
|
|
100
100
|
style={{
|
|
101
101
|
color: row.status === 'out-of-stock' ? '#ef4444' :
|
|
102
102
|
row.status === 'low-stock' ? '#f59e0b' : '#22c55e'
|
|
@@ -114,7 +114,7 @@ export function DataGridShowcase({
|
|
|
114
114
|
const intent = value === 'in-stock' ? 'success' :
|
|
115
115
|
value === 'low-stock' ? 'warning' : 'error';
|
|
116
116
|
return (
|
|
117
|
-
<Badge variant="filled" intent={intent} size="
|
|
117
|
+
<Badge variant="filled" intent={intent} size="sm">
|
|
118
118
|
{value}
|
|
119
119
|
</Badge>
|
|
120
120
|
);
|
|
@@ -171,13 +171,13 @@ export function DataGridShowcase({
|
|
|
171
171
|
return (
|
|
172
172
|
<Screen background="primary" safeArea scrollable={false}>
|
|
173
173
|
<View spacing={showAllColumns ? "lg" : "md"} style={{ padding: showAllColumns ? 16 : 12, flex: 1 }}>
|
|
174
|
-
<Text size={showAllColumns ? "
|
|
174
|
+
<Text size={showAllColumns ? "xl" : "lg"} weight="bold">
|
|
175
175
|
DataGrid {showAllColumns ? 'Component ' : ''}Showcase
|
|
176
176
|
</Text>
|
|
177
177
|
|
|
178
178
|
<Card variant="outlined">
|
|
179
179
|
<View spacing={showAllColumns ? "md" : "sm"}>
|
|
180
|
-
<Text weight="semibold" size="
|
|
180
|
+
<Text weight="semibold" size="md">Controls</Text>
|
|
181
181
|
|
|
182
182
|
<View
|
|
183
183
|
spacing="sm"
|
|
@@ -190,7 +190,7 @@ export function DataGridShowcase({
|
|
|
190
190
|
>
|
|
191
191
|
<Button
|
|
192
192
|
variant="outlined"
|
|
193
|
-
size="
|
|
193
|
+
size="sm"
|
|
194
194
|
onPress={() => setVirtualized(!virtualized)}
|
|
195
195
|
>
|
|
196
196
|
{virtualized ? 'Disable' : 'Enable'} Virtualization
|
|
@@ -200,7 +200,7 @@ export function DataGridShowcase({
|
|
|
200
200
|
<View style={{ flexDirection: 'row', gap: 8 }}>
|
|
201
201
|
<Button
|
|
202
202
|
variant="outlined"
|
|
203
|
-
size="
|
|
203
|
+
size="sm"
|
|
204
204
|
onPress={() => setSelectedRows([])}
|
|
205
205
|
disabled={selectedRows.length === 0}
|
|
206
206
|
>
|
|
@@ -210,7 +210,7 @@ export function DataGridShowcase({
|
|
|
210
210
|
<Button
|
|
211
211
|
variant="outlined"
|
|
212
212
|
intent="error"
|
|
213
|
-
size="
|
|
213
|
+
size="sm"
|
|
214
214
|
onPress={handleDeleteSelected}
|
|
215
215
|
disabled={selectedRows.length === 0}
|
|
216
216
|
>
|
|
@@ -223,7 +223,7 @@ export function DataGridShowcase({
|
|
|
223
223
|
<>
|
|
224
224
|
<Button
|
|
225
225
|
variant="outlined"
|
|
226
|
-
size="
|
|
226
|
+
size="sm"
|
|
227
227
|
onPress={() => setSelectedRows([])}
|
|
228
228
|
disabled={selectedRows.length === 0}
|
|
229
229
|
>
|
|
@@ -233,7 +233,7 @@ export function DataGridShowcase({
|
|
|
233
233
|
<Button
|
|
234
234
|
variant="outlined"
|
|
235
235
|
intent="error"
|
|
236
|
-
size="
|
|
236
|
+
size="sm"
|
|
237
237
|
onPress={handleDeleteSelected}
|
|
238
238
|
disabled={selectedRows.length === 0}
|
|
239
239
|
>
|
|
@@ -244,7 +244,7 @@ export function DataGridShowcase({
|
|
|
244
244
|
|
|
245
245
|
<Button
|
|
246
246
|
variant="outlined"
|
|
247
|
-
size="
|
|
247
|
+
size="sm"
|
|
248
248
|
onPress={handleResetData}
|
|
249
249
|
>
|
|
250
250
|
Reset Data
|
|
@@ -252,9 +252,9 @@ export function DataGridShowcase({
|
|
|
252
252
|
</View>
|
|
253
253
|
|
|
254
254
|
<View spacing="xs">
|
|
255
|
-
<Text size="
|
|
256
|
-
<Text size="
|
|
257
|
-
<Text size="
|
|
255
|
+
<Text size="sm">• {showAllColumns ? 'Total ' : ''}Products: {products.length}</Text>
|
|
256
|
+
<Text size="sm">• Selected: {selectedRows.length}</Text>
|
|
257
|
+
<Text size="sm">• Virtualization: {virtualized ? (showAllColumns ? 'Enabled' : 'On') : (showAllColumns ? 'Disabled' : 'Off')}</Text>
|
|
258
258
|
</View>
|
|
259
259
|
</View>
|
|
260
260
|
</Card>
|
|
@@ -280,14 +280,14 @@ export function DataGridShowcase({
|
|
|
280
280
|
|
|
281
281
|
<Card variant="outlined">
|
|
282
282
|
<View spacing={showAllColumns ? "sm" : "xs"}>
|
|
283
|
-
<Text weight="semibold" size="
|
|
284
|
-
<Text size="
|
|
285
|
-
<Text size="
|
|
286
|
-
<Text size="
|
|
287
|
-
<Text size="
|
|
288
|
-
<Text size="
|
|
289
|
-
<Text size="
|
|
290
|
-
{showAllColumns && <Text size="
|
|
283
|
+
<Text weight="semibold" size="md">Features {showAllColumns ? 'Demonstrated' : ''}</Text>
|
|
284
|
+
<Text size="sm">✓ Virtualized rendering{showAllColumns ? ` with ${productCount} rows` : ''}</Text>
|
|
285
|
+
<Text size="sm">✓ Sortable columns{showAllColumns ? ' (ID, Name, Category, Price, Stock, Vendor, Date)' : ''}</Text>
|
|
286
|
+
<Text size="sm">✓ Multi-row selection{showAllColumns ? ' with visual feedback' : ''}</Text>
|
|
287
|
+
<Text size="sm">✓ Custom cell rendering{showAllColumns ? ' (badges, colored text)' : ''}</Text>
|
|
288
|
+
<Text size="sm">✓ Sticky header{showAllColumns ? ' while scrolling' : ''}</Text>
|
|
289
|
+
<Text size="sm">✓ Alternating row colors</Text>
|
|
290
|
+
{showAllColumns && <Text size="sm">✓ Responsive column widths</Text>}
|
|
291
291
|
</View>
|
|
292
292
|
</Card>
|
|
293
293
|
</View>
|
|
@@ -69,11 +69,13 @@ interface TableCellProps {
|
|
|
69
69
|
style?: any;
|
|
70
70
|
width?: number | string;
|
|
71
71
|
colSpan?: number;
|
|
72
|
+
onPress?: () => void;
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
export const TableCell: React.FC<TableCellProps> = ({ children, style, width, colSpan }) => {
|
|
75
|
+
export const TableCell: React.FC<TableCellProps> = ({ children, style, width, colSpan, onPress }) => {
|
|
75
76
|
const flexValue = colSpan ? colSpan : 1;
|
|
76
|
-
|
|
77
|
+
const Wrapper = onPress ? TouchableOpacity : View;
|
|
78
|
+
|
|
77
79
|
let resolvedStyle = {};
|
|
78
80
|
if (typeof style === 'function') {
|
|
79
81
|
try {
|
|
@@ -84,16 +86,16 @@ export const TableCell: React.FC<TableCellProps> = ({ children, style, width, co
|
|
|
84
86
|
} else if (style) {
|
|
85
87
|
resolvedStyle = style;
|
|
86
88
|
}
|
|
87
|
-
|
|
89
|
+
|
|
88
90
|
const combinedStyle = [
|
|
89
91
|
{ justifyContent: 'center' },
|
|
90
92
|
width ? { width, flex: 0 } : { flex: flexValue },
|
|
91
93
|
resolvedStyle
|
|
92
94
|
];
|
|
93
|
-
|
|
95
|
+
|
|
94
96
|
return (
|
|
95
|
-
<
|
|
97
|
+
<Wrapper style={combinedStyle} onPress={onPress}>
|
|
96
98
|
{children}
|
|
97
|
-
</
|
|
99
|
+
</Wrapper>
|
|
98
100
|
);
|
|
99
101
|
};
|
|
@@ -74,9 +74,10 @@ interface TableCellProps {
|
|
|
74
74
|
style?: any;
|
|
75
75
|
width?: number | string;
|
|
76
76
|
colSpan?: number;
|
|
77
|
+
onPress?: () => void;
|
|
77
78
|
}
|
|
78
79
|
|
|
79
|
-
export const TableCell: React.FC<TableCellProps> = ({ children, style, width, colSpan }) => {
|
|
80
|
+
export const TableCell: React.FC<TableCellProps> = ({ children, style, width, colSpan, onPress }) => {
|
|
80
81
|
let resolvedStyle = {};
|
|
81
82
|
if (typeof style === 'function') {
|
|
82
83
|
try {
|
|
@@ -87,15 +88,27 @@ export const TableCell: React.FC<TableCellProps> = ({ children, style, width, co
|
|
|
87
88
|
} else if (style) {
|
|
88
89
|
resolvedStyle = style;
|
|
89
90
|
}
|
|
90
|
-
|
|
91
|
+
|
|
91
92
|
const combinedStyle = {
|
|
92
93
|
verticalAlign: 'middle',
|
|
93
94
|
...(width && { width }),
|
|
94
95
|
...resolvedStyle,
|
|
95
96
|
};
|
|
96
|
-
|
|
97
|
+
|
|
97
98
|
return (
|
|
98
|
-
<td
|
|
99
|
+
<td
|
|
100
|
+
style={combinedStyle}
|
|
101
|
+
colSpan={colSpan}
|
|
102
|
+
onClick={onPress}
|
|
103
|
+
role={onPress ? 'button' : undefined}
|
|
104
|
+
tabIndex={onPress ? 0 : undefined}
|
|
105
|
+
onKeyDown={onPress ? (e) => {
|
|
106
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
107
|
+
e.preventDefault();
|
|
108
|
+
onPress();
|
|
109
|
+
}
|
|
110
|
+
} : undefined}
|
|
111
|
+
>
|
|
99
112
|
{children}
|
|
100
113
|
</td>
|
|
101
114
|
);
|