@delightui/components 0.1.105 → 0.1.107

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 (102) 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/index.d.ts +2 -0
  9. package/dist/cjs/library.css +19 -6
  10. package/dist/cjs/library.js +3 -3
  11. package/dist/cjs/library.js.map +1 -1
  12. package/dist/esm/components/molecules/Modal/DemoModal.d.ts +8 -0
  13. package/dist/esm/components/molecules/Modal/ModalContext/ModalContext.d.ts +41 -0
  14. package/dist/esm/components/molecules/Modal/ModalContext/ModalContext.types.d.ts +87 -0
  15. package/dist/esm/components/molecules/Modal/ModalContext/index.d.ts +3 -0
  16. package/dist/esm/components/molecules/Modal/ModalContext/useModal.d.ts +34 -0
  17. package/dist/esm/components/molecules/Modal/index.d.ts +2 -0
  18. package/dist/esm/components/molecules/index.d.ts +2 -0
  19. package/dist/esm/library.css +19 -6
  20. package/dist/esm/library.js +3 -3
  21. package/dist/esm/library.js.map +1 -1
  22. package/dist/index.d.ts +108 -2
  23. package/docs/README.md +264 -0
  24. package/docs/components/atoms/ActionImage.md +119 -0
  25. package/docs/components/atoms/Button.md +197 -0
  26. package/docs/components/atoms/Checkbox.md +299 -0
  27. package/docs/components/atoms/CheckboxItem.md +314 -0
  28. package/docs/components/atoms/Chip.md +380 -0
  29. package/docs/components/atoms/CustomToggle.md +270 -0
  30. package/docs/components/atoms/Icon.md +365 -0
  31. package/docs/components/atoms/IconButton.md +407 -0
  32. package/docs/components/atoms/Image.md +448 -0
  33. package/docs/components/atoms/Input.md +430 -0
  34. package/docs/components/atoms/ListItem.md +502 -0
  35. package/docs/components/atoms/Password.md +472 -0
  36. package/docs/components/atoms/RadioButton.md +614 -0
  37. package/docs/components/atoms/RadioButtonItem.md +588 -0
  38. package/docs/components/atoms/ResponsiveComponent.md +612 -0
  39. package/docs/components/atoms/SelectListItem.md +609 -0
  40. package/docs/components/atoms/Slider.md +605 -0
  41. package/docs/components/atoms/Spinner.md +605 -0
  42. package/docs/components/atoms/Text.md +463 -0
  43. package/docs/components/atoms/TextArea.md +670 -0
  44. package/docs/components/atoms/ToastNotification.md +668 -0
  45. package/docs/components/atoms/Toggle.md +737 -0
  46. package/docs/components/atoms/ToggleButton.md +751 -0
  47. package/docs/components/atoms/Tooltip.md +391 -0
  48. package/docs/components/molecules/Accordion.md +440 -0
  49. package/docs/components/molecules/AccordionGroup.md +547 -0
  50. package/docs/components/molecules/ActionCard.md +546 -0
  51. package/docs/components/molecules/Breadcrumb.md +403 -0
  52. package/docs/components/molecules/Breadcrumbs.md +485 -0
  53. package/docs/components/molecules/ButtonGroup.md +383 -0
  54. package/docs/components/molecules/Card.md +298 -0
  55. package/docs/components/molecules/ChipInput.md +646 -0
  56. package/docs/components/molecules/ContextMenu.md +768 -0
  57. package/docs/components/molecules/CustomTimeSelector.md +116 -0
  58. package/docs/components/molecules/DatePicker.md +516 -0
  59. package/docs/components/molecules/DateTimeSelector.md +166 -0
  60. package/docs/components/molecules/FormField.md +312 -0
  61. package/docs/components/molecules/Grid.md +577 -0
  62. package/docs/components/molecules/GridItem.md +834 -0
  63. package/docs/components/molecules/GridList.md +244 -0
  64. package/docs/components/molecules/List.md +485 -0
  65. package/docs/components/molecules/Modal.md +470 -0
  66. package/docs/components/molecules/ModalFooter.md +702 -0
  67. package/docs/components/molecules/ModalHeader.md +756 -0
  68. package/docs/components/molecules/ModalProvider.md +205 -0
  69. package/docs/components/molecules/Nav.md +530 -0
  70. package/docs/components/molecules/NavItem.md +572 -0
  71. package/docs/components/molecules/NavLink.md +499 -0
  72. package/docs/components/molecules/Option.md +521 -0
  73. package/docs/components/molecules/Pagination.md +592 -0
  74. package/docs/components/molecules/PaginationNumberField.md +722 -0
  75. package/docs/components/molecules/Popover.md +516 -0
  76. package/docs/components/molecules/ProgressBar.md +624 -0
  77. package/docs/components/molecules/RadioGroup.md +831 -0
  78. package/docs/components/molecules/RepeaterList.md +185 -0
  79. package/docs/components/molecules/Select.md +402 -0
  80. package/docs/components/molecules/SortableTrigger.md +82 -0
  81. package/docs/components/molecules/useModal.md +379 -0
  82. package/docs/components/organisms/Dropzone.md +346 -0
  83. package/docs/components/organisms/DropzoneClear.md +135 -0
  84. package/docs/components/organisms/DropzoneContent.md +216 -0
  85. package/docs/components/organisms/DropzoneFilename.md +191 -0
  86. package/docs/components/organisms/DropzoneSupportedFormats.md +184 -0
  87. package/docs/components/organisms/DropzoneTrigger.md +209 -0
  88. package/docs/components/organisms/Form.md +533 -0
  89. package/docs/components/organisms/SlideOutPanel.md +662 -0
  90. package/docs/components/organisms/TabContent.md +902 -0
  91. package/docs/components/organisms/TabItem.md +1091 -0
  92. package/docs/components/organisms/Table.md +611 -0
  93. package/docs/components/organisms/TableBody.md +679 -0
  94. package/docs/components/organisms/TableCell.md +482 -0
  95. package/docs/components/organisms/TableHeader.md +513 -0
  96. package/docs/components/organisms/TableHeaderCell.md +661 -0
  97. package/docs/components/organisms/TableRow.md +715 -0
  98. package/docs/components/organisms/Tabs.md +1330 -0
  99. package/docs/components/utils/ConditionalView.md +568 -0
  100. package/docs/components/utils/RenderStateView.md +726 -0
  101. package/docs/components/utils/WrapTextNodes.md +614 -0
  102. package/package.json +3 -2
