@buoy-gg/core 1.7.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/README.md +43 -0
- package/lib/commonjs/floatingMenu/AppHost.js +410 -0
- package/lib/commonjs/floatingMenu/AppHostLogic.js +44 -0
- package/lib/commonjs/floatingMenu/DefaultConfigContext.js +45 -0
- package/lib/commonjs/floatingMenu/DevToolsSettingsModal.js +2274 -0
- package/lib/commonjs/floatingMenu/DevToolsVisibilityContext.js +49 -0
- package/lib/commonjs/floatingMenu/DraggableHeader.js +114 -0
- package/lib/commonjs/floatingMenu/FloatingDevTools.js +254 -0
- package/lib/commonjs/floatingMenu/FloatingMenu.js +364 -0
- package/lib/commonjs/floatingMenu/MinimizedToolsContext.js +247 -0
- package/lib/commonjs/floatingMenu/MinimizedToolsStack.js +206 -0
- package/lib/commonjs/floatingMenu/ToggleStateManager.js +36 -0
- package/lib/commonjs/floatingMenu/autoDiscoverPresets.js +241 -0
- package/lib/commonjs/floatingMenu/defaultConfig.js +160 -0
- package/lib/commonjs/floatingMenu/dial/DialDevTools.js +835 -0
- package/lib/commonjs/floatingMenu/dial/DialIcon.js +246 -0
- package/lib/commonjs/floatingMenu/dial/OnboardingTooltip.js +249 -0
- package/lib/commonjs/floatingMenu/dial/onboardingConstants.js +70 -0
- package/lib/commonjs/floatingMenu/floatingTools.js +771 -0
- package/lib/commonjs/floatingMenu/settingsBus.js +23 -0
- package/lib/commonjs/floatingMenu/types.js +5 -0
- package/lib/commonjs/index.js +240 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/module/floatingMenu/AppHost.js +402 -0
- package/lib/module/floatingMenu/AppHostLogic.js +39 -0
- package/lib/module/floatingMenu/DefaultConfigContext.js +39 -0
- package/lib/module/floatingMenu/DevToolsSettingsModal.js +2273 -0
- package/lib/module/floatingMenu/DevToolsVisibilityContext.js +44 -0
- package/lib/module/floatingMenu/DraggableHeader.js +110 -0
- package/lib/module/floatingMenu/FloatingDevTools.js +249 -0
- package/lib/module/floatingMenu/FloatingMenu.js +358 -0
- package/lib/module/floatingMenu/MinimizedToolsContext.js +239 -0
- package/lib/module/floatingMenu/MinimizedToolsStack.js +202 -0
- package/lib/module/floatingMenu/ToggleStateManager.js +32 -0
- package/lib/module/floatingMenu/autoDiscoverPresets.js +236 -0
- package/lib/module/floatingMenu/defaultConfig.js +151 -0
- package/lib/module/floatingMenu/dial/DialDevTools.js +829 -0
- package/lib/module/floatingMenu/dial/DialIcon.js +241 -0
- package/lib/module/floatingMenu/dial/OnboardingTooltip.js +244 -0
- package/lib/module/floatingMenu/dial/onboardingConstants.js +64 -0
- package/lib/module/floatingMenu/floatingTools.js +767 -0
- package/lib/module/floatingMenu/settingsBus.js +19 -0
- package/lib/module/floatingMenu/types.js +3 -0
- package/lib/module/index.js +29 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/commonjs/floatingMenu/AppHost.d.ts +39 -0
- package/lib/typescript/commonjs/floatingMenu/AppHost.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/AppHostLogic.d.ts +37 -0
- package/lib/typescript/commonjs/floatingMenu/AppHostLogic.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/DefaultConfigContext.d.ts +27 -0
- package/lib/typescript/commonjs/floatingMenu/DefaultConfigContext.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.d.ts +57 -0
- package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/DevToolsVisibilityContext.d.ts +25 -0
- package/lib/typescript/commonjs/floatingMenu/DevToolsVisibilityContext.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/DraggableHeader.d.ts +30 -0
- package/lib/typescript/commonjs/floatingMenu/DraggableHeader.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts +226 -0
- package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/FloatingMenu.d.ts +39 -0
- package/lib/typescript/commonjs/floatingMenu/FloatingMenu.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/MinimizedToolsContext.d.ts +95 -0
- package/lib/typescript/commonjs/floatingMenu/MinimizedToolsContext.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/MinimizedToolsStack.d.ts +10 -0
- package/lib/typescript/commonjs/floatingMenu/MinimizedToolsStack.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/ToggleStateManager.d.ts +21 -0
- package/lib/typescript/commonjs/floatingMenu/ToggleStateManager.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/autoDiscoverPresets.d.ts +75 -0
- package/lib/typescript/commonjs/floatingMenu/autoDiscoverPresets.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts +120 -0
- package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts +35 -0
- package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/dial/DialIcon.d.ts +14 -0
- package/lib/typescript/commonjs/floatingMenu/dial/DialIcon.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/dial/OnboardingTooltip.d.ts +12 -0
- package/lib/typescript/commonjs/floatingMenu/dial/OnboardingTooltip.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.d.ts +30 -0
- package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/floatingTools.d.ts +56 -0
- package/lib/typescript/commonjs/floatingMenu/floatingTools.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/settingsBus.d.ts +10 -0
- package/lib/typescript/commonjs/floatingMenu/settingsBus.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/types.d.ts +56 -0
- package/lib/typescript/commonjs/floatingMenu/types.d.ts.map +1 -0
- package/lib/typescript/commonjs/index.d.ts +18 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/package.json +1 -0
- package/lib/typescript/module/floatingMenu/AppHost.d.ts +39 -0
- package/lib/typescript/module/floatingMenu/AppHost.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/AppHostLogic.d.ts +37 -0
- package/lib/typescript/module/floatingMenu/AppHostLogic.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/DefaultConfigContext.d.ts +27 -0
- package/lib/typescript/module/floatingMenu/DefaultConfigContext.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.d.ts +57 -0
- package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/DevToolsVisibilityContext.d.ts +25 -0
- package/lib/typescript/module/floatingMenu/DevToolsVisibilityContext.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/DraggableHeader.d.ts +30 -0
- package/lib/typescript/module/floatingMenu/DraggableHeader.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts +226 -0
- package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/FloatingMenu.d.ts +39 -0
- package/lib/typescript/module/floatingMenu/FloatingMenu.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/MinimizedToolsContext.d.ts +95 -0
- package/lib/typescript/module/floatingMenu/MinimizedToolsContext.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/MinimizedToolsStack.d.ts +10 -0
- package/lib/typescript/module/floatingMenu/MinimizedToolsStack.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/ToggleStateManager.d.ts +21 -0
- package/lib/typescript/module/floatingMenu/ToggleStateManager.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/autoDiscoverPresets.d.ts +75 -0
- package/lib/typescript/module/floatingMenu/autoDiscoverPresets.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/defaultConfig.d.ts +120 -0
- package/lib/typescript/module/floatingMenu/defaultConfig.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts +35 -0
- package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/dial/DialIcon.d.ts +14 -0
- package/lib/typescript/module/floatingMenu/dial/DialIcon.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/dial/OnboardingTooltip.d.ts +12 -0
- package/lib/typescript/module/floatingMenu/dial/OnboardingTooltip.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/dial/onboardingConstants.d.ts +30 -0
- package/lib/typescript/module/floatingMenu/dial/onboardingConstants.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/floatingTools.d.ts +56 -0
- package/lib/typescript/module/floatingMenu/floatingTools.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/settingsBus.d.ts +10 -0
- package/lib/typescript/module/floatingMenu/settingsBus.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/types.d.ts +56 -0
- package/lib/typescript/module/floatingMenu/types.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +18 -0
- package/lib/typescript/module/index.d.ts.map +1 -0
- package/lib/typescript/module/package.json +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useRef } from "react";
|
|
4
|
+
import { StyleSheet, Pressable, View, Text, Dimensions, Animated } from "react-native";
|
|
5
|
+
import { getDialLayout, getIconPosition, getIconStaggerInputRange, DIAL_START_ANGLE, dialColors, dialStyles, dialAnimationConfig } from "@buoy-gg/floating-tools-core";
|
|
6
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
|
+
const {
|
|
8
|
+
width: SCREEN_WIDTH
|
|
9
|
+
} = Dimensions.get("window");
|
|
10
|
+
|
|
11
|
+
// Use shared layout calculation
|
|
12
|
+
const layout = getDialLayout({
|
|
13
|
+
screenWidth: SCREEN_WIDTH
|
|
14
|
+
});
|
|
15
|
+
const VIEW_SIZE = layout.iconSize;
|
|
16
|
+
const CIRCLE_SIZE = layout.circleSize;
|
|
17
|
+
const CIRCLE_RADIUS = layout.circleRadius;
|
|
18
|
+
export const DialIcon = ({
|
|
19
|
+
index,
|
|
20
|
+
icon,
|
|
21
|
+
iconsProgress,
|
|
22
|
+
onPress,
|
|
23
|
+
selectedIcon,
|
|
24
|
+
totalIcons
|
|
25
|
+
}) => {
|
|
26
|
+
// Use shared position calculation from core
|
|
27
|
+
const iconPosition = getIconPosition(index, totalIcons, layout.iconRadius, DIAL_START_ANGLE);
|
|
28
|
+
const {
|
|
29
|
+
x: finalX,
|
|
30
|
+
y: finalY,
|
|
31
|
+
angle
|
|
32
|
+
} = iconPosition;
|
|
33
|
+
const radius = layout.iconRadius;
|
|
34
|
+
|
|
35
|
+
// Animation values - using interpolation for better performance
|
|
36
|
+
const scale = useRef(new Animated.Value(1)).current;
|
|
37
|
+
|
|
38
|
+
// Hover animation on press in/out - using shared config
|
|
39
|
+
// Fallback values in case dialAnimationConfig hasn't loaded yet
|
|
40
|
+
const iconAnimConfig = dialAnimationConfig?.icons ?? {
|
|
41
|
+
pressIn: {
|
|
42
|
+
scale: 0.95,
|
|
43
|
+
damping: 15,
|
|
44
|
+
stiffness: 400
|
|
45
|
+
},
|
|
46
|
+
pressOut: {
|
|
47
|
+
scale: 1,
|
|
48
|
+
damping: 15,
|
|
49
|
+
stiffness: 400
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const {
|
|
53
|
+
pressIn,
|
|
54
|
+
pressOut
|
|
55
|
+
} = iconAnimConfig;
|
|
56
|
+
const handlePressIn = () => {
|
|
57
|
+
Animated.spring(scale, {
|
|
58
|
+
toValue: pressIn.scale,
|
|
59
|
+
damping: pressIn.damping,
|
|
60
|
+
stiffness: pressIn.stiffness,
|
|
61
|
+
useNativeDriver: true
|
|
62
|
+
}).start();
|
|
63
|
+
};
|
|
64
|
+
const handlePressOut = () => {
|
|
65
|
+
Animated.spring(scale, {
|
|
66
|
+
toValue: pressOut.scale,
|
|
67
|
+
damping: pressOut.damping,
|
|
68
|
+
stiffness: pressOut.stiffness,
|
|
69
|
+
useNativeDriver: true
|
|
70
|
+
}).start();
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Use shared stagger calculation from core
|
|
74
|
+
const staggerInputRange = getIconStaggerInputRange(index, totalIcons);
|
|
75
|
+
|
|
76
|
+
// Use interpolation for smooth animation that works both directions
|
|
77
|
+
const staggeredProgress = iconsProgress.interpolate({
|
|
78
|
+
inputRange: staggerInputRange,
|
|
79
|
+
outputRange: [0, 0, 1, 1],
|
|
80
|
+
extrapolate: "clamp"
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Spiral animation with interpolation
|
|
84
|
+
const spiralRotation = staggeredProgress.interpolate({
|
|
85
|
+
inputRange: [0, 1],
|
|
86
|
+
outputRange: [Math.PI * 2, 0] // Spiral from 2π to 0
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Distance from center
|
|
90
|
+
const distance = staggeredProgress.interpolate({
|
|
91
|
+
inputRange: [0, 1],
|
|
92
|
+
outputRange: [0, radius]
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Calculate X and Y positions using Animated operations
|
|
96
|
+
const translateX = Animated.add(Animated.multiply(distance, spiralRotation.interpolate({
|
|
97
|
+
inputRange: [0, Math.PI * 2],
|
|
98
|
+
outputRange: [Math.cos(angle), Math.cos(angle + Math.PI * 2)]
|
|
99
|
+
})), staggeredProgress.interpolate({
|
|
100
|
+
inputRange: [0, 1],
|
|
101
|
+
outputRange: [0, finalX - radius * Math.cos(angle + Math.PI * 2)]
|
|
102
|
+
}));
|
|
103
|
+
const translateY = Animated.add(Animated.multiply(distance, spiralRotation.interpolate({
|
|
104
|
+
inputRange: [0, Math.PI * 2],
|
|
105
|
+
outputRange: [Math.sin(angle), Math.sin(angle + Math.PI * 2)]
|
|
106
|
+
})), staggeredProgress.interpolate({
|
|
107
|
+
inputRange: [0, 1],
|
|
108
|
+
outputRange: [0, finalY - radius * Math.sin(angle + Math.PI * 2)]
|
|
109
|
+
}));
|
|
110
|
+
|
|
111
|
+
// Opacity animation
|
|
112
|
+
const itemOpacity = staggeredProgress.interpolate({
|
|
113
|
+
inputRange: [0, 0.3, 1],
|
|
114
|
+
outputRange: [0, 0.3, 1]
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Scale based on progress
|
|
118
|
+
const progressScale = staggeredProgress;
|
|
119
|
+
|
|
120
|
+
// Main animated style for position and appearance
|
|
121
|
+
const animatedStyle = {
|
|
122
|
+
position: "absolute",
|
|
123
|
+
left: CIRCLE_RADIUS - VIEW_SIZE / 2,
|
|
124
|
+
// Center position
|
|
125
|
+
top: CIRCLE_RADIUS - VIEW_SIZE / 2,
|
|
126
|
+
// Center position
|
|
127
|
+
opacity: itemOpacity,
|
|
128
|
+
transform: [{
|
|
129
|
+
translateX
|
|
130
|
+
},
|
|
131
|
+
// Apply translation from center
|
|
132
|
+
{
|
|
133
|
+
translateY
|
|
134
|
+
},
|
|
135
|
+
// Apply translation from center
|
|
136
|
+
{
|
|
137
|
+
scale: Animated.multiply(scale, progressScale)
|
|
138
|
+
}]
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// Check if this is an empty spot (no icon and no iconComponent)
|
|
142
|
+
const isEmpty = icon.icon === null && !icon.iconComponent;
|
|
143
|
+
return /*#__PURE__*/_jsx(Animated.View, {
|
|
144
|
+
style: [styles.view, animatedStyle],
|
|
145
|
+
children: isEmpty ?
|
|
146
|
+
/*#__PURE__*/
|
|
147
|
+
// Empty spot - just show a subtle circle
|
|
148
|
+
_jsx(View, {
|
|
149
|
+
style: styles.emptySpot,
|
|
150
|
+
children: /*#__PURE__*/_jsx(View, {
|
|
151
|
+
style: styles.emptyDot
|
|
152
|
+
})
|
|
153
|
+
}) : /*#__PURE__*/_jsxs(Pressable, {
|
|
154
|
+
onPress: () => onPress(index),
|
|
155
|
+
onPressIn: handlePressIn,
|
|
156
|
+
onPressOut: handlePressOut,
|
|
157
|
+
style: styles.pressable,
|
|
158
|
+
children: [/*#__PURE__*/_jsx(View, {
|
|
159
|
+
style: [styles.iconGradientBg, {
|
|
160
|
+
backgroundColor: "rgba(0, 0, 0, 0.2)"
|
|
161
|
+
}]
|
|
162
|
+
}), /*#__PURE__*/_jsx(View, {
|
|
163
|
+
style: [styles.iconInnerGlow, {
|
|
164
|
+
backgroundColor: "rgba(255, 255, 255, 0.02)"
|
|
165
|
+
}]
|
|
166
|
+
}), /*#__PURE__*/_jsx(View, {
|
|
167
|
+
style: styles.iconWrapper,
|
|
168
|
+
children: icon.iconComponent ? (() => {
|
|
169
|
+
const IconComp = icon.iconComponent;
|
|
170
|
+
return /*#__PURE__*/_jsx(IconComp, {
|
|
171
|
+
slot: "dial",
|
|
172
|
+
size: 32
|
|
173
|
+
});
|
|
174
|
+
})() : icon.icon
|
|
175
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
176
|
+
style: styles.label,
|
|
177
|
+
numberOfLines: 1,
|
|
178
|
+
adjustsFontSizeToFit: true,
|
|
179
|
+
minimumFontScale: 0.7,
|
|
180
|
+
children: icon.name.toUpperCase()
|
|
181
|
+
})]
|
|
182
|
+
})
|
|
183
|
+
});
|
|
184
|
+
};
|
|
185
|
+
const styles = StyleSheet.create({
|
|
186
|
+
view: {
|
|
187
|
+
width: VIEW_SIZE,
|
|
188
|
+
height: VIEW_SIZE,
|
|
189
|
+
justifyContent: "center",
|
|
190
|
+
alignItems: "center"
|
|
191
|
+
},
|
|
192
|
+
pressable: {
|
|
193
|
+
width: "100%",
|
|
194
|
+
height: "100%",
|
|
195
|
+
justifyContent: "center",
|
|
196
|
+
alignItems: "center",
|
|
197
|
+
padding: 4,
|
|
198
|
+
backgroundColor: "transparent"
|
|
199
|
+
},
|
|
200
|
+
iconGradientBg: {
|
|
201
|
+
position: "absolute",
|
|
202
|
+
width: "85%",
|
|
203
|
+
height: "85%",
|
|
204
|
+
borderRadius: 12,
|
|
205
|
+
opacity: 0.3
|
|
206
|
+
},
|
|
207
|
+
iconInnerGlow: {
|
|
208
|
+
position: "absolute",
|
|
209
|
+
width: "70%",
|
|
210
|
+
height: "70%",
|
|
211
|
+
borderRadius: 10,
|
|
212
|
+
opacity: 0.5
|
|
213
|
+
},
|
|
214
|
+
iconWrapper: {
|
|
215
|
+
marginBottom: 4,
|
|
216
|
+
alignItems: "center",
|
|
217
|
+
justifyContent: "center"
|
|
218
|
+
},
|
|
219
|
+
label: {
|
|
220
|
+
fontSize: dialStyles.icon.label.fontSize,
|
|
221
|
+
fontWeight: dialStyles.icon.label.fontWeight,
|
|
222
|
+
letterSpacing: dialStyles.icon.label.letterSpacing,
|
|
223
|
+
fontFamily: dialStyles.icon.label.fontFamily,
|
|
224
|
+
marginTop: dialStyles.icon.label.marginTop,
|
|
225
|
+
color: dialColors.iconLabel
|
|
226
|
+
},
|
|
227
|
+
emptySpot: {
|
|
228
|
+
width: "100%",
|
|
229
|
+
height: "100%",
|
|
230
|
+
justifyContent: "center",
|
|
231
|
+
alignItems: "center"
|
|
232
|
+
},
|
|
233
|
+
emptyDot: {
|
|
234
|
+
width: dialStyles.emptySlot.dotSize,
|
|
235
|
+
height: dialStyles.emptySlot.dotSize,
|
|
236
|
+
borderRadius: dialStyles.emptySlot.dotSize / 2,
|
|
237
|
+
backgroundColor: dialColors.emptyDotBackground,
|
|
238
|
+
borderWidth: dialStyles.emptySlot.borderWidth,
|
|
239
|
+
borderColor: dialColors.emptyDotBorder
|
|
240
|
+
}
|
|
241
|
+
});
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
|
+
import { Animated, Pressable, StyleSheet, Text, View, Easing } from "react-native";
|
|
5
|
+
import { dialColors, buoyColors } from "@buoy-gg/shared-ui";
|
|
6
|
+
import { calculateTooltipPosition, ARROW_BOTTOM_OFFSET, ARROW_HEIGHT } from "./onboardingConstants.js";
|
|
7
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
|
+
export const OnboardingTooltip = ({
|
|
9
|
+
visible,
|
|
10
|
+
onDismiss,
|
|
11
|
+
title = "Welcome to Buoy Tools!",
|
|
12
|
+
message = "Tap the center button to configure and add developer tools to your dial menu."
|
|
13
|
+
}) => {
|
|
14
|
+
const fadeAnim = useRef(new Animated.Value(0)).current;
|
|
15
|
+
const scaleAnim = useRef(new Animated.Value(0.8)).current;
|
|
16
|
+
const pulseAnim = useRef(new Animated.Value(1)).current;
|
|
17
|
+
const arrowBounceAnim = useRef(new Animated.Value(0)).current;
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (visible) {
|
|
20
|
+
// Entrance animation
|
|
21
|
+
Animated.parallel([Animated.timing(fadeAnim, {
|
|
22
|
+
toValue: 1,
|
|
23
|
+
duration: 300,
|
|
24
|
+
easing: Easing.out(Easing.ease),
|
|
25
|
+
useNativeDriver: true
|
|
26
|
+
}), Animated.spring(scaleAnim, {
|
|
27
|
+
toValue: 1,
|
|
28
|
+
damping: 12,
|
|
29
|
+
stiffness: 150,
|
|
30
|
+
useNativeDriver: true
|
|
31
|
+
})]).start();
|
|
32
|
+
|
|
33
|
+
// Continuous pulse animation for the tooltip
|
|
34
|
+
Animated.loop(Animated.sequence([Animated.timing(pulseAnim, {
|
|
35
|
+
toValue: 1.02,
|
|
36
|
+
duration: 1500,
|
|
37
|
+
easing: Easing.inOut(Easing.ease),
|
|
38
|
+
useNativeDriver: true
|
|
39
|
+
}), Animated.timing(pulseAnim, {
|
|
40
|
+
toValue: 1,
|
|
41
|
+
duration: 1500,
|
|
42
|
+
easing: Easing.inOut(Easing.ease),
|
|
43
|
+
useNativeDriver: true
|
|
44
|
+
})])).start();
|
|
45
|
+
|
|
46
|
+
// Bouncing arrow animation
|
|
47
|
+
Animated.loop(Animated.sequence([Animated.timing(arrowBounceAnim, {
|
|
48
|
+
toValue: -8,
|
|
49
|
+
duration: 800,
|
|
50
|
+
easing: Easing.inOut(Easing.ease),
|
|
51
|
+
useNativeDriver: true
|
|
52
|
+
}), Animated.timing(arrowBounceAnim, {
|
|
53
|
+
toValue: 0,
|
|
54
|
+
duration: 800,
|
|
55
|
+
easing: Easing.inOut(Easing.ease),
|
|
56
|
+
useNativeDriver: true
|
|
57
|
+
})])).start();
|
|
58
|
+
} else {
|
|
59
|
+
// Exit animation
|
|
60
|
+
Animated.parallel([Animated.timing(fadeAnim, {
|
|
61
|
+
toValue: 0,
|
|
62
|
+
duration: 200,
|
|
63
|
+
easing: Easing.in(Easing.ease),
|
|
64
|
+
useNativeDriver: true
|
|
65
|
+
}), Animated.timing(scaleAnim, {
|
|
66
|
+
toValue: 0.8,
|
|
67
|
+
duration: 200,
|
|
68
|
+
easing: Easing.in(Easing.ease),
|
|
69
|
+
useNativeDriver: true
|
|
70
|
+
})]).start();
|
|
71
|
+
}
|
|
72
|
+
}, [visible]);
|
|
73
|
+
if (!visible) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
return /*#__PURE__*/_jsx(Animated.View, {
|
|
77
|
+
style: [styles.container, {
|
|
78
|
+
opacity: fadeAnim,
|
|
79
|
+
transform: [{
|
|
80
|
+
scale: scaleAnim
|
|
81
|
+
}]
|
|
82
|
+
}],
|
|
83
|
+
pointerEvents: "box-none",
|
|
84
|
+
children: /*#__PURE__*/_jsx(Animated.View, {
|
|
85
|
+
style: [styles.tooltipContainer, {
|
|
86
|
+
transform: [{
|
|
87
|
+
scale: pulseAnim
|
|
88
|
+
}]
|
|
89
|
+
}],
|
|
90
|
+
children: /*#__PURE__*/_jsxs(View, {
|
|
91
|
+
style: styles.tooltip,
|
|
92
|
+
children: [/*#__PURE__*/_jsx(View, {
|
|
93
|
+
style: styles.glowEffect
|
|
94
|
+
}), /*#__PURE__*/_jsxs(View, {
|
|
95
|
+
style: styles.content,
|
|
96
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
97
|
+
style: styles.title,
|
|
98
|
+
children: title
|
|
99
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
100
|
+
style: styles.message,
|
|
101
|
+
children: message
|
|
102
|
+
}), /*#__PURE__*/_jsx(Pressable, {
|
|
103
|
+
onPress: onDismiss,
|
|
104
|
+
style: ({
|
|
105
|
+
pressed
|
|
106
|
+
}) => [styles.button, pressed && styles.buttonPressed],
|
|
107
|
+
children: /*#__PURE__*/_jsx(View, {
|
|
108
|
+
style: styles.buttonGradient,
|
|
109
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
110
|
+
style: styles.buttonText,
|
|
111
|
+
children: "Got it!"
|
|
112
|
+
})
|
|
113
|
+
})
|
|
114
|
+
})]
|
|
115
|
+
}), /*#__PURE__*/_jsx(Animated.View, {
|
|
116
|
+
style: [styles.arrowContainer, {
|
|
117
|
+
transform: [{
|
|
118
|
+
translateY: arrowBounceAnim
|
|
119
|
+
}]
|
|
120
|
+
}],
|
|
121
|
+
children: /*#__PURE__*/_jsx(View, {
|
|
122
|
+
style: styles.arrow
|
|
123
|
+
})
|
|
124
|
+
})]
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
const styles = StyleSheet.create({
|
|
130
|
+
container: {
|
|
131
|
+
...StyleSheet.absoluteFillObject,
|
|
132
|
+
alignItems: "center",
|
|
133
|
+
justifyContent: "center",
|
|
134
|
+
pointerEvents: "box-none"
|
|
135
|
+
},
|
|
136
|
+
tooltipContainer: {
|
|
137
|
+
position: "absolute",
|
|
138
|
+
bottom: calculateTooltipPosition(),
|
|
139
|
+
alignItems: "center",
|
|
140
|
+
maxWidth: 280
|
|
141
|
+
},
|
|
142
|
+
tooltip: {
|
|
143
|
+
backgroundColor: dialColors.dialBackground,
|
|
144
|
+
borderRadius: 16,
|
|
145
|
+
borderWidth: 2,
|
|
146
|
+
borderColor: dialColors.dialBorder,
|
|
147
|
+
padding: 20,
|
|
148
|
+
shadowColor: buoyColors.primary,
|
|
149
|
+
shadowOffset: {
|
|
150
|
+
width: 0,
|
|
151
|
+
height: 0
|
|
152
|
+
},
|
|
153
|
+
shadowOpacity: 0.6,
|
|
154
|
+
shadowRadius: 20,
|
|
155
|
+
elevation: 10
|
|
156
|
+
},
|
|
157
|
+
glowEffect: {
|
|
158
|
+
position: "absolute",
|
|
159
|
+
top: -4,
|
|
160
|
+
left: -4,
|
|
161
|
+
right: -4,
|
|
162
|
+
bottom: -4,
|
|
163
|
+
borderRadius: 18,
|
|
164
|
+
backgroundColor: "#FFFFFF",
|
|
165
|
+
opacity: 0.15
|
|
166
|
+
},
|
|
167
|
+
content: {
|
|
168
|
+
alignItems: "center",
|
|
169
|
+
gap: 12
|
|
170
|
+
},
|
|
171
|
+
title: {
|
|
172
|
+
color: buoyColors.primary,
|
|
173
|
+
fontSize: 16,
|
|
174
|
+
fontWeight: "900",
|
|
175
|
+
fontFamily: "monospace",
|
|
176
|
+
letterSpacing: 1,
|
|
177
|
+
textAlign: "center",
|
|
178
|
+
textTransform: "uppercase",
|
|
179
|
+
textShadowColor: buoyColors.primary,
|
|
180
|
+
textShadowOffset: {
|
|
181
|
+
width: 0,
|
|
182
|
+
height: 0
|
|
183
|
+
},
|
|
184
|
+
textShadowRadius: 4
|
|
185
|
+
},
|
|
186
|
+
message: {
|
|
187
|
+
color: "#FFFFFF",
|
|
188
|
+
fontSize: 13,
|
|
189
|
+
lineHeight: 20,
|
|
190
|
+
textAlign: "center",
|
|
191
|
+
fontFamily: "monospace"
|
|
192
|
+
},
|
|
193
|
+
button: {
|
|
194
|
+
marginTop: 8,
|
|
195
|
+
borderRadius: 8,
|
|
196
|
+
overflow: "hidden",
|
|
197
|
+
borderWidth: 2,
|
|
198
|
+
borderColor: "#FFFFFF"
|
|
199
|
+
},
|
|
200
|
+
buttonPressed: {
|
|
201
|
+
opacity: 0.7,
|
|
202
|
+
transform: [{
|
|
203
|
+
scale: 0.96
|
|
204
|
+
}]
|
|
205
|
+
},
|
|
206
|
+
buttonGradient: {
|
|
207
|
+
backgroundColor: "rgba(255, 255, 255, 0.1)",
|
|
208
|
+
paddingHorizontal: 24,
|
|
209
|
+
paddingVertical: 10,
|
|
210
|
+
alignItems: "center"
|
|
211
|
+
},
|
|
212
|
+
buttonText: {
|
|
213
|
+
color: "#FFFFFF",
|
|
214
|
+
fontSize: 14,
|
|
215
|
+
fontWeight: "900",
|
|
216
|
+
fontFamily: "monospace",
|
|
217
|
+
letterSpacing: 1.5,
|
|
218
|
+
textTransform: "uppercase"
|
|
219
|
+
},
|
|
220
|
+
arrowContainer: {
|
|
221
|
+
position: "absolute",
|
|
222
|
+
bottom: ARROW_BOTTOM_OFFSET,
|
|
223
|
+
left: 0,
|
|
224
|
+
right: 0,
|
|
225
|
+
alignItems: "center"
|
|
226
|
+
},
|
|
227
|
+
arrow: {
|
|
228
|
+
width: 0,
|
|
229
|
+
height: 0,
|
|
230
|
+
borderLeftWidth: 12,
|
|
231
|
+
borderRightWidth: 12,
|
|
232
|
+
borderTopWidth: ARROW_HEIGHT,
|
|
233
|
+
borderLeftColor: "transparent",
|
|
234
|
+
borderRightColor: "transparent",
|
|
235
|
+
borderTopColor: buoyColors.primary,
|
|
236
|
+
shadowColor: buoyColors.primary,
|
|
237
|
+
shadowOffset: {
|
|
238
|
+
width: 0,
|
|
239
|
+
height: 0
|
|
240
|
+
},
|
|
241
|
+
shadowOpacity: 0.8,
|
|
242
|
+
shadowRadius: 10
|
|
243
|
+
}
|
|
244
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { Dimensions } from "react-native";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Shared constants for onboarding tooltip positioning
|
|
7
|
+
* These ensure the floating menu aligns perfectly under the tooltip arrow
|
|
8
|
+
* All positions are calculated dynamically based on screen size
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// Arrow dimensions (relative positioning)
|
|
12
|
+
export const ARROW_BOTTOM_OFFSET = -40; // Arrow extends below tooltip
|
|
13
|
+
export const ARROW_HEIGHT = 20; // borderTopWidth of arrow
|
|
14
|
+
export const SPACING_GAP = 10; // Gap between arrow tip and target element
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Calculate the center Y position of the screen
|
|
18
|
+
* This is used to vertically center the onboarding UI
|
|
19
|
+
*
|
|
20
|
+
* @returns Y coordinate at the center of the screen
|
|
21
|
+
*/
|
|
22
|
+
export function getScreenCenterY() {
|
|
23
|
+
const {
|
|
24
|
+
height: screenHeight
|
|
25
|
+
} = Dimensions.get("window");
|
|
26
|
+
return screenHeight / 2;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Calculate the tooltip position to center it vertically on screen
|
|
31
|
+
* Uses flexbox-style centering - tooltip will center itself with its actual size
|
|
32
|
+
*
|
|
33
|
+
* @returns Distance from screen bottom for tooltip positioning
|
|
34
|
+
*/
|
|
35
|
+
export function calculateTooltipPosition() {
|
|
36
|
+
// Position from center - tooltip will be centered with transform or flex
|
|
37
|
+
// We return a value that's slightly above center to account for the arrow below
|
|
38
|
+
const centerY = getScreenCenterY();
|
|
39
|
+
|
|
40
|
+
// Approximate offset to center the tooltip+arrow combo
|
|
41
|
+
// This is a reasonable estimate that works for most tooltip sizes
|
|
42
|
+
const estimatedHalfHeight = 130; // ~half of (tooltip + arrow)
|
|
43
|
+
|
|
44
|
+
return centerY - estimatedHalfHeight;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Calculate the position where a target element should be placed
|
|
49
|
+
* to align directly under the tooltip arrow
|
|
50
|
+
*
|
|
51
|
+
* @returns Distance from screen bottom where element should be positioned
|
|
52
|
+
*/
|
|
53
|
+
export function calculateTargetPosition() {
|
|
54
|
+
const tooltipBottom = calculateTooltipPosition();
|
|
55
|
+
|
|
56
|
+
// Arrow tip position = tooltip bottom + arrow offset (arrow extends below, so negative)
|
|
57
|
+
const arrowTipFromBottom = tooltipBottom + ARROW_BOTTOM_OFFSET;
|
|
58
|
+
|
|
59
|
+
// Subtract arrow height to get to the actual tip
|
|
60
|
+
const actualArrowTip = arrowTipFromBottom - ARROW_HEIGHT;
|
|
61
|
+
|
|
62
|
+
// Subtract spacing gap to position element below arrow
|
|
63
|
+
return actualArrowTip - SPACING_GAP;
|
|
64
|
+
}
|