@urbint/cl 1.0.1

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 (206) hide show
  1. package/.cursor/rules +313 -0
  2. package/.rnstorybook/index.ts +11 -0
  3. package/.rnstorybook/main.ts +8 -0
  4. package/.rnstorybook/preview.tsx +14 -0
  5. package/.rnstorybook/storybook.requires.ts +49 -0
  6. package/.storybook/main.ts +16 -0
  7. package/.storybook/preview.ts +32 -0
  8. package/.storybook/vitest.setup.ts +7 -0
  9. package/App.tsx +422 -0
  10. package/README.md +229 -0
  11. package/app.json +33 -0
  12. package/assets/adaptive-icon.png +0 -0
  13. package/assets/favicon.png +0 -0
  14. package/assets/icon.png +0 -0
  15. package/assets/splash-icon.png +0 -0
  16. package/babel.config.js +16 -0
  17. package/docs/components/CodeBlock.tsx +80 -0
  18. package/docs/components/PropTable.tsx +93 -0
  19. package/docs/components/Sidebar.tsx +199 -0
  20. package/docs/components/index.ts +8 -0
  21. package/docs/data/colorTokens.ts +70 -0
  22. package/docs/data/componentData.tsx +1685 -0
  23. package/docs/data/index.ts +7 -0
  24. package/docs/index.ts +19 -0
  25. package/docs/navigation.ts +94 -0
  26. package/docs/pages/ColorsPage.tsx +226 -0
  27. package/docs/pages/ComponentPage.tsx +235 -0
  28. package/docs/pages/InstallationPage.tsx +232 -0
  29. package/docs/pages/IntroductionPage.tsx +163 -0
  30. package/docs/pages/ThemingPage.tsx +251 -0
  31. package/docs/pages/index.ts +10 -0
  32. package/docs/theme.ts +64 -0
  33. package/docs/types.ts +54 -0
  34. package/index.ts +8 -0
  35. package/llms.txt +1893 -0
  36. package/mcp-config.example.json +10 -0
  37. package/mcp-server/README.md +192 -0
  38. package/mcp-server/package-lock.json +1707 -0
  39. package/mcp-server/package.json +38 -0
  40. package/mcp-server/src/index.ts +1136 -0
  41. package/mcp-server/src/registry/components.ts +1446 -0
  42. package/mcp-server/src/registry/index.ts +3 -0
  43. package/mcp-server/src/registry/tokens.ts +256 -0
  44. package/mcp-server/tsconfig.json +19 -0
  45. package/package.json +92 -0
  46. package/src/components/Accordion/Accordion.stories.tsx +226 -0
  47. package/src/components/Accordion/Accordion.tsx +255 -0
  48. package/src/components/Accordion/index.ts +12 -0
  49. package/src/components/ActionSheet/ActionSheet.stories.tsx +393 -0
  50. package/src/components/ActionSheet/ActionSheet.tsx +258 -0
  51. package/src/components/ActionSheet/index.ts +2 -0
  52. package/src/components/Alert/Alert.stories.tsx +165 -0
  53. package/src/components/Alert/Alert.tsx +164 -0
  54. package/src/components/Alert/index.ts +2 -0
  55. package/src/components/AlertDialog/AlertDialog.stories.tsx +330 -0
  56. package/src/components/AlertDialog/AlertDialog.tsx +234 -0
  57. package/src/components/AlertDialog/index.ts +2 -0
  58. package/src/components/Avatar/Avatar.stories.tsx +154 -0
  59. package/src/components/Avatar/Avatar.tsx +219 -0
  60. package/src/components/Avatar/index.ts +2 -0
  61. package/src/components/Badge/Badge.stories.tsx +146 -0
  62. package/src/components/Badge/Badge.tsx +125 -0
  63. package/src/components/Badge/index.ts +2 -0
  64. package/src/components/Box/Box.stories.tsx +192 -0
  65. package/src/components/Box/Box.tsx +184 -0
  66. package/src/components/Box/index.ts +2 -0
  67. package/src/components/Button/Button.stories.tsx +157 -0
  68. package/src/components/Button/Button.tsx +180 -0
  69. package/src/components/Button/index.ts +2 -0
  70. package/src/components/Card/Card.stories.tsx +145 -0
  71. package/src/components/Card/Card.tsx +169 -0
  72. package/src/components/Card/index.ts +11 -0
  73. package/src/components/Center/Center.stories.tsx +215 -0
  74. package/src/components/Center/Center.tsx +29 -0
  75. package/src/components/Center/index.ts +2 -0
  76. package/src/components/Checkbox/Checkbox.stories.tsx +94 -0
  77. package/src/components/Checkbox/Checkbox.tsx +242 -0
  78. package/src/components/Checkbox/index.ts +2 -0
  79. package/src/components/DatePicker/DatePicker.stories.tsx +623 -0
  80. package/src/components/DatePicker/DatePicker.tsx +1228 -0
  81. package/src/components/DatePicker/index.ts +8 -0
  82. package/src/components/Divider/Divider.stories.tsx +224 -0
  83. package/src/components/Divider/Divider.tsx +73 -0
  84. package/src/components/Divider/index.ts +2 -0
  85. package/src/components/Drawer/Drawer.stories.tsx +414 -0
  86. package/src/components/Drawer/Drawer.tsx +342 -0
  87. package/src/components/Drawer/index.ts +11 -0
  88. package/src/components/Fab/Fab.stories.tsx +360 -0
  89. package/src/components/Fab/Fab.tsx +185 -0
  90. package/src/components/Fab/index.ts +2 -0
  91. package/src/components/FormControl/FormControl.stories.tsx +276 -0
  92. package/src/components/FormControl/FormControl.tsx +185 -0
  93. package/src/components/FormControl/index.ts +12 -0
  94. package/src/components/Grid/Grid.stories.tsx +244 -0
  95. package/src/components/Grid/Grid.tsx +93 -0
  96. package/src/components/Grid/index.ts +2 -0
  97. package/src/components/HStack/HStack.stories.tsx +230 -0
  98. package/src/components/HStack/HStack.tsx +80 -0
  99. package/src/components/HStack/index.ts +2 -0
  100. package/src/components/Heading/Heading.stories.tsx +111 -0
  101. package/src/components/Heading/Heading.tsx +85 -0
  102. package/src/components/Heading/index.ts +2 -0
  103. package/src/components/Icon/Icon.stories.tsx +320 -0
  104. package/src/components/Icon/Icon.tsx +117 -0
  105. package/src/components/Icon/index.ts +2 -0
  106. package/src/components/Image/Image.stories.tsx +357 -0
  107. package/src/components/Image/Image.tsx +168 -0
  108. package/src/components/Image/index.ts +2 -0
  109. package/src/components/Input/Input.stories.tsx +164 -0
  110. package/src/components/Input/Input.tsx +274 -0
  111. package/src/components/Input/index.ts +2 -0
  112. package/src/components/Link/Link.stories.tsx +187 -0
  113. package/src/components/Link/Link.tsx +104 -0
  114. package/src/components/Link/index.ts +2 -0
  115. package/src/components/Menu/Menu.stories.tsx +363 -0
  116. package/src/components/Menu/Menu.tsx +238 -0
  117. package/src/components/Menu/index.ts +2 -0
  118. package/src/components/Modal/Modal.stories.tsx +156 -0
  119. package/src/components/Modal/Modal.tsx +280 -0
  120. package/src/components/Modal/index.ts +11 -0
  121. package/src/components/Popover/Popover.stories.tsx +330 -0
  122. package/src/components/Popover/Popover.tsx +315 -0
  123. package/src/components/Popover/index.ts +11 -0
  124. package/src/components/Portal/Portal.stories.tsx +376 -0
  125. package/src/components/Portal/Portal.tsx +100 -0
  126. package/src/components/Portal/index.ts +2 -0
  127. package/src/components/Pressable/Pressable.stories.tsx +338 -0
  128. package/src/components/Pressable/Pressable.tsx +71 -0
  129. package/src/components/Pressable/index.ts +2 -0
  130. package/src/components/Progress/Progress.stories.tsx +131 -0
  131. package/src/components/Progress/Progress.tsx +219 -0
  132. package/src/components/Progress/index.ts +2 -0
  133. package/src/components/Radio/Radio.stories.tsx +101 -0
  134. package/src/components/Radio/Radio.tsx +234 -0
  135. package/src/components/Radio/index.ts +2 -0
  136. package/src/components/Select/Select.stories.tsx +908 -0
  137. package/src/components/Select/Select.tsx +659 -0
  138. package/src/components/Select/index.ts +8 -0
  139. package/src/components/Skeleton/Skeleton.stories.tsx +154 -0
  140. package/src/components/Skeleton/Skeleton.tsx +192 -0
  141. package/src/components/Skeleton/index.ts +8 -0
  142. package/src/components/Slider/Slider.stories.tsx +363 -0
  143. package/src/components/Slider/Slider.tsx +209 -0
  144. package/src/components/Slider/index.ts +2 -0
  145. package/src/components/Spinner/Spinner.stories.tsx +108 -0
  146. package/src/components/Spinner/Spinner.tsx +121 -0
  147. package/src/components/Spinner/index.ts +2 -0
  148. package/src/components/Switch/Switch.stories.tsx +116 -0
  149. package/src/components/Switch/Switch.tsx +172 -0
  150. package/src/components/Switch/index.ts +2 -0
  151. package/src/components/Table/Table.stories.tsx +417 -0
  152. package/src/components/Table/Table.tsx +233 -0
  153. package/src/components/Table/index.ts +2 -0
  154. package/src/components/Text/Text.stories.tsx +93 -0
  155. package/src/components/Text/Text.tsx +119 -0
  156. package/src/components/Text/index.ts +2 -0
  157. package/src/components/Textarea/Textarea.stories.tsx +280 -0
  158. package/src/components/Textarea/Textarea.tsx +212 -0
  159. package/src/components/Textarea/index.ts +2 -0
  160. package/src/components/Toast/Toast.stories.tsx +446 -0
  161. package/src/components/Toast/Toast.tsx +221 -0
  162. package/src/components/Toast/index.ts +2 -0
  163. package/src/components/Tooltip/Tooltip.stories.tsx +354 -0
  164. package/src/components/Tooltip/Tooltip.tsx +261 -0
  165. package/src/components/Tooltip/index.ts +2 -0
  166. package/src/components/VStack/VStack.stories.tsx +183 -0
  167. package/src/components/VStack/VStack.tsx +76 -0
  168. package/src/components/VStack/index.ts +2 -0
  169. package/src/components/index.ts +62 -0
  170. package/src/hooks/index.ts +7 -0
  171. package/src/hooks/useControllableState.ts +41 -0
  172. package/src/hooks/useDisclosure.ts +51 -0
  173. package/src/index.ts +22 -0
  174. package/src/stories/Button.stories.tsx +53 -0
  175. package/src/stories/Button.tsx +101 -0
  176. package/src/stories/Configure.mdx +364 -0
  177. package/src/stories/Header.stories.tsx +33 -0
  178. package/src/stories/Header.tsx +75 -0
  179. package/src/stories/Page.stories.tsx +25 -0
  180. package/src/stories/Page.tsx +154 -0
  181. package/src/stories/assets/accessibility.png +0 -0
  182. package/src/stories/assets/accessibility.svg +1 -0
  183. package/src/stories/assets/addon-library.png +0 -0
  184. package/src/stories/assets/assets.png +0 -0
  185. package/src/stories/assets/avif-test-image.avif +0 -0
  186. package/src/stories/assets/context.png +0 -0
  187. package/src/stories/assets/discord.svg +1 -0
  188. package/src/stories/assets/docs.png +0 -0
  189. package/src/stories/assets/figma-plugin.png +0 -0
  190. package/src/stories/assets/github.svg +1 -0
  191. package/src/stories/assets/share.png +0 -0
  192. package/src/stories/assets/styling.png +0 -0
  193. package/src/stories/assets/testing.png +0 -0
  194. package/src/stories/assets/theming.png +0 -0
  195. package/src/stories/assets/tutorials.svg +1 -0
  196. package/src/stories/assets/youtube.svg +1 -0
  197. package/src/styles/index.ts +7 -0
  198. package/src/styles/tokens.ts +318 -0
  199. package/src/styles/unistyles.ts +254 -0
  200. package/src/utils/createContext.tsx +25 -0
  201. package/src/utils/index.ts +7 -0
  202. package/src/utils/mergeRefs.ts +21 -0
  203. package/tsconfig.json +26 -0
  204. package/urbint-cl-1.0.0.tgz +0 -0
  205. package/vitest.config.ts +37 -0
  206. package/vitest.shims.d.ts +1 -0
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Switch Component
3
+ * Toggle switch for boolean values
4
+ */
5
+
6
+ import React, { forwardRef, useRef, useEffect } from 'react';
7
+ import { View, ViewProps, Pressable, Text, Animated, StyleSheet } from 'react-native';
8
+ import { colors, spacing, typography, elevation } from '../../styles/tokens';
9
+
10
+ export interface SwitchProps extends Omit<ViewProps, 'children'> {
11
+ /** Switch label */
12
+ label?: string;
13
+ /** Is checked */
14
+ isChecked?: boolean;
15
+ /** Default checked state */
16
+ defaultChecked?: boolean;
17
+ /** On change handler */
18
+ onChange?: (isChecked: boolean) => void;
19
+ /** Is disabled */
20
+ isDisabled?: boolean;
21
+ /** Switch size */
22
+ size?: 'sm' | 'md' | 'lg';
23
+ /** Color scheme */
24
+ colorScheme?: 'primary' | 'success' | 'danger';
25
+ /** Label position */
26
+ labelPosition?: 'left' | 'right';
27
+ }
28
+
29
+ const sizeConfig = {
30
+ sm: { track: { width: 32, height: 18 }, thumb: 14 },
31
+ md: { track: { width: 44, height: 24 }, thumb: 20 },
32
+ lg: { track: { width: 56, height: 30 }, thumb: 26 },
33
+ } as const;
34
+
35
+ const colorMap = {
36
+ primary: colors.brand.blue,
37
+ success: colors.feedback.success.content,
38
+ danger: colors.feedback.error.content,
39
+ };
40
+
41
+ export const Switch = forwardRef<View, SwitchProps>(
42
+ (
43
+ {
44
+ style,
45
+ label,
46
+ isChecked: controlledChecked,
47
+ defaultChecked = false,
48
+ onChange,
49
+ isDisabled = false,
50
+ size = 'md',
51
+ colorScheme = 'primary',
52
+ labelPosition = 'right',
53
+ ...props
54
+ },
55
+ ref
56
+ ) => {
57
+ const [internalChecked, setInternalChecked] = React.useState(defaultChecked);
58
+ const isControlled = controlledChecked !== undefined;
59
+ const isChecked = isControlled ? controlledChecked : internalChecked;
60
+
61
+ const config = sizeConfig[size];
62
+ const translateX = useRef(new Animated.Value(isChecked ? config.track.width - config.thumb - 4 : 2)).current;
63
+ const activeColor = colorMap[colorScheme];
64
+
65
+ useEffect(() => {
66
+ Animated.spring(translateX, {
67
+ toValue: isChecked ? config.track.width - config.thumb - 4 : 2,
68
+ useNativeDriver: true,
69
+ bounciness: 4,
70
+ }).start();
71
+ }, [isChecked, config]);
72
+
73
+ const handlePress = () => {
74
+ if (isDisabled) return;
75
+ const newValue = !isChecked;
76
+ if (!isControlled) {
77
+ setInternalChecked(newValue);
78
+ }
79
+ onChange?.(newValue);
80
+ };
81
+
82
+ const trackStyle = {
83
+ width: config.track.width,
84
+ height: config.track.height,
85
+ backgroundColor: isChecked
86
+ ? isDisabled
87
+ ? colors.border.disabled
88
+ : activeColor
89
+ : colors.border.default,
90
+ };
91
+
92
+ return (
93
+ <Pressable
94
+ ref={ref}
95
+ onPress={handlePress}
96
+ disabled={isDisabled}
97
+ style={[
98
+ styles.container,
99
+ labelPosition === 'left' && styles.containerReversed,
100
+ style,
101
+ ]}
102
+ accessibilityRole="switch"
103
+ accessibilityState={{ checked: isChecked, disabled: isDisabled }}
104
+ {...props}
105
+ >
106
+ {label && labelPosition === 'left' && (
107
+ <Text style={[styles.label, styles[`${size}Label` as keyof typeof styles], isDisabled && styles.labelDisabled]}>
108
+ {label}
109
+ </Text>
110
+ )}
111
+ <View style={[styles.track, trackStyle]}>
112
+ <Animated.View
113
+ style={[
114
+ styles.thumb,
115
+ {
116
+ width: config.thumb,
117
+ height: config.thumb,
118
+ transform: [{ translateX }],
119
+ },
120
+ isDisabled && styles.thumbDisabled,
121
+ ]}
122
+ />
123
+ </View>
124
+ {label && labelPosition === 'right' && (
125
+ <Text style={[styles.label, styles[`${size}Label` as keyof typeof styles], isDisabled && styles.labelDisabled]}>
126
+ {label}
127
+ </Text>
128
+ )}
129
+ </Pressable>
130
+ );
131
+ }
132
+ );
133
+
134
+ Switch.displayName = 'Switch';
135
+
136
+ const styles = StyleSheet.create({
137
+ container: {
138
+ flexDirection: 'row',
139
+ alignItems: 'center',
140
+ },
141
+ containerReversed: {
142
+ flexDirection: 'row-reverse',
143
+ },
144
+ track: {
145
+ borderRadius: 9999,
146
+ justifyContent: 'center',
147
+ },
148
+ thumb: {
149
+ borderRadius: 9999,
150
+ backgroundColor: colors.white,
151
+ ...elevation['10'],
152
+ },
153
+ thumbDisabled: {
154
+ backgroundColor: colors.background.secondary,
155
+ },
156
+ label: {
157
+ marginHorizontal: spacing['2x'],
158
+ color: colors.text.default,
159
+ },
160
+ smLabel: {
161
+ fontSize: typography.fontSize.small,
162
+ },
163
+ mdLabel: {
164
+ fontSize: typography.fontSize.body,
165
+ },
166
+ lgLabel: {
167
+ fontSize: typography.fontSize.body,
168
+ },
169
+ labelDisabled: {
170
+ color: colors.text.disabled,
171
+ },
172
+ });
@@ -0,0 +1,2 @@
1
+ export { Switch, type SwitchProps } from './Switch';
2
+
@@ -0,0 +1,417 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { useState } from 'react';
3
+ import { Table, TableColumn } from './Table';
4
+ import { VStack } from '../VStack';
5
+ import { HStack } from '../HStack';
6
+ import { Text } from '../Text';
7
+ import { Box } from '../Box';
8
+ import { Button } from '../Button';
9
+ import { Badge } from '../Badge';
10
+ import { colors, spacing } from '../../styles/tokens';
11
+
12
+ const meta: Meta<typeof Table> = {
13
+ title: 'Data Display/Table',
14
+ component: Table,
15
+ argTypes: {
16
+ variant: {
17
+ control: 'select',
18
+ options: ['simple', 'striped', 'unstyled'],
19
+ },
20
+ size: {
21
+ control: 'select',
22
+ options: ['sm', 'md', 'lg'],
23
+ },
24
+ },
25
+ args: {
26
+ variant: 'simple',
27
+ size: 'md',
28
+ },
29
+ };
30
+
31
+ export default meta;
32
+
33
+ type Story = StoryObj<typeof Table>;
34
+
35
+ interface User {
36
+ id: number;
37
+ name: string;
38
+ email: string;
39
+ role: string;
40
+ }
41
+
42
+ const users: User[] = [
43
+ { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin' },
44
+ { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
45
+ { id: 3, name: 'Bob Johnson', email: 'bob@example.com', role: 'Editor' },
46
+ { id: 4, name: 'Alice Brown', email: 'alice@example.com', role: 'User' },
47
+ ];
48
+
49
+ const columns: TableColumn<User>[] = [
50
+ { key: 'id', header: 'ID', width: 60 },
51
+ { key: 'name', header: 'Name' },
52
+ { key: 'email', header: 'Email' },
53
+ { key: 'role', header: 'Role' },
54
+ ];
55
+
56
+ export const Default: Story = {
57
+ render: () => (
58
+ <Table
59
+ data={users}
60
+ columns={columns}
61
+ keyExtractor={(item) => String(item.id)}
62
+ />
63
+ ),
64
+ };
65
+
66
+ export const Striped: Story = {
67
+ render: () => (
68
+ <VStack space={spacing.lg}>
69
+ <Text weight="semiBold">Striped Table</Text>
70
+ <Table
71
+ data={users}
72
+ columns={columns}
73
+ variant="striped"
74
+ keyExtractor={(item) => String(item.id)}
75
+ />
76
+ </VStack>
77
+ ),
78
+ };
79
+
80
+ export const Sizes: Story = {
81
+ render: () => (
82
+ <VStack space={spacing.xl}>
83
+ <VStack space={spacing.sm}>
84
+ <Text weight="semiBold">Small</Text>
85
+ <Table
86
+ data={users.slice(0, 2)}
87
+ columns={columns}
88
+ size="sm"
89
+ keyExtractor={(item) => String(item.id)}
90
+ />
91
+ </VStack>
92
+ <VStack space={spacing.sm}>
93
+ <Text weight="semiBold">Medium (Default)</Text>
94
+ <Table
95
+ data={users.slice(0, 2)}
96
+ columns={columns}
97
+ size="md"
98
+ keyExtractor={(item) => String(item.id)}
99
+ />
100
+ </VStack>
101
+ <VStack space={spacing.sm}>
102
+ <Text weight="semiBold">Large</Text>
103
+ <Table
104
+ data={users.slice(0, 2)}
105
+ columns={columns}
106
+ size="lg"
107
+ keyExtractor={(item) => String(item.id)}
108
+ />
109
+ </VStack>
110
+ </VStack>
111
+ ),
112
+ };
113
+
114
+ const SortableTableDemo = () => {
115
+ const [sortColumn, setSortColumn] = useState<string>('name');
116
+ const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc');
117
+
118
+ const sortedUsers = [...users].sort((a, b) => {
119
+ const aValue = a[sortColumn as keyof User];
120
+ const bValue = b[sortColumn as keyof User];
121
+ if (sortDirection === 'asc') {
122
+ return String(aValue).localeCompare(String(bValue));
123
+ }
124
+ return String(bValue).localeCompare(String(aValue));
125
+ });
126
+
127
+ const sortableColumns: TableColumn<User>[] = [
128
+ { key: 'id', header: 'ID', width: 60, sortable: true },
129
+ { key: 'name', header: 'Name', sortable: true },
130
+ { key: 'email', header: 'Email', sortable: true },
131
+ { key: 'role', header: 'Role', sortable: true },
132
+ ];
133
+
134
+ return (
135
+ <VStack space={spacing.lg}>
136
+ <Text weight="semiBold">Sortable Table</Text>
137
+ <Table
138
+ data={sortedUsers}
139
+ columns={sortableColumns}
140
+ sortColumn={sortColumn}
141
+ sortDirection={sortDirection}
142
+ onSortChange={(col, dir) => {
143
+ setSortColumn(col);
144
+ setSortDirection(dir);
145
+ }}
146
+ keyExtractor={(item) => String(item.id)}
147
+ />
148
+ </VStack>
149
+ );
150
+ };
151
+
152
+ export const Sortable: Story = {
153
+ render: () => <SortableTableDemo />,
154
+ };
155
+
156
+ interface Product {
157
+ id: number;
158
+ name: string;
159
+ price: number;
160
+ stock: number;
161
+ status: 'active' | 'inactive' | 'low_stock';
162
+ }
163
+
164
+ const products: Product[] = [
165
+ { id: 1, name: 'Widget A', price: 29.99, stock: 150, status: 'active' },
166
+ { id: 2, name: 'Widget B', price: 49.99, stock: 5, status: 'low_stock' },
167
+ { id: 3, name: 'Widget C', price: 19.99, stock: 0, status: 'inactive' },
168
+ { id: 4, name: 'Widget D', price: 99.99, stock: 75, status: 'active' },
169
+ ];
170
+
171
+ const CustomRenderDemo = () => {
172
+ const productColumns: TableColumn<Product>[] = [
173
+ { key: 'name', header: 'Product' },
174
+ {
175
+ key: 'price',
176
+ header: 'Price',
177
+ align: 'right',
178
+ render: (item) => <Text>${item.price.toFixed(2)}</Text>,
179
+ },
180
+ {
181
+ key: 'stock',
182
+ header: 'Stock',
183
+ align: 'center',
184
+ render: (item) => (
185
+ <Text color={item.stock < 10 ? colors.feedback.error.content : undefined}>
186
+ {item.stock}
187
+ </Text>
188
+ ),
189
+ },
190
+ {
191
+ key: 'status',
192
+ header: 'Status',
193
+ align: 'center',
194
+ render: (item) => {
195
+ const statusColors = {
196
+ active: { bg: colors.feedback.success.background, text: colors.feedback.success.content },
197
+ inactive: { bg: colors.background.secondary, text: colors.text.secondary },
198
+ low_stock: { bg: colors.feedback.warning.background, text: colors.feedback.warning.content },
199
+ };
200
+ const color = statusColors[item.status];
201
+ const labels = { active: 'Active', inactive: 'Inactive', low_stock: 'Low Stock' };
202
+ return (
203
+ <Box px={spacing.sm} py={spacing.xs} bg={color.bg} rounded="full">
204
+ <Text size="sm" color={color.text}>{labels[item.status]}</Text>
205
+ </Box>
206
+ );
207
+ },
208
+ },
209
+ ];
210
+
211
+ return (
212
+ <VStack space={spacing.lg}>
213
+ <Text weight="semiBold">Custom Cell Rendering</Text>
214
+ <Table
215
+ data={products}
216
+ columns={productColumns}
217
+ keyExtractor={(item) => String(item.id)}
218
+ />
219
+ </VStack>
220
+ );
221
+ };
222
+
223
+ export const CustomRender: Story = {
224
+ render: () => <CustomRenderDemo />,
225
+ };
226
+
227
+ const ClickableRowsDemo = () => {
228
+ return (
229
+ <VStack space={spacing.lg}>
230
+ <Text weight="semiBold">Clickable Rows</Text>
231
+ <Table
232
+ data={users}
233
+ columns={columns}
234
+ onRowPress={(item) => alert(`Clicked: ${item.name}`)}
235
+ keyExtractor={(item) => String(item.id)}
236
+ />
237
+ </VStack>
238
+ );
239
+ };
240
+
241
+ export const ClickableRows: Story = {
242
+ render: () => <ClickableRowsDemo />,
243
+ };
244
+
245
+ const ActionsDemo = () => {
246
+ const columnsWithActions: TableColumn<User>[] = [
247
+ { key: 'name', header: 'Name' },
248
+ { key: 'email', header: 'Email' },
249
+ { key: 'role', header: 'Role' },
250
+ {
251
+ key: 'actions',
252
+ header: 'Actions',
253
+ align: 'right',
254
+ render: (item) => (
255
+ <HStack space={spacing.sm} justifyContent="flex-end">
256
+ <Button
257
+ size="sm"
258
+ variant="ghost"
259
+ onPress={() => console.log('Edit', item.name)}
260
+ >
261
+ Edit
262
+ </Button>
263
+ <Button
264
+ size="sm"
265
+ variant="danger"
266
+ onPress={() => console.log('Delete', item.name)}
267
+ >
268
+ Delete
269
+ </Button>
270
+ </HStack>
271
+ ),
272
+ },
273
+ ];
274
+
275
+ return (
276
+ <VStack space={spacing.lg}>
277
+ <Text weight="semiBold">Table with Actions</Text>
278
+ <Table
279
+ data={users}
280
+ columns={columnsWithActions}
281
+ keyExtractor={(item) => String(item.id)}
282
+ />
283
+ </VStack>
284
+ );
285
+ };
286
+
287
+ export const WithActions: Story = {
288
+ render: () => <ActionsDemo />,
289
+ };
290
+
291
+ export const Empty: Story = {
292
+ render: () => (
293
+ <VStack space={spacing.lg}>
294
+ <Text weight="semiBold">Empty Table</Text>
295
+ <Table
296
+ data={[]}
297
+ columns={columns}
298
+ emptyState={
299
+ <VStack space={spacing.sm} alignItems="center">
300
+ <Text size="lg">📭</Text>
301
+ <Text color={colors.text.secondary}>No users found</Text>
302
+ <Button size="sm">Add User</Button>
303
+ </VStack>
304
+ }
305
+ />
306
+ </VStack>
307
+ ),
308
+ };
309
+
310
+ interface Order {
311
+ id: string;
312
+ customer: string;
313
+ date: string;
314
+ total: number;
315
+ status: 'pending' | 'processing' | 'shipped' | 'delivered';
316
+ }
317
+
318
+ const orders: Order[] = [
319
+ { id: 'ORD-001', customer: 'John Doe', date: '2024-01-15', total: 150.00, status: 'delivered' },
320
+ { id: 'ORD-002', customer: 'Jane Smith', date: '2024-01-14', total: 89.99, status: 'shipped' },
321
+ { id: 'ORD-003', customer: 'Bob Johnson', date: '2024-01-14', total: 249.50, status: 'processing' },
322
+ { id: 'ORD-004', customer: 'Alice Brown', date: '2024-01-13', total: 45.00, status: 'pending' },
323
+ ];
324
+
325
+ const OrdersDemo = () => {
326
+ const orderColumns: TableColumn<Order>[] = [
327
+ { key: 'id', header: 'Order ID', width: 100 },
328
+ { key: 'customer', header: 'Customer' },
329
+ { key: 'date', header: 'Date', width: 120 },
330
+ {
331
+ key: 'total',
332
+ header: 'Total',
333
+ align: 'right',
334
+ render: (item) => <Text weight="medium">${item.total.toFixed(2)}</Text>,
335
+ },
336
+ {
337
+ key: 'status',
338
+ header: 'Status',
339
+ render: (item) => {
340
+ const statusConfig = {
341
+ pending: { variant: 'warning' as const, label: 'Pending' },
342
+ processing: { variant: 'info' as const, label: 'Processing' },
343
+ shipped: { variant: 'info' as const, label: 'Shipped' },
344
+ delivered: { variant: 'success' as const, label: 'Delivered' },
345
+ };
346
+ const config = statusConfig[item.status];
347
+ return <Badge variant={config.variant}>{config.label}</Badge>;
348
+ },
349
+ },
350
+ ];
351
+
352
+ return (
353
+ <VStack space={spacing.lg}>
354
+ <Text weight="semiBold">Orders Table</Text>
355
+ <Table
356
+ data={orders}
357
+ columns={orderColumns}
358
+ variant="striped"
359
+ keyExtractor={(item) => item.id}
360
+ />
361
+ </VStack>
362
+ );
363
+ };
364
+
365
+ export const Orders: Story = {
366
+ render: () => <OrdersDemo />,
367
+ };
368
+
369
+ const ColumnAlignmentDemo = () => {
370
+ interface Stats {
371
+ metric: string;
372
+ value: number;
373
+ change: number;
374
+ }
375
+
376
+ const stats: Stats[] = [
377
+ { metric: 'Total Users', value: 12543, change: 12.5 },
378
+ { metric: 'Active Sessions', value: 3421, change: -5.2 },
379
+ { metric: 'Revenue', value: 54320, change: 8.7 },
380
+ ];
381
+
382
+ const statsColumns: TableColumn<Stats>[] = [
383
+ { key: 'metric', header: 'Metric', align: 'left' },
384
+ {
385
+ key: 'value',
386
+ header: 'Value',
387
+ align: 'center',
388
+ render: (item) => <Text weight="semiBold">{item.value.toLocaleString()}</Text>,
389
+ },
390
+ {
391
+ key: 'change',
392
+ header: 'Change',
393
+ align: 'right',
394
+ render: (item) => (
395
+ <Text color={item.change >= 0 ? colors.feedback.success.content : colors.feedback.error.content}>
396
+ {item.change >= 0 ? '+' : ''}{item.change}%
397
+ </Text>
398
+ ),
399
+ },
400
+ ];
401
+
402
+ return (
403
+ <VStack space={spacing.lg}>
404
+ <Text weight="semiBold">Column Alignment</Text>
405
+ <Table
406
+ data={stats}
407
+ columns={statsColumns}
408
+ keyExtractor={(item) => item.metric}
409
+ />
410
+ </VStack>
411
+ );
412
+ };
413
+
414
+ export const ColumnAlignment: Story = {
415
+ render: () => <ColumnAlignmentDemo />,
416
+ };
417
+