@metacells/mcellui-mcp-server 0.1.1 → 0.1.2

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 (49) hide show
  1. package/dist/index.js +8 -2
  2. package/package.json +5 -3
  3. package/registry/registry.json +717 -0
  4. package/registry/ui/accordion.tsx +416 -0
  5. package/registry/ui/action-sheet.tsx +396 -0
  6. package/registry/ui/alert-dialog.tsx +355 -0
  7. package/registry/ui/avatar-stack.tsx +278 -0
  8. package/registry/ui/avatar.tsx +116 -0
  9. package/registry/ui/badge.tsx +125 -0
  10. package/registry/ui/button.tsx +240 -0
  11. package/registry/ui/card.tsx +675 -0
  12. package/registry/ui/carousel.tsx +431 -0
  13. package/registry/ui/checkbox.tsx +252 -0
  14. package/registry/ui/chip.tsx +271 -0
  15. package/registry/ui/column.tsx +133 -0
  16. package/registry/ui/datetime-picker.tsx +578 -0
  17. package/registry/ui/dialog.tsx +292 -0
  18. package/registry/ui/fab.tsx +225 -0
  19. package/registry/ui/form.tsx +323 -0
  20. package/registry/ui/horizontal-list.tsx +200 -0
  21. package/registry/ui/icon-button.tsx +244 -0
  22. package/registry/ui/image-gallery.tsx +455 -0
  23. package/registry/ui/image.tsx +283 -0
  24. package/registry/ui/input.tsx +242 -0
  25. package/registry/ui/label.tsx +99 -0
  26. package/registry/ui/list.tsx +519 -0
  27. package/registry/ui/progress.tsx +168 -0
  28. package/registry/ui/pull-to-refresh.tsx +231 -0
  29. package/registry/ui/radio-group.tsx +294 -0
  30. package/registry/ui/rating.tsx +311 -0
  31. package/registry/ui/row.tsx +136 -0
  32. package/registry/ui/screen.tsx +153 -0
  33. package/registry/ui/search-input.tsx +281 -0
  34. package/registry/ui/section-header.tsx +258 -0
  35. package/registry/ui/segmented-control.tsx +229 -0
  36. package/registry/ui/select.tsx +311 -0
  37. package/registry/ui/separator.tsx +74 -0
  38. package/registry/ui/sheet.tsx +362 -0
  39. package/registry/ui/skeleton.tsx +156 -0
  40. package/registry/ui/slider.tsx +307 -0
  41. package/registry/ui/spinner.tsx +100 -0
  42. package/registry/ui/stepper.tsx +314 -0
  43. package/registry/ui/stories.tsx +463 -0
  44. package/registry/ui/swipeable-row.tsx +362 -0
  45. package/registry/ui/switch.tsx +246 -0
  46. package/registry/ui/tabs.tsx +348 -0
  47. package/registry/ui/textarea.tsx +265 -0
  48. package/registry/ui/toast.tsx +316 -0
  49. package/registry/ui/tooltip.tsx +369 -0
