@klyftig/mobile-ui 1.3.4 → 1.3.6

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": "@klyftig/mobile-ui",
3
- "version": "1.3.4",
3
+ "version": "1.3.6",
4
4
  "scripts": {
5
5
  "pack-install": "npm pack && mv klyftig-ui-*.tgz /tmp/klyftig-ui-local.tgz && cd ../../mobile && npm install /tmp/klyftig-ui-local.tgz"
6
6
  },
@@ -0,0 +1,88 @@
1
+ import React from 'react';
2
+ import {
3
+ View,
4
+ Modal,
5
+ StyleSheet,
6
+ TouchableOpacity,
7
+ KeyboardAvoidingView,
8
+ Platform,
9
+ StyleProp,
10
+ ViewStyle,
11
+ } from 'react-native';
12
+ import { useTheme } from '../hooks/useTheme';
13
+
14
+ export interface CenterDialogProps {
15
+ visible: boolean;
16
+ /** Called on backdrop tap and hardware back. */
17
+ onDismiss: () => void;
18
+ children: React.ReactNode;
19
+ /** Wrap the overlay in a KeyboardAvoidingView — use when the dialog hosts text inputs. */
20
+ avoidKeyboard?: boolean;
21
+ /** Disable tap-outside-to-dismiss. Hardware back still calls onDismiss. */
22
+ dismissable?: boolean;
23
+ /** Accessibility label for the tap-to-dismiss backdrop. */
24
+ backdropAccessibilityLabel?: string;
25
+ /** Styles merged into the white card (e.g. `{ alignItems: 'center' }`). */
26
+ contentStyle?: StyleProp<ViewStyle>;
27
+ }
28
+
29
+ /**
30
+ * Centered alert-style modal: dimmed scrim with tap-to-dismiss + a rounded white card.
31
+ * Callers own the card's content; this only provides the shared shell.
32
+ */
33
+ export function CenterDialog({
34
+ visible,
35
+ onDismiss,
36
+ children,
37
+ avoidKeyboard = false,
38
+ dismissable = true,
39
+ backdropAccessibilityLabel,
40
+ contentStyle,
41
+ }: CenterDialogProps) {
42
+ const { colors } = useTheme();
43
+ const Overlay = avoidKeyboard ? KeyboardAvoidingView : View;
44
+ const overlayProps = avoidKeyboard
45
+ ? { behavior: Platform.OS === 'ios' ? ('padding' as const) : undefined }
46
+ : {};
47
+
48
+ return (
49
+ <Modal visible={visible} transparent animationType="fade" onRequestClose={onDismiss}>
50
+ <Overlay style={styles.overlay} {...overlayProps}>
51
+ <TouchableOpacity
52
+ style={styles.backdrop}
53
+ activeOpacity={1}
54
+ onPress={dismissable ? onDismiss : undefined}
55
+ disabled={!dismissable}
56
+ accessibilityRole="button"
57
+ accessibilityLabel={backdropAccessibilityLabel}
58
+ />
59
+ <View style={[styles.content, { backgroundColor: colors.background.card }, contentStyle]}>
60
+ {children}
61
+ </View>
62
+ </Overlay>
63
+ </Modal>
64
+ );
65
+ }
66
+
67
+ const styles = StyleSheet.create({
68
+ overlay: {
69
+ flex: 1,
70
+ justifyContent: 'center',
71
+ alignItems: 'center',
72
+ },
73
+ backdrop: {
74
+ ...StyleSheet.absoluteFillObject,
75
+ backgroundColor: 'rgba(0,0,0,0.4)',
76
+ },
77
+ content: {
78
+ borderRadius: 20,
79
+ padding: 24,
80
+ width: '85%',
81
+ maxWidth: 360,
82
+ shadowColor: '#000',
83
+ shadowOffset: { width: 0, height: 4 },
84
+ shadowOpacity: 0.15,
85
+ shadowRadius: 12,
86
+ elevation: 8,
87
+ },
88
+ });
@@ -56,17 +56,22 @@ interface FABProps {
56
56
  overrideIcon?: string;
57
57
  /** If set, tapping the main button calls this directly instead of toggling the menu */
58
58
  overrideOnPress?: () => void;
59
+ /** Diameter of the main button. Defaults to FAB_SIZE (50). */
60
+ size?: number;
59
61
  }
60
62
 
61
63
  export const FAB_SIZE = 50;
62
64
  const SUB_BUTTON_SIZE = 44;
63
65
  const SUB_BUTTON_SPACING = 58;
