@titan-design/react-ui 0.1.0 → 0.2.5

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 (236) hide show
  1. package/README.md +47 -1
  2. package/dist/{chunk-MGW37MPO.mjs → chunk-UXVRPOME.mjs} +173 -3
  3. package/dist/chunk-UXVRPOME.mjs.map +1 -0
  4. package/dist/index.d.mts +101 -12
  5. package/dist/index.d.ts +101 -12
  6. package/dist/index.js +1267 -468
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +734 -112
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/theme/index.d.mts +86 -1
  11. package/dist/theme/index.d.ts +86 -1
  12. package/dist/theme/index.js +166 -0
  13. package/dist/theme/index.js.map +1 -1
  14. package/dist/theme/index.mjs +1 -1
  15. package/package.json +22 -6
  16. package/src/components/custom/DateTime/DateTime.stories.tsx +180 -0
  17. package/src/components/custom/DateTime/DateTime.test.tsx +152 -0
  18. package/src/components/custom/DateTime/DateTime.tsx +235 -0
  19. package/src/components/custom/DateTime/index.ts +2 -0
  20. package/src/components/custom/EmptyState/EmptyState.stories.tsx +99 -0
  21. package/src/components/custom/EmptyState/EmptyState.test.tsx +106 -0
  22. package/src/components/custom/EmptyState/EmptyState.tsx +68 -0
  23. package/src/components/custom/EmptyState/index.ts +2 -0
  24. package/src/components/custom/Metric/Metric.stories.tsx +133 -0
  25. package/src/components/custom/Metric/Metric.test.tsx +120 -0
  26. package/src/components/custom/Metric/Metric.tsx +92 -0
  27. package/src/components/custom/Metric/index.ts +2 -0
  28. package/src/components/custom/Sidebar/Sidebar.stories.tsx +227 -0
  29. package/src/components/custom/Sidebar/Sidebar.test.tsx +185 -0
  30. package/src/components/custom/Sidebar/Sidebar.tsx +257 -0
  31. package/src/components/custom/Sidebar/index.ts +18 -0
  32. package/src/components/custom/Table/Table.stories.tsx +481 -0
  33. package/src/components/custom/Table/Table.test.tsx +531 -0
  34. package/src/components/custom/Table/Table.tsx +696 -0
  35. package/src/components/custom/Table/index.ts +22 -0
  36. package/src/components/custom/Typography/Typography.stories.tsx +153 -0
  37. package/src/components/custom/Typography/Typography.test.tsx +107 -0
  38. package/src/components/custom/Typography/Typography.tsx +218 -0
  39. package/src/components/custom/Typography/index.ts +19 -0
  40. package/src/components/custom/Workout/RestTimer.stories.tsx +71 -0
  41. package/src/components/custom/Workout/RestTimer.test.tsx +145 -0
  42. package/src/components/custom/Workout/RestTimer.tsx +172 -0
  43. package/src/components/custom/Workout/SupersetWrapper.stories.tsx +144 -0
  44. package/src/components/custom/Workout/SupersetWrapper.test.tsx +130 -0
  45. package/src/components/custom/Workout/SupersetWrapper.tsx +63 -0
  46. package/src/components/custom/Workout/index.ts +2 -0
  47. package/src/components/custom/index.ts +9 -0
  48. package/src/components/custom/stepper/Stepper.stories.tsx +245 -0
  49. package/src/components/custom/stepper/Stepper.test.tsx +230 -0
  50. package/src/components/custom/stepper/Stepper.tsx +250 -0
  51. package/src/components/custom/stepper/index.ts +10 -0
  52. package/src/components/index.ts +5 -0
  53. package/src/components/ui/alert/Alert.stories.tsx +154 -0
  54. package/src/components/ui/alert/Alert.test.tsx +202 -0
  55. package/src/components/ui/alert/Alert.tsx +170 -0
  56. package/src/components/ui/alert/index.ts +8 -0
  57. package/src/components/ui/autocomplete/Autocomplete.stories.tsx +299 -0
  58. package/src/components/ui/autocomplete/Autocomplete.test.tsx +247 -0
  59. package/src/components/ui/autocomplete/Autocomplete.tsx +332 -0
  60. package/src/components/ui/autocomplete/index.ts +2 -0
  61. package/src/components/ui/avatar/Avatar.stories.tsx +105 -0
  62. package/src/components/ui/avatar/Avatar.test.tsx +152 -0
  63. package/src/components/ui/avatar/Avatar.tsx +176 -0
  64. package/src/components/ui/avatar/index.ts +2 -0
  65. package/src/components/ui/badge/Badge.stories.tsx +100 -0
  66. package/src/components/ui/badge/Badge.test.tsx +103 -0
  67. package/src/components/ui/badge/Badge.tsx +119 -0
  68. package/src/components/ui/badge/index.ts +3 -0
  69. package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +70 -0
  70. package/src/components/ui/breadcrumbs/Breadcrumbs.test.tsx +156 -0
  71. package/src/components/ui/breadcrumbs/Breadcrumbs.tsx +127 -0
  72. package/src/components/ui/breadcrumbs/index.ts +2 -0
  73. package/src/components/ui/button/Button.stories.tsx +216 -0
  74. package/src/components/ui/button/Button.test.tsx +132 -0
  75. package/src/components/ui/button/Button.tsx +297 -0
  76. package/src/components/ui/button/index.ts +10 -0
  77. package/src/components/ui/card/Card.stories.tsx +443 -0
  78. package/src/components/ui/card/Card.test.tsx +294 -0
  79. package/src/components/ui/card/Card.tsx +401 -0
  80. package/src/components/ui/card/index.ts +20 -0
  81. package/src/components/ui/checkbox/Checkbox.stories.tsx +126 -0
  82. package/src/components/ui/checkbox/Checkbox.test.tsx +152 -0
  83. package/src/components/ui/checkbox/Checkbox.tsx +163 -0
  84. package/src/components/ui/checkbox/index.ts +2 -0
  85. package/src/components/ui/chip/Chip.stories.tsx +112 -0
  86. package/src/components/ui/chip/Chip.test.tsx +119 -0
  87. package/src/components/ui/chip/Chip.tsx +151 -0
  88. package/src/components/ui/chip/index.ts +2 -0
  89. package/src/components/ui/collapse/Collapse.stories.tsx +313 -0
  90. package/src/components/ui/collapse/Collapse.test.tsx +298 -0
  91. package/src/components/ui/collapse/Collapse.tsx +275 -0
  92. package/src/components/ui/collapse/index.ts +18 -0
  93. package/src/components/ui/data-row/DataRow.stories.tsx +78 -0
  94. package/src/components/ui/data-row/DataRow.test.tsx +78 -0
  95. package/src/components/ui/data-row/DataRow.tsx +29 -0
  96. package/src/components/ui/data-row/index.ts +2 -0
  97. package/src/components/ui/divider/Divider.stories.tsx +71 -0
  98. package/src/components/ui/divider/Divider.test.tsx +55 -0
  99. package/src/components/ui/divider/Divider.tsx +37 -0
  100. package/src/components/ui/divider/index.ts +2 -0
  101. package/src/components/ui/drawer/Drawer.stories.tsx +228 -0
  102. package/src/components/ui/drawer/Drawer.test.tsx +142 -0
  103. package/src/components/ui/drawer/Drawer.tsx +228 -0
  104. package/src/components/ui/drawer/index.ts +9 -0
  105. package/src/components/ui/form-field/FormField.stories.tsx +260 -0
  106. package/src/components/ui/form-field/FormField.test.tsx +280 -0
  107. package/src/components/ui/form-field/FormField.tsx +308 -0
  108. package/src/components/ui/form-field/index.ts +2 -0
  109. package/src/components/ui/help-tip/HelpTip.stories.tsx +250 -0
  110. package/src/components/ui/help-tip/HelpTip.test.tsx +198 -0
  111. package/src/components/ui/help-tip/HelpTip.tsx +229 -0
  112. package/src/components/ui/help-tip/index.ts +2 -0
  113. package/src/components/ui/icon-box/IconBox.stories.tsx +139 -0
  114. package/src/components/ui/icon-box/IconBox.test.tsx +120 -0
  115. package/src/components/ui/icon-box/IconBox.tsx +57 -0
  116. package/src/components/ui/icon-box/index.ts +1 -0
  117. package/src/components/ui/index.ts +38 -0
  118. package/src/components/ui/indicator/Indicator.stories.tsx +74 -0
  119. package/src/components/ui/indicator/Indicator.test.tsx +55 -0
  120. package/src/components/ui/indicator/Indicator.tsx +73 -0
  121. package/src/components/ui/indicator/index.ts +2 -0
  122. package/src/components/ui/input/Input.stories.tsx +192 -0
  123. package/src/components/ui/input/Input.test.tsx +86 -0
  124. package/src/components/ui/input/Input.tsx +273 -0
  125. package/src/components/ui/input/index.ts +2 -0
  126. package/src/components/ui/link/Link.stories.tsx +78 -0
  127. package/src/components/ui/link/Link.test.tsx +94 -0
  128. package/src/components/ui/link/Link.tsx +86 -0
  129. package/src/components/ui/link/index.ts +2 -0
  130. package/src/components/ui/list-item/ListItem.stories.tsx +146 -0
  131. package/src/components/ui/list-item/ListItem.test.tsx +128 -0
  132. package/src/components/ui/list-item/ListItem.tsx +89 -0
  133. package/src/components/ui/list-item/index.ts +14 -0
  134. package/src/components/ui/menu/Menu.stories.tsx +180 -0
  135. package/src/components/ui/menu/Menu.test.tsx +353 -0
  136. package/src/components/ui/menu/Menu.tsx +222 -0
  137. package/src/components/ui/menu/index.ts +2 -0
  138. package/src/components/ui/modal/Modal.stories.tsx +350 -0
  139. package/src/components/ui/modal/Modal.test.tsx +159 -0
  140. package/src/components/ui/modal/Modal.tsx +254 -0
  141. package/src/components/ui/modal/index.ts +19 -0
  142. package/src/components/ui/pill/Pill.stories.tsx +71 -0
  143. package/src/components/ui/pill/Pill.test.tsx +69 -0
  144. package/src/components/ui/pill/Pill.tsx +104 -0
  145. package/src/components/ui/pill/index.ts +2 -0
  146. package/src/components/ui/popover/Popover.stories.tsx +190 -0
  147. package/src/components/ui/popover/Popover.test.tsx +419 -0
  148. package/src/components/ui/popover/Popover.tsx +237 -0
  149. package/src/components/ui/popover/index.ts +2 -0
  150. package/src/components/ui/progress/Progress.stories.tsx +190 -0
  151. package/src/components/ui/progress/Progress.test.tsx +210 -0
  152. package/src/components/ui/progress/Progress.tsx +339 -0
  153. package/src/components/ui/progress/index.ts +9 -0
  154. package/src/components/ui/radio/Radio.stories.tsx +199 -0
  155. package/src/components/ui/radio/Radio.test.tsx +166 -0
  156. package/src/components/ui/radio/Radio.tsx +200 -0
  157. package/src/components/ui/radio/index.ts +2 -0
  158. package/src/components/ui/section/Section.stories.tsx +111 -0
  159. package/src/components/ui/section/Section.test.tsx +121 -0
  160. package/src/components/ui/section/Section.tsx +52 -0
  161. package/src/components/ui/section/index.ts +6 -0
  162. package/src/components/ui/select/Select.stories.tsx +286 -0
  163. package/src/components/ui/select/Select.test.tsx +200 -0
  164. package/src/components/ui/select/Select.tsx +243 -0
  165. package/src/components/ui/select/index.ts +2 -0
  166. package/src/components/ui/skeleton/Skeleton.stories.tsx +153 -0
  167. package/src/components/ui/skeleton/Skeleton.test.tsx +162 -0
  168. package/src/components/ui/skeleton/Skeleton.tsx +229 -0
  169. package/src/components/ui/skeleton/index.ts +16 -0
  170. package/src/components/ui/spinner/Spinner.stories.tsx +67 -0
  171. package/src/components/ui/spinner/Spinner.test.tsx +67 -0
  172. package/src/components/ui/spinner/Spinner.tsx +72 -0
  173. package/src/components/ui/spinner/index.ts +2 -0
  174. package/src/components/ui/stack/Stack.stories.tsx +127 -0
  175. package/src/components/ui/stack/Stack.test.tsx +184 -0
  176. package/src/components/ui/stack/Stack.tsx +76 -0
  177. package/src/components/ui/stack/index.ts +2 -0
  178. package/src/components/ui/surface/Surface.stories.tsx +91 -0
  179. package/src/components/ui/surface/Surface.test.tsx +63 -0
  180. package/src/components/ui/surface/Surface.tsx +51 -0
  181. package/src/components/ui/surface/index.ts +1 -0
  182. package/src/components/ui/switch/Switch.stories.tsx +111 -0
  183. package/src/components/ui/switch/Switch.test.tsx +112 -0
  184. package/src/components/ui/switch/Switch.tsx +103 -0
  185. package/src/components/ui/switch/index.ts +2 -0
  186. package/src/components/ui/tabs/Tabs.stories.tsx +213 -0
  187. package/src/components/ui/tabs/Tabs.test.tsx +192 -0
  188. package/src/components/ui/tabs/Tabs.tsx +253 -0
  189. package/src/components/ui/tabs/index.ts +16 -0
  190. package/src/components/ui/toast/Toast.stories.tsx +255 -0
  191. package/src/components/ui/toast/Toast.test.tsx +207 -0
  192. package/src/components/ui/toast/Toast.tsx +327 -0
  193. package/src/components/ui/toast/index.ts +9 -0
  194. package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +415 -0
  195. package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +224 -0
  196. package/src/components/ui/toolbar-button/ToolbarButton.tsx +307 -0
  197. package/src/components/ui/toolbar-button/index.ts +7 -0
  198. package/src/components/ui/tooltip/Tooltip.stories.tsx +126 -0
  199. package/src/components/ui/tooltip/Tooltip.test.tsx +262 -0
  200. package/src/components/ui/tooltip/Tooltip.tsx +234 -0
  201. package/src/components/ui/tooltip/index.ts +2 -0
  202. package/src/index.ts +10 -0
  203. package/src/stories/PresetShowcase.stories.tsx +462 -0
  204. package/src/stories/ThemePresets.stories.tsx +85 -0
  205. package/src/test/setup.ts +19 -0
  206. package/src/theme/Colors.stories.tsx +258 -0
  207. package/src/theme/config.ts +193 -0
  208. package/src/theme/elevation.stories.tsx +346 -0
  209. package/src/theme/elevation.test.ts +27 -0
  210. package/src/theme/elevation.ts +567 -0
  211. package/src/theme/global.css +129 -1
  212. package/src/theme/index.ts +40 -0
  213. package/src/theme/presets/apply.ts +97 -0
  214. package/src/theme/presets/audiobook.ts +93 -0
  215. package/src/theme/presets/default.ts +6 -0
  216. package/src/theme/presets/index.ts +4 -0
  217. package/src/theme/presets/presets.test.ts +51 -0
  218. package/src/theme/presets/types.ts +76 -0
  219. package/src/theme/shadows.stories.tsx +523 -0
  220. package/src/theme/shadows.ts +439 -0
  221. package/src/theme/tokens/index.ts +2 -0
  222. package/src/theme/tokens/primitives.ts +354 -0
  223. package/src/theme/tokens/semantic.ts +381 -0
  224. package/src/types/jest-axe.d.ts +16 -0
  225. package/src/types/nativewind.d.ts +54 -0
  226. package/src/utils/avatar-color.test.ts +30 -0
  227. package/src/utils/avatar-color.ts +20 -0
  228. package/src/utils/cn.ts +14 -0
  229. package/src/utils/colors.ts +140 -0
  230. package/src/utils/form.ts +188 -0
  231. package/src/utils/index.ts +29 -0
  232. package/src/utils/useTheme.ts +72 -0
  233. package/src/web-jsx/jsx-dev-runtime.ts +28 -0
  234. package/src/web-jsx/jsx-runtime.ts +41 -0
  235. package/tailwind.config.js +53 -3
  236. package/dist/chunk-MGW37MPO.mjs.map +0 -1
