@delightui/components 0.1.104 → 0.1.106

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.
Files changed (120) hide show
  1. package/README.md +104 -1
  2. package/dist/cjs/components/molecules/Modal/DemoModal.d.ts +8 -0
  3. package/dist/cjs/components/molecules/Modal/ModalContext/ModalContext.d.ts +41 -0
  4. package/dist/cjs/components/molecules/Modal/ModalContext/ModalContext.types.d.ts +87 -0
  5. package/dist/cjs/components/molecules/Modal/ModalContext/index.d.ts +3 -0
  6. package/dist/cjs/components/molecules/Modal/ModalContext/useModal.d.ts +34 -0
  7. package/dist/cjs/components/molecules/Modal/index.d.ts +2 -0
  8. package/dist/cjs/components/molecules/Popover/Popover.presenter.d.ts +26 -0
  9. package/dist/cjs/components/molecules/Select/Option/Option.types.d.ts +6 -0
  10. package/dist/cjs/components/molecules/Select/Select.Context.d.ts +1 -1
  11. package/dist/cjs/components/molecules/Select/Select.d.ts +5 -5
  12. package/dist/cjs/components/molecules/Select/Select.presenter.d.ts +1 -0
  13. package/dist/cjs/components/molecules/Select/Select.types.d.ts +5 -0
  14. package/dist/cjs/components/molecules/Select/index.d.ts +2 -9
  15. package/dist/cjs/components/molecules/index.d.ts +2 -0
  16. package/dist/cjs/components/utils/accessibilityUtils.d.ts +41 -0
  17. package/dist/cjs/components/utils/index.d.ts +2 -0
  18. package/dist/cjs/library.css +13 -0
  19. package/dist/cjs/library.js +2 -2
  20. package/dist/cjs/library.js.map +1 -1
  21. package/dist/esm/components/molecules/Modal/DemoModal.d.ts +8 -0
  22. package/dist/esm/components/molecules/Modal/ModalContext/ModalContext.d.ts +41 -0
  23. package/dist/esm/components/molecules/Modal/ModalContext/ModalContext.types.d.ts +87 -0
  24. package/dist/esm/components/molecules/Modal/ModalContext/index.d.ts +3 -0
  25. package/dist/esm/components/molecules/Modal/ModalContext/useModal.d.ts +34 -0
  26. package/dist/esm/components/molecules/Modal/index.d.ts +2 -0
  27. package/dist/esm/components/molecules/Popover/Popover.presenter.d.ts +26 -0
  28. package/dist/esm/components/molecules/Select/Option/Option.types.d.ts +6 -0
  29. package/dist/esm/components/molecules/Select/Select.Context.d.ts +1 -1
  30. package/dist/esm/components/molecules/Select/Select.d.ts +5 -5
  31. package/dist/esm/components/molecules/Select/Select.presenter.d.ts +1 -0
  32. package/dist/esm/components/molecules/Select/Select.types.d.ts +5 -0
  33. package/dist/esm/components/molecules/Select/index.d.ts +2 -9
  34. package/dist/esm/components/molecules/index.d.ts +2 -0
  35. package/dist/esm/components/utils/accessibilityUtils.d.ts +41 -0
  36. package/dist/esm/components/utils/index.d.ts +2 -0
  37. package/dist/esm/library.css +13 -0
  38. package/dist/esm/library.js +3 -3
  39. package/dist/esm/library.js.map +1 -1
  40. package/dist/index.d.ts +156 -12
  41. package/docs/README.md +264 -0
  42. package/docs/components/atoms/ActionImage.md +119 -0
  43. package/docs/components/atoms/Button.md +197 -0
  44. package/docs/components/atoms/Checkbox.md +299 -0
  45. package/docs/components/atoms/CheckboxItem.md +314 -0
  46. package/docs/components/atoms/Chip.md +380 -0
  47. package/docs/components/atoms/CustomToggle.md +270 -0
  48. package/docs/components/atoms/Icon.md +365 -0
  49. package/docs/components/atoms/IconButton.md +407 -0
  50. package/docs/components/atoms/Image.md +448 -0
  51. package/docs/components/atoms/Input.md +430 -0
  52. package/docs/components/atoms/ListItem.md +502 -0
  53. package/docs/components/atoms/Password.md +472 -0
  54. package/docs/components/atoms/RadioButton.md +614 -0
  55. package/docs/components/atoms/RadioButtonItem.md +588 -0
  56. package/docs/components/atoms/ResponsiveComponent.md +612 -0
  57. package/docs/components/atoms/SelectListItem.md +609 -0
  58. package/docs/components/atoms/Slider.md +605 -0
  59. package/docs/components/atoms/Spinner.md +605 -0
  60. package/docs/components/atoms/Text.md +463 -0
  61. package/docs/components/atoms/TextArea.md +670 -0
  62. package/docs/components/atoms/ToastNotification.md +668 -0
  63. package/docs/components/atoms/Toggle.md +737 -0
  64. package/docs/components/atoms/ToggleButton.md +751 -0
  65. package/docs/components/atoms/Tooltip.md +391 -0
  66. package/docs/components/molecules/Accordion.md +440 -0
  67. package/docs/components/molecules/AccordionGroup.md +547 -0
  68. package/docs/components/molecules/ActionCard.md +546 -0
  69. package/docs/components/molecules/Breadcrumb.md +403 -0
  70. package/docs/components/molecules/Breadcrumbs.md +485 -0
  71. package/docs/components/molecules/ButtonGroup.md +383 -0
  72. package/docs/components/molecules/Card.md +298 -0
  73. package/docs/components/molecules/ChipInput.md +646 -0
  74. package/docs/components/molecules/ContextMenu.md +768 -0
  75. package/docs/components/molecules/CustomTimeSelector.md +116 -0
  76. package/docs/components/molecules/DatePicker.md +516 -0
  77. package/docs/components/molecules/DateTimeSelector.md +166 -0
  78. package/docs/components/molecules/FormField.md +312 -0
  79. package/docs/components/molecules/Grid.md +577 -0
  80. package/docs/components/molecules/GridItem.md +834 -0
  81. package/docs/components/molecules/GridList.md +244 -0
  82. package/docs/components/molecules/List.md +485 -0
  83. package/docs/components/molecules/Modal.md +470 -0
  84. package/docs/components/molecules/ModalFooter.md +702 -0
  85. package/docs/components/molecules/ModalHeader.md +756 -0
  86. package/docs/components/molecules/ModalProvider.md +205 -0
  87. package/docs/components/molecules/Nav.md +530 -0
  88. package/docs/components/molecules/NavItem.md +572 -0
  89. package/docs/components/molecules/NavLink.md +499 -0
  90. package/docs/components/molecules/Option.md +521 -0
  91. package/docs/components/molecules/Pagination.md +592 -0
  92. package/docs/components/molecules/PaginationNumberField.md +722 -0
  93. package/docs/components/molecules/Popover.md +516 -0
  94. package/docs/components/molecules/ProgressBar.md +624 -0
  95. package/docs/components/molecules/RadioGroup.md +831 -0
  96. package/docs/components/molecules/RepeaterList.md +185 -0
  97. package/docs/components/molecules/Select.md +402 -0
  98. package/docs/components/molecules/SortableTrigger.md +82 -0
  99. package/docs/components/molecules/useModal.md +379 -0
  100. package/docs/components/organisms/Dropzone.md +346 -0
  101. package/docs/components/organisms/DropzoneClear.md +135 -0
  102. package/docs/components/organisms/DropzoneContent.md +216 -0
  103. package/docs/components/organisms/DropzoneFilename.md +191 -0
  104. package/docs/components/organisms/DropzoneSupportedFormats.md +184 -0
  105. package/docs/components/organisms/DropzoneTrigger.md +209 -0
  106. package/docs/components/organisms/Form.md +533 -0
  107. package/docs/components/organisms/SlideOutPanel.md +662 -0
  108. package/docs/components/organisms/TabContent.md +902 -0
  109. package/docs/components/organisms/TabItem.md +1091 -0
  110. package/docs/components/organisms/Table.md +611 -0
  111. package/docs/components/organisms/TableBody.md +679 -0
  112. package/docs/components/organisms/TableCell.md +482 -0
  113. package/docs/components/organisms/TableHeader.md +513 -0
  114. package/docs/components/organisms/TableHeaderCell.md +661 -0
  115. package/docs/components/organisms/TableRow.md +715 -0
  116. package/docs/components/organisms/Tabs.md +1330 -0
  117. package/docs/components/utils/ConditionalView.md +568 -0
  118. package/docs/components/utils/RenderStateView.md +726 -0
  119. package/docs/components/utils/WrapTextNodes.md +614 -0
  120. package/package.json +3 -2
