@webority-technologies/mobile 0.0.7 → 0.0.8

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 (63) hide show
  1. package/lib/commonjs/components/Badge/Badge.js +23 -13
  2. package/lib/commonjs/components/BottomSheet/BottomSheet.js +39 -5
  3. package/lib/commonjs/components/Button/Button.js +25 -6
  4. package/lib/commonjs/components/Card/Card.js +13 -1
  5. package/lib/commonjs/components/Checkbox/Checkbox.js +5 -3
  6. package/lib/commonjs/components/Chip/Chip.js +12 -3
  7. package/lib/commonjs/components/Dialog/Dialog.js +15 -8
  8. package/lib/commonjs/components/EmptyState/EmptyState.js +32 -26
  9. package/lib/commonjs/components/FormField/FormField.js +3 -3
  10. package/lib/commonjs/components/Input/Input.js +13 -5
  11. package/lib/commonjs/components/ListItem/ListItem.js +33 -27
  12. package/lib/commonjs/components/OTPInput/OTPInput.js +6 -3
  13. package/lib/commonjs/components/Radio/Radio.js +7 -6
  14. package/lib/commonjs/components/SearchBar/SearchBar.js +9 -5
  15. package/lib/commonjs/components/Switch/Switch.js +31 -2
  16. package/lib/commonjs/components/Toast/Toast.js +16 -11
  17. package/lib/commonjs/theme/Gradient.js +57 -0
  18. package/lib/commonjs/theme/index.js +20 -0
  19. package/lib/commonjs/theme/textStyle.js +37 -0
  20. package/lib/commonjs/theme/tokens.js +260 -2
  21. package/lib/module/components/Badge/Badge.js +24 -14
  22. package/lib/module/components/BottomSheet/BottomSheet.js +40 -6
  23. package/lib/module/components/Button/Button.js +26 -7
  24. package/lib/module/components/Card/Card.js +14 -2
  25. package/lib/module/components/Checkbox/Checkbox.js +5 -3
  26. package/lib/module/components/Chip/Chip.js +13 -4
  27. package/lib/module/components/Dialog/Dialog.js +16 -9
  28. package/lib/module/components/EmptyState/EmptyState.js +33 -27
  29. package/lib/module/components/FormField/FormField.js +4 -4
  30. package/lib/module/components/Input/Input.js +14 -6
  31. package/lib/module/components/ListItem/ListItem.js +34 -28
  32. package/lib/module/components/OTPInput/OTPInput.js +7 -4
  33. package/lib/module/components/Radio/Radio.js +7 -6
  34. package/lib/module/components/SearchBar/SearchBar.js +10 -6
  35. package/lib/module/components/Switch/Switch.js +31 -2
  36. package/lib/module/components/Toast/Toast.js +17 -12
  37. package/lib/module/theme/Gradient.js +50 -0
  38. package/lib/module/theme/index.js +2 -0
  39. package/lib/module/theme/textStyle.js +32 -0
  40. package/lib/module/theme/tokens.js +260 -2
  41. package/lib/typescript/commonjs/components/BottomSheet/BottomSheet.d.ts +10 -0
  42. package/lib/typescript/commonjs/components/Button/Button.d.ts +8 -0
  43. package/lib/typescript/commonjs/components/Card/Card.d.ts +8 -0
  44. package/lib/typescript/commonjs/components/Dialog/Dialog.d.ts +5 -0
  45. package/lib/typescript/commonjs/components/Input/Input.d.ts +12 -0
  46. package/lib/typescript/commonjs/components/Switch/Switch.d.ts +5 -0
  47. package/lib/typescript/commonjs/components/Toast/Toast.d.ts +5 -0
  48. package/lib/typescript/commonjs/theme/Gradient.d.ts +11 -0
  49. package/lib/typescript/commonjs/theme/index.d.ts +5 -1
  50. package/lib/typescript/commonjs/theme/textStyle.d.ts +18 -0
  51. package/lib/typescript/commonjs/theme/types.d.ts +178 -0
  52. package/lib/typescript/module/components/BottomSheet/BottomSheet.d.ts +10 -0
  53. package/lib/typescript/module/components/Button/Button.d.ts +8 -0
  54. package/lib/typescript/module/components/Card/Card.d.ts +8 -0
  55. package/lib/typescript/module/components/Dialog/Dialog.d.ts +5 -0
  56. package/lib/typescript/module/components/Input/Input.d.ts +12 -0
  57. package/lib/typescript/module/components/Switch/Switch.d.ts +5 -0
  58. package/lib/typescript/module/components/Toast/Toast.d.ts +5 -0
  59. package/lib/typescript/module/theme/Gradient.d.ts +11 -0
  60. package/lib/typescript/module/theme/index.d.ts +5 -1
  61. package/lib/typescript/module/theme/textStyle.d.ts +18 -0
  62. package/lib/typescript/module/theme/types.d.ts +178 -0
  63. package/package.json +5 -1