@@ -0,0 +1,481 @@
1
+ import React, { useState } from 'react'
2
+ import type { Meta, StoryObj } from '@storybook/react-vite'
3
+ import { View, Text } from 'react-native'
4
+ import { cn } from '../../../utils/cn'
5
+ import {
6
+ Table,
7
+ TableHeader,
8
+ TableBody,
9
+ TableRow,
10
+ TableHeaderCell,
11
+ TableCell,
12
+ TablePagination,
13
+ TableEmptyState,
14
+ TableSelectAllCell,
15
+ TableSelectCell,
16
+ useTable,
17
+ } from './Table'
18
+ import { Button, ButtonText } from '../../ui/button/Button'
19
+
20
+ interface User {
21
+ id: string
22
+ name: string
23
+ email: string
24
+ role: string
25
+ status: string
26
+ }
27
+
28
+ const sampleUsers: User[] = [
29
+ { id: '1', name: 'Alice Johnson', email: 'alice@example.com', role: 'Admin', status: 'Active' },
30
+ { id: '2', name: 'Bob Smith', email: 'bob@example.com', role: 'Editor', status: 'Active' },
31
+ { id: '3', name: 'Carol White', email: 'carol@example.com', role: 'Viewer', status: 'Inactive' },
32
+ { id: '4', name: 'David Brown', email: 'david@example.com', role: 'Editor', status: 'Active' },
33
+ { id: '5', name: 'Eve Davis', email: 'eve@example.com', role: 'Admin', status: 'Active' },
34
+ ]
35
+
36
+ const manyUsers: User[] = Array.from({ length: 50 }, (_, i) => ({
37
+ id: String(i + 1),
38
+ name: `User ${i + 1}`,
39
+ email: `user${i + 1}@example.com`,
40
+ role: ['Admin', 'Editor', 'Viewer'][i % 3],
41
+ status: i % 5 === 0 ? 'Inactive' : 'Active',
42
+ }))
43
+
44
+ const meta: Meta<typeof Table> = {
45
+ title: 'Custom/Table',
46
+ component: Table,
47
+ tags: ['autodocs'],
48
+ argTypes: {
49
+ isLoading: {
50
+ control: 'boolean',
51
+ description: 'Whether the table is in a loading state',
52
+ },
53
+ selectable: {
54
+ control: 'boolean',
55
+ description: 'Enable row selection checkboxes',
56
+ },
57
+ loadingRowCount: {
58
+ control: 'number',
59
+ description: 'Number of skeleton rows when loading',
60
+ },
61
+ },
62
+ }
63
+
64
+ export default meta
65
+ type Story = StoryObj<typeof Table>
66
+
67
+ export const Default: Story = {
68
+ render: () => (
69
+ <Table>
70
+ <TableHeader>
71
+ <TableRow isHoverable={false}>
72
+ <TableHeaderCell>Name</TableHeaderCell>
73
+ <TableHeaderCell>Email</TableHeaderCell>
74
+ <TableHeaderCell>Role</TableHeaderCell>
75
+ <TableHeaderCell>Status</TableHeaderCell>
76
+ </TableRow>
77
+ </TableHeader>
78
+ <TableBody>
79
+ {sampleUsers.map((user) => (
80
+ <TableRow key={user.id}>
81
+ <TableCell>
82
+ <Text className="text-sm text-text-primary font-medium">{user.name}</Text>
83
+ </TableCell>
84
+ <TableCell>{user.email}</TableCell>
85
+ <TableCell>{user.role}</TableCell>
86
+ <TableCell>
87
+ <View
88
+ className={cn(
89
+ 'px-2 py-0.5 rounded-full self-start',
90
+ user.status === 'Active' ? 'bg-status-success/20' : 'bg-status-warning/20'
91
+ )}
92
+ >
93
+ <Text
94
+ className={cn(
95
+ 'text-xs font-medium',
96
+ user.status === 'Active' ? 'text-status-success' : 'text-status-warning'
97
+ )}
98
+ >
99
+ {user.status}
100
+ </Text>
101
+ </View>
102
+ </TableCell>
103
+ </TableRow>
104
+ ))}
105
+ </TableBody>
106
+ </Table>
107
+ ),
108
+ }
109
+
110
+ export const WithSorting: Story = {
111
+ render: function Render() {
112
+ const [sortColumn, setSortColumn] = useState<string | undefined>(undefined)
113
+ const [sortDirection, setSortDirection] = useState<'asc' | 'desc' | null>(null)
114
+
115
+ const handleSort = (column: string) => {
116
+ if (sortColumn === column) {
117
+ setSortDirection((prev) =>
118
+ prev === 'asc' ? 'desc' : prev === 'desc' ? null : 'asc'
119
+ )
120
+ } else {
121
+ setSortColumn(column)
122
+ setSortDirection('asc')
123
+ }
124
+ }
125
+
126
+ const sorted = [...sampleUsers].sort((a, b) => {
127
+ if (!sortColumn || !sortDirection) return 0
128
+ const aVal = a[sortColumn as keyof User]
129
+ const bVal = b[sortColumn as keyof User]
130
+ const cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0
131
+ return sortDirection === 'asc' ? cmp : -cmp
132
+ })
133
+
134
+ return (
135
+ <Table sortColumn={sortColumn} sortDirection={sortDirection} onSort={handleSort}>
136
+ <TableHeader>
137
+ <TableRow isHoverable={false}>
138
+ <TableHeaderCell sortKey="name">Name</TableHeaderCell>
139
+ <TableHeaderCell sortKey="email">Email</TableHeaderCell>
140
+ <TableHeaderCell sortKey="role">Role</TableHeaderCell>
141
+ <TableHeaderCell sortKey="status">Status</TableHeaderCell>
142
+ </TableRow>
143
+ </TableHeader>
144
+ <TableBody>
145
+ {sorted.map((user) => (
146
+ <TableRow key={user.id}>
147
+ <TableCell>{user.name}</TableCell>
148
+ <TableCell>{user.email}</TableCell>
149
+ <TableCell>{user.role}</TableCell>
150
+ <TableCell>{user.status}</TableCell>
151
+ </TableRow>
152
+ ))}
153
+ </TableBody>
154
+ </Table>
155
+ )
156
+ },
157
+ }
158
+
159
+ export const WithRowSelection: Story = {
160
+ render: function Render() {
161
+ const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set())
162
+ const rowIds = sampleUsers.map((u) => u.id)
163
+
164
+ const handleSelectRow = (id: string, selected: boolean) => {
165
+ setSelectedRows((prev) => {
166
+ const next = new Set(prev)
167
+ if (selected) {
168
+ next.add(id)
169
+ } else {
170
+ next.delete(id)
171
+ }
172
+ return next
173
+ })
174
+ }
175
+
176
+ const handleSelectAll = (selected: boolean) => {
177
+ setSelectedRows(selected ? new Set(rowIds) : new Set())
178
+ }
179
+
180
+ return (
181
+ <View style={{ gap: 8 }}>
182
+ <Text className="text-sm text-text-secondary">
183
+ Selected: {selectedRows.size} of {sampleUsers.length}
184
+ </Text>
185
+ <Table
186
+ selectable
187
+ selectedRows={selectedRows}
188
+ onSelectRow={handleSelectRow}
189
+ onSelectAll={handleSelectAll}
190
+ rowIds={rowIds}
191
+ >
192
+ <TableHeader>
193
+ <TableRow isHoverable={false}>
194
+ <TableSelectAllCell />
195
+ <TableHeaderCell>Name</TableHeaderCell>
196
+ <TableHeaderCell>Email</TableHeaderCell>
197
+ <TableHeaderCell>Role</TableHeaderCell>
198
+ </TableRow>
199
+ </TableHeader>
200
+ <TableBody>
201
+ {sampleUsers.map((user) => (
202
+ <TableRow key={user.id} isSelected={selectedRows.has(user.id)}>
203
+ <TableSelectCell rowId={user.id} />
204
+ <TableCell>{user.name}</TableCell>
205
+ <TableCell>{user.email}</TableCell>
206
+ <TableCell>{user.role}</TableCell>
207
+ </TableRow>
208
+ ))}
209
+ </TableBody>
210
+ </Table>
211
+ </View>
212
+ )
213
+ },
214
+ }
215
+
216
+ export const Loading: Story = {
217
+ render: () => (
218
+ <Table isLoading loadingRowCount={5}>
219
+ <TableHeader>
220
+ <TableRow isHoverable={false}>
221
+ <TableHeaderCell>Name</TableHeaderCell>
222
+ <TableHeaderCell>Email</TableHeaderCell>
223
+ <TableHeaderCell>Role</TableHeaderCell>
224
+ <TableHeaderCell>Status</TableHeaderCell>
225
+ </TableRow>
226
+ </TableHeader>
227
+ <TableBody>
228
+ <TableRow>
229
+ <TableCell>Placeholder</TableCell>
230
+ </TableRow>
231
+ </TableBody>
232
+ </Table>
233
+ ),
234
+ }
235
+
236
+ export const EmptyState: Story = {
237
+ render: () => (
238
+ <Table>
239
+ <TableHeader>
240
+ <TableRow isHoverable={false}>
241
+ <TableHeaderCell>Name</TableHeaderCell>
242
+ <TableHeaderCell>Email</TableHeaderCell>
243
+ <TableHeaderCell>Role</TableHeaderCell>
244
+ <TableHeaderCell>Status</TableHeaderCell>
245
+ </TableRow>
246
+ </TableHeader>
247
+ <TableEmptyState
248
+ title="No users found"
249
+ description="There are no users matching your current filters. Try adjusting your search criteria."
250
+ action={
251
+ <Button variant="outline" color="primary">
252
+ <ButtonText>Clear Filters</ButtonText>
253
+ </Button>
254
+ }
255
+ />
256
+ </Table>
257
+ ),
258
+ }
259
+
260
+ export const WithPagination: Story = {
261
+ render: function Render() {
262
+ const {
263
+ paginatedData,
264
+ page,
265
+ pageSize,
266
+ totalItems,
267
+ sortColumn,
268
+ sortDirection,
269
+ setPage,
270
+ setPageSize,
271
+ handleSort,
272
+ } = useTable({
273
+ data: manyUsers,
274
+ defaultPageSize: 10,
275
+ })
276
+
277
+ return (
278
+ <View>
279
+ <Table sortColumn={sortColumn} sortDirection={sortDirection} onSort={handleSort}>
280
+ <TableHeader>
281
+ <TableRow isHoverable={false}>
282
+ <TableHeaderCell sortKey="name">Name</TableHeaderCell>
283
+ <TableHeaderCell sortKey="email">Email</TableHeaderCell>
284
+ <TableHeaderCell sortKey="role">Role</TableHeaderCell>
285
+ <TableHeaderCell sortKey="status">Status</TableHeaderCell>
286
+ </TableRow>
287
+ </TableHeader>
288
+ <TableBody>
289
+ {paginatedData.map((user) => (
290
+ <TableRow key={user.id}>
291
+ <TableCell>{user.name}</TableCell>
292
+ <TableCell>{user.email}</TableCell>
293
+ <TableCell>{user.role}</TableCell>
294
+ <TableCell>{user.status}</TableCell>
295
+ </TableRow>
296
+ ))}
297
+ </TableBody>
298
+ </Table>
299
+ <TablePagination
300
+ page={page}
301
+ pageSize={pageSize}
302
+ totalItems={totalItems}
303
+ onPageChange={setPage}
304
+ onPageSizeChange={setPageSize}
305
+ />
306
+ </View>
307
+ )
308
+ },
309
+ }
310
+
311
+ export const WithColumnWidths: Story = {
312
+ render: () => (
313
+ <Table>
314
+ <TableHeader>
315
+ <TableRow isHoverable={false}>
316
+ <TableHeaderCell width={60} align="center">#</TableHeaderCell>
317
+ <TableHeaderCell width={200}>Name</TableHeaderCell>
318
+ <TableHeaderCell>Email</TableHeaderCell>
319
+ <TableHeaderCell width={120} align="center">Role</TableHeaderCell>
320
+ <TableHeaderCell width={100} align="right">Status</TableHeaderCell>
321
+ </TableRow>
322
+ </TableHeader>
323
+ <TableBody>
324
+ {sampleUsers.map((user, i) => (
325
+ <TableRow key={user.id}>
326
+ <TableCell width={60} align="center">
327
+ <Text className="text-sm text-text-secondary">{i + 1}</Text>
328
+ </TableCell>
329
+ <TableCell width={200}>
330
+ <Text className="text-sm text-text-primary font-medium">{user.name}</Text>
331
+ </TableCell>
332
+ <TableCell>{user.email}</TableCell>
333
+ <TableCell width={120} align="center">{user.role}</TableCell>
334
+ <TableCell width={100} align="right">{user.status}</TableCell>
335
+ </TableRow>
336
+ ))}
337
+ </TableBody>
338
+ </Table>
339
+ ),
340
+ }
341
+
342
+ export const UseTableHook: Story = {
343
+ render: function Render() {
344
+ const {
345
+ paginatedData,
346
+ page,
347
+ pageSize,
348
+ totalItems,
349
+ sortColumn,
350
+ sortDirection,
351
+ setPage,
352
+ setPageSize,
353
+ handleSort,
354
+ } = useTable({
355
+ data: manyUsers,
356
+ defaultPageSize: 10,
357
+ defaultSortColumn: 'name',
358
+ defaultSortDirection: 'asc',
359
+ })
360
+
361
+ return (
362
+ <View style={{ gap: 8 }}>
363
+ <Text className="text-sm text-text-secondary">
364
+ Showing {paginatedData.length} of {totalItems} users, sorted by{' '}
365
+ {sortColumn ?? 'none'} ({sortDirection ?? 'none'})
366
+ </Text>
367
+ <Table sortColumn={sortColumn} sortDirection={sortDirection} onSort={handleSort}>
368
+ <TableHeader>
369
+ <TableRow isHoverable={false}>
370
+ <TableHeaderCell sortKey="name">Name</TableHeaderCell>
371
+ <TableHeaderCell sortKey="email">Email</TableHeaderCell>
372
+ <TableHeaderCell sortKey="role">Role</TableHeaderCell>
373
+ <TableHeaderCell sortKey="status">Status</TableHeaderCell>
374
+ </TableRow>
375
+ </TableHeader>
376
+ <TableBody>
377
+ {paginatedData.map((user) => (
378
+ <TableRow key={user.id}>
379
+ <TableCell>{user.name}</TableCell>
380
+ <TableCell>{user.email}</TableCell>
381
+ <TableCell>{user.role}</TableCell>
382
+ <TableCell>{user.status}</TableCell>
383
+ </TableRow>
384
+ ))}
385
+ </TableBody>
386
+ </Table>
387
+ <TablePagination
388
+ page={page}
389
+ pageSize={pageSize}
390
+ totalItems={totalItems}
391
+ onPageChange={setPage}
392
+ onPageSizeChange={setPageSize}
393
+ />
394
+ </View>
395
+ )
396
+ },
397
+ }
398
+
399
+ export const SortingAndSelection: Story = {
400
+ render: function Render() {
401
+ const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set())
402
+
403
+ const {
404
+ paginatedData,
405
+ page,
406
+ pageSize,
407
+ totalItems,
408
+ sortColumn,
409
+ sortDirection,
410
+ setPage,
411
+ setPageSize,
412
+ handleSort,
413
+ } = useTable({
414
+ data: manyUsers,
415
+ defaultPageSize: 10,
416
+ })
417
+
418
+ const handleSelectRow = (id: string, selected: boolean) => {
419
+ setSelectedRows((prev) => {
420
+ const next = new Set(prev)
421
+ if (selected) {
422
+ next.add(id)
423
+ } else {
424
+ next.delete(id)
425
+ }
426
+ return next
427
+ })
428
+ }
429
+
430
+ const handleSelectAll = (selected: boolean) => {
431
+ const currentPageIds = paginatedData.map((u) => u.id)
432
+ setSelectedRows(selected ? new Set(currentPageIds) : new Set())
433
+ }
434
+
435
+ return (
436
+ <View style={{ gap: 8 }}>
437
+ <Text className="text-sm text-text-secondary">
438
+ Selected: {selectedRows.size} rows
439
+ </Text>
440
+ <Table
441
+ sortColumn={sortColumn}
442
+ sortDirection={sortDirection}
443
+ onSort={handleSort}
444
+ selectable
445
+ selectedRows={selectedRows}
446
+ onSelectRow={handleSelectRow}
447
+ onSelectAll={handleSelectAll}
448
+ rowIds={paginatedData.map((u) => u.id)}
449
+ >
450
+ <TableHeader>
451
+ <TableRow isHoverable={false}>
452
+ <TableSelectAllCell />
453
+ <TableHeaderCell sortKey="name">Name</TableHeaderCell>
454
+ <TableHeaderCell sortKey="email">Email</TableHeaderCell>
455
+ <TableHeaderCell sortKey="role">Role</TableHeaderCell>
456
+ <TableHeaderCell sortKey="status">Status</TableHeaderCell>
457
+ </TableRow>
458
+ </TableHeader>
459
+ <TableBody>
460
+ {paginatedData.map((user) => (
461
+ <TableRow key={user.id} isSelected={selectedRows.has(user.id)}>
462
+ <TableSelectCell rowId={user.id} />
463
+ <TableCell>{user.name}</TableCell>
464
+ <TableCell>{user.email}</TableCell>
465
+ <TableCell>{user.role}</TableCell>
466
+ <TableCell>{user.status}</TableCell>
467
+ </TableRow>
468
+ ))}
469
+ </TableBody>
470
+ </Table>
471
+ <TablePagination
472
+ page={page}
473
+ pageSize={pageSize}
474
+ totalItems={totalItems}
475
+ onPageChange={setPage}
476
+ onPageSizeChange={setPageSize}
477
+ />
478
+ </View>
479
+ )
480
+ },
481
+ }