@umituz/react-native-design-system 2.9.29 → 2.9.31
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 +2 -1
- package/src/exports/molecules.ts +1 -5
- package/src/index.ts +33 -25
- package/src/molecules/circular-menu/CircularMenu.tsx +151 -0
- package/src/molecules/circular-menu/CircularMenuBackground.tsx +32 -0
- package/src/molecules/circular-menu/CircularMenuCloseButton.tsx +44 -0
- package/src/molecules/circular-menu/CircularMenuItem.tsx +69 -0
- package/src/molecules/circular-menu/constants.ts +73 -0
- package/src/molecules/circular-menu/index.ts +4 -0
- package/src/molecules/index.ts +1 -0
- package/src/molecules/navigation/components/NavigationContainer.tsx +13 -0
- package/src/molecules/navigation/components/index.ts +4 -0
- package/src/molecules/navigation/index.ts +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-design-system",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.31",
|
|
4
4
|
"description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive, safe area, exception, infinite scroll, UUID, image, timezone, offline, onboarding, and loading utilities",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"./responsive": "./src/responsive/index.ts",
|
|
20
20
|
"./safe-area": "./src/safe-area/index.ts",
|
|
21
21
|
"./device": "./src/device/index.ts",
|
|
22
|
+
"./exception": "./src/exception/index.ts",
|
|
22
23
|
"./image": "./src/image/index.ts",
|
|
23
24
|
"./timezone": "./src/timezone/index.ts",
|
|
24
25
|
"./offline": "./src/offline/index.ts",
|
package/src/exports/molecules.ts
CHANGED
|
@@ -29,7 +29,6 @@ export {
|
|
|
29
29
|
// Bottom Sheet
|
|
30
30
|
BottomSheet,
|
|
31
31
|
BottomSheetModal,
|
|
32
|
-
|
|
33
32
|
FilterBottomSheet,
|
|
34
33
|
FilterSheet,
|
|
35
34
|
useBottomSheet,
|
|
@@ -114,8 +113,6 @@ export {
|
|
|
114
113
|
type FabButtonProps,
|
|
115
114
|
type TabScreen,
|
|
116
115
|
type TabNavigatorConfig,
|
|
117
|
-
type StackScreen,
|
|
118
|
-
type StackNavigatorConfig,
|
|
119
116
|
type BaseScreen,
|
|
120
117
|
type BaseNavigatorConfig,
|
|
121
118
|
type IconRendererProps,
|
|
@@ -190,5 +187,4 @@ export {
|
|
|
190
187
|
InfoGrid,
|
|
191
188
|
type InfoGridProps,
|
|
192
189
|
type InfoGridItem,
|
|
193
|
-
} from
|
|
194
|
-
|
|
190
|
+
} from "../molecules";
|
package/src/index.ts
CHANGED
|
@@ -15,123 +15,131 @@
|
|
|
15
15
|
// =============================================================================
|
|
16
16
|
// THEME EXPORTS
|
|
17
17
|
// =============================================================================
|
|
18
|
-
export * from
|
|
18
|
+
export * from "./exports/theme";
|
|
19
19
|
|
|
20
20
|
// =============================================================================
|
|
21
21
|
// TYPOGRAPHY EXPORTS
|
|
22
22
|
// =============================================================================
|
|
23
|
-
export * from
|
|
23
|
+
export * from "./exports/typography";
|
|
24
24
|
|
|
25
25
|
// =============================================================================
|
|
26
26
|
// RESPONSIVE EXPORTS
|
|
27
27
|
// =============================================================================
|
|
28
|
-
export * from
|
|
28
|
+
export * from "./exports/responsive";
|
|
29
29
|
|
|
30
30
|
// =============================================================================
|
|
31
31
|
// DEVICE EXPORTS
|
|
32
32
|
// =============================================================================
|
|
33
|
-
export * from
|
|
33
|
+
export * from "./exports/device";
|
|
34
34
|
|
|
35
35
|
// =============================================================================
|
|
36
36
|
// ATOMS EXPORTS
|
|
37
37
|
// =============================================================================
|
|
38
|
-
export * from
|
|
38
|
+
export * from "./exports/atoms";
|
|
39
39
|
|
|
40
40
|
// =============================================================================
|
|
41
41
|
// LAYOUTS EXPORTS
|
|
42
42
|
// =============================================================================
|
|
43
|
-
export * from
|
|
43
|
+
export * from "./exports/layouts";
|
|
44
44
|
|
|
45
45
|
// =============================================================================
|
|
46
46
|
// MOLECULES EXPORTS
|
|
47
47
|
// =============================================================================
|
|
48
|
-
export * from
|
|
48
|
+
export * from "./exports/molecules";
|
|
49
49
|
|
|
50
50
|
// =============================================================================
|
|
51
51
|
// ORGANISMS EXPORTS
|
|
52
52
|
// =============================================================================
|
|
53
|
-
export * from
|
|
53
|
+
export * from "./exports/organisms";
|
|
54
54
|
|
|
55
55
|
// =============================================================================
|
|
56
56
|
// SAFE AREA EXPORTS
|
|
57
57
|
// =============================================================================
|
|
58
|
-
export * from
|
|
58
|
+
export * from "./exports/safe-area";
|
|
59
59
|
|
|
60
60
|
// =============================================================================
|
|
61
61
|
// EXCEPTION EXPORTS
|
|
62
62
|
// =============================================================================
|
|
63
|
-
export * from
|
|
63
|
+
export * from "./exports/exception";
|
|
64
|
+
export { ErrorBoundary } from "./exception/presentation/components/ErrorBoundary";
|
|
64
65
|
|
|
65
66
|
// =============================================================================
|
|
66
67
|
// INFINITE SCROLL EXPORTS
|
|
67
68
|
// =============================================================================
|
|
68
|
-
export * from
|
|
69
|
+
export * from "./exports/infinite-scroll";
|
|
69
70
|
|
|
70
71
|
// =============================================================================
|
|
71
72
|
// UUID EXPORTS
|
|
72
73
|
// =============================================================================
|
|
73
|
-
export * from
|
|
74
|
+
export * from "./exports/uuid";
|
|
74
75
|
|
|
75
76
|
// =============================================================================
|
|
76
77
|
// TIMEZONE EXPORTS
|
|
77
78
|
// =============================================================================
|
|
78
|
-
export * from
|
|
79
|
+
export * from "./exports/timezone";
|
|
79
80
|
|
|
80
81
|
// =============================================================================
|
|
81
82
|
// OFFLINE EXPORTS
|
|
82
83
|
// =============================================================================
|
|
83
|
-
export * from
|
|
84
|
+
export * from "./exports/offline";
|
|
85
|
+
export { NetworkProvider, useOffline, OfflineBanner } from "./offline";
|
|
84
86
|
|
|
85
87
|
// =============================================================================
|
|
86
88
|
// IMAGE EXPORTS
|
|
87
89
|
// =============================================================================
|
|
88
|
-
export * from
|
|
90
|
+
export * from "./exports/image";
|
|
89
91
|
|
|
90
92
|
// =============================================================================
|
|
91
93
|
// HAPTICS EXPORTS
|
|
92
94
|
// =============================================================================
|
|
93
|
-
export * from
|
|
95
|
+
export * from "./exports/haptics";
|
|
94
96
|
|
|
95
97
|
// =============================================================================
|
|
96
98
|
// VARIANT UTILITIES
|
|
97
99
|
// =============================================================================
|
|
98
|
-
export * from
|
|
100
|
+
export * from "./exports/variants";
|
|
99
101
|
|
|
100
102
|
// =============================================================================
|
|
101
103
|
// UTILITIES
|
|
102
104
|
// =============================================================================
|
|
103
|
-
export * from
|
|
105
|
+
export * from "./exports/utilities";
|
|
104
106
|
|
|
105
107
|
// =============================================================================
|
|
106
108
|
// STORAGE EXPORTS
|
|
107
109
|
// =============================================================================
|
|
108
|
-
export * from
|
|
110
|
+
export * from "./exports/storage";
|
|
111
|
+
|
|
112
|
+
// =============================================================================
|
|
113
|
+
// STORAGE STATE EXPORTS
|
|
114
|
+
// =============================================================================
|
|
115
|
+
export * from "./storage/presentation/hooks/useStorageState";
|
|
109
116
|
|
|
110
117
|
// =============================================================================
|
|
111
118
|
// ONBOARDING EXPORTS
|
|
112
119
|
// =============================================================================
|
|
113
|
-
export * from
|
|
120
|
+
export * from "./exports/onboarding";
|
|
114
121
|
// =============================================================================
|
|
115
122
|
// FILESYSTEM EXPORTS
|
|
116
123
|
// =============================================================================
|
|
117
|
-
export * from
|
|
124
|
+
export * from "./exports/filesystem";
|
|
118
125
|
|
|
119
126
|
// =============================================================================
|
|
120
127
|
// MEDIA EXPORTS
|
|
121
128
|
// =============================================================================
|
|
122
|
-
export * from
|
|
129
|
+
export * from "./exports/media";
|
|
123
130
|
|
|
124
131
|
// =============================================================================
|
|
125
132
|
// TANSTACK EXPORTS
|
|
126
133
|
// =============================================================================
|
|
127
|
-
export * from
|
|
134
|
+
export * from "./exports/tanstack";
|
|
135
|
+
export { TanstackProvider } from "./tanstack";
|
|
128
136
|
|
|
129
137
|
// =============================================================================
|
|
130
138
|
// LOADING EXPORTS
|
|
131
139
|
// =============================================================================
|
|
132
|
-
export * from
|
|
140
|
+
export * from "./exports/loading";
|
|
133
141
|
|
|
134
142
|
// =============================================================================
|
|
135
143
|
// INIT EXPORTS
|
|
136
144
|
// =============================================================================
|
|
137
|
-
export * from
|
|
145
|
+
export * from "./exports/init";
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import React, { useMemo } from "react";
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Modal,
|
|
5
|
+
TouchableWithoutFeedback,
|
|
6
|
+
StyleSheet,
|
|
7
|
+
ViewStyle,
|
|
8
|
+
} from "react-native";
|
|
9
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
10
|
+
import {
|
|
11
|
+
ARC_BACKGROUND,
|
|
12
|
+
OVERLAY,
|
|
13
|
+
ROW_CONFIG,
|
|
14
|
+
LAYOUT,
|
|
15
|
+
getTopRowPosition,
|
|
16
|
+
getBottomRowPosition,
|
|
17
|
+
} from "./constants";
|
|
18
|
+
import { CircularMenuBackground } from "./CircularMenuBackground";
|
|
19
|
+
import { CircularMenuItem } from "./CircularMenuItem";
|
|
20
|
+
import { CircularMenuCloseButton } from "./CircularMenuCloseButton";
|
|
21
|
+
|
|
22
|
+
export interface CircularMenuAction {
|
|
23
|
+
id: string;
|
|
24
|
+
label: string;
|
|
25
|
+
icon: string;
|
|
26
|
+
onPress: () => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface CircularMenuProps {
|
|
30
|
+
visible: boolean;
|
|
31
|
+
onClose: () => void;
|
|
32
|
+
actions: CircularMenuAction[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const CircularMenu: React.FC<CircularMenuProps> = ({
|
|
36
|
+
visible,
|
|
37
|
+
onClose,
|
|
38
|
+
actions,
|
|
39
|
+
}) => {
|
|
40
|
+
const insets = useSafeAreaInsets();
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Determine layout based on number of items.
|
|
44
|
+
* If strictly 3 items, use the specific "Top Triangle" layout requested.
|
|
45
|
+
* Otherwise, use a generic algorithm.
|
|
46
|
+
*/
|
|
47
|
+
const layoutItems = useMemo(() => {
|
|
48
|
+
if (actions.length === 3) {
|
|
49
|
+
// Triangle Layout: Top Center (Index 1), Bottom Left (Index 0), Bottom Right (Index 2)
|
|
50
|
+
return [
|
|
51
|
+
{ ...actions[1], position: getTopRowPosition(0, 1) }, // Text to Video (Top Center)
|
|
52
|
+
{ ...actions[0], position: getBottomRowPosition("left") }, // Text to Image (Bottom Left)
|
|
53
|
+
{ ...actions[2], position: getBottomRowPosition("right") }, // Image to Video (Bottom Right)
|
|
54
|
+
];
|
|
55
|
+
} else {
|
|
56
|
+
// Default: 2 on top, 2 on-bottom, etc.
|
|
57
|
+
// This is a fallback if actions change.
|
|
58
|
+
// For now, implementing simple distribute:
|
|
59
|
+
const topCount = Math.min(actions.length, 2);
|
|
60
|
+
const bottomCount = Math.max(0, actions.length - 2);
|
|
61
|
+
|
|
62
|
+
const mapped = [];
|
|
63
|
+
// Top row
|
|
64
|
+
for (let i = 0; i < topCount; i++) {
|
|
65
|
+
mapped.push({
|
|
66
|
+
...actions[i],
|
|
67
|
+
position: getTopRowPosition(i, topCount),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
// Bottom row (left/right)
|
|
71
|
+
if (bottomCount > 0) {
|
|
72
|
+
mapped.push({
|
|
73
|
+
...actions[2],
|
|
74
|
+
position: getBottomRowPosition("left")
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
if (bottomCount > 1) {
|
|
78
|
+
mapped.push({
|
|
79
|
+
...actions[3],
|
|
80
|
+
position: getBottomRowPosition("right")
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
return mapped;
|
|
84
|
+
}
|
|
85
|
+
}, [actions]);
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<Modal
|
|
89
|
+
visible={visible}
|
|
90
|
+
transparent
|
|
91
|
+
animationType="fade"
|
|
92
|
+
onRequestClose={onClose}
|
|
93
|
+
statusBarTranslucent
|
|
94
|
+
>
|
|
95
|
+
<TouchableWithoutFeedback onPress={onClose}>
|
|
96
|
+
<View style={styles.overlay}>
|
|
97
|
+
<TouchableWithoutFeedback>
|
|
98
|
+
<View
|
|
99
|
+
style={[
|
|
100
|
+
styles.container,
|
|
101
|
+
{
|
|
102
|
+
paddingBottom: insets.bottom + OVERLAY.PADDING_BOTTOM_OFFSET,
|
|
103
|
+
},
|
|
104
|
+
]}
|
|
105
|
+
>
|
|
106
|
+
<CircularMenuBackground />
|
|
107
|
+
|
|
108
|
+
<View style={styles.itemsContainer}>
|
|
109
|
+
{layoutItems.map((item) => (
|
|
110
|
+
<View key={item.id} style={item.position as ViewStyle}>
|
|
111
|
+
<CircularMenuItem
|
|
112
|
+
icon={item.icon}
|
|
113
|
+
label={item.label}
|
|
114
|
+
onPress={item.onPress}
|
|
115
|
+
/>
|
|
116
|
+
</View>
|
|
117
|
+
))}
|
|
118
|
+
|
|
119
|
+
<View style={styles.closeButton}>
|
|
120
|
+
<CircularMenuCloseButton onPress={onClose} />
|
|
121
|
+
</View>
|
|
122
|
+
</View>
|
|
123
|
+
</View>
|
|
124
|
+
</TouchableWithoutFeedback>
|
|
125
|
+
</View>
|
|
126
|
+
</TouchableWithoutFeedback>
|
|
127
|
+
</Modal>
|
|
128
|
+
);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const styles = StyleSheet.create({
|
|
132
|
+
overlay: {
|
|
133
|
+
flex: 1,
|
|
134
|
+
justifyContent: "flex-end",
|
|
135
|
+
backgroundColor: "rgba(0, 0, 0, 0.85)",
|
|
136
|
+
},
|
|
137
|
+
container: {
|
|
138
|
+
alignItems: "center",
|
|
139
|
+
},
|
|
140
|
+
itemsContainer: {
|
|
141
|
+
width: ARC_BACKGROUND.WIDTH,
|
|
142
|
+
height: ARC_BACKGROUND.HEIGHT,
|
|
143
|
+
position: "relative",
|
|
144
|
+
},
|
|
145
|
+
closeButton: {
|
|
146
|
+
position: "absolute",
|
|
147
|
+
left: ARC_BACKGROUND.WIDTH / 2 - LAYOUT.CLOSE_BUTTON_SIZE / 2,
|
|
148
|
+
top:
|
|
149
|
+
ROW_CONFIG.CENTER_Y + ROW_CONFIG.BOTTOM_Y - LAYOUT.CLOSE_BUTTON_SIZE / 2,
|
|
150
|
+
},
|
|
151
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View, StyleSheet } from "react-native";
|
|
3
|
+
import { useAppDesignTokens } from "../../theme/useAppDesignTokens";
|
|
4
|
+
import { ARC_BACKGROUND } from "./constants";
|
|
5
|
+
|
|
6
|
+
export const CircularMenuBackground: React.FC = () => {
|
|
7
|
+
const tokens = useAppDesignTokens();
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<View
|
|
11
|
+
style={[
|
|
12
|
+
styles.arc,
|
|
13
|
+
{
|
|
14
|
+
backgroundColor: tokens.colors.surface,
|
|
15
|
+
width: ARC_BACKGROUND.WIDTH,
|
|
16
|
+
height: ARC_BACKGROUND.HEIGHT,
|
|
17
|
+
borderTopLeftRadius: ARC_BACKGROUND.BORDER_RADIUS_TOP,
|
|
18
|
+
borderTopRightRadius: ARC_BACKGROUND.BORDER_RADIUS_TOP,
|
|
19
|
+
},
|
|
20
|
+
]}
|
|
21
|
+
/>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const styles = StyleSheet.create({
|
|
26
|
+
arc: {
|
|
27
|
+
position: "absolute",
|
|
28
|
+
bottom: 0,
|
|
29
|
+
borderBottomLeftRadius: 0,
|
|
30
|
+
borderBottomRightRadius: 0,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { TouchableOpacity, StyleSheet } from "react-native";
|
|
3
|
+
import { AtomicIcon } from "../../atoms/AtomicIcon";
|
|
4
|
+
import { useAppDesignTokens } from "../../theme/useAppDesignTokens";
|
|
5
|
+
import { LAYOUT } from "./constants";
|
|
6
|
+
|
|
7
|
+
export interface CircularMenuCloseButtonProps {
|
|
8
|
+
onPress: () => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const CircularMenuCloseButton: React.FC<CircularMenuCloseButtonProps> = ({
|
|
12
|
+
onPress,
|
|
13
|
+
}) => {
|
|
14
|
+
const tokens = useAppDesignTokens();
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<TouchableOpacity
|
|
18
|
+
onPress={onPress}
|
|
19
|
+
style={[
|
|
20
|
+
styles.container,
|
|
21
|
+
{
|
|
22
|
+
backgroundColor: tokens.colors.surfaceVariant,
|
|
23
|
+
width: LAYOUT.CLOSE_BUTTON_SIZE,
|
|
24
|
+
height: LAYOUT.CLOSE_BUTTON_SIZE,
|
|
25
|
+
borderRadius: LAYOUT.CLOSE_BUTTON_SIZE / 2,
|
|
26
|
+
borderWidth: 1,
|
|
27
|
+
borderColor: tokens.colors.border,
|
|
28
|
+
},
|
|
29
|
+
]}
|
|
30
|
+
activeOpacity={0.8}
|
|
31
|
+
accessibilityRole="button"
|
|
32
|
+
accessibilityLabel="Close menu"
|
|
33
|
+
>
|
|
34
|
+
<AtomicIcon name="close" size="md" color="secondary" />
|
|
35
|
+
</TouchableOpacity>
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const styles = StyleSheet.create({
|
|
40
|
+
container: {
|
|
41
|
+
justifyContent: "center",
|
|
42
|
+
alignItems: "center",
|
|
43
|
+
},
|
|
44
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View, StyleSheet, TouchableOpacity } from "react-native";
|
|
3
|
+
import { AtomicIcon } from "../../atoms/AtomicIcon";
|
|
4
|
+
import { AtomicText } from "../../atoms/AtomicText";
|
|
5
|
+
import { useAppDesignTokens } from "../../theme/useAppDesignTokens";
|
|
6
|
+
import { LAYOUT } from "./constants";
|
|
7
|
+
|
|
8
|
+
export interface CircularMenuItemProps {
|
|
9
|
+
icon: string;
|
|
10
|
+
label: string;
|
|
11
|
+
onPress: () => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const CircularMenuItem: React.FC<CircularMenuItemProps> = ({
|
|
15
|
+
icon,
|
|
16
|
+
label,
|
|
17
|
+
onPress,
|
|
18
|
+
}) => {
|
|
19
|
+
const tokens = useAppDesignTokens();
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<TouchableOpacity
|
|
23
|
+
onPress={onPress}
|
|
24
|
+
style={styles.container}
|
|
25
|
+
activeOpacity={0.7}
|
|
26
|
+
accessibilityRole="button"
|
|
27
|
+
accessibilityLabel={label}
|
|
28
|
+
>
|
|
29
|
+
<View
|
|
30
|
+
style={[
|
|
31
|
+
styles.iconContainer,
|
|
32
|
+
{
|
|
33
|
+
backgroundColor: tokens.colors.surfaceVariant,
|
|
34
|
+
width: LAYOUT.ITEM_SIZE,
|
|
35
|
+
height: LAYOUT.ITEM_SIZE,
|
|
36
|
+
borderRadius: LAYOUT.ITEM_SIZE / 2,
|
|
37
|
+
borderWidth: 1,
|
|
38
|
+
borderColor: tokens.colors.border,
|
|
39
|
+
},
|
|
40
|
+
]}
|
|
41
|
+
>
|
|
42
|
+
<AtomicIcon name={icon as any} size="lg" color="primary" />
|
|
43
|
+
</View>
|
|
44
|
+
<AtomicText
|
|
45
|
+
type="labelSmall"
|
|
46
|
+
style={[styles.label, { color: tokens.colors.textPrimary }]}
|
|
47
|
+
>
|
|
48
|
+
{label}
|
|
49
|
+
</AtomicText>
|
|
50
|
+
</TouchableOpacity>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const styles = StyleSheet.create({
|
|
55
|
+
container: {
|
|
56
|
+
alignItems: "center",
|
|
57
|
+
gap: 6,
|
|
58
|
+
width: 90,
|
|
59
|
+
},
|
|
60
|
+
iconContainer: {
|
|
61
|
+
justifyContent: "center",
|
|
62
|
+
alignItems: "center",
|
|
63
|
+
},
|
|
64
|
+
label: {
|
|
65
|
+
fontSize: 11,
|
|
66
|
+
fontWeight: "500",
|
|
67
|
+
textAlign: "center",
|
|
68
|
+
},
|
|
69
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { ViewStyle } from "react-native";
|
|
2
|
+
|
|
3
|
+
export const LAYOUT = {
|
|
4
|
+
ITEM_SIZE: 52,
|
|
5
|
+
CLOSE_BUTTON_SIZE: 48,
|
|
6
|
+
} as const;
|
|
7
|
+
|
|
8
|
+
export const ROW_CONFIG = {
|
|
9
|
+
TOP_RADIUS: 80,
|
|
10
|
+
TOP_START_ANGLE: -135,
|
|
11
|
+
TOP_END_ANGLE: -45,
|
|
12
|
+
BOTTOM_OFFSET_X: 160,
|
|
13
|
+
BOTTOM_Y: 60,
|
|
14
|
+
CENTER_Y: 150,
|
|
15
|
+
} as const;
|
|
16
|
+
|
|
17
|
+
export const ARC_BACKGROUND = {
|
|
18
|
+
WIDTH: 450,
|
|
19
|
+
HEIGHT: 280,
|
|
20
|
+
BORDER_RADIUS_TOP: 225,
|
|
21
|
+
} as const;
|
|
22
|
+
|
|
23
|
+
export const OVERLAY = {
|
|
24
|
+
PADDING_BOTTOM_OFFSET: 0,
|
|
25
|
+
} as const;
|
|
26
|
+
|
|
27
|
+
const CENTER_X = ARC_BACKGROUND.WIDTH / 2;
|
|
28
|
+
|
|
29
|
+
function toRadians(degrees: number): number {
|
|
30
|
+
return (degrees * Math.PI) / 180;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function getTopRowPosition(
|
|
34
|
+
index: number,
|
|
35
|
+
totalInRow: number
|
|
36
|
+
): ViewStyle {
|
|
37
|
+
if (totalInRow <= 1) {
|
|
38
|
+
const radian = toRadians(-90);
|
|
39
|
+
const x = ROW_CONFIG.TOP_RADIUS * Math.cos(radian);
|
|
40
|
+
const y = ROW_CONFIG.TOP_RADIUS * Math.sin(radian);
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
position: "absolute",
|
|
44
|
+
left: CENTER_X + x - LAYOUT.ITEM_SIZE / 2,
|
|
45
|
+
top: ROW_CONFIG.CENTER_Y + y - LAYOUT.ITEM_SIZE / 2,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const arcSpan = ROW_CONFIG.TOP_END_ANGLE - ROW_CONFIG.TOP_START_ANGLE;
|
|
50
|
+
const step = arcSpan / (totalInRow - 1);
|
|
51
|
+
const angle = ROW_CONFIG.TOP_START_ANGLE + step * index;
|
|
52
|
+
const radian = toRadians(angle);
|
|
53
|
+
|
|
54
|
+
const x = ROW_CONFIG.TOP_RADIUS * Math.cos(radian);
|
|
55
|
+
const y = ROW_CONFIG.TOP_RADIUS * Math.sin(radian);
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
position: "absolute",
|
|
59
|
+
left: CENTER_X + x - LAYOUT.ITEM_SIZE / 2,
|
|
60
|
+
top: ROW_CONFIG.CENTER_Y + y - LAYOUT.ITEM_SIZE / 2,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function getBottomRowPosition(side: "left" | "right"): ViewStyle {
|
|
65
|
+
const offsetX =
|
|
66
|
+
side === "left" ? -ROW_CONFIG.BOTTOM_OFFSET_X : ROW_CONFIG.BOTTOM_OFFSET_X;
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
position: "absolute",
|
|
70
|
+
left: CENTER_X + offsetX - LAYOUT.ITEM_SIZE / 2,
|
|
71
|
+
top: ROW_CONFIG.CENTER_Y + ROW_CONFIG.BOTTOM_Y - LAYOUT.ITEM_SIZE / 2,
|
|
72
|
+
};
|
|
73
|
+
}
|
package/src/molecules/index.ts
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { NavigationContainerRef } from "@react-navigation/native";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* NavigationContainer Component
|
|
5
|
+
*
|
|
6
|
+
* Wrapper around React Navigation's NavigationContainerRef
|
|
7
|
+
* Provides navigation support to applications.
|
|
8
|
+
*/
|
|
9
|
+
export const NavigationContainer: React.FC<{
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
}> = ({ children }) => {
|
|
12
|
+
return <NavigationContainerRef>{children}</NavigationContainerRef>;
|
|
13
|
+
};
|
|
@@ -3,6 +3,7 @@ export { createStackNavigator } from "./createStackNavigator";
|
|
|
3
3
|
export { TabsNavigator, type TabsNavigatorProps } from "./TabsNavigator";
|
|
4
4
|
export { StackNavigator, type StackNavigatorProps } from "./StackNavigator";
|
|
5
5
|
export { FabButton, type FabButtonProps } from "./components/FabButton";
|
|
6
|
+
export { NavigationContainer } from "./components/NavigationContainer";
|
|
6
7
|
|
|
7
8
|
export type {
|
|
8
9
|
TabScreen,
|
|
@@ -34,7 +35,7 @@ export type { NavigationCleanup } from "./utils/NavigationCleanup";
|
|
|
34
35
|
// Navigation Utilities
|
|
35
36
|
export { AppNavigation } from "./utils/AppNavigation";
|
|
36
37
|
export { TabLabel, type TabLabelProps } from "./components/TabLabel";
|
|
37
|
-
export * from
|
|
38
|
+
export * from "./components/NavigationHeader";
|
|
38
39
|
export { useTabBarStyles, type TabBarConfig } from "./hooks/useTabBarStyles";
|
|
39
40
|
export { useTabConfig, type UseTabConfigProps } from "./hooks/useTabConfig";
|
|
40
41
|
export { useAppNavigation } from "./hooks/useAppNavigation";
|