@@ -0,0 +1,715 @@
1
+ # TableRow
2
+
3
+ ## Description
4
+
5
+ A table row component that represents individual rows within table structures. TableRow provides a semantic container for table cells with consistent styling, hover effects, and accessibility features. It extends the standard HTML table row element with enhanced functionality for data presentation and user interactions.
6
+
7
+ ## Aliases
8
+
9
+ - TableRow
10
+ - Table Row
11
+ - TR Element
12
+ - Row Container
13
+ - Data Row
14
+
15
+ ## Props Breakdown
16
+
17
+ **Extends:** `HTMLAttributes<HTMLTableRowElement>`
18
+
19
+ | Prop | Type | Default | Required | Description |
20
+ |------|------|---------|----------|-------------|
21
+ | `children` | `ReactNode` | - | No | Content to display in the row (typically TableCell components) |
22
+ | `className` | `string` | - | No | Additional CSS class names |
23
+
24
+ Plus all standard HTML tr attributes (onClick, onMouseOver, role, etc.).
25
+
26
+ ## Examples
27
+
28
+ ### Basic Table Row
29
+
30
+ ```tsx
31
+ import { Table, TableBody, TableRow, TableCell, TableHeader, TableHeaderCell } from '@delightui/components';
32
+
33
+ function BasicTableRowExample() {
34
+ const users = [
35
+ { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin' },
36
+ { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
37
+ { id: 3, name: 'Bob Johnson', email: 'bob@example.com', role: 'Moderator' }
38
+ ];
39
+
40
+ return (
41
+ <Table>
42
+ <TableHeader>
43
+ <TableRow>
44
+ <TableHeaderCell>ID</TableHeaderCell>
45
+ <TableHeaderCell>Name</TableHeaderCell>
46
+ <TableHeaderCell>Email</TableHeaderCell>
47
+ <TableHeaderCell>Role</TableHeaderCell>
48
+ </TableRow>
49
+ </TableHeader>
50
+ <TableBody>
51
+ {users.map(user => (
52
+ <TableRow key={user.id}>
53
+ <TableCell>{user.id}</TableCell>
54
+ <TableCell>{user.name}</TableCell>
55
+ <TableCell>{user.email}</TableCell>
56
+ <TableCell>{user.role}</TableCell>
57
+ </TableRow>
58
+ ))}
59
+ </TableBody>
60
+ </Table>
61
+ );
62
+ }
63
+ ```
64
+
65
+ ### Interactive Table Rows
66
+
67
+ ```tsx
68
+ import { Table, TableBody, TableRow, TableCell, TableHeader, TableHeaderCell, Button, Text } from '@delightui/components';
69
+
70
+ function InteractiveTableRowExample() {
71
+ const [selectedRow, setSelectedRow] = useState<number | null>(null);
72
+
73
+ const products = [
74
+ { id: 1, name: 'Wireless Headphones', price: 199.99, stock: 25, category: 'Electronics' },
75
+ { id: 2, name: 'Coffee Maker', price: 89.99, stock: 12, category: 'Appliances' },
76
+ { id: 3, name: 'Desk Lamp', price: 45.99, stock: 30, category: 'Furniture' },
77
+ { id: 4, name: 'Notebook Set', price: 15.99, stock: 100, category: 'Stationery' }
78
+ ];
79
+
80
+ const handleRowClick = (productId: number) => {
81
+ setSelectedRow(selectedRow === productId ? null : productId);
82
+ };
83
+
84
+ return (
85
+ <Table>
86
+ <TableHeader>
87
+ <TableRow>
88
+ <TableHeaderCell>Product</TableHeaderCell>
89
+ <TableHeaderCell>Price</TableHeaderCell>
90
+ <TableHeaderCell>Stock</TableHeaderCell>
91
+ <TableHeaderCell>Category</TableHeaderCell>
92
+ <TableHeaderCell>Actions</TableHeaderCell>
93
+ </TableRow>
94
+ </TableHeader>
95
+ <TableBody>
96
+ {products.map(product => (
97
+ <TableRow
98
+ key={product.id}
99
+ onClick={() => handleRowClick(product.id)}
100
+ style={{
101
+ backgroundColor: selectedRow === product.id ? '#e3f2fd' : 'transparent',
102
+ cursor: 'pointer',
103
+ transition: 'background-color 0.2s ease'
104
+ }}
105
+ onMouseEnter={(e) => {
106
+ if (selectedRow !== product.id) {
107
+ (e.target as HTMLElement).style.backgroundColor = '#f5f5f5';
108
+ }
109
+ }}
110
+ onMouseLeave={(e) => {
111
+ if (selectedRow !== product.id) {
112
+ (e.target as HTMLElement).style.backgroundColor = 'transparent';
113
+ }
114
+ }}
115
+ >
116
+ <TableCell>
117
+ <Text weight={selectedRow === product.id ? 'Bold' : 'Medium'}>
118
+ {product.name}
119
+ </Text>
120
+ </TableCell>
121
+ <TableCell>${product.price.toFixed(2)}</TableCell>
122
+ <TableCell>{product.stock}</TableCell>
123
+ <TableCell>{product.category}</TableCell>
124
+ <TableCell>
125
+ <div style={{ display: 'flex', gap: '8px' }}>
126
+ <Button size="Small" type="Outlined">Edit</Button>
127
+ <Button size="Small" style="Destructive">Delete</Button>
128
+ </div>
129
+ </TableCell>
130
+ </TableRow>
131
+ ))}
132
+ </TableBody>
133
+ </Table>
134
+ );
135
+ }
136
+ ```
137
+
138
+ ### Expandable Table Rows
139
+
140
+ ```tsx
141
+ import { Table, TableBody, TableRow, TableCell, TableHeader, TableHeaderCell, Button, Text } from '@delightui/components';
142
+
143
+ function ExpandableTableRowExample() {
144
+ const [expandedRows, setExpandedRows] = useState<Set<number>>(new Set());
145
+
146
+ const orders = [
147
+ {
148
+ id: 1001,
149
+ customer: 'Alice Johnson',
150
+ date: '2024-01-15',
151
+ total: 299.99,
152
+ status: 'Shipped',
153
+ items: [
154
+ { name: 'Wireless Headphones', quantity: 1, price: 199.99 },
155
+ { name: 'Phone Case', quantity: 2, price: 50.00 }
156
+ ]
157
+ },
158
+ {
159
+ id: 1002,
160
+ customer: 'Bob Wilson',
161
+ date: '2024-01-16',
162
+ total: 156.50,
163
+ status: 'Processing',
164
+ items: [
165
+ { name: 'Coffee Mug', quantity: 3, price: 15.99 },
166
+ { name: 'Notebook', quantity: 5, price: 18.99 }
167
+ ]
168
+ }
169
+ ];
170
+
171
+ const toggleExpanded = (orderId: number) => {
172
+ const newExpanded = new Set(expandedRows);
173
+ if (newExpanded.has(orderId)) {
174
+ newExpanded.delete(orderId);
175
+ } else {
176
+ newExpanded.add(orderId);
177
+ }
178
+ setExpandedRows(newExpanded);
179
+ };
180
+
181
+ return (
182
+ <Table>
183
+ <TableHeader>
184
+ <TableRow>
185
+ <TableHeaderCell>Order ID</TableHeaderCell>
186
+ <TableHeaderCell>Customer</TableHeaderCell>
187
+ <TableHeaderCell>Date</TableHeaderCell>
188
+ <TableHeaderCell>Total</TableHeaderCell>
189
+ <TableHeaderCell>Status</TableHeaderCell>
190
+ <TableHeaderCell>Actions</TableHeaderCell>
191
+ </TableRow>
192
+ </TableHeader>
193
+ <TableBody>
194
+ {orders.map(order => (
195
+ <React.Fragment key={order.id}>
196
+ <TableRow>
197
+ <TableCell>#{order.id}</TableCell>
198
+ <TableCell>{order.customer}</TableCell>
199
+ <TableCell>{order.date}</TableCell>
200
+ <TableCell>${order.total.toFixed(2)}</TableCell>
201
+ <TableCell>{order.status}</TableCell>
202
+ <TableCell>
203
+ <Button
204
+ size="Small"
205
+ type="Outlined"
206
+ onClick={() => toggleExpanded(order.id)}
207
+ >
208
+ {expandedRows.has(order.id) ? 'Hide Details' : 'Show Details'}
209
+ </Button>
210
+ </TableCell>
211
+ </TableRow>
212
+ {expandedRows.has(order.id) && (
213
+ <TableRow style={{ backgroundColor: '#f8f9fa' }}>
214
+ <TableCell colSpan={6}>
215
+ <div style={{ padding: '16px' }}>
216
+ <Text weight="Bold" style={{ marginBottom: '12px' }}>
217
+ Order Items:
218
+ </Text>
219
+ <Table style={{ marginBottom: '0' }}>
220
+ <TableHeader>
221
+ <TableRow>
222
+ <TableHeaderCell>Item</TableHeaderCell>
223
+ <TableHeaderCell>Quantity</TableHeaderCell>
224
+ <TableHeaderCell>Price</TableHeaderCell>
225
+ </TableRow>
226
+ </TableHeader>
227
+ <TableBody>
228
+ {order.items.map((item, index) => (
229
+ <TableRow key={index}>
230
+ <TableCell>{item.name}</TableCell>
231
+ <TableCell>{item.quantity}</TableCell>
232
+ <TableCell>${item.price.toFixed(2)}</TableCell>
233
+ </TableRow>
234
+ ))}
235
+ </TableBody>
236
+ </Table>
237
+ </div>
238
+ </TableCell>
239
+ </TableRow>
240
+ )}
241
+ </React.Fragment>
242
+ ))}
243
+ </TableBody>
244
+ </Table>
245
+ );
246
+ }
247
+ ```
248
+
249
+ ### Striped Table Rows
250
+
251
+ ```tsx
252
+ import { Table, TableBody, TableRow, TableCell, TableHeader, TableHeaderCell } from '@delightui/components';
253
+
254
+ function StripedTableRowExample() {
255
+ const employees = [
256
+ { name: 'Alice Brown', department: 'Engineering', salary: 75000, startDate: '2023-01-15' },
257
+ { name: 'Bob Davis', department: 'Marketing', salary: 65000, startDate: '2023-02-20' },
258
+ { name: 'Carol Evans', department: 'Design', salary: 70000, startDate: '2023-03-10' },
259
+ { name: 'David Wilson', department: 'Sales', salary: 60000, startDate: '2023-04-05' },
260
+ { name: 'Emma Johnson', department: 'HR', salary: 58000, startDate: '2023-05-12' }
261
+ ];
262
+
263
+ return (
264
+ <Table>
265
+ <TableHeader>
266
+ <TableRow>
267
+ <TableHeaderCell>Employee Name</TableHeaderCell>
268
+ <TableHeaderCell>Department</TableHeaderCell>
269
+ <TableHeaderCell>Salary</TableHeaderCell>
270
+ <TableHeaderCell>Start Date</TableHeaderCell>
271
+ </TableRow>
272
+ </TableHeader>
273
+ <TableBody>
274
+ {employees.map((employee, index) => (
275
+ <TableRow
276
+ key={index}
277
+ style={{
278
+ backgroundColor: index % 2 === 0 ? '#ffffff' : '#f8f9fa'
279
+ }}
280
+ >
281
+ <TableCell>{employee.name}</TableCell>
282
+ <TableCell>{employee.department}</TableCell>
283
+ <TableCell>${employee.salary.toLocaleString()}</TableCell>
284
+ <TableCell>{employee.startDate}</TableCell>
285
+ </TableRow>
286
+ ))}
287
+ </TableBody>
288
+ </Table>
289
+ );
290
+ }
291
+ ```
292
+
293
+ ### Conditional Row Styling
294
+
295
+ ```tsx
296
+ import { Table, TableBody, TableRow, TableCell, TableHeader, TableHeaderCell, Chip } from '@delightui/components';
297
+
298
+ function ConditionalStyledRowExample() {
299
+ const projects = [
300
+ { name: 'Website Redesign', status: 'Completed', progress: 100, priority: 'High', dueDate: '2024-01-15' },
301
+ { name: 'Mobile App', status: 'In Progress', progress: 75, priority: 'High', dueDate: '2024-02-28' },
302
+ { name: 'Database Migration', status: 'Overdue', progress: 40, priority: 'Critical', dueDate: '2024-01-20' },
303
+ { name: 'Security Audit', status: 'Not Started', progress: 0, priority: 'Medium', dueDate: '2024-03-15' },
304
+ { name: 'Performance Optimization', status: 'In Progress', progress: 60, priority: 'Low', dueDate: '2024-04-01' }
305
+ ];
306
+
307
+ const getRowStyle = (project: any) => {
308
+ if (project.status === 'Overdue') {
309
+ return {
310
+ backgroundColor: '#ffebee',
311
+ borderLeft: '4px solid #f44336'
312
+ };
313
+ }
314
+ if (project.status === 'Completed') {
315
+ return {
316
+ backgroundColor: '#e8f5e8',
317
+ borderLeft: '4px solid #4caf50'
318
+ };
319
+ }
320
+ if (project.priority === 'Critical') {
321
+ return {
322
+ backgroundColor: '#fff3e0',
323
+ borderLeft: '4px solid #ff9800'
324
+ };
325
+ }
326
+ return {};
327
+ };
328
+
329
+ const getStatusColor = (status: string) => {
330
+ switch (status) {
331
+ case 'Completed': return 'success';
332
+ case 'In Progress': return 'primary';
333
+ case 'Overdue': return 'error';
334
+ case 'Not Started': return 'neutral';
335
+ default: return 'neutral';
336
+ }
337
+ };
338
+
339
+ const getPriorityColor = (priority: string) => {
340
+ switch (priority) {
341
+ case 'Critical': return 'error';
342
+ case 'High': return 'warning';
343
+ case 'Medium': return 'primary';
344
+ case 'Low': return 'neutral';
345
+ default: return 'neutral';
346
+ }
347
+ };
348
+
349
+ return (
350
+ <Table>
351
+ <TableHeader>
352
+ <TableRow>
353
+ <TableHeaderCell>Project Name</TableHeaderCell>
354
+ <TableHeaderCell>Status</TableHeaderCell>
355
+ <TableHeaderCell>Progress</TableHeaderCell>
356
+ <TableHeaderCell>Priority</TableHeaderCell>
357
+ <TableHeaderCell>Due Date</TableHeaderCell>
358
+ </TableRow>
359
+ </TableHeader>
360
+ <TableBody>
361
+ {projects.map((project, index) => (
362
+ <TableRow
363
+ key={index}
364
+ style={getRowStyle(project)}
365
+ >
366
+ <TableCell style={{ fontWeight: 'bold' }}>
367
+ {project.name}
368
+ </TableCell>
369
+ <TableCell>
370
+ <Chip color={getStatusColor(project.status)}>
371
+ {project.status}
372
+ </Chip>
373
+ </TableCell>
374
+ <TableCell>
375
+ <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
376
+ <div style={{
377
+ width: '100px',
378
+ height: '8px',
379
+ backgroundColor: '#e0e0e0',
380
+ borderRadius: '4px',
381
+ overflow: 'hidden'
382
+ }}>
383
+ <div style={{
384
+ width: `${project.progress}%`,
385
+ height: '100%',
386
+ backgroundColor: project.progress === 100 ? '#4caf50' : '#2196f3',
387
+ transition: 'width 0.3s ease'
388
+ }} />
389
+ </div>
390
+ <span>{project.progress}%</span>
391
+ </div>
392
+ </TableCell>
393
+ <TableCell>
394
+ <Chip color={getPriorityColor(project.priority)}>
395
+ {project.priority}
396
+ </Chip>
397
+ </TableCell>
398
+ <TableCell>{project.dueDate}</TableCell>
399
+ </TableRow>
400
+ ))}
401
+ </TableBody>
402
+ </Table>
403
+ );
404
+ }
405
+ ```
406
+
407
+ ### Draggable Table Rows
408
+
409
+ ```tsx
410
+ import { Table, TableBody, TableRow, TableCell, TableHeader, TableHeaderCell, Button, Icon } from '@delightui/components';
411
+
412
+ function DraggableTableRowExample() {
413
+ const [tasks, setTasks] = useState([
414
+ { id: 1, name: 'Review code changes', priority: 1, status: 'To Do' },
415
+ { id: 2, name: 'Update documentation', priority: 2, status: 'In Progress' },
416
+ { id: 3, name: 'Fix responsive issues', priority: 3, status: 'To Do' },
417
+ { id: 4, name: 'Optimize database queries', priority: 4, status: 'Done' }
418
+ ]);
419
+
420
+ const [draggedRow, setDraggedRow] = useState<number | null>(null);
421
+
422
+ const handleDragStart = (e: React.DragEvent, taskId: number) => {
423
+ setDraggedRow(taskId);
424
+ e.dataTransfer.effectAllowed = 'move';
425
+ };
426
+
427
+ const handleDragOver = (e: React.DragEvent) => {
428
+ e.preventDefault();
429
+ };
430
+
431
+ const handleDrop = (e: React.DragEvent, targetTaskId: number) => {
432
+ e.preventDefault();
433
+ if (draggedRow === null || draggedRow === targetTaskId) return;
434
+
435
+ const draggedIndex = tasks.findIndex(task => task.id === draggedRow);
436
+ const targetIndex = tasks.findIndex(task => task.id === targetTaskId);
437
+
438
+ const newTasks = [...tasks];
439
+ const [draggedTask] = newTasks.splice(draggedIndex, 1);
440
+ newTasks.splice(targetIndex, 0, draggedTask);
441
+
442
+ // Update priorities
443
+ const updatedTasks = newTasks.map((task, index) => ({
444
+ ...task,
445
+ priority: index + 1
446
+ }));
447
+
448
+ setTasks(updatedTasks);
449
+ setDraggedRow(null);
450
+ };
451
+
452
+ const handleDragEnd = () => {
453
+ setDraggedRow(null);
454
+ };
455
+
456
+ return (
457
+ <Table>
458
+ <TableHeader>
459
+ <TableRow>
460
+ <TableHeaderCell>Priority</TableHeaderCell>
461
+ <TableHeaderCell>Task</TableHeaderCell>
462
+ <TableHeaderCell>Status</TableHeaderCell>
463
+ <TableHeaderCell>Actions</TableHeaderCell>
464
+ </TableRow>
465
+ </TableHeader>
466
+ <TableBody>
467
+ {tasks.map(task => (
468
+ <TableRow
469
+ key={task.id}
470
+ draggable
471
+ onDragStart={(e) => handleDragStart(e, task.id)}
472
+ onDragOver={handleDragOver}
473
+ onDrop={(e) => handleDrop(e, task.id)}
474
+ onDragEnd={handleDragEnd}
475
+ style={{
476
+ opacity: draggedRow === task.id ? 0.5 : 1,
477
+ cursor: 'move',
478
+ backgroundColor: draggedRow === task.id ? '#e3f2fd' : 'transparent',
479
+ transition: 'opacity 0.2s ease, background-color 0.2s ease'
480
+ }}
481
+ >
482
+ <TableCell>
483
+ <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
484
+ <Icon name="DragOutlined" style={{ cursor: 'grab' }} />
485
+ {task.priority}
486
+ </div>
487
+ </TableCell>
488
+ <TableCell>{task.name}</TableCell>
489
+ <TableCell>{task.status}</TableCell>
490
+ <TableCell>
491
+ <Button size="Small" type="Outlined">
492
+ Edit
493
+ </Button>
494
+ </TableCell>
495
+ </TableRow>
496
+ ))}
497
+ </TableBody>
498
+ </Table>
499
+ );
500
+ }
501
+ ```
502
+
503
+ ### Grouped Table Rows
504
+
505
+ ```tsx
506
+ import { Table, TableBody, TableRow, TableCell, TableHeader, TableHeaderCell, Text } from '@delightui/components';
507
+
508
+ function GroupedTableRowExample() {
509
+ const salesData = [
510
+ { region: 'North America', country: 'USA', sales: 125000, target: 120000 },
511
+ { region: 'North America', country: 'Canada', sales: 45000, target: 50000 },
512
+ { region: 'Europe', country: 'Germany', sales: 85000, target: 80000 },
513
+ { region: 'Europe', country: 'France', sales: 65000, target: 70000 },
514
+ { region: 'Europe', country: 'UK', sales: 75000, target: 75000 },
515
+ { region: 'Asia', country: 'Japan', sales: 95000, target: 90000 },
516
+ { region: 'Asia', country: 'China', sales: 110000, target: 100000 }
517
+ ];
518
+
519
+ const groupedData = salesData.reduce((acc, item) => {
520
+ if (!acc[item.region]) {
521
+ acc[item.region] = [];
522
+ }
523
+ acc[item.region].push(item);
524
+ return acc;
525
+ }, {} as Record<string, typeof salesData>);
526
+
527
+ const getRegionTotals = (regionData: typeof salesData) => {
528
+ return {
529
+ sales: regionData.reduce((sum, item) => sum + item.sales, 0),
530
+ target: regionData.reduce((sum, item) => sum + item.target, 0)
531
+ };
532
+ };
533
+
534
+ return (
535
+ <Table>
536
+ <TableHeader>
537
+ <TableRow>
538
+ <TableHeaderCell>Region/Country</TableHeaderCell>
539
+ <TableHeaderCell>Sales</TableHeaderCell>
540
+ <TableHeaderCell>Target</TableHeaderCell>
541
+ <TableHeaderCell>Achievement</TableHeaderCell>
542
+ </TableRow>
543
+ </TableHeader>
544
+ <TableBody>
545
+ {Object.entries(groupedData).map(([region, countries]) => {
546
+ const totals = getRegionTotals(countries);
547
+ const achievement = ((totals.sales / totals.target) * 100).toFixed(1);
548
+
549
+ return (
550
+ <React.Fragment key={region}>
551
+ <TableRow style={{
552
+ backgroundColor: '#f5f5f5',
553
+ fontWeight: 'bold',
554
+ borderTop: '2px solid #ddd'
555
+ }}>
556
+ <TableCell>
557
+ <Text weight="Bold" size="Medium">
558
+ {region}
559
+ </Text>
560
+ </TableCell>
561
+ <TableCell>
562
+ <Text weight="Bold">
563
+ ${totals.sales.toLocaleString()}
564
+ </Text>
565
+ </TableCell>
566
+ <TableCell>
567
+ <Text weight="Bold">
568
+ ${totals.target.toLocaleString()}
569
+ </Text>
570
+ </TableCell>
571
+ <TableCell>
572
+ <Text
573
+ weight="Bold"
574
+ style={{
575
+ color: parseFloat(achievement) >= 100 ? '#4caf50' : '#f44336'
576
+ }}
577
+ >
578
+ {achievement}%
579
+ </Text>
580
+ </TableCell>
581
+ </TableRow>
582
+ {countries.map((country, index) => (
583
+ <TableRow key={`${region}-${country.country}`}>
584
+ <TableCell style={{ paddingLeft: '32px' }}>
585
+ {country.country}
586
+ </TableCell>
587
+ <TableCell>${country.sales.toLocaleString()}</TableCell>
588
+ <TableCell>${country.target.toLocaleString()}</TableCell>
589
+ <TableCell>
590
+ {((country.sales / country.target) * 100).toFixed(1)}%
591
+ </TableCell>
592
+ </TableRow>
593
+ ))}
594
+ </React.Fragment>
595
+ );
596
+ })}
597
+ </TableBody>
598
+ </Table>
599
+ );
600
+ }
601
+ ```
602
+
603
+ ### Accessible Table Rows
604
+
605
+ ```tsx
606
+ import { Table, TableBody, TableRow, TableCell, TableHeader, TableHeaderCell, Button } from '@delightui/components';
607
+
608
+ function AccessibleTableRowExample() {
609
+ const [selectedRows, setSelectedRows] = useState<Set<number>>(new Set());
610
+
611
+ const invoices = [
612
+ { id: 1, invoice: 'INV-001', client: 'Acme Corp', amount: 2500.00, status: 'Paid', date: '2024-01-15' },
613
+ { id: 2, invoice: 'INV-002', client: 'Global Solutions', amount: 1800.00, status: 'Pending', date: '2024-01-18' },
614
+ { id: 3, invoice: 'INV-003', client: 'Tech Innovations', amount: 3200.00, status: 'Overdue', date: '2024-01-10' }
615
+ ];
616
+
617
+ const toggleRowSelection = (invoiceId: number) => {
618
+ const newSelection = new Set(selectedRows);
619
+ if (newSelection.has(invoiceId)) {
620
+ newSelection.delete(invoiceId);
621
+ } else {
622
+ newSelection.add(invoiceId);
623
+ }
624
+ setSelectedRows(newSelection);
625
+ };
626
+
627
+ const handleKeyDown = (e: React.KeyboardEvent, invoiceId: number) => {
628
+ if (e.key === 'Enter' || e.key === ' ') {
629
+ e.preventDefault();
630
+ toggleRowSelection(invoiceId);
631
+ }
632
+ };
633
+
634
+ return (
635
+ <Table
636
+ role="table"
637
+ aria-label="Invoice management table"
638
+ >
639
+ <TableHeader>
640
+ <TableRow role="row">
641
+ <TableHeaderCell scope="col">Invoice</TableHeaderCell>
642
+ <TableHeaderCell scope="col">Client</TableHeaderCell>
643
+ <TableHeaderCell scope="col">Amount</TableHeaderCell>
644
+ <TableHeaderCell scope="col">Status</TableHeaderCell>
645
+ <TableHeaderCell scope="col">Date</TableHeaderCell>
646
+ <TableHeaderCell scope="col">Actions</TableHeaderCell>
647
+ </TableRow>
648
+ </TableHeader>
649
+ <TableBody>
650
+ {invoices.map(invoice => (
651
+ <TableRow
652
+ key={invoice.id}
653
+ role="row"
654
+ tabIndex={0}
655
+ aria-selected={selectedRows.has(invoice.id)}
656
+ onClick={() => toggleRowSelection(invoice.id)}
657
+ onKeyDown={(e) => handleKeyDown(e, invoice.id)}
658
+ style={{
659
+ backgroundColor: selectedRows.has(invoice.id) ? '#e3f2fd' : 'transparent',
660
+ cursor: 'pointer',
661
+ outline: 'none'
662
+ }}
663
+ onFocus={(e) => {
664
+ (e.target as HTMLElement).style.boxShadow = '0 0 0 2px #2196f3';
665
+ }}
666
+ onBlur={(e) => {
667
+ (e.target as HTMLElement).style.boxShadow = 'none';
668
+ }}
669
+ aria-label={`Invoice ${invoice.invoice} for ${invoice.client}, amount $${invoice.amount}, status ${invoice.status}`}
670
+ >
671
+ <TableCell role="cell">
672
+ <strong>{invoice.invoice}</strong>
673
+ </TableCell>
674
+ <TableCell role="cell">{invoice.client}</TableCell>
675
+ <TableCell role="cell" aria-label={`Amount: ${invoice.amount} dollars`}>
676
+ ${invoice.amount.toFixed(2)}
677
+ </TableCell>
678
+ <TableCell role="cell">
679
+ <span
680
+ style={{
681
+ padding: '4px 8px',
682
+ borderRadius: '4px',
683
+ fontSize: '12px',
684
+ fontWeight: 'bold',
685
+ backgroundColor:
686
+ invoice.status === 'Paid' ? '#4caf50' :
687
+ invoice.status === 'Pending' ? '#ff9800' : '#f44336',
688
+ color: 'white'
689
+ }}
690
+ aria-label={`Status: ${invoice.status}`}
691
+ >
692
+ {invoice.status}
693
+ </span>
694
+ </TableCell>
695
+ <TableCell role="cell">{invoice.date}</TableCell>
696
+ <TableCell role="cell">
697
+ <Button
698
+ size="Small"
699
+ type="Outlined"
700
+ aria-label={`View details for invoice ${invoice.invoice}`}
701
+ onClick={(e) => {
702
+ e.stopPropagation();
703
+ console.log('View invoice:', invoice.id);
704
+ }}
705
+ >
706
+ View
707
+ </Button>
708
+ </TableCell>
709
+ </TableRow>
710
+ ))}
711
+ </TableBody>
712
+ </Table>
713
+ );
714
+ }
715
+ ```