@@ -2,7 +2,7 @@
2
2
 
3
3
  import React, { forwardRef, useEffect, useMemo, useRef } from 'react';
4
4
  import { Animated, Easing, StyleSheet, Text, View } from 'react-native';
5
- import { useTheme } from "../../theme/index.js";
5
+ import { fontFor, useTheme } from "../../theme/index.js";
6
6
  import { SkeletonContent } from "../Skeleton/index.js";
7
7
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
8
  const toneFor = (theme, tone) => {
@@ -56,28 +56,29 @@ const sizeMap = {
56
56
  dot: 10
57
57
  }
58
58
  };
59
- const anchorPosition = anchor => {
59
+ const anchorPosition = (anchor, offset) => {
60
+ const o = -offset;
60
61
  switch (anchor) {
61
62
  case 'topLeft':
62
63
  return {
63
- top: -4,
64
- left: -4
64
+ top: o,
65
+ left: o
65
66
  };
66
67
  case 'bottomRight':
67
68
  return {
68
- bottom: -4,
69
- right: -4
69
+ bottom: o,
70
+ right: o
70
71
  };
71
72
  case 'bottomLeft':
72
73
  return {
73
- bottom: -4,
74
- left: -4
74
+ bottom: o,
75
+ left: o
75
76
  };
76
77
  case 'topRight':
77
78
  default:
78
79
  return {
79
- top: -4,
80
- right: -4
80
+ top: o,
81
+ right: o
81
82
  };
82
83
  }
83
84
  };
@@ -110,7 +111,16 @@ const Badge = /*#__PURE__*/forwardRef((props, ref) => {
110
111
  } = props;
111
112
  const theme = useTheme();
112
113
  const tones = useMemo(() => toneFor(theme, tone), [theme, tone]);
113
- const sz = sizeMap[size];
114
+ const overrides = theme.components.badge?.[size];
115
+ const sz = {
116
+ fontSize: overrides?.fontSize ?? sizeMap[size].fontSize,
117
+ minWidth: overrides?.minWidth ?? sizeMap[size].minWidth,
118
+ height: overrides?.height ?? sizeMap[size].height,
119
+ paddingH: overrides?.paddingHorizontal ?? sizeMap[size].paddingH,
120
+ dot: overrides?.dotSize ?? sizeMap[size].dot
121
+ };
122
+ const badgeBorderWidth = theme.components.badge?.borderWidth ?? 1.5;
123
+ const badgeAnchorOffset = theme.components.badge?.anchorOffset ?? 4;
114
124
  const formatted = variant === 'dot' ? null : formatValue(value, max);
115
125
  const shouldRender = !invisible && (variant === 'dot' || formatted !== null);
116
126
  const pulseScale = useRef(new Animated.Value(1)).current;
@@ -156,9 +166,9 @@ const Badge = /*#__PURE__*/forwardRef((props, ref) => {
156
166
  }]
157
167
  }, children ? {
158
168
  position: 'absolute',
159
- borderWidth: 1.5,
169
+ borderWidth: badgeBorderWidth,
160
170
  borderColor: theme.colors.background.elevated,
161
- ...anchorPosition(anchor)
171
+ ...anchorPosition(anchor, badgeAnchorOffset)
162
172
  } : null, badgeStyle],
163
173
  accessible: true,
164
174
  accessibilityRole: a11yRole,