66
+ // Main icon ≈ 48% of the button diameter (24 at the default 50px).
67
+ const ICON_RATIO = 0.48;
64
68
 
65
69
  // Fallback colors for sub-buttons when no color is specified
66
70
  const ACTION_COLORS = ['#10b981', '#f59e0b', '#0ea5e9', '#8b5cf6', '#ec4899'];
67
71
 
68
- export const FAB: React.FC<FABProps> = ({ actions, overrideIcon, overrideOnPress }) => {
72
+ export const FAB: React.FC<FABProps> = ({ actions, overrideIcon, overrideOnPress, size = FAB_SIZE }) => {
69
73
  const insets = useSafeAreaInsets();
74
+ const iconSize = Math.round(size * ICON_RATIO);
70
75
  const [isExpanded, setIsExpanded] = useState(false);
71
76
  const expandAnim = useRef(new Animated.Value(0)).current;
72
77
 
@@ -124,8 +129,8 @@ export const FAB: React.FC<FABProps> = ({ actions, overrideIcon, overrideOnPress
124
129
  style={[
125
130
  styles.subButtonRow,
126
131
  {
127
- bottom: fabBottom + (FAB_SIZE - SUB_BUTTON_SIZE) / 2,
128
- right: 16 + (FAB_SIZE - SUB_BUTTON_SIZE) / 2,
132
+ bottom: fabBottom + (size - SUB_BUTTON_SIZE) / 2,
133
+ right: 16 + (size - SUB_BUTTON_SIZE) / 2,
129
134
  transform: [{ translateY }, { scale: subButtonScale }],
130
135
  opacity: subButtonOpacity,
131
136
  },
@@ -157,11 +162,11 @@ export const FAB: React.FC<FABProps> = ({ actions, overrideIcon, overrideOnPress
157
162
  <AdaptiveGlassView
158
163
  variant="clear"
159
164
  interactive={true}
160
- style={[styles.fabGlass, { bottom: fabBottom, right: 16 }]}
165
+ style={[styles.fabGlass, { bottom: fabBottom, right: 16, width: size, height: size, borderRadius: size / 2 }]}
161
166
  fallbackStyle={styles.fabFallback}
162
167
  >
163
168
  <TouchableOpacity
164
- style={styles.fabTouchable}
169
+ style={[styles.fabTouchable, { borderRadius: size / 2 }]}
165
170
  onPress={overrideOnPress ?? toggle}
166
171
  activeOpacity={0.85}
167
172
  accessibilityRole="button"
@@ -170,7 +175,7 @@ export const FAB: React.FC<FABProps> = ({ actions, overrideIcon, overrideOnPress
170
175
  >
171
176
  <Icon
172
177
  name={overrideIcon ?? (isExpanded ? 'close' : 'plus')}
173
- size={24}
178
+ size={iconSize}
174
179
  color={GLASS_AVAILABLE ? '#1A1A1A' : '#FFFFFF'}
175
180
  />
176
181
  </TouchableOpacity>
@@ -46,7 +46,8 @@ export function ScreenHeader({
46
46
  onPress={onLeftPress}
47
47
  accessibilityLabel={leftAccessibilityLabel}
48
48
  accessibilityRole="button"
49
- hitSlop={8}
49
+ style={styles.leftButton}
50
+ hitSlop={12}
50
51
  >
51
52
  <Icon name={leftIcon as any} size={26} color={colors.icon.primary} />
52
53
  </TouchableOpacity>
@@ -86,10 +87,17 @@ const styles = StyleSheet.create({
86
87
  marginBottom: 8,
87
88
  },
88
89
  side: {
89
- width: 40,
90
+ width: 44,
90
91
  alignItems: 'center',
91
92
  justifyContent: 'center',
92
93
  },
94
+ leftButton: {
95
+ width: 44,
96
+ height: 44,
97
+ alignItems: 'center',
98
+ justifyContent: 'center',
99
+ marginLeft: -9,
100
+ },
93
101
  center: {
94
102
  flex: 1,
95
103
  alignItems: 'center',
package/src/index.ts CHANGED
@@ -44,6 +44,8 @@ export { WheelPicker } from './components/WheelPicker';
44
44
  export type { WheelPickerProps } from './components/WheelPicker';
45
45
  export { DisclaimerPill } from './components/DisclaimerPill';
46
46
  export type { DisclaimerPillProps } from './components/DisclaimerPill';
47
+ export { CenterDialog } from './components/CenterDialog';
48
+ export type { CenterDialogProps } from './components/CenterDialog';
47
49
 
48
50
  // Progress
49
51
  export { CircularProgress } from './components/progress/CircularProgress';