@@ -0,0 +1,679 @@
1
+ # TableBody
2
+
3
+ ## Description
4
+
5
+ A semantic container component that wraps the main content section of a table. TableBody provides proper HTML structure for table data rows and ensures accessibility compliance by creating the `<tbody>` element that groups the table's primary content together.
6
+
7
+ ## Aliases
8
+
9
+ - TableBody
10
+ - TBody
11
+ - TableContent
12
+ - BodySection
13
+ - TableDataGroup
14
+
15
+ ## Props Breakdown
16
+
17
+ **Extends:** `HTMLAttributes<HTMLTableSectionElement>`
18
+
19
+ | Prop | Type | Default | Required | Description |
20
+ |------|------|---------|----------|-------------|
21
+ | `children` | `ReactNode` | - | Yes | Table body content, typically TableRow components containing TableCell elements |
22
+
23
+ Plus all standard HTML table section attributes (id, className, style, role, etc.).
24
+
25
+ ## Examples
26
+
27
+ ### Basic Table Body
28
+ ```tsx
29
+ import { Table, TableHeader, TableBody, TableRow, TableHeaderCell, TableCell } from '@delightui/components';
30
+
31
+ function BasicBodyExample() {
32
+ const users = [
33
+ { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin' },
34
+ { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
35
+ { id: 3, name: 'Bob Johnson', email: 'bob@example.com', role: 'Editor' }
36
+ ];
37
+
38
+ return (
39
+ <Table>
40
+ <TableHeader>
41
+ <TableRow>
42
+ <TableHeaderCell>Name</TableHeaderCell>
43
+ <TableHeaderCell>Email</TableHeaderCell>
44
+ <TableHeaderCell>Role</TableHeaderCell>
45
+ </TableRow>
46
+ </TableHeader>
47
+ <TableBody>
48
+ {users.map(user => (
49
+ <TableRow key={user.id}>
50
+ <TableCell>{user.name}</TableCell>
51
+ <TableCell>{user.email}</TableCell>
52
+ <TableCell>{user.role}</TableCell>
53
+ </TableRow>
54
+ ))}
55
+ </TableBody>
56
+ </Table>
57
+ );
58
+ }
59
+ ```
60
+
61
+ ### Dynamic Content Loading
62
+ ```tsx
63
+ function LoadingBodyExample() {
64
+ const [data, setData] = useState([]);
65
+ const [isLoading, setIsLoading] = useState(true);
66
+ const [error, setError] = useState(null);
67
+
68
+ useEffect(() => {
69
+ const fetchData = async () => {
70
+ try {
71
+ setIsLoading(true);
72
+ // Simulate API call
73
+ await new Promise(resolve => setTimeout(resolve, 2000));
74
+ setData([
75
+ { id: 1, product: 'Laptop', category: 'Electronics', price: 999.99, stock: 15 },
76
+ { id: 2, product: 'Desk Chair', category: 'Furniture', price: 299.99, stock: 8 },
77
+ { id: 3, product: 'Coffee Mug', category: 'Kitchen', price: 12.99, stock: 50 }
78
+ ]);
79
+ } catch (err) {
80
+ setError('Failed to load data');
81
+ } finally {
82
+ setIsLoading(false);
83
+ }
84
+ };
85
+
86
+ fetchData();
87
+ }, []);
88
+
89
+ return (
90
+ <Table>
91
+ <TableHeader>
92
+ <TableRow>
93
+ <TableHeaderCell>Product</TableHeaderCell>
94
+ <TableHeaderCell>Category</TableHeaderCell>
95
+ <TableHeaderCell>Price</TableHeaderCell>
96
+ <TableHeaderCell>Stock</TableHeaderCell>
97
+ </TableRow>
98
+ </TableHeader>
99
+ <TableBody>
100
+ {isLoading ? (
101
+ Array.from({ length: 3 }).map((_, index) => (
102
+ <TableRow key={`loading-${index}`}>
103
+ <TableCell>
104
+ <div className="skeleton-loader skeleton-text"></div>
105
+ </TableCell>
106
+ <TableCell>
107
+ <div className="skeleton-loader skeleton-text-small"></div>
108
+ </TableCell>
109
+ <TableCell>
110
+ <div className="skeleton-loader skeleton-text-small"></div>
111
+ </TableCell>
112
+ <TableCell>
113
+ <div className="skeleton-loader skeleton-text-small"></div>
114
+ </TableCell>
115
+ </TableRow>
116
+ ))
117
+ ) : error ? (
118
+ <TableRow>
119
+ <TableCell colSpan={4} className="error-state">
120
+ <div className="error-message">
121
+ <Icon icon="Error" className="error-icon" />
122
+ <Text type="BodyMedium">{error}</Text>
123
+ <Button size="Small" onClick={() => window.location.reload()}>
124
+ Retry
125
+ </Button>
126
+ </div>
127
+ </TableCell>
128
+ </TableRow>
129
+ ) : data.length === 0 ? (
130
+ <TableRow>
131
+ <TableCell colSpan={4} className="empty-state">
132
+ <div className="empty-message">
133
+ <Icon icon="Inventory" className="empty-icon" />
134
+ <Text type="BodyMedium">No products found</Text>
135
+ <Text type="BodySmall">Add your first product to get started</Text>
136
+ </div>
137
+ </TableCell>
138
+ </TableRow>
139
+ ) : (
140
+ data.map(item => (
141
+ <TableRow key={item.id}>
142
+ <TableCell>{item.product}</TableCell>
143
+ <TableCell>{item.category}</TableCell>
144
+ <TableCell>${item.price}</TableCell>
145
+ <TableCell>{item.stock}</TableCell>
146
+ </TableRow>
147
+ ))
148
+ )}
149
+ </TableBody>
150
+ </Table>
151
+ );
152
+ }
153
+ ```
154
+
155
+ ### Striped Rows Body
156
+ ```tsx
157
+ function StripedBodyExample() {
158
+ const transactions = [
159
+ { id: 'T001', date: '2023-10-15', description: 'Online Purchase', amount: -89.99, status: 'Completed' },
160
+ { id: 'T002', date: '2023-10-14', description: 'Salary Deposit', amount: 3500.00, status: 'Completed' },
161
+ { id: 'T003', date: '2023-10-13', description: 'ATM Withdrawal', amount: -100.00, status: 'Completed' },
162
+ { id: 'T004', date: '2023-10-12', description: 'Restaurant', amount: -45.67, status: 'Pending' },
163
+ { id: 'T005', date: '2023-10-11', description: 'Gas Station', amount: -55.20, status: 'Completed' }
164
+ ];
165
+
166
+ return (
167
+ <Table className="striped-table">
168
+ <TableHeader>
169
+ <TableRow>
170
+ <TableHeaderCell>Transaction ID</TableHeaderCell>
171
+ <TableHeaderCell>Date</TableHeaderCell>
172
+ <TableHeaderCell>Description</TableHeaderCell>
173
+ <TableHeaderCell>Amount</TableHeaderCell>
174
+ <TableHeaderCell>Status</TableHeaderCell>
175
+ </TableRow>
176
+ </TableHeader>
177
+ <TableBody className="striped-body">
178
+ {transactions.map((transaction, index) => (
179
+ <TableRow key={transaction.id} className={index % 2 === 0 ? 'even-row' : 'odd-row'}>
180
+ <TableCell>{transaction.id}</TableCell>
181
+ <TableCell>{transaction.date}</TableCell>
182
+ <TableCell>{transaction.description}</TableCell>
183
+ <TableCell className={transaction.amount > 0 ? 'positive-amount' : 'negative-amount'}>
184
+ ${Math.abs(transaction.amount).toFixed(2)}
185
+ </TableCell>
186
+ <TableCell>
187
+ <Chip style={transaction.status === 'Completed' ? 'Success' : 'Warning'}>
188
+ {transaction.status}
189
+ </Chip>
190
+ </TableCell>
191
+ </TableRow>
192
+ ))}
193
+ </TableBody>
194
+ </Table>
195
+ );
196
+ }
197
+ ```
198
+
199
+ ### Grouped Data Body
200
+ ```tsx
201
+ function GroupedBodyExample() {
202
+ const groupedData = {
203
+ 'Engineering': [
204
+ { id: 1, name: 'Alice Johnson', role: 'Senior Developer', salary: 95000 },
205
+ { id: 2, name: 'Bob Smith', role: 'Frontend Developer', salary: 75000 },
206
+ { id: 3, name: 'Carol Wilson', role: 'Backend Developer', salary: 80000 }
207
+ ],
208
+ 'Design': [
209
+ { id: 4, name: 'David Brown', role: 'UI Designer', salary: 70000 },
210
+ { id: 5, name: 'Eve Davis', role: 'UX Designer', salary: 75000 }
211
+ ],
212
+ 'Marketing': [
213
+ { id: 6, name: 'Frank Miller', role: 'Marketing Manager', salary: 85000 },
214
+ { id: 7, name: 'Grace Taylor', role: 'Content Specialist', salary: 55000 }
215
+ ]
216
+ };
217
+
218
+ return (
219
+ <Table>
220
+ <TableHeader>
221
+ <TableRow>
222
+ <TableHeaderCell>Employee</TableHeaderCell>
223
+ <TableHeaderCell>Role</TableHeaderCell>
224
+ <TableHeaderCell>Salary</TableHeaderCell>
225
+ <TableHeaderCell>Actions</TableHeaderCell>
226
+ </TableRow>
227
+ </TableHeader>
228
+ <TableBody className="grouped-body">
229
+ {Object.entries(groupedData).map(([department, employees]) => (
230
+ <React.Fragment key={department}>
231
+ <TableRow className="group-header-row">
232
+ <TableCell colSpan={4} className="group-header">
233
+ <div className="group-title">
234
+ <Icon icon="Group" size="Small" />
235
+ <Text type="BodyMedium">{department} Department</Text>
236
+ <Chip size="Small">{employees.length} employees</Chip>
237
+ </div>
238
+ </TableCell>
239
+ </TableRow>
240
+ {employees.map(employee => (
241
+ <TableRow key={employee.id} className="group-item-row">
242
+ <TableCell>{employee.name}</TableCell>
243
+ <TableCell>{employee.role}</TableCell>
244
+ <TableCell>${employee.salary.toLocaleString()}</TableCell>
245
+ <TableCell>
246
+ <Button size="Small" type="Ghost">Edit</Button>
247
+ </TableCell>
248
+ </TableRow>
249
+ ))}
250
+ </React.Fragment>
251
+ ))}
252
+ </TableBody>
253
+ </Table>
254
+ );
255
+ }
256
+ ```
257
+
258
+ ### Interactive Row Selection
259
+ ```tsx
260
+ function SelectableBodyExample() {
261
+ const [selectedRows, setSelectedRows] = useState(new Set());
262
+ const [hoveredRow, setHoveredRow] = useState(null);
263
+
264
+ const data = [
265
+ { id: 1, task: 'Update documentation', assignee: 'John', priority: 'High', status: 'In Progress' },
266
+ { id: 2, task: 'Fix login bug', assignee: 'Jane', priority: 'Critical', status: 'Todo' },
267
+ { id: 3, task: 'Design new feature', assignee: 'Bob', priority: 'Medium', status: 'Review' },
268
+ { id: 4, task: 'Write unit tests', assignee: 'Alice', priority: 'Low', status: 'Done' }
269
+ ];
270
+
271
+ const handleRowSelect = (id, selected) => {
272
+ const newSelected = new Set(selectedRows);
273
+ if (selected) {
274
+ newSelected.add(id);
275
+ } else {
276
+ newSelected.delete(id);
277
+ }
278
+ setSelectedRows(newSelected);
279
+ };
280
+
281
+ const isRowSelected = (id) => selectedRows.has(id);
282
+
283
+ return (
284
+ <Table>
285
+ <TableHeader>
286
+ <TableRow>
287
+ <TableHeaderCell>
288
+ <Checkbox
289
+ checked={selectedRows.size === data.length}
290
+ indeterminate={selectedRows.size > 0 && selectedRows.size < data.length}
291
+ onChange={(e) => {
292
+ if (e.target.checked) {
293
+ setSelectedRows(new Set(data.map(item => item.id)));
294
+ } else {
295
+ setSelectedRows(new Set());
296
+ }
297
+ }}
298
+ />
299
+ </TableHeaderCell>
300
+ <TableHeaderCell>Task</TableHeaderCell>
301
+ <TableHeaderCell>Assignee</TableHeaderCell>
302
+ <TableHeaderCell>Priority</TableHeaderCell>
303
+ <TableHeaderCell>Status</TableHeaderCell>
304
+ </TableRow>
305
+ </TableHeader>
306
+ <TableBody className="selectable-body">
307
+ {data.map(item => (
308
+ <TableRow
309
+ key={item.id}
310
+ className={`
311
+ ${isRowSelected(item.id) ? 'selected-row' : ''}
312
+ ${hoveredRow === item.id ? 'hovered-row' : ''}
313
+ `}
314
+ onMouseEnter={() => setHoveredRow(item.id)}
315
+ onMouseLeave={() => setHoveredRow(null)}
316
+ >
317
+ <TableCell>
318
+ <Checkbox
319
+ checked={isRowSelected(item.id)}
320
+ onChange={(e) => handleRowSelect(item.id, e.target.checked)}
321
+ />
322
+ </TableCell>
323
+ <TableCell>{item.task}</TableCell>
324
+ <TableCell>{item.assignee}</TableCell>
325
+ <TableCell>
326
+ <Chip
327
+ style={
328
+ item.priority === 'Critical' ? 'Error' :
329
+ item.priority === 'High' ? 'Warning' :
330
+ item.priority === 'Medium' ? 'Default' : 'Success'
331
+ }
332
+ >
333
+ {item.priority}
334
+ </Chip>
335
+ </TableCell>
336
+ <TableCell>
337
+ <Chip
338
+ style={
339
+ item.status === 'Done' ? 'Success' :
340
+ item.status === 'In Progress' ? 'Warning' : 'Default'
341
+ }
342
+ >
343
+ {item.status}
344
+ </Chip>
345
+ </TableCell>
346
+ </TableRow>
347
+ ))}
348
+ </TableBody>
349
+ </Table>
350
+ );
351
+ }
352
+ ```
353
+
354
+ ### Expandable Rows Body
355
+ ```tsx
356
+ function ExpandableBodyExample() {
357
+ const [expandedRows, setExpandedRows] = useState(new Set());
358
+
359
+ const orders = [
360
+ {
361
+ id: 'ORD-001',
362
+ customer: 'John Doe',
363
+ date: '2023-10-15',
364
+ total: 299.99,
365
+ status: 'Delivered',
366
+ items: [
367
+ { name: 'Wireless Headphones', quantity: 1, price: 199.99 },
368
+ { name: 'Phone Case', quantity: 2, price: 50.00 }
369
+ ]
370
+ },
371
+ {
372
+ id: 'ORD-002',
373
+ customer: 'Jane Smith',
374
+ date: '2023-10-14',
375
+ total: 159.97,
376
+ status: 'Shipped',
377
+ items: [
378
+ { name: 'Bluetooth Speaker', quantity: 1, price: 89.99 },
379
+ { name: 'USB Cable', quantity: 1, price: 19.99 },
380
+ { name: 'Wall Charger', quantity: 2, price: 25.00 }
381
+ ]
382
+ }
383
+ ];
384
+
385
+ const toggleExpanded = (orderId) => {
386
+ const newExpanded = new Set(expandedRows);
387
+ if (newExpanded.has(orderId)) {
388
+ newExpanded.delete(orderId);
389
+ } else {
390
+ newExpanded.add(orderId);
391
+ }
392
+ setExpandedRows(newExpanded);
393
+ };
394
+
395
+ return (
396
+ <Table>
397
+ <TableHeader>
398
+ <TableRow>
399
+ <TableHeaderCell></TableHeaderCell>
400
+ <TableHeaderCell>Order ID</TableHeaderCell>
401
+ <TableHeaderCell>Customer</TableHeaderCell>
402
+ <TableHeaderCell>Date</TableHeaderCell>
403
+ <TableHeaderCell>Total</TableHeaderCell>
404
+ <TableHeaderCell>Status</TableHeaderCell>
405
+ </TableRow>
406
+ </TableHeader>
407
+ <TableBody className="expandable-body">
408
+ {orders.map(order => (
409
+ <React.Fragment key={order.id}>
410
+ <TableRow className="main-row">
411
+ <TableCell>
412
+ <Button
413
+ size="Small"
414
+ type="Ghost"
415
+ onClick={() => toggleExpanded(order.id)}
416
+ >
417
+ <Icon
418
+ icon={expandedRows.has(order.id) ? 'ExpandLess' : 'ExpandMore'}
419
+ size="Small"
420
+ />
421
+ </Button>
422
+ </TableCell>
423
+ <TableCell>{order.id}</TableCell>
424
+ <TableCell>{order.customer}</TableCell>
425
+ <TableCell>{order.date}</TableCell>
426
+ <TableCell>${order.total}</TableCell>
427
+ <TableCell>
428
+ <Chip style={order.status === 'Delivered' ? 'Success' : 'Warning'}>
429
+ {order.status}
430
+ </Chip>
431
+ </TableCell>
432
+ </TableRow>
433
+ {expandedRows.has(order.id) && (
434
+ <TableRow className="expanded-row">
435
+ <TableCell colSpan={6}>
436
+ <div className="expanded-content">
437
+ <Text type="BodyMedium">Order Items</Text>
438
+ <div className="items-list">
439
+ {order.items.map((item, index) => (
440
+ <div key={index} className="order-item">
441
+ <Text type="BodySmall">{item.name}</Text>
442
+ <Text type="BodySmall">Qty: {item.quantity}</Text>
443
+ <Text type="BodySmall">${item.price}</Text>
444
+ </div>
445
+ ))}
446
+ </div>
447
+ </div>
448
+ </TableCell>
449
+ </TableRow>
450
+ )}
451
+ </React.Fragment>
452
+ ))}
453
+ </TableBody>
454
+ </Table>
455
+ );
456
+ }
457
+ ```
458
+
459
+ ### Infinite Scroll Body
460
+ ```tsx
461
+ function InfiniteScrollBodyExample() {
462
+ const [data, setData] = useState([]);
463
+ const [loading, setLoading] = useState(false);
464
+ const [hasMore, setHasMore] = useState(true);
465
+
466
+ const loadMoreData = useCallback(async () => {
467
+ if (loading || !hasMore) return;
468
+
469
+ setLoading(true);
470
+ // Simulate API call
471
+ await new Promise(resolve => setTimeout(resolve, 1000));
472
+
473
+ const newItems = Array.from({ length: 20 }, (_, i) => ({
474
+ id: data.length + i + 1,
475
+ name: `Item ${data.length + i + 1}`,
476
+ value: Math.floor(Math.random() * 1000),
477
+ status: ['Active', 'Inactive', 'Pending'][Math.floor(Math.random() * 3)]
478
+ }));
479
+
480
+ setData(prev => [...prev, ...newItems]);
481
+ setLoading(false);
482
+
483
+ // Stop loading after 100 items
484
+ if (data.length + newItems.length >= 100) {
485
+ setHasMore(false);
486
+ }
487
+ }, [data.length, loading, hasMore]);
488
+
489
+ useEffect(() => {
490
+ loadMoreData();
491
+ }, []);
492
+
493
+ return (
494
+ <div className="infinite-scroll-container" style={{ height: '400px', overflow: 'auto' }}>
495
+ <Table>
496
+ <TableHeader>
497
+ <TableRow>
498
+ <TableHeaderCell>Name</TableHeaderCell>
499
+ <TableHeaderCell>Value</TableHeaderCell>
500
+ <TableHeaderCell>Status</TableHeaderCell>
501
+ </TableRow>
502
+ </TableHeader>
503
+ <TableBody className="infinite-scroll-body">
504
+ {data.map(item => (
505
+ <TableRow key={item.id}>
506
+ <TableCell>{item.name}</TableCell>
507
+ <TableCell>{item.value}</TableCell>
508
+ <TableCell>
509
+ <Chip style={item.status === 'Active' ? 'Success' : 'Default'}>
510
+ {item.status}
511
+ </Chip>
512
+ </TableCell>
513
+ </TableRow>
514
+ ))}
515
+ {loading && (
516
+ <TableRow>
517
+ <TableCell colSpan={3} className="loading-row">
518
+ <div className="loading-indicator">
519
+ <Spinner size="Small" />
520
+ <Text type="BodySmall">Loading more items...</Text>
521
+ </div>
522
+ </TableCell>
523
+ </TableRow>
524
+ )}
525
+ {!hasMore && data.length > 0 && (
526
+ <TableRow>
527
+ <TableCell colSpan={3} className="end-row">
528
+ <Text type="BodySmall">No more items to load</Text>
529
+ </TableCell>
530
+ </TableRow>
531
+ )}
532
+ </TableBody>
533
+ </Table>
534
+ </div>
535
+ );
536
+ }
537
+ ```
538
+
539
+ ### Drag and Drop Body
540
+ ```tsx
541
+ function DragDropBodyExample() {
542
+ const [items, setItems] = useState([
543
+ { id: 1, task: 'High priority task', priority: 1 },
544
+ { id: 2, task: 'Medium priority task', priority: 2 },
545
+ { id: 3, task: 'Low priority task', priority: 3 },
546
+ { id: 4, task: 'Another task', priority: 4 }
547
+ ]);
548
+
549
+ const [draggedItem, setDraggedItem] = useState(null);
550
+
551
+ const handleDragStart = (e, item) => {
552
+ setDraggedItem(item);
553
+ e.dataTransfer.effectAllowed = 'move';
554
+ };
555
+
556
+ const handleDragOver = (e) => {
557
+ e.preventDefault();
558
+ e.dataTransfer.dropEffect = 'move';
559
+ };
560
+
561
+ const handleDrop = (e, targetItem) => {
562
+ e.preventDefault();
563
+ if (!draggedItem || draggedItem.id === targetItem.id) return;
564
+
565
+ const newItems = [...items];
566
+ const draggedIndex = newItems.findIndex(item => item.id === draggedItem.id);
567
+ const targetIndex = newItems.findIndex(item => item.id === targetItem.id);
568
+
569
+ // Remove dragged item and insert at target position
570
+ const [draggedItemData] = newItems.splice(draggedIndex, 1);
571
+ newItems.splice(targetIndex, 0, draggedItemData);
572
+
573
+ // Update priority values
574
+ const updatedItems = newItems.map((item, index) => ({
575
+ ...item,
576
+ priority: index + 1
577
+ }));
578
+
579
+ setItems(updatedItems);
580
+ setDraggedItem(null);
581
+ };
582
+
583
+ return (
584
+ <Table>
585
+ <TableHeader>
586
+ <TableRow>
587
+ <TableHeaderCell>Priority</TableHeaderCell>
588
+ <TableHeaderCell>Task</TableHeaderCell>
589
+ <TableHeaderCell>Actions</TableHeaderCell>
590
+ </TableRow>
591
+ </TableHeader>
592
+ <TableBody className="draggable-body">
593
+ {items.map(item => (
594
+ <TableRow
595
+ key={item.id}
596
+ draggable
597
+ onDragStart={(e) => handleDragStart(e, item)}
598
+ onDragOver={handleDragOver}
599
+ onDrop={(e) => handleDrop(e, item)}
600
+ className={`draggable-row ${draggedItem?.id === item.id ? 'dragging' : ''}`}
601
+ >
602
+ <TableCell>
603
+ <div className="priority-cell">
604
+ <Icon icon="DragIndicator" className="drag-handle" />
605
+ <Text type="BodyMedium">{item.priority}</Text>
606
+ </div>
607
+ </TableCell>
608
+ <TableCell>{item.task}</TableCell>
609
+ <TableCell>
610
+ <Button size="Small" type="Ghost">Edit</Button>
611
+ </TableCell>
612
+ </TableRow>
613
+ ))}
614
+ </TableBody>
615
+ </Table>
616
+ );
617
+ }
618
+ ```
619
+
620
+ ### Virtual Scrolling Body
621
+ ```tsx
622
+ function VirtualScrollBodyExample() {
623
+ const [data] = useState(
624
+ Array.from({ length: 10000 }, (_, i) => ({
625
+ id: i + 1,
626
+ name: `User ${i + 1}`,
627
+ email: `user${i + 1}@example.com`,
628
+ score: Math.floor(Math.random() * 1000)
629
+ }))
630
+ );
631
+
632
+ const [scrollTop, setScrollTop] = useState(0);
633
+ const itemHeight = 50;
634
+ const containerHeight = 400;
635
+ const visibleCount = Math.ceil(containerHeight / itemHeight);
636
+ const startIndex = Math.floor(scrollTop / itemHeight);
637
+ const endIndex = Math.min(startIndex + visibleCount + 5, data.length);
638
+ const visibleItems = data.slice(startIndex, endIndex);
639
+
640
+ const handleScroll = (e) => {
641
+ setScrollTop(e.target.scrollTop);
642
+ };
643
+
644
+ return (
645
+ <div
646
+ className="virtual-scroll-container"
647
+ style={{ height: containerHeight, overflow: 'auto' }}
648
+ onScroll={handleScroll}
649
+ >
650
+ <Table>
651
+ <TableHeader>
652
+ <TableRow>
653
+ <TableHeaderCell>ID</TableHeaderCell>
654
+ <TableHeaderCell>Name</TableHeaderCell>
655
+ <TableHeaderCell>Email</TableHeaderCell>
656
+ <TableHeaderCell>Score</TableHeaderCell>
657
+ </TableRow>
658
+ </TableHeader>
659
+ <TableBody
660
+ className="virtual-body"
661
+ style={{
662
+ height: data.length * itemHeight,
663
+ paddingTop: startIndex * itemHeight
664
+ }}
665
+ >
666
+ {visibleItems.map(item => (
667
+ <TableRow key={item.id} style={{ height: itemHeight }}>
668
+ <TableCell>{item.id}</TableCell>
669
+ <TableCell>{item.name}</TableCell>
670
+ <TableCell>{item.email}</TableCell>
671
+ <TableCell>{item.score}</TableCell>
672
+ </TableRow>
673
+ ))}
674
+ </TableBody>
675
+ </Table>
676
+ </div>
677
+ );
678
+ }
679
+ ```