@@ -167,7 +177,7 @@ const Badge = /*#__PURE__*/forwardRef((props, ref) => {
167
177
  style: [styles.text, {
168
178
  color: tones.text,
169
179
  fontSize: sz.fontSize,
170
- fontWeight: theme.typography.fontWeight.bold
180
+ ...fontFor(theme, 'bold')
171
181
  }, textStyle],
172
182
  numberOfLines: 1,
173
183
  allowFontScaling: false,
@@ -20,7 +20,7 @@
20
20
  */
21
21
 
22
22
  import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
23
- import { Dimensions, Pressable, StyleSheet, View } from 'react-native';
23
+ import { Dimensions, Keyboard, Platform, Pressable, StyleSheet, View } from 'react-native';
24
24
  import { Gesture, GestureDetector } from 'react-native-gesture-handler';
25
25
  import Animated, { Extrapolation, interpolate, runOnJS, useAnimatedStyle, useSharedValue, withSpring, withTiming } from 'react-native-reanimated';
26
26
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
@@ -43,6 +43,7 @@ const BottomSheet = /*#__PURE__*/forwardRef((props, ref) => {
43
43
  enablePanDownToClose = true,
44
44
  enableBackdropPress = true,
45
45
  backdropOpacity = 0.5,
46
+ keyboardBehavior = 'none',
46
47
  handleIndicatorStyle,
47
48
  containerStyle,
48
49
  children,
@@ -235,12 +236,45 @@ const BottomSheet = /*#__PURE__*/forwardRef((props, ref) => {
235
236
  });
236
237
  }, [closedY, dragStartY, enablePanDownToClose, handleSnapEnd, minTopY, snapTargets, translateY]);
237
238
 
239
+ // ───────── Keyboard tracking ─────────
240
+ // keyboardOffset is added to translateY in sheetStyle so we don't disturb
241
+ // snap-point math. iOS gets keyboardWillShow/Hide (smoother), Android only
242
+ // emits keyboardDidShow/Hide.
243
+ const keyboardOffset = useSharedValue(0);
244
+ useEffect(() => {
245
+ if (keyboardBehavior === 'none') return;
246
+ const showEvt = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
247
+ const hideEvt = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';
248
+ const onShow = e => {
249
+ const h = e.endCoordinates?.height ?? 0;
250
+ keyboardOffset.value = withTiming(-h, {
251
+ duration: 220
252
+ });
253
+ };
254
+ const onHide = () => {
255
+ keyboardOffset.value = withTiming(0, {
256
+ duration: 220
257
+ });
258
+ };
259
+ const showSub = Keyboard.addListener(showEvt, onShow);
260
+ const hideSub = Keyboard.addListener(hideEvt, onHide);
261
+ return () => {
262
+ showSub.remove();
263
+ hideSub.remove();
264
+ };
265
+ }, [keyboardBehavior, keyboardOffset]);
266
+
238
267
  // ───────── Animated styles ─────────
239
- const sheetStyle = useAnimatedStyle(() => ({
240
- transform: [{
241
- translateY: translateY.value
242
- }]
243
- }));
268
+ const sheetStyle = useAnimatedStyle(() => {
269
+ // Don't push past the screen top — keyboard offset is clamped to minTopY.
270
+ const yWithKb = translateY.value + keyboardOffset.value;
271
+ const clamped = yWithKb < minTopY ? minTopY : yWithKb;
272
+ return {
273
+ transform: [{
274
+ translateY: clamped
275
+ }]
276
+ };
277
+ });
244
278
  const backdropStyle = useAnimatedStyle(() => {
245
279
  // 0 opacity when sheet is closed, full backdropOpacity at minSnap height (and above).
246
280
  const closedAt = screenHeight;
@@ -2,7 +2,7 @@
2
2
 
3
3
  import React, { forwardRef, useMemo } from 'react';
4
4
  import { Animated, Pressable, StyleSheet, Text, View } from 'react-native';
5
- import { useTheme } from "../../theme/index.js";
5
+ import { fontFor, Gradient, useTheme } from "../../theme/index.js";
6
6
  import { usePressAnimation } from "../../hooks/usePressAnimation.js";
7
7
  import { triggerHaptic } from "../../utils/hapticUtils.js";
8
8
  import { Spinner } from "../Spinner/index.js";
@@ -21,6 +21,7 @@ const Button = /*#__PURE__*/forwardRef((props, ref) => {
21
21
  rightIcon,
22
22
  haptic = 'selection',
23
23
  rounded = false,
24
+ gradient,
24
25
  style,
25
26
  textStyle,
26
27
  pressAnimation = true,
@@ -40,9 +41,22 @@ const Button = /*#__PURE__*/forwardRef((props, ref) => {
40
41
  enabled: pressAnimation && isInteractive
41
42
  });
42
43
  const styles = useMemo(() => buildStyles(theme), [theme]);
43
- const sizeStyles = sizeMap[size];
44
+ const sizeStyles = {
45
+ ...sizeMap[size],
46
+ ...(theme.components.button?.[size] ?? {})
47
+ };
44
48
  const toneColors = useMemo(() => toneFor(theme, tone), [theme, tone]);
45
49
  const variantStyles = useMemo(() => variantFor(theme, variant, toneColors, disabled), [theme, variant, toneColors, disabled]);
50
+
51
+ // Resolve gradient: string token → theme.gradients[token]; literal passes through.
52
+ // Skipped when disabled (parity with disabled flat-color treatment).
53
+ const resolvedGradient = useMemo(() => {
54
+ if (!gradient || disabled || variant !== 'solid') return null;
55
+ if (typeof gradient === 'string') {
56
+ return theme.gradients[gradient] ?? null;
57
+ }
58
+ return gradient;
59
+ }, [gradient, disabled, variant, theme.gradients]);
46
60
  const handlePress = event => {
47
61
  if (!isInteractive) return;
48
62
  if (haptic !== false) triggerHaptic(haptic);
@@ -64,7 +78,7 @@ const Button = /*#__PURE__*/forwardRef((props, ref) => {
64
78
  style: [styles.label, {
65
79
  color: variantStyles.textColor,
66
80
  fontSize: sizeStyles.fontSize,
67
- fontWeight: theme.typography.fontWeight.semibold
81
+ ...fontFor(theme, 'semibold')
68
82
  }, textStyle],
69
83
  numberOfLines: 1,
70
84
  children: title
@@ -80,7 +94,7 @@ const Button = /*#__PURE__*/forwardRef((props, ref) => {
80
94
  scale
81
95
  }]
82
96
  }, fullWidth ? styles.fullWidth : null],
83
- children: /*#__PURE__*/_jsx(Pressable, {
97
+ children: /*#__PURE__*/_jsxs(Pressable, {
84
98
  ref: ref,
85
99
  onPress: handlePress,
86
100
  onPressIn: pressIn,
@@ -108,15 +122,20 @@ const Button = /*#__PURE__*/forwardRef((props, ref) => {
108
122
  paddingVertical: sizeStyles.paddingVertical,
109
123
  minHeight: sizeStyles.minHeight,
110
124
  borderRadius: rounded ? theme.radius.full : sizeStyles.borderRadius,
111
- backgroundColor: variantStyles.backgroundColor,
125
+ // Hide solid bg when a gradient layer covers it; preserve outline borders.
126
+ backgroundColor: resolvedGradient ? 'transparent' : variantStyles.backgroundColor,
112
127
  borderWidth: variantStyles.borderWidth,
113
128
  borderColor: variantStyles.borderColor,
114
- opacity: !isInteractive ? 0.55 : 1
129
+ opacity: !isInteractive ? 0.55 : 1,
130
+ overflow: resolvedGradient ? 'hidden' : undefined
115
131
  }, pressed && variant !== 'solid' ? {
116
132
  backgroundColor: theme.colors.surface.pressed
117
133
  } : null, fullWidth ? styles.fullWidth : null, style],
118
134
  ...rest,
119
- children: content
135
+ children: [resolvedGradient ? /*#__PURE__*/_jsx(Gradient, {
136
+ gradient: resolvedGradient,
137
+ style: StyleSheet.absoluteFill
138
+ }) : null, content]
120
139
  })
121
140
  });
122
141
  });
@@ -2,7 +2,7 @@
2
2
 
3
3
  import React, { forwardRef, useMemo } from 'react';
4
4
  import { Animated, Image, Pressable, StyleSheet, View } from 'react-native';
5
- import { useTheme } from "../../theme/index.js";
5
+ import { Gradient, useTheme } from "../../theme/index.js";
6
6
  import { usePressAnimation } from "../../hooks/usePressAnimation.js";
7
7
  import { triggerHaptic } from "../../utils/hapticUtils.js";
8
8
  import { SkeletonContent } from "../Skeleton/index.js";
@@ -29,6 +29,7 @@ const Card = /*#__PURE__*/forwardRef((props, ref) => {
29
29
  imageAspectRatio,
30
30
  imageOverlay,
31
31
  loading = false,
32
+ gradient,
32
33
  onPress,
33
34
  accessibilityLabel,
34
35
  accessibilityHint,
@@ -49,6 +50,11 @@ const Card = /*#__PURE__*/forwardRef((props, ref) => {
49
50
  const styles = useMemo(() => buildStyles(theme), [theme]);
50
51
  const hasImage = Boolean(imageSource);
51
52
  const contentPadding = theme.spacing[paddingMap[padding]];
53
+ const resolvedGradient = useMemo(() => {
54
+ if (!gradient) return null;
55
+ if (typeof gradient === 'string') return theme.gradients[gradient] ?? null;
56
+ return gradient;
57
+ }, [gradient, theme.gradients]);
52
58
  const containerStyle = useMemo(() => {
53
59
  const base = {
54
60
  borderRadius: theme.radius[radius],
@@ -108,7 +114,7 @@ const Card = /*#__PURE__*/forwardRef((props, ref) => {
108
114
  children: footer
109
115
  }) : null]
110
116
  });
111
- const content = hasImage ? /*#__PURE__*/_jsxs(_Fragment, {
117
+ const baseContent = hasImage ? /*#__PURE__*/_jsxs(_Fragment, {
112
118
  children: [/*#__PURE__*/_jsxs(View, {
113
119
  style: styles.imageWrapper,
114
120
  children: [/*#__PURE__*/_jsx(Image, {
@@ -129,6 +135,12 @@ const Card = /*#__PURE__*/forwardRef((props, ref) => {
129
135
  children: innerContent
130
136
  })]
131
137
  }) : innerContent;
138
+ const content = resolvedGradient ? /*#__PURE__*/_jsxs(_Fragment, {
139
+ children: [/*#__PURE__*/_jsx(Gradient, {
140
+ gradient: resolvedGradient,
141
+ style: StyleSheet.absoluteFillObject
142
+ }), baseContent]
143
+ }) : baseContent;
132
144
  let rendered;
133
145
  if (isInteractive) {
134
146
  const handlePress = event => {
@@ -41,7 +41,9 @@ const Checkbox = /*#__PURE__*/forwardRef((props, ref) => {
41
41
  } = props;
42
42
  const theme = useTheme();
43
43
  const styles = useMemo(() => buildStyles(theme), [theme]);
44
- const boxSize = sizeMap[size];
44
+ const boxSize = theme.components.checkbox?.[size]?.boxSize ?? sizeMap[size];
45
+ const checkboxBorderWidth = theme.components.checkbox?.borderWidth ?? 1.5;
46
+ const checkboxLabelGap = theme.components.checkbox?.labelGap ?? 10;
45
47
  const fillColor = toneColor(theme, tone);
46
48
  const isActive = checked || indeterminate;
47
49
  const progress = useRef(new Animated.Value(isActive ? 1 : 0)).current;
@@ -93,6 +95,7 @@ const Checkbox = /*#__PURE__*/forwardRef((props, ref) => {
93
95
  style: [styles.box, {
94
96
  width: boxSize,
95
97
  height: boxSize,
98
+ borderWidth: checkboxBorderWidth,
96
99
  borderRadius: theme.radius.xs,
97
100
  borderColor,
98
101
  backgroundColor
@@ -123,6 +126,7 @@ const Checkbox = /*#__PURE__*/forwardRef((props, ref) => {
123
126
  })
124
127
  }), label ? /*#__PURE__*/_jsx(Text, {
125
128
  style: [styles.label, {
129
+ marginLeft: checkboxLabelGap,
126
130
  color: disabled ? theme.colors.text.disabled : theme.colors.text.primary,
127
131
  fontSize: theme.typography.fontSize.base
128
132
  }],
@@ -138,7 +142,6 @@ const buildStyles = _theme => StyleSheet.create({
138
142
  alignItems: 'center'
139
143
  },
140
144
  box: {
141
- borderWidth: 1.5,
142
145
  alignItems: 'center',
143
146
  justifyContent: 'center'
144
147
  },
@@ -152,7 +155,6 @@ const buildStyles = _theme => StyleSheet.create({
152
155
  includeFontPadding: false
153
156
  },
154
157
  label: {
155
- marginLeft: 10,
156
158
  flexShrink: 1
157
159
  }
158
160
  });
@@ -2,7 +2,7 @@
2
2
 
3
3
  import React, { forwardRef, useMemo } from 'react';
4
4
  import { Animated, Pressable, StyleSheet, Text, View } from 'react-native';
5
- import { useTheme } from "../../theme/index.js";
5
+ import { fontFor, useTheme } from "../../theme/index.js";
6
6
  import { usePressAnimation } from "../../hooks/usePressAnimation.js";
7
7
  import { triggerHaptic } from "../../utils/hapticUtils.js";
8
8
  import { SkeletonContent } from "../Skeleton/index.js";
@@ -113,7 +113,16 @@ const Chip = /*#__PURE__*/forwardRef((props, ref) => {
113
113
  enabled: isPressable
114
114
  });
115
115
  const tones = useMemo(() => toneFor(theme, tone), [theme, tone]);
116
- const sz = sizeMap[size];
116
+ const overrides = theme.components.chip?.[size];
117
+ const sz = {
118
+ paddingH: overrides?.paddingHorizontal ?? sizeMap[size].paddingH,
119
+ paddingV: overrides?.paddingVertical ?? sizeMap[size].paddingV,
120
+ fontSize: overrides?.fontSize ?? sizeMap[size].fontSize,
121
+ minHeight: overrides?.minHeight ?? sizeMap[size].minHeight,
122
+ closeSize: overrides?.closeSize ?? sizeMap[size].closeSize,
123
+ closeFont: overrides?.closeFontSize ?? sizeMap[size].closeFont,
124
+ gap: overrides?.gap ?? sizeMap[size].gap
125
+ };
117
126
  const isFilled = variant === 'filled';
118
127
  const backgroundColor = (() => {
119
128
  if (selected) return isFilled ? tones.selectedBg : 'transparent';
@@ -153,7 +162,7 @@ const Chip = /*#__PURE__*/forwardRef((props, ref) => {
153
162
  style: [styles.label, {
154
163
  color: textColor,
155
164
  fontSize: sz.fontSize,
156
- fontWeight: theme.typography.fontWeight.medium
165
+ ...fontFor(theme, 'medium')
157
166
  }, textStyle],
158
167
  numberOfLines: 1,
159
168
  children: label ?? ' '
@@ -179,7 +188,7 @@ const Chip = /*#__PURE__*/forwardRef((props, ref) => {
179
188
  style: {
180
189
  color: textColor,
181
190
  fontSize: sz.closeFont,
182
- fontWeight: theme.typography.fontWeight.bold,
191
+ ...fontFor(theme, 'bold'),
183
192
  lineHeight: sz.closeFont + 2
184
193
  },
185
194
  allowFontScaling: false,
@@ -2,7 +2,7 @@
2
2
 
3
3
  import React, { useCallback, useMemo } from 'react';
4
4
  import { ActivityIndicator, Pressable, StyleSheet, Text, View } from 'react-native';
5
- import { useTheme } from "../../theme/index.js";
5
+ import { fontFor, useTheme } from "../../theme/index.js";
6
6
  import { triggerHaptic } from "../../utils/hapticUtils.js";
7
7
  import { Modal } from "../Modal/Modal.js";
8
8
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
@@ -17,11 +17,17 @@ const Dialog = props => {
17
17
  actions,
18
18
  dismissOnAction = true,
19
19
  accessibilityLabel,
20
+ contentStyle,
21
+ containerStyle,
20
22
  testID
21
23
  } = props;
22
24
  const theme = useTheme();
23
25
  const styles = useMemo(() => buildStyles(theme), [theme]);
24
26
  const variantTint = useMemo(() => tintForVariant(theme, variant), [theme, variant]);
27
+ const dialogTokens = theme.components.dialog;
28
+ const iconWrapperSize = dialogTokens?.iconWrapperSize ?? 48;
29
+ const iconWrapperBorderRadius = dialogTokens?.iconWrapperBorderRadius ?? 24;
30
+ const actionButtonMinHeight = dialogTokens?.actionButtonMinHeight ?? 44;
25
31
  const handleAction = useCallback(action => {
26
32
  if (action.loading) return;
27
33
  triggerHaptic('selection');
@@ -35,13 +41,17 @@ const Dialog = props => {
35
41
  onRequestClose: onClose,
36
42
  presentation: "centered",
37
43
  accessibilityLabel: accessibilityLabel ?? title,
44
+ contentStyle: contentStyle,
38
45
  testID: testID,
39
46
  children: /*#__PURE__*/_jsxs(View, {
40
47
  accessibilityRole: 'alertdialog',
41
48
  accessibilityLabel: accessibilityLabel ?? title,
42
- style: styles.container,
49
+ style: [styles.container, containerStyle],
43
50
  children: [icon ? /*#__PURE__*/_jsx(View, {
44
51
  style: [styles.iconWrapper, {
52
+ width: iconWrapperSize,
53
+ height: iconWrapperSize,
54
+ borderRadius: iconWrapperBorderRadius,
45
55
  backgroundColor: variantTint.muted,
46
56
  marginBottom: theme.spacing.md
47
57
  }],
@@ -50,7 +60,7 @@ const Dialog = props => {
50
60
  style: [styles.title, {
51
61
  color: theme.colors.text.primary,
52
62
  fontSize: theme.typography.fontSize.xl,
53
- fontWeight: theme.typography.fontWeight.semibold,
63
+ ...fontFor(theme, 'semibold'),
54
64
  marginBottom: message ? theme.spacing.sm : theme.spacing.md
55
65
  }],
56
66
  accessibilityRole: "header",
@@ -86,6 +96,7 @@ const Dialog = props => {
86
96
  style: ({
87
97
  pressed
88
98
  }) => [styles.actionButton, {
99
+ minHeight: actionButtonMinHeight,
89
100
  backgroundColor: buttonStyle.backgroundColor,
90
101
  borderColor: buttonStyle.borderColor,
91
102
  borderWidth: buttonStyle.borderWidth,
@@ -102,7 +113,7 @@ const Dialog = props => {
102
113
  style: {
103
114
  color: buttonStyle.textColor,
104
115
  fontSize: theme.typography.fontSize.base,
105
- fontWeight: theme.typography.fontWeight.semibold,
116
+ ...fontFor(theme, 'semibold'),
106
117
  textAlign: 'center'
107
118
  },
108
119
  numberOfLines: 1,
@@ -180,9 +191,6 @@ const buildStyles = _theme => StyleSheet.create({
180
191
  alignItems: 'stretch'
181
192
  },
182
193
  iconWrapper: {
183
- width: 48,
184
- height: 48,
185
- borderRadius: 24,
186
194
  alignItems: 'center',
187
195
  justifyContent: 'center',
188
196
  alignSelf: 'center'
@@ -201,8 +209,7 @@ const buildStyles = _theme => StyleSheet.create({
201
209
  actionButton: {
202
210
  flex: 1,
203
211
  alignItems: 'center',
204
- justifyContent: 'center',
205
- minHeight: 44
212
+ justifyContent: 'center'
206
213
  }
207
214
  });
208
215
  export { Dialog };
@@ -2,36 +2,42 @@
2
2
 
3
3
  import React, { forwardRef, useMemo } from 'react';
4
4
  import { Animated, Pressable, StyleSheet, Text, View } from 'react-native';
5
- import { useTheme } from "../../theme/index.js";
5
+ import { fontFor, useTheme } from "../../theme/index.js";
6
6
  import { usePressAnimation } from "../../hooks/usePressAnimation.js";
7
7
  import { triggerHaptic } from "../../utils/hapticUtils.js";
8
8
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
- const sizeFor = (theme, size) => {
10
- switch (size) {
11
- case 'sm':
12
- return {
13
- iconSize: 48,
14
- titleSize: theme.typography.fontSize.lg,
15
- descriptionSize: theme.typography.fontSize.sm,
16
- paddingVertical: theme.spacing.lg
17
- };
18
- case 'lg':
19
- return {
20
- iconSize: 80,
21
- titleSize: theme.typography.fontSize['2xl'],
22
- descriptionSize: theme.typography.fontSize.base,
23
- paddingVertical: theme.spacing['2xl']
24
- };
25
- case 'md':
26
- default:
27
- return {
28
- iconSize: 64,
29
- titleSize: theme.typography.fontSize.xl,
30
- descriptionSize: theme.typography.fontSize.base,
31
- paddingVertical: theme.spacing.xl
32
- };
9
+ const SIZE_FALLBACK = {
10
+ sm: {
11
+ iconSize: 48,
12
+ titleFontSize: 'lg',
13
+ descriptionFontSize: 'sm',
14
+ paddingVertical: 'lg'
15
+ },
16
+ md: {
17
+ iconSize: 64,
18
+ titleFontSize: 'xl',
19
+ descriptionFontSize: 'base',
20
+ paddingVertical: 'xl'
21
+ },
22
+ lg: {
23
+ iconSize: 80,
24
+ titleFontSize: '2xl',
25
+ descriptionFontSize: 'base',
26
+ paddingVertical: '2xl'
33
27
  }
34
28
  };
29
+ const sizeFor = (theme, size) => {
30
+ const tokens = {
31
+ ...SIZE_FALLBACK[size],
32
+ ...(theme.components.emptyState?.[size] ?? {})
33
+ };
34
+ return {
35
+ iconSize: tokens.iconSize,
36
+ titleSize: theme.typography.fontSize[tokens.titleFontSize],
37
+ descriptionSize: theme.typography.fontSize[tokens.descriptionFontSize],
38
+ paddingVertical: theme.spacing[tokens.paddingVertical]
39
+ };
40
+ };
35
41
  const ActionButton = ({
36
42
  action,
37
43
  variant
@@ -110,7 +116,7 @@ const EmptyState = /*#__PURE__*/forwardRef((props, ref) => {
110
116
  }) : null, /*#__PURE__*/_jsx(Text, {
111
117
  style: [styles.title, {
112
118
  fontSize: sz.titleSize,
113
- fontWeight: theme.typography.fontWeight.semibold
119
+ ...fontFor(theme, 'semibold')
114
120
  }],
115
121
  children: title
116
122
  }), description ? /*#__PURE__*/_jsx(Text, {
@@ -183,7 +189,7 @@ const buildActionStyles = theme => StyleSheet.create({
183
189
  },
184
190
  buttonText: {
185
191
  fontSize: theme.typography.fontSize.base,
186
- fontWeight: theme.typography.fontWeight.semibold
192
+ ...fontFor(theme, 'semibold')
187
193
  },
188
194
  buttonTextPrimary: {
189
195
  color: theme.colors.text.inverse
@@ -2,7 +2,7 @@
2
2
 
3
3
  import React, { forwardRef, useEffect, useMemo, useRef } from 'react';
4
4
  import { Animated, StyleSheet, Text, View } from 'react-native';
5
- import { useTheme } from "../../theme/index.js";
5
+ import { fontFor, useTheme } from "../../theme/index.js";
6
6
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
7
7
  const FormField = /*#__PURE__*/forwardRef((props, ref) => {
8
8
  const {
@@ -92,15 +92,15 @@ const buildStyles = theme => StyleSheet.create({
92
92
  },
93
93
  label: {
94
94
  fontSize: theme.typography.fontSize.sm,
95
- fontWeight: theme.typography.fontWeight.medium,
95
+ ...fontFor(theme, 'medium'),
96
96
  color: theme.colors.text.secondary
97
97
  },
98
98
  labelInline: {
99
- width: '35%'
99
+ width: theme.components.formField?.inlineLabelWidth ?? '35%'
100
100
  },
101
101
  requiredMark: {
102
102
  color: theme.colors.error,
103
- fontWeight: theme.typography.fontWeight.medium
103
+ ...fontFor(theme, 'medium')
104
104
  },
105
105
  inputStacked: {
106
106
  width: '100%'
@@ -5,7 +5,7 @@ import { Animated, Easing, Pressable, StyleSheet, Text, TextInput, View } from '
5
5
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
6
6
  // @ts-ignore - react-native-vector-icons ships no bundled types in this version
7
7
  import Feather from 'react-native-vector-icons/Feather';
8
- import { useTheme } from "../../theme/index.js";
8
+ import { fontFor, useTheme } from "../../theme/index.js";
9
9
  import { triggerHaptic } from "../../utils/index.js";
10
10
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
11
  const sizeMap = {
@@ -55,6 +55,8 @@ const Input = /*#__PURE__*/forwardRef((props, ref) => {
55
55
  variant = 'outlined',
56
56
  required = false,
57
57
  maxLength,
58
+ format,
59
+ parse,
58
60
  style,
59
61
  inputStyle,
60
62
  labelStyle,
@@ -67,7 +69,10 @@ const Input = /*#__PURE__*/forwardRef((props, ref) => {
67
69
  } = props;
68
70
  const theme = useTheme();
69
71
  const styles = useMemo(() => buildStyles(theme), [theme]);
70
- const sizeStyles = sizeMap[size];
72
+ const sizeStyles = {
73
+ ...sizeMap[size],
74
+ ...(theme.components.input?.[size] ?? {})
75
+ };
71
76
  const [isFocused, setIsFocused] = useState(false);
72
77
  const [isPasswordVisible, setIsPasswordVisible] = useState(false);
73
78
  const hasValue = typeof value === 'string' && value.length > 0;
@@ -217,7 +222,7 @@ const Input = /*#__PURE__*/forwardRef((props, ref) => {
217
222
  style: [styles.staticLabel, {
218
223
  color: hasError ? theme.colors.error : isFocused ? theme.colors.border.focus : theme.colors.text.secondary,
219
224
  fontSize: theme.typography.fontSize.sm,
220
- fontWeight: theme.typography.fontWeight.medium,
225
+ ...fontFor(theme, 'medium'),
221
226
  marginBottom: theme.spacing.xxs
222
227
  }, labelStyle],
223
228
  children: [label, required ? /*#__PURE__*/_jsx(Text, {
@@ -251,7 +256,7 @@ const Input = /*#__PURE__*/forwardRef((props, ref) => {
251
256
  left: leftPad,
252
257
  color: labelColor,
253
258
  fontSize: sizeStyles.fontSize,
254
- fontWeight: theme.typography.fontWeight.medium,
259
+ ...fontFor(theme, 'medium'),
255
260
  backgroundColor: shouldFloat && variant === 'outlined' ? theme.colors.background.primary : 'transparent',
256
261
  paddingHorizontal: shouldFloat && variant === 'outlined' ? 4 : 0,
257
262
  transform: [{
@@ -277,8 +282,11 @@ const Input = /*#__PURE__*/forwardRef((props, ref) => {
277
282
  }, inputStyle],
278
283
  placeholder: showFloatingLabel && !shouldFloat ? undefined : placeholder,
279
284
  placeholderTextColor: placeholderTextColor,
280
- value: value,
281
- onChangeText: onChangeText,
285
+ value: format && value != null ? format(value) : value,
286
+ onChangeText: text => {
287
+ if (!onChangeText) return;
288
+ onChangeText(parse ? parse(text) : text);
289
+ },
282
290
  onFocus: handleFocus,
283
291
  onBlur: handleBlur,
284
292
  editable: isEditable,