@@ -0,0 +1,396 @@
1
+ /**
2
+ * ActionSheet
3
+ *
4
+ * iOS-style bottom action menu for presenting a set of choices.
5
+ * Includes support for destructive actions and cancel button.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * <ActionSheet
10
+ * open={isOpen}
11
+ * onOpenChange={setIsOpen}
12
+ * title="Photo Options"
13
+ * message="Choose an action for this photo"
14
+ * >
15
+ * <ActionSheetItem
16
+ * icon={<CameraIcon />}
17
+ * onPress={() => handleCamera()}
18
+ * >
19
+ * Take Photo
20
+ * </ActionSheetItem>
21
+ * <ActionSheetItem
22
+ * icon={<GalleryIcon />}
23
+ * onPress={() => handleGallery()}
24
+ * >
25
+ * Choose from Library
26
+ * </ActionSheetItem>
27
+ * <ActionSheetItem
28
+ * icon={<TrashIcon />}
29
+ * destructive
30
+ * onPress={() => handleDelete()}
31
+ * >
32
+ * Delete Photo
33
+ * </ActionSheetItem>
34
+ * </ActionSheet>
35
+ * ```
36
+ */
37
+
38
+ import React, { useCallback } from 'react';
39
+ import {
40
+ View,
41
+ Text,
42
+ Pressable,
43
+ StyleSheet,
44
+ Modal,
45
+ Dimensions,
46
+ } from 'react-native';
47
+ import Animated, {
48
+ useAnimatedStyle,
49
+ useSharedValue,
50
+ withSpring,
51
+ withTiming,
52
+ runOnJS,
53
+ Easing,
54
+ } from 'react-native-reanimated';
55
+ import { Gesture, GestureDetector } from 'react-native-gesture-handler';
56
+ import { useTheme, haptic } from '@nativeui/core';
57
+
58
+ const { height: SCREEN_HEIGHT } = Dimensions.get('window');
59
+
60
+ // ─────────────────────────────────────────────────────────────────────────────
61
+ // Types
62
+ // ─────────────────────────────────────────────────────────────────────────────
63
+
64
+ export interface ActionSheetProps {
65
+ /** Whether sheet is open */
66
+ open: boolean;
67
+ /** Callback when open state changes */
68
+ onOpenChange: (open: boolean) => void;
69
+ /** Optional title */
70
+ title?: string;
71
+ /** Optional message/description */
72
+ message?: string;
73
+ /** Action items */
74
+ children: React.ReactNode;
75
+ /** Cancel button label */
76
+ cancelLabel?: string;
77
+ }
78
+
79
+ export interface ActionSheetItemProps {
80
+ /** Item label */
81
+ children: string;
82
+ /** Icon element */
83
+ icon?: React.ReactNode;
84
+ /** Whether action is destructive */
85
+ destructive?: boolean;
86
+ /** Whether item is disabled */
87
+ disabled?: boolean;
88
+ /** Press handler */
89
+ onPress?: () => void;
90
+ }
91
+
92
+ // ─────────────────────────────────────────────────────────────────────────────
93
+ // ActionSheet Component
94
+ // ─────────────────────────────────────────────────────────────────────────────
95
+
96
+ export function ActionSheet({
97
+ open,
98
+ onOpenChange,
99
+ title,
100
+ message,
101
+ children,
102
+ cancelLabel = 'Cancel',
103
+ }: ActionSheetProps) {
104
+ const { colors, spacing, radius, fontWeight, fontSize } = useTheme();
105
+
106
+ const translateY = useSharedValue(SCREEN_HEIGHT);
107
+ const backdropOpacity = useSharedValue(0);
108
+
109
+ React.useEffect(() => {
110
+ if (open) {
111
+ backdropOpacity.value = withTiming(1, { duration: 200 });
112
+ translateY.value = withTiming(0, { duration: 250, easing: Easing.out(Easing.ease) });
113
+ } else {
114
+ backdropOpacity.value = withTiming(0, { duration: 150 });
115
+ translateY.value = withTiming(SCREEN_HEIGHT, {
116
+ duration: 200,
117
+ easing: Easing.in(Easing.ease),
118
+ });
119
+ }
120
+ }, [open, translateY, backdropOpacity]);
121
+
122
+ const close = useCallback(() => {
123
+ onOpenChange(false);
124
+ }, [onOpenChange]);
125
+
126
+ const handleCancel = () => {
127
+ haptic('light');
128
+ close();
129
+ };
130
+
131
+ // Gesture for swipe to dismiss
132
+ const panGesture = Gesture.Pan()
133
+ .onUpdate((event) => {
134
+ if (event.translationY > 0) {
135
+ translateY.value = event.translationY;
136
+ }
137
+ })
138
+ .onEnd((event) => {
139
+ if (event.translationY > 100 || event.velocityY > 500) {
140
+ translateY.value = withTiming(SCREEN_HEIGHT, { duration: 200 });
141
+ backdropOpacity.value = withTiming(0, { duration: 150 });
142
+ runOnJS(close)();
143
+ } else {
144
+ translateY.value = withTiming(0, { duration: 150 });
145
+ }
146
+ });
147
+
148
+ const backdropStyle = useAnimatedStyle(() => ({
149
+ opacity: backdropOpacity.value,
150
+ }));
151
+
152
+ const sheetStyle = useAnimatedStyle(() => ({
153
+ transform: [{ translateY: translateY.value }],
154
+ }));
155
+
156
+ // Clone children to inject close function
157
+ const items = React.Children.map(children, (child) => {
158
+ if (React.isValidElement(child)) {
159
+ return React.cloneElement(child as React.ReactElement<ActionSheetItemProps & { _onClose?: () => void }>, {
160
+ _onClose: close,
161
+ });
162
+ }
163
+ return child;
164
+ });
165
+
166
+ if (!open) return null;
167
+
168
+ return (
169
+ <Modal transparent visible={open} animationType="none" statusBarTranslucent>
170
+ <View style={styles.container}>
171
+ {/* Backdrop */}
172
+ <Animated.View style={[styles.backdrop, backdropStyle]}>
173
+ <Pressable style={StyleSheet.absoluteFill} onPress={close} />
174
+ </Animated.View>
175
+
176
+ {/* Sheet */}
177
+ <GestureDetector gesture={panGesture}>
178
+ <Animated.View
179
+ style={[
180
+ styles.sheet,
181
+ {
182
+ backgroundColor: colors.card,
183
+ paddingBottom: spacing[8],
184
+ },
185
+ sheetStyle,
186
+ ]}
187
+ >
188
+ {/* Handle */}
189
+ <View style={styles.handleContainer}>
190
+ <View
191
+ style={[
192
+ styles.handle,
193
+ { backgroundColor: colors.border },
194
+ ]}
195
+ />
196
+ </View>
197
+
198
+ {/* Header */}
199
+ {(title || message) && (
200
+ <View
201
+ style={[
202
+ styles.header,
203
+ {
204
+ paddingHorizontal: spacing[4],
205
+ paddingBottom: spacing[3],
206
+ borderBottomWidth: 1,
207
+ borderBottomColor: colors.border,
208
+ },
209
+ ]}
210
+ >
211
+ {title && (
212
+ <Text
213
+ style={[
214
+ styles.title,
215
+ {
216
+ color: colors.foreground,
217
+ fontSize: fontSize.lg,
218
+ fontWeight: fontWeight.semibold,
219
+ },
220
+ ]}
221
+ >
222
+ {title}
223
+ </Text>
224
+ )}
225
+ {message && (
226
+ <Text
227
+ style={[
228
+ styles.message,
229
+ {
230
+ color: colors.foregroundMuted,
231
+ fontSize: fontSize.sm,
232
+ marginTop: title ? spacing[1] : 0,
233
+ },
234
+ ]}
235
+ >
236
+ {message}
237
+ </Text>
238
+ )}
239
+ </View>
240
+ )}
241
+
242
+ {/* Items */}
243
+ <View style={{ paddingHorizontal: spacing[2] }}>
244
+ {items}
245
+ </View>
246
+
247
+ {/* Cancel button */}
248
+ <View style={{ paddingHorizontal: spacing[2], marginTop: spacing[2] }}>
249
+ <Pressable
250
+ onPress={handleCancel}
251
+ style={({ pressed }) => [
252
+ styles.cancelButton,
253
+ {
254
+ backgroundColor: colors.secondary,
255
+ borderRadius: radius.lg,
256
+ opacity: pressed ? 0.7 : 1,
257
+ },
258
+ ]}
259
+ >
260
+ <Text
261
+ style={[
262
+ styles.cancelText,
263
+ {
264
+ color: colors.primary,
265
+ fontSize: fontSize.base,
266
+ fontWeight: fontWeight.semibold,
267
+ },
268
+ ]}
269
+ >
270
+ {cancelLabel}
271
+ </Text>
272
+ </Pressable>
273
+ </View>
274
+ </Animated.View>
275
+ </GestureDetector>
276
+ </View>
277
+ </Modal>
278
+ );
279
+ }
280
+
281
+ // ─────────────────────────────────────────────────────────────────────────────
282
+ // ActionSheetItem Component
283
+ // ─────────────────────────────────────────────────────────────────────────────
284
+
285
+ export function ActionSheetItem({
286
+ children,
287
+ icon,
288
+ destructive = false,
289
+ disabled = false,
290
+ onPress,
291
+ _onClose,
292
+ }: ActionSheetItemProps & { _onClose?: () => void }) {
293
+ const { colors, spacing, radius, fontWeight, fontSize } = useTheme();
294
+
295
+ const handlePress = () => {
296
+ haptic('light');
297
+ onPress?.();
298
+ _onClose?.();
299
+ };
300
+
301
+ const textColor = destructive
302
+ ? colors.destructive
303
+ : colors.foreground;
304
+
305
+ return (
306
+ <Pressable
307
+ onPress={handlePress}
308
+ disabled={disabled}
309
+ style={({ pressed }) => [
310
+ styles.item,
311
+ {
312
+ backgroundColor: pressed ? colors.backgroundMuted : 'transparent',
313
+ borderRadius: radius.lg,
314
+ paddingVertical: spacing[3],
315
+ paddingHorizontal: spacing[4],
316
+ opacity: disabled ? 0.5 : 1,
317
+ },
318
+ ]}
319
+ accessibilityRole="button"
320
+ accessibilityState={{ disabled }}
321
+ >
322
+ {icon && (
323
+ <View style={{ marginRight: spacing[3] }}>
324
+ {React.isValidElement(icon)
325
+ ? React.cloneElement(icon as React.ReactElement<{ width?: number; height?: number; color?: string }>, {
326
+ width: 22,
327
+ height: 22,
328
+ color: textColor,
329
+ })
330
+ : icon}
331
+ </View>
332
+ )}
333
+ <Text
334
+ style={[
335
+ styles.itemText,
336
+ {
337
+ color: textColor,
338
+ fontSize: fontSize.base,
339
+ fontWeight: fontWeight.medium,
340
+ },
341
+ ]}
342
+ >
343
+ {children}
344
+ </Text>
345
+ </Pressable>
346
+ );
347
+ }
348
+
349
+ // ─────────────────────────────────────────────────────────────────────────────
350
+ // Styles
351
+ // ─────────────────────────────────────────────────────────────────────────────
352
+
353
+ const styles = StyleSheet.create({
354
+ container: {
355
+ flex: 1,
356
+ justifyContent: 'flex-end',
357
+ },
358
+ backdrop: {
359
+ ...StyleSheet.absoluteFillObject,
360
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
361
+ },
362
+ sheet: {
363
+ borderTopLeftRadius: 20,
364
+ borderTopRightRadius: 20,
365
+ minHeight: 100,
366
+ maxHeight: SCREEN_HEIGHT * 0.7,
367
+ },
368
+ handleContainer: {
369
+ alignItems: 'center',
370
+ paddingVertical: 12,
371
+ },
372
+ handle: {
373
+ width: 36,
374
+ height: 4,
375
+ borderRadius: 2,
376
+ },
377
+ header: {
378
+ alignItems: 'center',
379
+ },
380
+ title: {
381
+ textAlign: 'center',
382
+ },
383
+ message: {
384
+ textAlign: 'center',
385
+ },
386
+ item: {
387
+ flexDirection: 'row',
388
+ alignItems: 'center',
389
+ },
390
+ itemText: {},
391
+ cancelButton: {
392
+ paddingVertical: 14,
393
+ alignItems: 'center',
394
+ },
395
+ cancelText: {},
396
+ });