@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.
- package/dist/index.js +8 -2
- package/package.json +5 -3
- package/registry/registry.json +717 -0
- package/registry/ui/accordion.tsx +416 -0
- package/registry/ui/action-sheet.tsx +396 -0
- package/registry/ui/alert-dialog.tsx +355 -0
- package/registry/ui/avatar-stack.tsx +278 -0
- package/registry/ui/avatar.tsx +116 -0
- package/registry/ui/badge.tsx +125 -0
- package/registry/ui/button.tsx +240 -0
- package/registry/ui/card.tsx +675 -0
- package/registry/ui/carousel.tsx +431 -0
- package/registry/ui/checkbox.tsx +252 -0
- package/registry/ui/chip.tsx +271 -0
- package/registry/ui/column.tsx +133 -0
- package/registry/ui/datetime-picker.tsx +578 -0
- package/registry/ui/dialog.tsx +292 -0
- package/registry/ui/fab.tsx +225 -0
- package/registry/ui/form.tsx +323 -0
- package/registry/ui/horizontal-list.tsx +200 -0
- package/registry/ui/icon-button.tsx +244 -0
- package/registry/ui/image-gallery.tsx +455 -0
- package/registry/ui/image.tsx +283 -0
- package/registry/ui/input.tsx +242 -0
- package/registry/ui/label.tsx +99 -0
- package/registry/ui/list.tsx +519 -0
- package/registry/ui/progress.tsx +168 -0
- package/registry/ui/pull-to-refresh.tsx +231 -0
- package/registry/ui/radio-group.tsx +294 -0
- package/registry/ui/rating.tsx +311 -0
- package/registry/ui/row.tsx +136 -0
- package/registry/ui/screen.tsx +153 -0
- package/registry/ui/search-input.tsx +281 -0
- package/registry/ui/section-header.tsx +258 -0
- package/registry/ui/segmented-control.tsx +229 -0
- package/registry/ui/select.tsx +311 -0
- package/registry/ui/separator.tsx +74 -0
- package/registry/ui/sheet.tsx +362 -0
- package/registry/ui/skeleton.tsx +156 -0
- package/registry/ui/slider.tsx +307 -0
- package/registry/ui/spinner.tsx +100 -0
- package/registry/ui/stepper.tsx +314 -0
- package/registry/ui/stories.tsx +463 -0
- package/registry/ui/swipeable-row.tsx +362 -0
- package/registry/ui/switch.tsx +246 -0
- package/registry/ui/tabs.tsx +348 -0
- package/registry/ui/textarea.tsx +265 -0
- package/registry/ui/toast.tsx +316 -0
- package/registry/ui/tooltip.tsx +369 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chip
|
|
3
|
+
*
|
|
4
|
+
* Selectable filter pills for filtering content or making selections.
|
|
5
|
+
* Supports single and multi-select modes with optional icons.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* // Single chip
|
|
10
|
+
* <Chip selected={isSelected} onPress={() => setSelected(!isSelected)}>
|
|
11
|
+
* Electronics
|
|
12
|
+
* </Chip>
|
|
13
|
+
*
|
|
14
|
+
* // With icon
|
|
15
|
+
* <Chip icon={<TagIcon />} selected>
|
|
16
|
+
* Sale
|
|
17
|
+
* </Chip>
|
|
18
|
+
*
|
|
19
|
+
* // Chip group
|
|
20
|
+
* <ChipGroup>
|
|
21
|
+
* {categories.map(cat => (
|
|
22
|
+
* <Chip
|
|
23
|
+
* key={cat.id}
|
|
24
|
+
* selected={selectedIds.includes(cat.id)}
|
|
25
|
+
* onPress={() => toggleSelection(cat.id)}
|
|
26
|
+
* >
|
|
27
|
+
* {cat.name}
|
|
28
|
+
* </Chip>
|
|
29
|
+
* ))}
|
|
30
|
+
* </ChipGroup>
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import React from 'react';
|
|
35
|
+
import {
|
|
36
|
+
View,
|
|
37
|
+
Text,
|
|
38
|
+
Pressable,
|
|
39
|
+
StyleSheet,
|
|
40
|
+
ViewStyle,
|
|
41
|
+
TextStyle,
|
|
42
|
+
} from 'react-native';
|
|
43
|
+
import Animated, {
|
|
44
|
+
useAnimatedStyle,
|
|
45
|
+
useSharedValue,
|
|
46
|
+
withSpring,
|
|
47
|
+
interpolateColor,
|
|
48
|
+
} from 'react-native-reanimated';
|
|
49
|
+
import { useTheme } from '@nativeui/core';
|
|
50
|
+
|
|
51
|
+
const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
|
|
52
|
+
|
|
53
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
54
|
+
// Types
|
|
55
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
export type ChipVariant = 'outline' | 'filled';
|
|
58
|
+
export type ChipSize = 'sm' | 'md' | 'lg';
|
|
59
|
+
|
|
60
|
+
export interface ChipProps {
|
|
61
|
+
/** Chip label */
|
|
62
|
+
children: string;
|
|
63
|
+
/** Whether chip is selected */
|
|
64
|
+
selected?: boolean;
|
|
65
|
+
/** Visual variant */
|
|
66
|
+
variant?: ChipVariant;
|
|
67
|
+
/** Size preset */
|
|
68
|
+
size?: ChipSize;
|
|
69
|
+
/** Icon element (left side) */
|
|
70
|
+
icon?: React.ReactNode;
|
|
71
|
+
/** Whether chip is disabled */
|
|
72
|
+
disabled?: boolean;
|
|
73
|
+
/** Press handler */
|
|
74
|
+
onPress?: () => void;
|
|
75
|
+
/** Container style */
|
|
76
|
+
style?: ViewStyle;
|
|
77
|
+
/** Label style */
|
|
78
|
+
labelStyle?: TextStyle;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface ChipGroupProps {
|
|
82
|
+
/** Chip elements */
|
|
83
|
+
children: React.ReactNode;
|
|
84
|
+
/** Gap between chips */
|
|
85
|
+
spacing?: number;
|
|
86
|
+
/** Container style */
|
|
87
|
+
style?: ViewStyle;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
91
|
+
// Size configs
|
|
92
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
93
|
+
|
|
94
|
+
const SIZE_CONFIG = {
|
|
95
|
+
sm: {
|
|
96
|
+
paddingHorizontal: 10,
|
|
97
|
+
paddingVertical: 4,
|
|
98
|
+
fontSize: 12,
|
|
99
|
+
iconSize: 14,
|
|
100
|
+
radiusKey: 'md' as const,
|
|
101
|
+
},
|
|
102
|
+
md: {
|
|
103
|
+
paddingHorizontal: 14,
|
|
104
|
+
paddingVertical: 6,
|
|
105
|
+
fontSize: 14,
|
|
106
|
+
iconSize: 16,
|
|
107
|
+
radiusKey: 'lg' as const,
|
|
108
|
+
},
|
|
109
|
+
lg: {
|
|
110
|
+
paddingHorizontal: 18,
|
|
111
|
+
paddingVertical: 8,
|
|
112
|
+
fontSize: 16,
|
|
113
|
+
iconSize: 18,
|
|
114
|
+
radiusKey: 'xl' as const,
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
119
|
+
// Chip Component
|
|
120
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
121
|
+
|
|
122
|
+
export function Chip({
|
|
123
|
+
children,
|
|
124
|
+
selected = false,
|
|
125
|
+
variant = 'outline',
|
|
126
|
+
size = 'md',
|
|
127
|
+
icon,
|
|
128
|
+
disabled = false,
|
|
129
|
+
onPress,
|
|
130
|
+
style,
|
|
131
|
+
labelStyle,
|
|
132
|
+
}: ChipProps) {
|
|
133
|
+
const { colors, fontWeight, radius } = useTheme();
|
|
134
|
+
const config = SIZE_CONFIG[size];
|
|
135
|
+
|
|
136
|
+
const scale = useSharedValue(1);
|
|
137
|
+
const selectedAnim = useSharedValue(selected ? 1 : 0);
|
|
138
|
+
|
|
139
|
+
React.useEffect(() => {
|
|
140
|
+
selectedAnim.value = withSpring(selected ? 1 : 0, {
|
|
141
|
+
damping: 20,
|
|
142
|
+
stiffness: 300,
|
|
143
|
+
});
|
|
144
|
+
}, [selected, selectedAnim]);
|
|
145
|
+
|
|
146
|
+
const animatedStyle = useAnimatedStyle(() => {
|
|
147
|
+
const backgroundColor =
|
|
148
|
+
variant === 'filled'
|
|
149
|
+
? interpolateColor(
|
|
150
|
+
selectedAnim.value,
|
|
151
|
+
[0, 1],
|
|
152
|
+
[colors.secondary, colors.primary]
|
|
153
|
+
)
|
|
154
|
+
: interpolateColor(
|
|
155
|
+
selectedAnim.value,
|
|
156
|
+
[0, 1],
|
|
157
|
+
['transparent', colors.primary + '15']
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const borderColor = interpolateColor(
|
|
161
|
+
selectedAnim.value,
|
|
162
|
+
[0, 1],
|
|
163
|
+
[colors.border, colors.primary]
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
transform: [{ scale: scale.value }],
|
|
168
|
+
backgroundColor,
|
|
169
|
+
borderColor,
|
|
170
|
+
};
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const textColor =
|
|
174
|
+
variant === 'filled'
|
|
175
|
+
? selected
|
|
176
|
+
? colors.primaryForeground
|
|
177
|
+
: colors.secondaryForeground
|
|
178
|
+
: selected
|
|
179
|
+
? colors.primary
|
|
180
|
+
: colors.foreground;
|
|
181
|
+
|
|
182
|
+
const handlePressIn = () => {
|
|
183
|
+
scale.value = withSpring(0.95, { damping: 20, stiffness: 400 });
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const handlePressOut = () => {
|
|
187
|
+
scale.value = withSpring(1, { damping: 20, stiffness: 400 });
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
return (
|
|
191
|
+
<AnimatedPressable
|
|
192
|
+
onPress={onPress}
|
|
193
|
+
onPressIn={handlePressIn}
|
|
194
|
+
onPressOut={handlePressOut}
|
|
195
|
+
disabled={disabled}
|
|
196
|
+
style={[
|
|
197
|
+
styles.chip,
|
|
198
|
+
{
|
|
199
|
+
paddingHorizontal: config.paddingHorizontal,
|
|
200
|
+
paddingVertical: config.paddingVertical,
|
|
201
|
+
borderRadius: radius[config.radiusKey],
|
|
202
|
+
opacity: disabled ? 0.5 : 1,
|
|
203
|
+
},
|
|
204
|
+
animatedStyle,
|
|
205
|
+
style,
|
|
206
|
+
]}
|
|
207
|
+
accessibilityRole="button"
|
|
208
|
+
accessibilityState={{ selected, disabled }}
|
|
209
|
+
>
|
|
210
|
+
{icon && (
|
|
211
|
+
<View style={{ marginRight: 6 }}>
|
|
212
|
+
{React.isValidElement(icon)
|
|
213
|
+
? React.cloneElement(icon as React.ReactElement<{ width?: number; height?: number; color?: string }>, {
|
|
214
|
+
width: config.iconSize,
|
|
215
|
+
height: config.iconSize,
|
|
216
|
+
color: textColor,
|
|
217
|
+
})
|
|
218
|
+
: icon}
|
|
219
|
+
</View>
|
|
220
|
+
)}
|
|
221
|
+
<Text
|
|
222
|
+
style={[
|
|
223
|
+
styles.label,
|
|
224
|
+
{
|
|
225
|
+
fontSize: config.fontSize,
|
|
226
|
+
fontWeight: fontWeight.medium,
|
|
227
|
+
color: textColor,
|
|
228
|
+
},
|
|
229
|
+
labelStyle,
|
|
230
|
+
]}
|
|
231
|
+
>
|
|
232
|
+
{children}
|
|
233
|
+
</Text>
|
|
234
|
+
</AnimatedPressable>
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
239
|
+
// ChipGroup Component
|
|
240
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
241
|
+
|
|
242
|
+
export function ChipGroup({ children, spacing, style }: ChipGroupProps) {
|
|
243
|
+
const { spacing: themeSpacing } = useTheme();
|
|
244
|
+
const gap = spacing ?? themeSpacing[2];
|
|
245
|
+
|
|
246
|
+
return (
|
|
247
|
+
<View style={[styles.group, { gap }, style]}>
|
|
248
|
+
{children}
|
|
249
|
+
</View>
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
254
|
+
// Styles
|
|
255
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
256
|
+
|
|
257
|
+
const styles = StyleSheet.create({
|
|
258
|
+
chip: {
|
|
259
|
+
flexDirection: 'row',
|
|
260
|
+
alignItems: 'center',
|
|
261
|
+
justifyContent: 'center',
|
|
262
|
+
borderWidth: 1,
|
|
263
|
+
},
|
|
264
|
+
label: {
|
|
265
|
+
textAlign: 'center',
|
|
266
|
+
},
|
|
267
|
+
group: {
|
|
268
|
+
flexDirection: 'row',
|
|
269
|
+
flexWrap: 'wrap',
|
|
270
|
+
},
|
|
271
|
+
});
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Column
|
|
3
|
+
*
|
|
4
|
+
* Vertical flex container with gap, alignment, and padding props.
|
|
5
|
+
* Uses design tokens for consistent spacing.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* <Column gap="md" align="center">
|
|
10
|
+
* <Text>Top</Text>
|
|
11
|
+
* <Text>Bottom</Text>
|
|
12
|
+
* </Column>
|
|
13
|
+
*
|
|
14
|
+
* <Column gap={4} flex={1} justify="between">
|
|
15
|
+
* <Header />
|
|
16
|
+
* <Content />
|
|
17
|
+
* <Footer />
|
|
18
|
+
* </Column>
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import React, { forwardRef } from 'react';
|
|
23
|
+
import { View, ViewProps, ViewStyle } from 'react-native';
|
|
24
|
+
import { useTheme } from '@nativeui/core';
|
|
25
|
+
|
|
26
|
+
// Semantic gap values
|
|
27
|
+
type GapSemantic = 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
|
|
28
|
+
|
|
29
|
+
// Gap can be either semantic string or numeric spacing key
|
|
30
|
+
export type GapValue = GapSemantic | number;
|
|
31
|
+
|
|
32
|
+
// Semantic → Spacing Key Mapping
|
|
33
|
+
const semanticGapMap: Record<GapSemantic, number> = {
|
|
34
|
+
'none': 0,
|
|
35
|
+
'xs': 2,
|
|
36
|
+
'sm': 3,
|
|
37
|
+
'md': 4,
|
|
38
|
+
'lg': 6,
|
|
39
|
+
'xl': 8,
|
|
40
|
+
'2xl': 12,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Alignment value mappings
|
|
44
|
+
const alignMap = {
|
|
45
|
+
'start': 'flex-start',
|
|
46
|
+
'center': 'center',
|
|
47
|
+
'end': 'flex-end',
|
|
48
|
+
'stretch': 'stretch',
|
|
49
|
+
} as const;
|
|
50
|
+
|
|
51
|
+
const justifyMap = {
|
|
52
|
+
'start': 'flex-start',
|
|
53
|
+
'center': 'center',
|
|
54
|
+
'end': 'flex-end',
|
|
55
|
+
'between': 'space-between',
|
|
56
|
+
'around': 'space-around',
|
|
57
|
+
'evenly': 'space-evenly',
|
|
58
|
+
} as const;
|
|
59
|
+
|
|
60
|
+
export interface ColumnProps extends ViewProps {
|
|
61
|
+
/** Gap between children (semantic or spacing key) */
|
|
62
|
+
gap?: GapValue;
|
|
63
|
+
/** Horizontal alignment of children */
|
|
64
|
+
align?: 'start' | 'center' | 'end' | 'stretch';
|
|
65
|
+
/** Vertical distribution of children */
|
|
66
|
+
justify?: 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly';
|
|
67
|
+
/** Flex value for the container */
|
|
68
|
+
flex?: number;
|
|
69
|
+
/** Padding (all sides) */
|
|
70
|
+
p?: GapValue;
|
|
71
|
+
/** Horizontal padding */
|
|
72
|
+
px?: GapValue;
|
|
73
|
+
/** Vertical padding */
|
|
74
|
+
py?: GapValue;
|
|
75
|
+
/** Additional styles */
|
|
76
|
+
style?: ViewStyle;
|
|
77
|
+
/** Children */
|
|
78
|
+
children?: React.ReactNode;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function resolveGap(
|
|
82
|
+
value: GapValue | undefined,
|
|
83
|
+
spacing: ReturnType<typeof useTheme>['spacing']
|
|
84
|
+
): number | undefined {
|
|
85
|
+
if (value === undefined) return undefined;
|
|
86
|
+
|
|
87
|
+
if (typeof value === 'string') {
|
|
88
|
+
const key = semanticGapMap[value as GapSemantic];
|
|
89
|
+
return (spacing as Record<number, number>)[key];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return (spacing as Record<number, number>)[value];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const Column = forwardRef<View, ColumnProps>(function Column(
|
|
96
|
+
{
|
|
97
|
+
gap,
|
|
98
|
+
align,
|
|
99
|
+
justify,
|
|
100
|
+
flex,
|
|
101
|
+
p,
|
|
102
|
+
px,
|
|
103
|
+
py,
|
|
104
|
+
style,
|
|
105
|
+
children,
|
|
106
|
+
...props
|
|
107
|
+
},
|
|
108
|
+
ref
|
|
109
|
+
) {
|
|
110
|
+
const { spacing } = useTheme();
|
|
111
|
+
|
|
112
|
+
const resolvedGap = resolveGap(gap, spacing);
|
|
113
|
+
const resolvedP = resolveGap(p, spacing);
|
|
114
|
+
const resolvedPx = resolveGap(px, spacing);
|
|
115
|
+
const resolvedPy = resolveGap(py, spacing);
|
|
116
|
+
|
|
117
|
+
const containerStyle: ViewStyle = {
|
|
118
|
+
flexDirection: 'column',
|
|
119
|
+
...(resolvedGap !== undefined && { gap: resolvedGap }),
|
|
120
|
+
...(align && { alignItems: alignMap[align] }),
|
|
121
|
+
...(justify && { justifyContent: justifyMap[justify] }),
|
|
122
|
+
...(flex !== undefined && { flex }),
|
|
123
|
+
...(resolvedP !== undefined && { padding: resolvedP }),
|
|
124
|
+
...(resolvedPx !== undefined && { paddingHorizontal: resolvedPx }),
|
|
125
|
+
...(resolvedPy !== undefined && { paddingVertical: resolvedPy }),
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<View ref={ref} style={[containerStyle, style]} {...props}>
|
|
130
|
+
{children}
|
|
131
|
+
</View>
|
|
132
|
+
);
|
|
133
|
+
});
|