related-ui-components 1.8.5 → 1.8.7
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/lib/commonjs/app.js +62 -120
- package/lib/commonjs/app.js.map +1 -1
- package/lib/commonjs/components/Wheel/Wheel.js +193 -178
- package/lib/commonjs/components/Wheel/Wheel.js.map +1 -1
- package/lib/commonjs/index.js +5 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/app.js +63 -121
- package/lib/module/app.js.map +1 -1
- package/lib/module/components/Wheel/Wheel.js +194 -180
- package/lib/module/components/Wheel/Wheel.js.map +1 -1
- package/lib/module/index.js +4 -7
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/commonjs/app.d.ts +2 -2
- package/lib/typescript/commonjs/app.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/Wheel/Wheel.d.ts +3 -1
- package/lib/typescript/commonjs/components/Wheel/Wheel.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +1 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/module/app.d.ts +2 -2
- package/lib/typescript/module/app.d.ts.map +1 -1
- package/lib/typescript/module/components/Wheel/Wheel.d.ts +3 -1
- package/lib/typescript/module/components/Wheel/Wheel.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +1 -0
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/app.tsx +35 -99
- package/src/components/Wheel/Wheel.tsx +233 -220
- package/src/index.ts +4 -4
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
useState,
|
|
3
|
-
useRef,
|
|
4
|
-
useEffect,
|
|
5
|
-
useMemo, // Added useMemo for wheelPaths
|
|
6
|
-
} from "react";
|
|
1
|
+
import React, { useState, useRef, useEffect } from "react";
|
|
7
2
|
import {
|
|
8
3
|
View,
|
|
9
4
|
StyleSheet,
|
|
@@ -20,46 +15,57 @@ export interface SpinWheelItem {
|
|
|
20
15
|
id: string | number;
|
|
21
16
|
label: string;
|
|
22
17
|
value?: any;
|
|
23
|
-
color: string;
|
|
18
|
+
color: string;
|
|
24
19
|
textColor?: string;
|
|
25
20
|
}
|
|
26
21
|
|
|
27
|
-
//
|
|
28
|
-
const
|
|
29
|
-
"#FF0000",
|
|
30
|
-
"#FFA500",
|
|
31
|
-
"#FFFF00",
|
|
32
|
-
"#008000",
|
|
33
|
-
"#0000FF",
|
|
34
|
-
"#800080",
|
|
35
|
-
"#FFC0CB",
|
|
36
|
-
"#00FFFF",
|
|
37
|
-
"#FF00FF",
|
|
38
|
-
"#00FF00",
|
|
39
|
-
"#4B0082",
|
|
40
|
-
"#EE82EE",
|
|
41
|
-
"#40E0D0",
|
|
42
|
-
"#FFD700",
|
|
43
|
-
"#C0C0C0",
|
|
44
|
-
"#FFDAB9",
|
|
45
|
-
"#E6E6FA",
|
|
46
|
-
"#008080",
|
|
47
|
-
"#FF7F50",
|
|
48
|
-
"#DC143C",
|
|
49
|
-
"#87CEEB",
|
|
50
|
-
"#7FFF00",
|
|
51
|
-
"#CCCCFF",
|
|
52
|
-
"#FF6347",
|
|
53
|
-
"#FA8072",
|
|
22
|
+
//default random colors
|
|
23
|
+
const colors = [
|
|
24
|
+
"#FF0000", // Red
|
|
25
|
+
"#FFA500", // Orange
|
|
26
|
+
"#FFFF00", // Yellow
|
|
27
|
+
"#008000", // Green
|
|
28
|
+
"#0000FF", // Blue
|
|
29
|
+
"#800080", // Purple
|
|
30
|
+
"#FFC0CB", // Pink
|
|
31
|
+
"#00FFFF", // Cyan
|
|
32
|
+
"#FF00FF", // Magenta
|
|
33
|
+
"#00FF00", // Lime
|
|
34
|
+
"#4B0082", // Indigo
|
|
35
|
+
"#EE82EE", // Violet
|
|
36
|
+
"#40E0D0", // Turquoise
|
|
37
|
+
"#FFD700", // Gold
|
|
38
|
+
"#C0C0C0", // Silver
|
|
39
|
+
"#FFDAB9", // Peach
|
|
40
|
+
"#E6E6FA", // Lavender
|
|
41
|
+
"#008080", // Teal
|
|
42
|
+
"#FF7F50", // Coral
|
|
43
|
+
"#DC143C", // Crimson
|
|
44
|
+
"#87CEEB", // Sky Blue
|
|
45
|
+
"#7FFF00", // Chartreuse
|
|
46
|
+
"#CCCCFF", // Periwinkle
|
|
47
|
+
"#FF6347", // Tomato
|
|
48
|
+
"#FA8072", // Salmon
|
|
54
49
|
];
|
|
55
50
|
|
|
56
51
|
interface SpinWheelProps {
|
|
52
|
+
// Data
|
|
57
53
|
items: SpinWheelItem[];
|
|
54
|
+
predeterminedWinner?: SpinWheelItem | string | number; // New prop
|
|
55
|
+
|
|
56
|
+
// Dimensions
|
|
58
57
|
size?: number;
|
|
58
|
+
|
|
59
|
+
// Behavior
|
|
59
60
|
spinDuration?: number;
|
|
61
|
+
friction?: number; // Note: friction prop is declared but not used in original logic
|
|
60
62
|
enabled?: boolean;
|
|
63
|
+
|
|
64
|
+
// Events
|
|
61
65
|
onSpinStart?: () => void;
|
|
62
66
|
onSpinEnd?: (item: SpinWheelItem) => void;
|
|
67
|
+
|
|
68
|
+
// Styling
|
|
63
69
|
containerStyle?: ViewStyle;
|
|
64
70
|
centerStyle?: ViewStyle;
|
|
65
71
|
spinButtonText?: string;
|
|
@@ -67,14 +73,19 @@ interface SpinWheelProps {
|
|
|
67
73
|
knobStyle?: ViewStyle;
|
|
68
74
|
actionButtonStyle?: ViewStyle;
|
|
69
75
|
actionButtonTextStyle?: TextStyle;
|
|
76
|
+
|
|
77
|
+
// Custom colors
|
|
78
|
+
wheelBorderColor?: string;
|
|
70
79
|
wheelTextColor?: string;
|
|
71
80
|
knobColor?: string;
|
|
72
|
-
|
|
73
|
-
|
|
81
|
+
|
|
82
|
+
// Custom components
|
|
83
|
+
centerComponent?: React.ReactNode; // Note: centerComponent prop is declared but not used
|
|
74
84
|
}
|
|
75
85
|
|
|
76
86
|
const SpinWheel: React.FC<SpinWheelProps> = ({
|
|
77
87
|
items,
|
|
88
|
+
predeterminedWinner, // Destructure new prop
|
|
78
89
|
size = 300,
|
|
79
90
|
spinDuration = 5000,
|
|
80
91
|
enabled = true,
|
|
@@ -88,189 +99,167 @@ const SpinWheel: React.FC<SpinWheelProps> = ({
|
|
|
88
99
|
knobColor = "#D81E5B",
|
|
89
100
|
actionButtonStyle,
|
|
90
101
|
actionButtonTextStyle,
|
|
102
|
+
wheelBorderColor,
|
|
91
103
|
wheelTextColor = "#FFFFFF",
|
|
92
|
-
centerComponent,
|
|
93
|
-
predeterminedWinnerId = null, // Default to null
|
|
94
104
|
}) => {
|
|
95
|
-
const wheelItems =
|
|
96
|
-
() => (items && items.length > 0 ? items : []),
|
|
97
|
-
[items]
|
|
98
|
-
);
|
|
105
|
+
const wheelItems = items.length > 0 ? items : [];
|
|
99
106
|
|
|
100
107
|
const [spinning, setSpinning] = useState(false);
|
|
101
|
-
const [
|
|
108
|
+
const [_, setWinner] = useState<SpinWheelItem | null>(null);
|
|
102
109
|
const rotateValue = useRef(new Animated.Value(0)).current;
|
|
103
|
-
const rotationRef = useRef(0); // Tracks cumulative rotation in "number of turns"
|
|
104
110
|
|
|
111
|
+
// Track rotation manually for calculations
|
|
112
|
+
const rotationRef = useRef(0);
|
|
113
|
+
|
|
114
|
+
// Update tracked rotation when animation completes
|
|
105
115
|
useEffect(() => {
|
|
106
|
-
const
|
|
116
|
+
const listener = rotateValue.addListener(({ value }) => {
|
|
107
117
|
rotationRef.current = value;
|
|
108
118
|
});
|
|
119
|
+
|
|
109
120
|
return () => {
|
|
110
|
-
rotateValue.removeListener(
|
|
121
|
+
rotateValue.removeListener(listener);
|
|
111
122
|
};
|
|
112
123
|
}, [rotateValue]);
|
|
113
124
|
|
|
125
|
+
// Calculate angle for each segment
|
|
114
126
|
const anglePerItem =
|
|
115
127
|
wheelItems.length > 0 ? 360 / wheelItems.length : 0;
|
|
116
128
|
|
|
117
|
-
|
|
129
|
+
// Create wheel segments
|
|
130
|
+
const generateWheelPaths = () => {
|
|
118
131
|
if (wheelItems.length === 0) return [];
|
|
119
132
|
return wheelItems.map((item, index) => {
|
|
120
133
|
const startAngle = index * anglePerItem;
|
|
121
134
|
const endAngle = (index + 1) * anglePerItem;
|
|
135
|
+
|
|
122
136
|
const startRad = (startAngle * Math.PI) / 180;
|
|
123
137
|
const endRad = (endAngle * Math.PI) / 180;
|
|
138
|
+
|
|
124
139
|
const x1 = size / 2 + (size / 2) * Math.cos(startRad);
|
|
125
140
|
const y1 = size / 2 + (size / 2) * Math.sin(startRad);
|
|
126
141
|
const x2 = size / 2 + (size / 2) * Math.cos(endRad);
|
|
127
142
|
const y2 = size / 2 + (size / 2) * Math.sin(endRad);
|
|
128
|
-
|
|
143
|
+
|
|
144
|
+
const largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";
|
|
145
|
+
|
|
129
146
|
const pathData = [
|
|
130
147
|
`M ${size / 2} ${size / 2}`,
|
|
131
148
|
`L ${x1} ${y1}`,
|
|
132
149
|
`A ${size / 2} ${size / 2} 0 ${largeArcFlag} 1 ${x2} ${y2}`,
|
|
133
150
|
"Z",
|
|
134
151
|
].join(" ");
|
|
135
|
-
|
|
136
|
-
const midRad = (
|
|
152
|
+
|
|
153
|
+
const midRad = ((startAngle + endAngle) / 2) * (Math.PI / 180);
|
|
137
154
|
const textX = size / 2 + size * 0.32 * Math.cos(midRad);
|
|
138
155
|
const textY = size / 2 + size * 0.32 * Math.sin(midRad);
|
|
156
|
+
|
|
157
|
+
// decorationX and decorationY are calculated but not used in the provided JSX
|
|
158
|
+
// const decorationX = size / 2 + size * 0.43 * Math.cos(midRad);
|
|
159
|
+
// const decorationY = size / 2 + size * 0.43 * Math.sin(midRad);
|
|
160
|
+
|
|
139
161
|
return {
|
|
140
162
|
path: pathData,
|
|
141
163
|
item,
|
|
142
164
|
textX,
|
|
143
165
|
textY,
|
|
144
|
-
|
|
145
|
-
|
|
166
|
+
// decorationX,
|
|
167
|
+
// decorationY,
|
|
168
|
+
angle: (startAngle + endAngle) / 2,
|
|
146
169
|
};
|
|
147
170
|
});
|
|
148
|
-
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const wheelPaths = generateWheelPaths();
|
|
149
174
|
|
|
175
|
+
// Handle spin button press
|
|
150
176
|
const handleSpin = () => {
|
|
151
|
-
if (spinning || !enabled || wheelItems.length === 0
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
177
|
+
if (spinning || !enabled || wheelItems.length === 0) return;
|
|
154
178
|
|
|
155
179
|
setSpinning(true);
|
|
156
180
|
onSpinStart?.();
|
|
157
181
|
|
|
158
|
-
let
|
|
159
|
-
let
|
|
182
|
+
let targetRotation = 0;
|
|
183
|
+
let winnerTargetIndex = -1;
|
|
160
184
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
targetWinnerItem = foundItem;
|
|
168
|
-
} else {
|
|
169
|
-
console.warn(
|
|
170
|
-
`SpinWheel: Predetermined winner with id "${currentWinnerId}" not found. Spinning randomly.`
|
|
171
|
-
);
|
|
172
|
-
}
|
|
185
|
+
if (predeterminedWinner) {
|
|
186
|
+
const winnerId =
|
|
187
|
+
typeof predeterminedWinner === "object"
|
|
188
|
+
? predeterminedWinner.id
|
|
189
|
+
: predeterminedWinner;
|
|
190
|
+
winnerTargetIndex = wheelItems.findIndex((item) => item.id === winnerId);
|
|
173
191
|
}
|
|
174
192
|
|
|
175
|
-
if (
|
|
176
|
-
|
|
177
|
-
|
|
193
|
+
if (winnerTargetIndex !== -1) {
|
|
194
|
+
const targetSegmentCenterAngle =
|
|
195
|
+
(winnerTargetIndex + 0.5) * anglePerItem;
|
|
178
196
|
|
|
179
|
-
|
|
180
|
-
|
|
197
|
+
const targetNormalizedAngle =
|
|
198
|
+
(270 - targetSegmentCenterAngle + 360) % 360;
|
|
181
199
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
// If the wheel rotates by R, a point A on wheel is at (A+R)%360.
|
|
185
|
-
// So, (targetSegmentMidAngle_deg + R) % 360 = 270.
|
|
186
|
-
// R % 360 = (270 - targetSegmentMidAngle_deg + 360*k) % 360.
|
|
187
|
-
// This R % 360 is the target orientation for the wheel's 0-degree mark.
|
|
188
|
-
const targetWheelOrientation_deg =
|
|
189
|
-
(270 - targetSegmentMidAngle_deg + 360 * 10) % 360; // *10 to ensure positive
|
|
200
|
+
const currentAbsoluteAngle = rotationRef.current * 360;
|
|
201
|
+
const currentNormalizedAngle = (currentAbsoluteAngle % 360 + 360) % 360;
|
|
190
202
|
|
|
191
|
-
|
|
203
|
+
// Additional angle needed to reach the targetNormalizedAngle from currentNormalizedAngle
|
|
204
|
+
const angleOffset =
|
|
205
|
+
(targetNormalizedAngle - currentNormalizedAngle + 360) % 360;
|
|
192
206
|
|
|
193
|
-
//
|
|
194
|
-
const
|
|
195
|
-
|
|
207
|
+
// Number of full spins (e.g., 3 to 5)
|
|
208
|
+
const numberOfFullSpins = 3 + Math.floor(Math.random() * 3); // 3, 4, or 5 spins
|
|
209
|
+
targetRotation = numberOfFullSpins * 360 + angleOffset;
|
|
196
210
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
211
|
+
// Ensure minimum rotation if already aligned (e.g. if angleOffset is 0)
|
|
212
|
+
if (targetRotation < 360) {
|
|
213
|
+
targetRotation += 360 * (3 + Math.floor(Math.random() * 3));
|
|
214
|
+
}
|
|
200
215
|
|
|
201
|
-
targetAnimationToValue =
|
|
202
|
-
rotationRef.current + totalAdditionalRotation_deg / 360;
|
|
203
216
|
} else {
|
|
204
|
-
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
const totalRandomRotation_deg = 360 * randomSpins + randomAngleOffset;
|
|
208
|
-
targetAnimationToValue =
|
|
209
|
-
rotationRef.current + totalRandomRotation_deg / 360;
|
|
217
|
+
const randomSpins = 3 + Math.random() * 2;
|
|
218
|
+
const randomAngle = Math.random() * 360;
|
|
219
|
+
targetRotation = 360 * randomSpins + randomAngle;
|
|
210
220
|
}
|
|
211
221
|
|
|
212
222
|
Animated.timing(rotateValue, {
|
|
213
|
-
toValue:
|
|
223
|
+
toValue: rotationRef.current + targetRotation / 360,
|
|
214
224
|
duration: spinDuration,
|
|
215
225
|
easing: Easing.out(Easing.cubic),
|
|
216
226
|
useNativeDriver: true,
|
|
217
|
-
}).start(() =>
|
|
218
|
-
setSpinning(false);
|
|
219
|
-
|
|
220
|
-
if (winnerToAnnounce) {
|
|
221
|
-
setWinnerState(winnerToAnnounce);
|
|
222
|
-
onSpinEnd?.(winnerToAnnounce);
|
|
223
|
-
} else {
|
|
224
|
-
// Calculate winner from random spin based on final rotation
|
|
225
|
-
const finalRotationDegrees = rotationRef.current * 360;
|
|
226
|
-
const pointerFixedAt_deg = 270; // Pointer is at the top
|
|
227
|
-
|
|
228
|
-
// Determine which original angle on the wheel is now under the pointer
|
|
229
|
-
const angleUnderPointer_orig =
|
|
230
|
-
(pointerFixedAt_deg - finalRotationDegrees + 360 * 10) % 360; // *10 for positive
|
|
231
|
-
|
|
232
|
-
let winningSegmentIndex = Math.floor(
|
|
233
|
-
angleUnderPointer_orig / anglePerItem
|
|
234
|
-
);
|
|
235
|
-
// Ensure index is within bounds
|
|
236
|
-
winningSegmentIndex = Math.max(
|
|
237
|
-
0,
|
|
238
|
-
Math.min(wheelItems.length - 1, winningSegmentIndex)
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
const calculatedWinner = wheelItems[winningSegmentIndex];
|
|
242
|
-
|
|
243
|
-
if (calculatedWinner) {
|
|
244
|
-
setWinnerState(calculatedWinner);
|
|
245
|
-
onSpinEnd?.(calculatedWinner);
|
|
246
|
-
} else if (wheelItems.length > 0) {
|
|
247
|
-
// Fallback, should not happen if items exist and calculation is correct
|
|
248
|
-
console.error(
|
|
249
|
-
"SpinWheel: Could not determine winner from random spin. Falling back to first item."
|
|
250
|
-
);
|
|
251
|
-
setWinnerState(wheelItems[0]);
|
|
252
|
-
onSpinEnd?.(wheelItems[0]);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
});
|
|
227
|
+
}).start(() => handleSpinEnd());
|
|
256
228
|
};
|
|
257
229
|
|
|
258
|
-
|
|
230
|
+
// Handle spin completion
|
|
231
|
+
const handleSpinEnd = () => {
|
|
232
|
+
setSpinning(false);
|
|
233
|
+
|
|
234
|
+
const normalizedAngle = (rotationRef.current * 360) % 360;
|
|
235
|
+
const winningIndex = Math.floor(
|
|
236
|
+
((normalizedAngle - 270) % 360) / anglePerItem
|
|
237
|
+
);
|
|
238
|
+
const adjustedIndex =
|
|
239
|
+
(wheelItems.length - 1 - winningIndex) % wheelItems.length;
|
|
240
|
+
|
|
241
|
+
const finalWinnerIndex =
|
|
242
|
+
adjustedIndex >= 0 ? adjustedIndex : wheelItems.length + adjustedIndex;
|
|
243
|
+
|
|
244
|
+
if (finalWinnerIndex >= 0 && finalWinnerIndex < wheelItems.length) {
|
|
245
|
+
const winningItem = wheelItems[finalWinnerIndex];
|
|
246
|
+
setWinner(winningItem);
|
|
247
|
+
onSpinEnd?.(winningItem);
|
|
248
|
+
} else if (wheelItems.length > 0) {
|
|
249
|
+
onSpinEnd?.(wheelItems[0]);
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
// Animation interpolation for rotation
|
|
254
|
+
const rotate = rotateValue.interpolate({
|
|
259
255
|
inputRange: [0, 1],
|
|
260
256
|
outputRange: ["0deg", "360deg"],
|
|
261
257
|
});
|
|
262
258
|
|
|
263
|
-
if (wheelItems.length === 0) {
|
|
264
|
-
return (
|
|
265
|
-
<View style={[styles.container, containerStyle, { height: size }]}>
|
|
266
|
-
<Text>No items to display in the wheel.</Text>
|
|
267
|
-
</View>
|
|
268
|
-
);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
259
|
return (
|
|
272
260
|
<View style={[styles.container, containerStyle]}>
|
|
273
|
-
<View style={{ width: size, height: size
|
|
261
|
+
<View style={{ width: size, height: size }}>
|
|
262
|
+
{/* The wheel */}
|
|
274
263
|
<Animated.View
|
|
275
264
|
style={[
|
|
276
265
|
styles.wheelContainer,
|
|
@@ -278,66 +267,100 @@ const SpinWheel: React.FC<SpinWheelProps> = ({
|
|
|
278
267
|
width: size,
|
|
279
268
|
height: size,
|
|
280
269
|
borderRadius: size / 2,
|
|
281
|
-
transform: [{ rotate
|
|
270
|
+
transform: [{ rotate }],
|
|
282
271
|
},
|
|
283
272
|
]}
|
|
284
273
|
>
|
|
285
274
|
<Svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
|
|
286
275
|
<G>
|
|
287
|
-
{wheelPaths.map(
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
276
|
+
{wheelPaths.map(
|
|
277
|
+
({ path, item, textX, textY, angle }, index) => {
|
|
278
|
+
return (
|
|
279
|
+
<React.Fragment key={item.id}>
|
|
280
|
+
<Path
|
|
281
|
+
d={path}
|
|
282
|
+
fill={
|
|
283
|
+
item.color == "" || item.color == null
|
|
284
|
+
? colors[
|
|
285
|
+
Math.floor(Math.random() * colors.length)
|
|
286
|
+
]
|
|
287
|
+
: item.color
|
|
288
|
+
}
|
|
289
|
+
stroke="#000000"
|
|
290
|
+
strokeWidth={1}
|
|
291
|
+
/>
|
|
292
|
+
<SvgText
|
|
293
|
+
x={textX}
|
|
294
|
+
y={textY}
|
|
295
|
+
fill={item.textColor || wheelTextColor}
|
|
296
|
+
fontSize={wheelTextStyle?.fontSize || 14}
|
|
297
|
+
fontWeight={
|
|
298
|
+
(wheelTextStyle?.fontWeight as any) || "bold"
|
|
299
|
+
}
|
|
300
|
+
textAnchor="middle"
|
|
301
|
+
alignmentBaseline="central"
|
|
302
|
+
transform={`rotate(${angle + 180}, ${textX}, ${textY} )`}
|
|
303
|
+
>
|
|
304
|
+
{item.label}
|
|
305
|
+
</SvgText>
|
|
306
|
+
</React.Fragment>
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
)}
|
|
304
310
|
</G>
|
|
305
311
|
</Svg>
|
|
306
312
|
</Animated.View>
|
|
307
313
|
|
|
308
|
-
{
|
|
309
|
-
|
|
310
|
-
|
|
314
|
+
{/* The center circle */}
|
|
315
|
+
<View
|
|
316
|
+
style={[
|
|
317
|
+
styles.wheelCenter,
|
|
318
|
+
{
|
|
319
|
+
width: size / 5,
|
|
320
|
+
height: size / 5,
|
|
321
|
+
transform: [
|
|
322
|
+
{ translateX: -size / 10 },
|
|
323
|
+
{ translateY: -size / 10 },
|
|
324
|
+
],
|
|
325
|
+
borderRadius: size / 5, // Should be (size / 5) / 2 for a circle
|
|
326
|
+
},
|
|
327
|
+
centerStyle,
|
|
328
|
+
]}
|
|
329
|
+
/>
|
|
330
|
+
|
|
331
|
+
{/* The pointer is a triangle on top */}
|
|
332
|
+
<View style={styles.pointerPosition}>
|
|
311
333
|
<View
|
|
312
334
|
style={[
|
|
313
|
-
styles.
|
|
314
|
-
{
|
|
315
|
-
|
|
316
|
-
height: size / 5,
|
|
317
|
-
borderRadius: size / 10, // Should be half of width/height
|
|
318
|
-
// Centering is now handled by absolute positioning from parent
|
|
319
|
-
},
|
|
320
|
-
centerStyle,
|
|
335
|
+
styles.pointer,
|
|
336
|
+
{ borderBottomColor: knobColor },
|
|
337
|
+
knobStyle,
|
|
321
338
|
]}
|
|
322
339
|
/>
|
|
323
|
-
|
|
340
|
+
</View>
|
|
324
341
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
342
|
+
{/* Action Button */}
|
|
343
|
+
<View
|
|
344
|
+
style={{
|
|
345
|
+
position: "absolute",
|
|
346
|
+
width: "100%",
|
|
347
|
+
alignItems: "center",
|
|
348
|
+
justifyContent: "center",
|
|
349
|
+
bottom: -70,
|
|
350
|
+
zIndex: 2,
|
|
351
|
+
}}
|
|
352
|
+
>
|
|
353
|
+
<TouchableOpacity
|
|
354
|
+
onPress={handleSpin}
|
|
355
|
+
disabled={spinning || !enabled || wheelItems.length === 0}
|
|
356
|
+
style={[styles.actionButton, actionButtonStyle]}
|
|
357
|
+
>
|
|
358
|
+
<Text style={[styles.actionButtonText, actionButtonTextStyle]}>
|
|
359
|
+
{spinButtonText}
|
|
360
|
+
</Text>
|
|
361
|
+
</TouchableOpacity>
|
|
329
362
|
</View>
|
|
330
363
|
</View>
|
|
331
|
-
|
|
332
|
-
<TouchableOpacity
|
|
333
|
-
onPress={handleSpin}
|
|
334
|
-
disabled={spinning || !enabled}
|
|
335
|
-
style={[styles.actionButton, actionButtonStyle]}
|
|
336
|
-
>
|
|
337
|
-
<Text style={[styles.actionButtonText, actionButtonTextStyle]}>
|
|
338
|
-
{spinButtonText}
|
|
339
|
-
</Text>
|
|
340
|
-
</TouchableOpacity>
|
|
341
364
|
</View>
|
|
342
365
|
);
|
|
343
366
|
};
|
|
@@ -346,58 +369,48 @@ const styles = StyleSheet.create({
|
|
|
346
369
|
container: {
|
|
347
370
|
alignItems: "center",
|
|
348
371
|
justifyContent: "center",
|
|
349
|
-
|
|
372
|
+
marginTop: 20,
|
|
373
|
+
marginBottom: 70, // Space for the button
|
|
350
374
|
},
|
|
351
375
|
wheelContainer: {
|
|
352
376
|
overflow: "hidden",
|
|
353
|
-
backgroundColor: "transparent",
|
|
354
|
-
alignItems: "center",
|
|
355
|
-
justifyContent: "center",
|
|
356
|
-
},
|
|
357
|
-
centerComponentWrapper: { // For custom center component
|
|
358
|
-
position: 'absolute',
|
|
359
|
-
zIndex: 2,
|
|
377
|
+
backgroundColor: "transparent",
|
|
360
378
|
},
|
|
361
|
-
wheelCenter: {
|
|
379
|
+
wheelCenter: {
|
|
362
380
|
position: "absolute",
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
381
|
+
top: "50%",
|
|
382
|
+
left: "50%",
|
|
383
|
+
backgroundColor: "#000000",
|
|
384
|
+
borderWidth: 1,
|
|
385
|
+
borderColor: "#333333",
|
|
386
|
+
zIndex: 1,
|
|
367
387
|
},
|
|
368
|
-
|
|
388
|
+
pointerPosition: {
|
|
369
389
|
position: "absolute",
|
|
370
|
-
top: -5, // Adjust to make pointer sit nicely on the edge
|
|
371
390
|
left: "50%",
|
|
372
|
-
transform: [{ translateX: -10 }
|
|
373
|
-
zIndex:
|
|
391
|
+
transform: [{ translateX: -10 }, { rotate: "180deg" }],
|
|
392
|
+
zIndex: 2,
|
|
374
393
|
},
|
|
375
394
|
pointer: {
|
|
376
395
|
width: 0,
|
|
377
396
|
height: 0,
|
|
378
397
|
backgroundColor: "transparent",
|
|
379
398
|
borderStyle: "solid",
|
|
380
|
-
borderLeftWidth: 10,
|
|
381
|
-
borderRightWidth: 10,
|
|
382
|
-
borderBottomWidth:
|
|
399
|
+
borderLeftWidth: 10,
|
|
400
|
+
borderRightWidth: 10,
|
|
401
|
+
borderBottomWidth: 15,
|
|
383
402
|
borderLeftColor: "transparent",
|
|
384
403
|
borderRightColor: "transparent",
|
|
385
|
-
// borderBottomColor is set by knobColor prop
|
|
386
404
|
},
|
|
387
405
|
actionButton: {
|
|
388
|
-
marginTop: 30, // Space between wheel and button
|
|
389
406
|
paddingHorizontal: 30,
|
|
390
407
|
paddingVertical: 12,
|
|
391
408
|
borderRadius: 25,
|
|
392
|
-
backgroundColor: "#4CAF50", // Example button color
|
|
393
409
|
shadowColor: "#000",
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
shadowRadius: 3.84,
|
|
397
|
-
elevation: 5,
|
|
410
|
+
shadowRadius: 3,
|
|
411
|
+
backgroundColor: "grey",
|
|
398
412
|
},
|
|
399
413
|
actionButtonText: {
|
|
400
|
-
color: "#FFFFFF",
|
|
401
414
|
fontWeight: "bold",
|
|
402
415
|
fontSize: 16,
|
|
403
416
|
},
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { registerRootComponent } from 'expo';
|
|
2
|
+
import "react-native-reanimated";
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
import App from "./app";
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
registerRootComponent(App);
|
|
8
8
|
|
|
9
9
|
export * from "./theme"
|
|
10
10
|
export * from "./components";
|