@idealyst/datagrid 1.0.41 → 1.0.44
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 +3 -3
- package/src/DataGrid/DataGrid.native.tsx +267 -0
- package/src/DataGrid/DataGrid.tsx +211 -96
- package/src/DataGrid/SimpleDataGrid.tsx +74 -0
- package/src/DataGrid/index.native.ts +2 -0
- package/src/DataGrid/index.web.ts +2 -0
- package/src/examples/BasicExample.tsx +1 -1
- package/src/examples/DataGridShowcase.tsx +296 -0
- package/src/examples/index.ts +2 -1
- package/src/index.native.ts +1 -1
- package/src/primitives/Cell/Cell.native.tsx +15 -0
- package/src/primitives/Cell/Cell.web.tsx +36 -0
- package/src/primitives/Cell/index.native.ts +1 -0
- package/src/primitives/Cell/index.ts +1 -0
- package/src/primitives/Row/Row.native.tsx +21 -0
- package/src/primitives/Row/Row.web.tsx +48 -0
- package/src/primitives/Row/index.native.ts +1 -0
- package/src/primitives/Row/index.ts +1 -0
- package/src/primitives/ScrollView/ScrollView.web.tsx +3 -0
- package/src/primitives/Table/Table.native.tsx +99 -0
- package/src/primitives/Table/Table.web.tsx +102 -0
- package/src/primitives/Table/TableBody.native.tsx +27 -0
- package/src/primitives/Table/TableBody.web.tsx +26 -0
- package/src/primitives/Table/TableHeader.native.tsx +27 -0
- package/src/primitives/Table/TableHeader.web.tsx +26 -0
- package/src/primitives/Table/index.native.ts +3 -0
- package/src/primitives/Table/index.ts +3 -0
- package/src/primitives/VirtualizedList/VirtualizedList.web.tsx +9 -7
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface SimpleColumn {
|
|
4
|
+
key: string;
|
|
5
|
+
header: string;
|
|
6
|
+
width?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface SimpleDataGridProps {
|
|
10
|
+
data: any[];
|
|
11
|
+
columns: SimpleColumn[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const SimpleDataGrid: React.FC<SimpleDataGridProps> = ({ data, columns }) => {
|
|
15
|
+
return (
|
|
16
|
+
<div style={{
|
|
17
|
+
border: '1px solid #ccc',
|
|
18
|
+
borderRadius: '4px',
|
|
19
|
+
overflow: 'hidden'
|
|
20
|
+
}}>
|
|
21
|
+
<table style={{
|
|
22
|
+
width: '100%',
|
|
23
|
+
borderCollapse: 'collapse',
|
|
24
|
+
tableLayout: 'fixed'
|
|
25
|
+
}}>
|
|
26
|
+
<thead>
|
|
27
|
+
<tr style={{
|
|
28
|
+
backgroundColor: '#f5f5f5',
|
|
29
|
+
borderBottom: '2px solid #ddd'
|
|
30
|
+
}}>
|
|
31
|
+
{columns.map(column => (
|
|
32
|
+
<th
|
|
33
|
+
key={column.key}
|
|
34
|
+
style={{
|
|
35
|
+
padding: '12px 8px',
|
|
36
|
+
textAlign: 'left',
|
|
37
|
+
fontWeight: 'bold',
|
|
38
|
+
borderRight: '1px solid #ddd',
|
|
39
|
+
...(column.width ? { width: column.width } : {})
|
|
40
|
+
}}
|
|
41
|
+
>
|
|
42
|
+
{column.header}
|
|
43
|
+
</th>
|
|
44
|
+
))}
|
|
45
|
+
</tr>
|
|
46
|
+
</thead>
|
|
47
|
+
<tbody>
|
|
48
|
+
{data.map((row, index) => (
|
|
49
|
+
<tr
|
|
50
|
+
key={index}
|
|
51
|
+
style={{
|
|
52
|
+
borderBottom: '1px solid #eee',
|
|
53
|
+
backgroundColor: index % 2 === 0 ? '#fafafa' : '#fff'
|
|
54
|
+
}}
|
|
55
|
+
>
|
|
56
|
+
{columns.map(column => (
|
|
57
|
+
<td
|
|
58
|
+
key={column.key}
|
|
59
|
+
style={{
|
|
60
|
+
padding: '12px 8px',
|
|
61
|
+
borderRight: '1px solid #eee',
|
|
62
|
+
verticalAlign: 'middle'
|
|
63
|
+
}}
|
|
64
|
+
>
|
|
65
|
+
{row[column.key]}
|
|
66
|
+
</td>
|
|
67
|
+
))}
|
|
68
|
+
</tr>
|
|
69
|
+
))}
|
|
70
|
+
</tbody>
|
|
71
|
+
</table>
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
};
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { View, Text, Button, Screen, Card, Badge } from '@idealyst/components';
|
|
3
|
+
import { DataGrid } from '../DataGrid';
|
|
4
|
+
import type { Column } from '../DataGrid/types';
|
|
5
|
+
|
|
6
|
+
interface Product {
|
|
7
|
+
id: number;
|
|
8
|
+
name: string;
|
|
9
|
+
category: string;
|
|
10
|
+
price: number;
|
|
11
|
+
stock: number;
|
|
12
|
+
status: 'in-stock' | 'low-stock' | 'out-of-stock';
|
|
13
|
+
vendor: string;
|
|
14
|
+
lastUpdated: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const generateSampleProducts = (count: number = 100): Product[] => {
|
|
18
|
+
const categories = ['Electronics', 'Clothing', 'Food', 'Books', 'Toys', 'Sports'];
|
|
19
|
+
const vendors = ['Vendor A', 'Vendor B', 'Vendor C', 'Vendor D'];
|
|
20
|
+
const products: Product[] = [];
|
|
21
|
+
|
|
22
|
+
for (let i = 1; i <= count; i++) {
|
|
23
|
+
const stock = Math.floor(Math.random() * 100);
|
|
24
|
+
products.push({
|
|
25
|
+
id: i,
|
|
26
|
+
name: `Product ${i}`,
|
|
27
|
+
category: categories[Math.floor(Math.random() * categories.length)],
|
|
28
|
+
price: parseFloat((Math.random() * 999 + 1).toFixed(2)),
|
|
29
|
+
stock,
|
|
30
|
+
status: stock === 0 ? 'out-of-stock' : stock < 10 ? 'low-stock' : 'in-stock',
|
|
31
|
+
vendor: vendors[Math.floor(Math.random() * vendors.length)],
|
|
32
|
+
lastUpdated: new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return products;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
interface DataGridShowcaseProps {
|
|
40
|
+
/** Number of sample products to generate */
|
|
41
|
+
productCount?: number;
|
|
42
|
+
/** Whether to show all columns (false hides vendor and lastUpdated) */
|
|
43
|
+
showAllColumns?: boolean;
|
|
44
|
+
/** Height of the DataGrid */
|
|
45
|
+
height?: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function DataGridShowcase({
|
|
49
|
+
productCount = 100,
|
|
50
|
+
showAllColumns = true,
|
|
51
|
+
height = 500
|
|
52
|
+
}: DataGridShowcaseProps = {}) {
|
|
53
|
+
const [products, setProducts] = useState<Product[]>(() => generateSampleProducts(productCount));
|
|
54
|
+
const [selectedRows, setSelectedRows] = useState<number[]>([]);
|
|
55
|
+
const [virtualized, setVirtualized] = useState(false);
|
|
56
|
+
|
|
57
|
+
const baseColumns: Column<Product>[] = [
|
|
58
|
+
{
|
|
59
|
+
key: 'id',
|
|
60
|
+
header: 'ID',
|
|
61
|
+
width: showAllColumns ? 60 : 50,
|
|
62
|
+
sortable: true,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
key: 'name',
|
|
66
|
+
header: showAllColumns ? 'Product Name' : 'Product',
|
|
67
|
+
width: showAllColumns ? 150 : 120,
|
|
68
|
+
sortable: true,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
key: 'category',
|
|
72
|
+
header: 'Category',
|
|
73
|
+
width: showAllColumns ? 120 : 100,
|
|
74
|
+
sortable: true,
|
|
75
|
+
render: (value) => (
|
|
76
|
+
<Badge variant="outlined" size="small">
|
|
77
|
+
{value}
|
|
78
|
+
</Badge>
|
|
79
|
+
),
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
key: 'price',
|
|
83
|
+
header: 'Price',
|
|
84
|
+
width: showAllColumns ? 100 : 80,
|
|
85
|
+
sortable: true,
|
|
86
|
+
render: (value) => (
|
|
87
|
+
<Text weight="semibold" size={showAllColumns ? 'medium' : 'small'}>
|
|
88
|
+
${value.toFixed(2)}
|
|
89
|
+
</Text>
|
|
90
|
+
),
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
key: 'stock',
|
|
94
|
+
header: 'Stock',
|
|
95
|
+
width: showAllColumns ? 80 : 60,
|
|
96
|
+
sortable: true,
|
|
97
|
+
render: (value, row) => (
|
|
98
|
+
<Text
|
|
99
|
+
size="small"
|
|
100
|
+
style={{
|
|
101
|
+
color: row.status === 'out-of-stock' ? '#ef4444' :
|
|
102
|
+
row.status === 'low-stock' ? '#f59e0b' : '#22c55e'
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
{value}
|
|
106
|
+
</Text>
|
|
107
|
+
),
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
key: 'status',
|
|
111
|
+
header: 'Status',
|
|
112
|
+
width: showAllColumns ? 120 : 100,
|
|
113
|
+
render: (value) => {
|
|
114
|
+
const intent = value === 'in-stock' ? 'success' :
|
|
115
|
+
value === 'low-stock' ? 'warning' : 'error';
|
|
116
|
+
return (
|
|
117
|
+
<Badge variant="filled" intent={intent} size="small">
|
|
118
|
+
{value}
|
|
119
|
+
</Badge>
|
|
120
|
+
);
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
const additionalColumns: Column<Product>[] = showAllColumns ? [
|
|
126
|
+
{
|
|
127
|
+
key: 'vendor',
|
|
128
|
+
header: 'Vendor',
|
|
129
|
+
width: 100,
|
|
130
|
+
sortable: true,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
key: 'lastUpdated',
|
|
134
|
+
header: 'Last Updated',
|
|
135
|
+
width: 120,
|
|
136
|
+
sortable: true,
|
|
137
|
+
},
|
|
138
|
+
] : [];
|
|
139
|
+
|
|
140
|
+
const columns = [...baseColumns, ...additionalColumns];
|
|
141
|
+
|
|
142
|
+
const handleSort = (column: Column<Product>, direction: 'asc' | 'desc') => {
|
|
143
|
+
const sorted = [...products].sort((a, b) => {
|
|
144
|
+
const aVal = a[column.key as keyof Product];
|
|
145
|
+
const bVal = b[column.key as keyof Product];
|
|
146
|
+
|
|
147
|
+
if (typeof aVal === 'number' && typeof bVal === 'number') {
|
|
148
|
+
return direction === 'asc' ? aVal - bVal : bVal - aVal;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const aStr = String(aVal);
|
|
152
|
+
const bStr = String(bVal);
|
|
153
|
+
return direction === 'asc'
|
|
154
|
+
? aStr.localeCompare(bStr)
|
|
155
|
+
: bStr.localeCompare(aStr);
|
|
156
|
+
});
|
|
157
|
+
setProducts(sorted);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const handleDeleteSelected = () => {
|
|
161
|
+
const newProducts = products.filter((_, index) => !selectedRows.includes(index));
|
|
162
|
+
setProducts(newProducts);
|
|
163
|
+
setSelectedRows([]);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const handleResetData = () => {
|
|
167
|
+
setProducts(generateSampleProducts(productCount));
|
|
168
|
+
setSelectedRows([]);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<Screen background="primary" safeArea scrollable={false}>
|
|
173
|
+
<View spacing={showAllColumns ? "lg" : "md"} style={{ padding: showAllColumns ? 16 : 12, flex: 1 }}>
|
|
174
|
+
<Text size={showAllColumns ? "xlarge" : "large"} weight="bold">
|
|
175
|
+
DataGrid {showAllColumns ? 'Component ' : ''}Showcase
|
|
176
|
+
</Text>
|
|
177
|
+
|
|
178
|
+
<Card variant="outlined">
|
|
179
|
+
<View spacing={showAllColumns ? "md" : "sm"}>
|
|
180
|
+
<Text weight="semibold" size="medium">Controls</Text>
|
|
181
|
+
|
|
182
|
+
<View
|
|
183
|
+
spacing="sm"
|
|
184
|
+
style={{
|
|
185
|
+
flexDirection: showAllColumns ? 'row' : 'column',
|
|
186
|
+
alignItems: showAllColumns ? 'center' : 'stretch',
|
|
187
|
+
flexWrap: showAllColumns ? 'wrap' : 'nowrap',
|
|
188
|
+
gap: 8
|
|
189
|
+
}}
|
|
190
|
+
>
|
|
191
|
+
<Button
|
|
192
|
+
variant="outlined"
|
|
193
|
+
size="small"
|
|
194
|
+
onPress={() => setVirtualized(!virtualized)}
|
|
195
|
+
>
|
|
196
|
+
{virtualized ? 'Disable' : 'Enable'} Virtualization
|
|
197
|
+
</Button>
|
|
198
|
+
|
|
199
|
+
{!showAllColumns && (
|
|
200
|
+
<View style={{ flexDirection: 'row', gap: 8 }}>
|
|
201
|
+
<Button
|
|
202
|
+
variant="outlined"
|
|
203
|
+
size="small"
|
|
204
|
+
onPress={() => setSelectedRows([])}
|
|
205
|
+
disabled={selectedRows.length === 0}
|
|
206
|
+
>
|
|
207
|
+
Clear ({selectedRows.length})
|
|
208
|
+
</Button>
|
|
209
|
+
|
|
210
|
+
<Button
|
|
211
|
+
variant="outlined"
|
|
212
|
+
intent="error"
|
|
213
|
+
size="small"
|
|
214
|
+
onPress={handleDeleteSelected}
|
|
215
|
+
disabled={selectedRows.length === 0}
|
|
216
|
+
>
|
|
217
|
+
Delete Selected
|
|
218
|
+
</Button>
|
|
219
|
+
</View>
|
|
220
|
+
)}
|
|
221
|
+
|
|
222
|
+
{showAllColumns && (
|
|
223
|
+
<>
|
|
224
|
+
<Button
|
|
225
|
+
variant="outlined"
|
|
226
|
+
size="small"
|
|
227
|
+
onPress={() => setSelectedRows([])}
|
|
228
|
+
disabled={selectedRows.length === 0}
|
|
229
|
+
>
|
|
230
|
+
Clear Selection ({selectedRows.length})
|
|
231
|
+
</Button>
|
|
232
|
+
|
|
233
|
+
<Button
|
|
234
|
+
variant="outlined"
|
|
235
|
+
intent="error"
|
|
236
|
+
size="small"
|
|
237
|
+
onPress={handleDeleteSelected}
|
|
238
|
+
disabled={selectedRows.length === 0}
|
|
239
|
+
>
|
|
240
|
+
Delete Selected
|
|
241
|
+
</Button>
|
|
242
|
+
</>
|
|
243
|
+
)}
|
|
244
|
+
|
|
245
|
+
<Button
|
|
246
|
+
variant="outlined"
|
|
247
|
+
size="small"
|
|
248
|
+
onPress={handleResetData}
|
|
249
|
+
>
|
|
250
|
+
Reset Data
|
|
251
|
+
</Button>
|
|
252
|
+
</View>
|
|
253
|
+
|
|
254
|
+
<View spacing="xs">
|
|
255
|
+
<Text size="small">• {showAllColumns ? 'Total ' : ''}Products: {products.length}</Text>
|
|
256
|
+
<Text size="small">• Selected: {selectedRows.length}</Text>
|
|
257
|
+
<Text size="small">• Virtualization: {virtualized ? (showAllColumns ? 'Enabled' : 'On') : (showAllColumns ? 'Disabled' : 'Off')}</Text>
|
|
258
|
+
</View>
|
|
259
|
+
</View>
|
|
260
|
+
</Card>
|
|
261
|
+
|
|
262
|
+
<Card variant="elevated" style={{ flex: 1, overflow: 'hidden' }}>
|
|
263
|
+
<DataGrid
|
|
264
|
+
data={products}
|
|
265
|
+
columns={columns}
|
|
266
|
+
height={height}
|
|
267
|
+
virtualized={virtualized}
|
|
268
|
+
selectedRows={selectedRows}
|
|
269
|
+
onSelectionChange={setSelectedRows}
|
|
270
|
+
multiSelect={true}
|
|
271
|
+
onSort={handleSort}
|
|
272
|
+
stickyHeader={true}
|
|
273
|
+
rowHeight={showAllColumns ? 48 : 44}
|
|
274
|
+
headerHeight={48}
|
|
275
|
+
rowStyle={(row, index) => ({
|
|
276
|
+
backgroundColor: index % 2 === 0 ? '#fafafa' : '#ffffff',
|
|
277
|
+
})}
|
|
278
|
+
/>
|
|
279
|
+
</Card>
|
|
280
|
+
|
|
281
|
+
<Card variant="outlined">
|
|
282
|
+
<View spacing={showAllColumns ? "sm" : "xs"}>
|
|
283
|
+
<Text weight="semibold" size="medium">Features {showAllColumns ? 'Demonstrated' : ''}</Text>
|
|
284
|
+
<Text size="small">✓ Virtualized rendering{showAllColumns ? ` with ${productCount} rows` : ''}</Text>
|
|
285
|
+
<Text size="small">✓ Sortable columns{showAllColumns ? ' (ID, Name, Category, Price, Stock, Vendor, Date)' : ''}</Text>
|
|
286
|
+
<Text size="small">✓ Multi-row selection{showAllColumns ? ' with visual feedback' : ''}</Text>
|
|
287
|
+
<Text size="small">✓ Custom cell rendering{showAllColumns ? ' (badges, colored text)' : ''}</Text>
|
|
288
|
+
<Text size="small">✓ Sticky header{showAllColumns ? ' while scrolling' : ''}</Text>
|
|
289
|
+
<Text size="small">✓ Alternating row colors</Text>
|
|
290
|
+
{showAllColumns && <Text size="small">✓ Responsive column widths</Text>}
|
|
291
|
+
</View>
|
|
292
|
+
</Card>
|
|
293
|
+
</View>
|
|
294
|
+
</Screen>
|
|
295
|
+
);
|
|
296
|
+
}
|
package/src/examples/index.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { BasicExample } from './BasicExample';
|
|
1
|
+
export { BasicExample } from './BasicExample';
|
|
2
|
+
export { DataGridShowcase } from './DataGridShowcase';
|
package/src/index.native.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { DataGrid } from './DataGrid';
|
|
1
|
+
export { DataGrid } from './DataGrid/DataGrid.native';
|
|
2
2
|
export type { DataGridProps, Column, CellProps, RowProps, HeaderCellProps } from './DataGrid/types';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
interface CellProps {
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
style?: any;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const Cell: React.FC<CellProps> = ({ children, style }) => {
|
|
10
|
+
return (
|
|
11
|
+
<View style={[{ justifyContent: 'center' }, style]}>
|
|
12
|
+
{children}
|
|
13
|
+
</View>
|
|
14
|
+
);
|
|
15
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { UnistylesRuntime } from 'react-native-unistyles';
|
|
3
|
+
|
|
4
|
+
interface CellProps {
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
style?: any;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const Cell: React.FC<CellProps> = ({ children, style }) => {
|
|
10
|
+
// Handle function-based styles (Unistyles 3) with proper theme access
|
|
11
|
+
let resolvedStyle = {};
|
|
12
|
+
if (typeof style === 'function') {
|
|
13
|
+
try {
|
|
14
|
+
resolvedStyle = style(UnistylesRuntime.theme);
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.warn('Error resolving Cell style:', error);
|
|
17
|
+
resolvedStyle = {};
|
|
18
|
+
}
|
|
19
|
+
} else if (style) {
|
|
20
|
+
resolvedStyle = style;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const combinedStyle = {
|
|
24
|
+
display: 'flex',
|
|
25
|
+
flexDirection: 'column' as const,
|
|
26
|
+
justifyContent: 'center',
|
|
27
|
+
alignItems: 'stretch',
|
|
28
|
+
...resolvedStyle,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div style={combinedStyle}>
|
|
33
|
+
{children}
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Cell } from './Cell.native';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Cell } from './Cell.web';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { TouchableOpacity, View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
interface RowProps {
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
style?: any;
|
|
7
|
+
onPress?: () => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const Row: React.FC<RowProps> = ({ children, style, onPress }) => {
|
|
11
|
+
const Wrapper = onPress ? TouchableOpacity : View;
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<Wrapper
|
|
15
|
+
style={[{ flexDirection: 'row' }, style]}
|
|
16
|
+
onPress={onPress}
|
|
17
|
+
>
|
|
18
|
+
{children}
|
|
19
|
+
</Wrapper>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { UnistylesRuntime } from 'react-native-unistyles';
|
|
3
|
+
|
|
4
|
+
interface RowProps {
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
style?: any;
|
|
7
|
+
onPress?: () => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const Row: React.FC<RowProps> = ({ children, style, onPress }) => {
|
|
11
|
+
// Handle function-based styles (Unistyles 3) with proper theme access
|
|
12
|
+
let resolvedStyle = {};
|
|
13
|
+
if (typeof style === 'function') {
|
|
14
|
+
try {
|
|
15
|
+
resolvedStyle = style(UnistylesRuntime.theme);
|
|
16
|
+
} catch (error) {
|
|
17
|
+
console.warn('Error resolving Row style:', error);
|
|
18
|
+
resolvedStyle = {};
|
|
19
|
+
}
|
|
20
|
+
} else if (style) {
|
|
21
|
+
resolvedStyle = style;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const combinedStyle = {
|
|
25
|
+
display: 'flex',
|
|
26
|
+
flexDirection: 'row' as const,
|
|
27
|
+
width: '100%',
|
|
28
|
+
boxSizing: 'border-box' as const,
|
|
29
|
+
...resolvedStyle,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div
|
|
34
|
+
style={combinedStyle}
|
|
35
|
+
onClick={onPress}
|
|
36
|
+
role={onPress ? 'button' : undefined}
|
|
37
|
+
tabIndex={onPress ? 0 : undefined}
|
|
38
|
+
onKeyDown={onPress ? (e) => {
|
|
39
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
40
|
+
e.preventDefault();
|
|
41
|
+
onPress();
|
|
42
|
+
}
|
|
43
|
+
} : undefined}
|
|
44
|
+
>
|
|
45
|
+
{children}
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Row } from './Row.native';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Row } from './Row.web';
|
|
@@ -20,6 +20,9 @@ export const ScrollView: React.FC<ScrollViewProps> = ({
|
|
|
20
20
|
onScroll,
|
|
21
21
|
showsHorizontalScrollIndicator = true,
|
|
22
22
|
showsVerticalScrollIndicator = true,
|
|
23
|
+
scrollEventThrottle: _scrollEventThrottle, // native property
|
|
24
|
+
bounces: _bounces, // native property
|
|
25
|
+
directionalLockEnabled: _directionalLockEnabled, // native property
|
|
23
26
|
...props
|
|
24
27
|
}) => {
|
|
25
28
|
const scrollStyle = {
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, TouchableOpacity } from 'react-native';
|
|
3
|
+
import { UnistylesRuntime } from 'react-native-unistyles';
|
|
4
|
+
|
|
5
|
+
interface TableProps {
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
style?: any;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const Table: React.FC<TableProps> = ({ children, style }) => {
|
|
11
|
+
let resolvedStyle = {};
|
|
12
|
+
if (typeof style === 'function') {
|
|
13
|
+
try {
|
|
14
|
+
resolvedStyle = style(UnistylesRuntime.theme);
|
|
15
|
+
} catch (error) {
|
|
16
|
+
resolvedStyle = {};
|
|
17
|
+
}
|
|
18
|
+
} else if (style) {
|
|
19
|
+
resolvedStyle = style;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<View style={[
|
|
24
|
+
{ flex: 1 },
|
|
25
|
+
resolvedStyle,
|
|
26
|
+
// If width is specified, don't flex
|
|
27
|
+
resolvedStyle?.width ? { flex: 0 } : {}
|
|
28
|
+
]}>
|
|
29
|
+
{children}
|
|
30
|
+
</View>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
interface TableRowProps {
|
|
35
|
+
children: React.ReactNode;
|
|
36
|
+
style?: any;
|
|
37
|
+
onPress?: () => void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const TableRow: React.FC<TableRowProps> = ({ children, style, onPress }) => {
|
|
41
|
+
const Wrapper = onPress ? TouchableOpacity : View;
|
|
42
|
+
|
|
43
|
+
let resolvedStyle = {};
|
|
44
|
+
if (typeof style === 'function') {
|
|
45
|
+
try {
|
|
46
|
+
resolvedStyle = style(UnistylesRuntime.theme);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
resolvedStyle = {};
|
|
49
|
+
}
|
|
50
|
+
} else if (style) {
|
|
51
|
+
resolvedStyle = style;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Ensure flexDirection is always 'row' - force it with !important-like behavior
|
|
55
|
+
const finalStyle = [resolvedStyle, { flexDirection: 'row', display: 'flex' }];
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<Wrapper
|
|
59
|
+
style={finalStyle}
|
|
60
|
+
onPress={onPress}
|
|
61
|
+
>
|
|
62
|
+
{children}
|
|
63
|
+
</Wrapper>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
interface TableCellProps {
|
|
68
|
+
children: React.ReactNode;
|
|
69
|
+
style?: any;
|
|
70
|
+
width?: number | string;
|
|
71
|
+
colSpan?: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const TableCell: React.FC<TableCellProps> = ({ children, style, width, colSpan }) => {
|
|
75
|
+
const flexValue = colSpan ? colSpan : 1;
|
|
76
|
+
|
|
77
|
+
let resolvedStyle = {};
|
|
78
|
+
if (typeof style === 'function') {
|
|
79
|
+
try {
|
|
80
|
+
resolvedStyle = style(UnistylesRuntime.theme);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
resolvedStyle = {};
|
|
83
|
+
}
|
|
84
|
+
} else if (style) {
|
|
85
|
+
resolvedStyle = style;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const combinedStyle = [
|
|
89
|
+
{ justifyContent: 'center' },
|
|
90
|
+
width ? { width, flex: 0 } : { flex: flexValue },
|
|
91
|
+
resolvedStyle
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<View style={combinedStyle}>
|
|
96
|
+
{children}
|
|
97
|
+
</View>
|
|
98
|
+
);
|
|
99
|
+
};
|