@metacells/mcellui-mcp-server 0.1.0 → 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 +70 -7
  2. package/package.json +7 -5
  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,362 @@
1
+ /**
2
+ * Sheet
3
+ *
4
+ * A bottom sheet component with snap points and gesture support.
5
+ * Slides up from the bottom with backdrop.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * const [open, setOpen] = useState(false);
10
+ *
11
+ * <Sheet open={open} onOpenChange={setOpen}>
12
+ * <SheetContent>
13
+ * <SheetHeader>
14
+ * <SheetTitle>Edit Profile</SheetTitle>
15
+ * <SheetDescription>Make changes to your profile</SheetDescription>
16
+ * </SheetHeader>
17
+ * <View>{content}</View>
18
+ * </SheetContent>
19
+ * </Sheet>
20
+ * ```
21
+ */
22
+
23
+ import React, { useEffect, useCallback, createContext, useContext } from 'react';
24
+ import {
25
+ View,
26
+ Text,
27
+ Modal,
28
+ Pressable,
29
+ StyleSheet,
30
+ ViewStyle,
31
+ TextStyle,
32
+ Dimensions,
33
+ KeyboardAvoidingView,
34
+ Platform,
35
+ } from 'react-native';
36
+ import Animated, {
37
+ useSharedValue,
38
+ useAnimatedStyle,
39
+ withSpring,
40
+ withTiming,
41
+ runOnJS,
42
+ interpolate,
43
+ Extrapolation,
44
+ } from 'react-native-reanimated';
45
+ import {
46
+ Gesture,
47
+ GestureDetector,
48
+ GestureHandlerRootView,
49
+ } from 'react-native-gesture-handler';
50
+ import { useTheme, SHEET_CONSTANTS } from '@nativeui/core';
51
+
52
+ const { height: SCREEN_HEIGHT } = Dimensions.get('window');
53
+
54
+ // Context for sheet state
55
+ const SheetContext = createContext<{
56
+ onClose: () => void;
57
+ } | null>(null);
58
+
59
+ const useSheet = () => {
60
+ const context = useContext(SheetContext);
61
+ if (!context) {
62
+ throw new Error('Sheet components must be used within a Sheet');
63
+ }
64
+ return context;
65
+ };
66
+
67
+ export interface SheetProps {
68
+ /** Controlled open state */
69
+ open: boolean;
70
+ /** Callback when open state changes */
71
+ onOpenChange: (open: boolean) => void;
72
+ /** Children (should be SheetContent) */
73
+ children: React.ReactNode;
74
+ }
75
+
76
+ export function Sheet({ open, onOpenChange, children }: SheetProps) {
77
+ const handleClose = useCallback(() => {
78
+ onOpenChange(false);
79
+ }, [onOpenChange]);
80
+
81
+ return (
82
+ <Modal
83
+ visible={open}
84
+ transparent
85
+ animationType="none"
86
+ statusBarTranslucent
87
+ onRequestClose={handleClose}
88
+ >
89
+ <GestureHandlerRootView style={styles.gestureRoot}>
90
+ <SheetContext.Provider value={{ onClose: handleClose }}>
91
+ {children}
92
+ </SheetContext.Provider>
93
+ </GestureHandlerRootView>
94
+ </Modal>
95
+ );
96
+ }
97
+
98
+ export interface SheetContentProps {
99
+ /** Content children */
100
+ children: React.ReactNode;
101
+ /** Height of sheet (default: 50% of screen) */
102
+ height?: number | string;
103
+ /** Show drag handle */
104
+ showHandle?: boolean;
105
+ /** Threshold (0-1) für automatisches Schließen beim Ziehen */
106
+ closeThreshold?: number;
107
+ /** Geschwindigkeit (px/s) für Swipe-to-Close */
108
+ velocityThreshold?: number;
109
+ /** Additional styles */
110
+ style?: ViewStyle;
111
+ }
112
+
113
+ export function SheetContent({
114
+ children,
115
+ height = '50%',
116
+ showHandle = true,
117
+ closeThreshold = SHEET_CONSTANTS.closeThreshold,
118
+ velocityThreshold = SHEET_CONSTANTS.velocityThreshold,
119
+ style,
120
+ }: SheetContentProps) {
121
+ const { colors, radius, springs } = useTheme();
122
+ const { onClose } = useSheet();
123
+
124
+ const translateY = useSharedValue(SCREEN_HEIGHT);
125
+ const backdropOpacity = useSharedValue(0);
126
+ const isClosing = useSharedValue(false);
127
+
128
+ // Calculate actual height
129
+ const sheetHeight = typeof height === 'string'
130
+ ? (parseFloat(height) / 100) * SCREEN_HEIGHT
131
+ : height;
132
+
133
+ useEffect(() => {
134
+ translateY.value = withSpring(0, springs.snappy);
135
+ backdropOpacity.value = withTiming(1, { duration: SHEET_CONSTANTS.backdropFadeInDuration });
136
+ }, []);
137
+
138
+ const closeSheet = useCallback(() => {
139
+ if (isClosing.value) return;
140
+ isClosing.value = true;
141
+ backdropOpacity.value = withTiming(0, { duration: SHEET_CONSTANTS.backdropFadeOutDuration });
142
+ translateY.value = withSpring(
143
+ SCREEN_HEIGHT,
144
+ springs.snappy,
145
+ (finished) => {
146
+ if (finished) {
147
+ runOnJS(onClose)();
148
+ }
149
+ }
150
+ );
151
+ }, [onClose, springs.snappy]);
152
+
153
+ const panGesture = Gesture.Pan()
154
+ .onUpdate((event) => {
155
+ if (event.translationY > 0) {
156
+ translateY.value = event.translationY;
157
+ }
158
+ })
159
+ .onEnd((event) => {
160
+ if (event.translationY > sheetHeight * closeThreshold || event.velocityY > velocityThreshold) {
161
+ runOnJS(closeSheet)();
162
+ } else {
163
+ translateY.value = withSpring(0, springs.snappy);
164
+ }
165
+ });
166
+
167
+ const animatedSheetStyle = useAnimatedStyle(() => ({
168
+ transform: [{ translateY: translateY.value }],
169
+ }));
170
+
171
+ const animatedBackdropStyle = useAnimatedStyle(() => ({
172
+ opacity: interpolate(
173
+ backdropOpacity.value,
174
+ [0, 1],
175
+ [0, SHEET_CONSTANTS.backdropMaxOpacity],
176
+ Extrapolation.CLAMP
177
+ ),
178
+ }));
179
+
180
+ return (
181
+ <View style={styles.container}>
182
+ <Animated.View
183
+ style={[
184
+ styles.backdrop,
185
+ { backgroundColor: colors.foreground },
186
+ animatedBackdropStyle,
187
+ ]}
188
+ >
189
+ <Pressable style={styles.backdropPressable} onPress={closeSheet} />
190
+ </Animated.View>
191
+
192
+ <KeyboardAvoidingView
193
+ behavior={Platform.OS === 'ios' ? 'padding' : undefined}
194
+ style={styles.keyboardView}
195
+ >
196
+ <GestureDetector gesture={panGesture}>
197
+ <Animated.View
198
+ style={[
199
+ styles.sheet,
200
+ {
201
+ height: sheetHeight,
202
+ backgroundColor: colors.card,
203
+ borderTopLeftRadius: radius.xl,
204
+ borderTopRightRadius: radius.xl,
205
+ },
206
+ animatedSheetStyle,
207
+ style,
208
+ ]}
209
+ >
210
+ {showHandle && (
211
+ <View style={styles.handleContainer}>
212
+ <View
213
+ style={[
214
+ styles.handle,
215
+ { backgroundColor: colors.border },
216
+ ]}
217
+ />
218
+ </View>
219
+ )}
220
+ <View style={styles.content}>{children}</View>
221
+ </Animated.View>
222
+ </GestureDetector>
223
+ </KeyboardAvoidingView>
224
+ </View>
225
+ );
226
+ }
227
+
228
+ export interface SheetHeaderProps {
229
+ children: React.ReactNode;
230
+ style?: ViewStyle;
231
+ }
232
+
233
+ export function SheetHeader({ children, style }: SheetHeaderProps) {
234
+ const { spacing } = useTheme();
235
+ return (
236
+ <View style={[styles.header, { paddingBottom: spacing[4] }, style]}>
237
+ {children}
238
+ </View>
239
+ );
240
+ }
241
+
242
+ export interface SheetTitleProps {
243
+ children: React.ReactNode;
244
+ style?: TextStyle;
245
+ }
246
+
247
+ export function SheetTitle({ children, style }: SheetTitleProps) {
248
+ const { colors } = useTheme();
249
+ return (
250
+ <Text style={[styles.title, { color: colors.foreground }, style]}>
251
+ {children}
252
+ </Text>
253
+ );
254
+ }
255
+
256
+ export interface SheetDescriptionProps {
257
+ children: React.ReactNode;
258
+ style?: TextStyle;
259
+ }
260
+
261
+ export function SheetDescription({ children, style }: SheetDescriptionProps) {
262
+ const { colors, spacing } = useTheme();
263
+ return (
264
+ <Text
265
+ style={[
266
+ styles.description,
267
+ { color: colors.foregroundMuted, marginTop: spacing[1] },
268
+ style,
269
+ ]}
270
+ >
271
+ {children}
272
+ </Text>
273
+ );
274
+ }
275
+
276
+ export interface SheetFooterProps {
277
+ children: React.ReactNode;
278
+ style?: ViewStyle;
279
+ }
280
+
281
+ export function SheetFooter({ children, style }: SheetFooterProps) {
282
+ const { spacing } = useTheme();
283
+ return (
284
+ <View style={[styles.footer, { paddingTop: spacing[4] }, style]}>
285
+ {children}
286
+ </View>
287
+ );
288
+ }
289
+
290
+ export interface SheetCloseProps {
291
+ children: React.ReactNode;
292
+ asChild?: boolean;
293
+ }
294
+
295
+ export function SheetClose({ children, asChild }: SheetCloseProps) {
296
+ const { onClose } = useSheet();
297
+
298
+ if (asChild && React.isValidElement(children)) {
299
+ return React.cloneElement(children as React.ReactElement<any>, {
300
+ onPress: onClose,
301
+ });
302
+ }
303
+
304
+ return (
305
+ <Pressable onPress={onClose}>
306
+ {children}
307
+ </Pressable>
308
+ );
309
+ }
310
+
311
+ const styles = StyleSheet.create({
312
+ gestureRoot: {
313
+ flex: 1,
314
+ },
315
+ container: {
316
+ flex: 1,
317
+ justifyContent: 'flex-end',
318
+ },
319
+ backdrop: {
320
+ ...StyleSheet.absoluteFillObject,
321
+ },
322
+ backdropPressable: {
323
+ flex: 1,
324
+ },
325
+ keyboardView: {
326
+ justifyContent: 'flex-end',
327
+ },
328
+ sheet: {
329
+ overflow: 'hidden',
330
+ },
331
+ handleContainer: {
332
+ alignItems: 'center',
333
+ paddingTop: SHEET_CONSTANTS.handlePaddingTop,
334
+ paddingBottom: SHEET_CONSTANTS.handlePaddingBottom,
335
+ },
336
+ handle: {
337
+ width: SHEET_CONSTANTS.handleWidth,
338
+ height: SHEET_CONSTANTS.handleHeight,
339
+ borderRadius: SHEET_CONSTANTS.handleHeight / 2,
340
+ },
341
+ content: {
342
+ flex: 1,
343
+ paddingHorizontal: SHEET_CONSTANTS.contentPaddingHorizontal,
344
+ },
345
+ header: {
346
+ alignItems: 'center',
347
+ },
348
+ title: {
349
+ fontSize: 18,
350
+ fontWeight: '600',
351
+ textAlign: 'center',
352
+ },
353
+ description: {
354
+ fontSize: 14,
355
+ textAlign: 'center',
356
+ },
357
+ footer: {
358
+ flexDirection: 'row',
359
+ justifyContent: 'flex-end',
360
+ gap: 8,
361
+ },
362
+ });
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Skeleton
3
+ *
4
+ * A loading placeholder with shimmer animation.
5
+ * Use to indicate content is loading.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * <Skeleton width={200} height={20} />
10
+ * <Skeleton width="100%" height={48} radius="lg" />
11
+ * <Skeleton circle size={40} />
12
+ * ```
13
+ */
14
+
15
+ import React, { useEffect } from 'react';
16
+ import { View, StyleSheet, ViewStyle, DimensionValue } from 'react-native';
17
+ import Animated, {
18
+ useSharedValue,
19
+ useAnimatedStyle,
20
+ withRepeat,
21
+ withTiming,
22
+ interpolate,
23
+ Easing,
24
+ } from 'react-native-reanimated';
25
+ import { useTheme } from '@nativeui/core';
26
+
27
+ export type SkeletonRadius = 'none' | 'sm' | 'md' | 'lg' | 'full';
28
+
29
+ export interface SkeletonProps {
30
+ /** Width of the skeleton */
31
+ width?: DimensionValue;
32
+ /** Height of the skeleton */
33
+ height?: DimensionValue;
34
+ /** Border radius preset */
35
+ radius?: SkeletonRadius;
36
+ /** Render as circle (uses size for width/height) */
37
+ circle?: boolean;
38
+ /** Size when circle is true */
39
+ size?: number;
40
+ /** Disable animation */
41
+ animate?: boolean;
42
+ /** Additional styles */
43
+ style?: ViewStyle;
44
+ }
45
+
46
+ const radiusMap: Record<SkeletonRadius, number> = {
47
+ none: 0,
48
+ sm: 4,
49
+ md: 8,
50
+ lg: 12,
51
+ full: 9999,
52
+ };
53
+
54
+ export function Skeleton({
55
+ width = '100%',
56
+ height = 20,
57
+ radius = 'md',
58
+ circle = false,
59
+ size = 40,
60
+ animate = true,
61
+ style,
62
+ }: SkeletonProps) {
63
+ const { colors } = useTheme();
64
+ const shimmerProgress = useSharedValue(0);
65
+
66
+ useEffect(() => {
67
+ if (animate) {
68
+ shimmerProgress.value = withRepeat(
69
+ withTiming(1, {
70
+ duration: 1500,
71
+ easing: Easing.inOut(Easing.ease),
72
+ }),
73
+ -1,
74
+ false
75
+ );
76
+ }
77
+ }, [animate]);
78
+
79
+ const animatedStyle = useAnimatedStyle(() => {
80
+ const opacity = interpolate(
81
+ shimmerProgress.value,
82
+ [0, 0.5, 1],
83
+ [0.3, 0.6, 0.3]
84
+ );
85
+
86
+ return {
87
+ opacity: animate ? opacity : 0.3,
88
+ };
89
+ });
90
+
91
+ const dimensions = circle
92
+ ? { width: size, height: size, borderRadius: size / 2 }
93
+ : {
94
+ width,
95
+ height,
96
+ borderRadius: radiusMap[radius],
97
+ };
98
+
99
+ return (
100
+ <Animated.View
101
+ style={[
102
+ styles.skeleton,
103
+ { backgroundColor: colors.foregroundMuted },
104
+ dimensions,
105
+ animatedStyle,
106
+ style,
107
+ ]}
108
+ accessibilityRole="progressbar"
109
+ accessibilityLabel="Loading"
110
+ accessibilityState={{ busy: true }}
111
+ />
112
+ );
113
+ }
114
+
115
+ export interface SkeletonTextProps {
116
+ /** Number of lines */
117
+ lines?: number;
118
+ /** Gap between lines */
119
+ gap?: number;
120
+ /** Last line width percentage */
121
+ lastLineWidth?: DimensionValue;
122
+ /** Line height */
123
+ lineHeight?: number;
124
+ /** Container style */
125
+ style?: ViewStyle;
126
+ }
127
+
128
+ export function SkeletonText({
129
+ lines = 3,
130
+ gap = 8,
131
+ lastLineWidth = '60%',
132
+ lineHeight = 16,
133
+ style,
134
+ }: SkeletonTextProps) {
135
+ return (
136
+ <View style={[styles.textContainer, { gap }, style]}>
137
+ {Array.from({ length: lines }).map((_, index) => (
138
+ <Skeleton
139
+ key={index}
140
+ width={index === lines - 1 ? lastLineWidth : '100%'}
141
+ height={lineHeight}
142
+ radius="sm"
143
+ />
144
+ ))}
145
+ </View>
146
+ );
147
+ }
148
+
149
+ const styles = StyleSheet.create({
150
+ skeleton: {
151
+ overflow: 'hidden',
152
+ },
153
+ textContainer: {
154
+ width: '100%',
155
+ },
156
+ });