@compsych/mobile-ui 1.0.15 → 1.0.16

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compsych/mobile-ui",
3
- "version": "1.0.15",
3
+ "version": "1.0.16",
4
4
  "description": "ComPsych React Native Design System — 19 mobile UI components with self-contained design tokens",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -32,11 +32,17 @@
32
32
  "peerDependencies": {
33
33
  "@expo/vector-icons": ">=15.0.0",
34
34
  "expo-font": ">=13.0.0",
35
+ "expo-linear-gradient": ">=13.0.0",
35
36
  "lucide-react-native": ">=0.400.0",
36
37
  "react": ">=18.0.0",
37
38
  "react-native": ">=0.73.0",
38
39
  "react-native-svg": ">=15.0.0"
39
40
  },
41
+ "peerDependenciesMeta": {
42
+ "expo-linear-gradient": {
43
+ "optional": true
44
+ }
45
+ },
40
46
  "devDependencies": {
41
47
  "@babel/core": "^7.25.2",
42
48
  "@babel/plugin-transform-flow-strip-types": "^7.27.1",
@@ -52,6 +58,7 @@
52
58
  "babel-jest": "^29.7.0",
53
59
  "eslint": "^9.39.4",
54
60
  "eslint-plugin-unused-imports": "^4.4.1",
61
+ "expo-linear-gradient": "^56.0.4",
55
62
  "husky": "^9.1.7",
56
63
  "jest": "^29.7.0",
57
64
  "jest-expo": "^55.0.17",
@@ -1,13 +1,15 @@
1
- import React, { useMemo, useRef, useState } from 'react';
1
+ import React, { useCallback, useMemo, useRef, useState } from 'react';
2
2
 
3
3
  import {
4
4
  Image,
5
5
  ImageSourcePropType,
6
+ NativeSyntheticEvent,
6
7
  Pressable,
7
8
  ScrollView,
8
9
  StyleProp,
9
10
  StyleSheet,
10
11
  TextInput,
12
+ TextInputContentSizeChangeEventData,
11
13
  View,
12
14
  ViewStyle,
13
15
  } from 'react-native';
@@ -47,6 +49,18 @@ export function ChatInput({
47
49
  const { colorRoles: cr, dimensions: dim, iconography: ico } = useTheme();
48
50
  const [isFocused, setIsFocused] = useState(false);
49
51
  const inputRef = useRef<TextInput>(null);
52
+ const [inputHeight, setInputHeight] = useState(24);
53
+
54
+ const handleContentSizeChange = useCallback(
55
+ (e: NativeSyntheticEvent<TextInputContentSizeChangeEventData>) => {
56
+ const newHeight = Math.max(
57
+ 24,
58
+ Math.min(120, e.nativeEvent.contentSize.height),
59
+ );
60
+ setInputHeight(newHeight);
61
+ },
62
+ [],
63
+ );
50
64
 
51
65
  const hasText = value.trim().length > 0;
52
66
  const hasAttachments = !!attachments && attachments.length > 0;
@@ -182,12 +196,15 @@ export function ChatInput({
182
196
  multiline
183
197
  onFocus={() => setIsFocused(true)}
184
198
  onBlur={() => setIsFocused(false)}
199
+ onContentSizeChange={handleContentSizeChange}
200
+ scrollEnabled={inputHeight >= 120}
185
201
  style={[
186
202
  styles.textInput,
187
203
  {
188
204
  color: cr.surface.surface.sysOnSurface,
189
205
  fontSize: 16,
190
206
  lineHeight: 24,
207
+ height: inputHeight,
191
208
  },
192
209
  ]}
193
210
  accessibilityLabel={placeholder}
@@ -237,7 +254,6 @@ const styles = StyleSheet.create({
237
254
  includeFontPadding: false,
238
255
  padding: 0,
239
256
  margin: 0,
240
- maxHeight: 120,
241
257
  },
242
258
  attachStrip: {
243
259
  flexShrink: 0,
@@ -10,6 +10,8 @@ import {
10
10
  ViewStyle,
11
11
  } from 'react-native';
12
12
 
13
+ import { LinearGradient } from 'expo-linear-gradient';
14
+
13
15
  import { type IconName, SIZE_MAP, resolveIcon } from '../../icons';
14
16
  import { useTheme } from '../../theme';
15
17
  import { BodyText } from '../BodyText';
@@ -37,6 +39,12 @@ export interface ServiceCardProps {
37
39
  accessibilityLabel?: string;
38
40
  fullWidth?: boolean;
39
41
  style?: StyleProp<ViewStyle>;
42
+ /** Gradient colors for the "doubled" variant inner card (e.g. ['#a855f7', '#6366f1']). Falls back to the solid token color when omitted. */
43
+ gradient?: readonly [string, string, ...string[]];
44
+ /** Start point of the gradient in 0–1 coordinate space. Defaults to { x: 0, y: 0 } (top). */
45
+ gradientStart?: { x: number; y: number };
46
+ /** End point of the gradient in 0–1 coordinate space. Defaults to { x: 0, y: 1 } (bottom). */
47
+ gradientEnd?: { x: number; y: number };
40
48
  }
41
49
 
42
50
  const ELEVATION = {
@@ -64,6 +72,9 @@ export function ServiceCard({
64
72
  accessibilityLabel,
65
73
  fullWidth = false,
66
74
  style,
75
+ gradient,
76
+ gradientStart,
77
+ gradientEnd,
67
78
  }: ServiceCardProps) {
68
79
  const { colorRoles: cr, dimensions: dim } = useTheme();
69
80
 
@@ -225,19 +236,18 @@ export function ServiceCard({
225
236
  let inner: React.ReactNode;
226
237
 
227
238
  if (variant === 'doubled') {
228
- inner = (
229
- <View
230
- style={[
231
- styles.colRoot,
232
- {
233
- backgroundColor: v.innerBg,
234
- borderRadius: s.borderRadius,
235
- paddingHorizontal: s.paddingH,
236
- paddingVertical: s.paddingV,
237
- gap: dim.spacing.padding.sysPadding24,
238
- },
239
- ]}
240
- >
239
+ const doubledInnerStyle: StyleProp<ViewStyle> = [
240
+ styles.colRoot,
241
+ {
242
+ borderRadius: s.borderRadius,
243
+ paddingHorizontal: s.paddingH,
244
+ paddingVertical: s.paddingV,
245
+ gap: dim.spacing.padding.sysPadding24,
246
+ },
247
+ ];
248
+
249
+ const doubledInnerContent = (
250
+ <>
241
251
  {renderedIcon && (
242
252
  <View
243
253
  style={[
@@ -253,6 +263,21 @@ export function ServiceCard({
253
263
  )}
254
264
  {textBlock}
255
265
  {children}
266
+ </>
267
+ );
268
+
269
+ inner = gradient ? (
270
+ <LinearGradient
271
+ colors={gradient}
272
+ start={gradientStart ?? { x: 0, y: 0 }}
273
+ end={gradientEnd ?? { x: 0, y: 1 }}
274
+ style={doubledInnerStyle}
275
+ >
276
+ {doubledInnerContent}
277
+ </LinearGradient>
278
+ ) : (
279
+ <View style={[doubledInnerStyle, { backgroundColor: v.innerBg }]}>
280
+ {doubledInnerContent}
256
281
  </View>
257
282
  );
258
283
  } else if (variant === 'image') {