react-spin-prize 2.0.0 → 2.1.0
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 +10 -0
- package/dist/SpinnerWheel.d.ts.map +1 -1
- package/dist/SpinnerWheel.js +49 -19
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -78,6 +78,7 @@ function App() {
|
|
|
78
78
|
| `disabled` | `boolean` | false | Disable the spin button |
|
|
79
79
|
| `winningIndex` | `number` | - | Force a specific winner (for testing) |
|
|
80
80
|
| `autoSpinTrigger` | `string \| number \| boolean \| null` | - | Change this value to trigger a programmatic spin |
|
|
81
|
+
| `textLayout` | `'radial' \| 'horizontal'` | 'horizontal' | Text layout style: 'radial' for rotating text, 'horizontal' for edge-to-center text |
|
|
81
82
|
|
|
82
83
|
## SpinnerWheelItem
|
|
83
84
|
|
|
@@ -125,6 +126,15 @@ interface SpinnerWheelItem {
|
|
|
125
126
|
console.log('Winner:', item);
|
|
126
127
|
}}
|
|
127
128
|
/>
|
|
129
|
+
|
|
130
|
+
// Horizontal text layout (edge-to-center)
|
|
131
|
+
<SpinnerWheel
|
|
132
|
+
items={items}
|
|
133
|
+
textLayout="horizontal"
|
|
134
|
+
onSpinComplete={(item) => {
|
|
135
|
+
console.log('Winner:', item);
|
|
136
|
+
}}
|
|
137
|
+
/>
|
|
128
138
|
```
|
|
129
139
|
|
|
130
140
|
## License
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpinnerWheel.d.ts","sourceRoot":"","sources":["../src/SpinnerWheel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AACxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAQjD,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,
|
|
1
|
+
{"version":3,"file":"SpinnerWheel.d.ts","sourceRoot":"","sources":["../src/SpinnerWheel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AACxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAQjD,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAoWpD,CAAC"}
|
package/dist/SpinnerWheel.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useRef, useEffect, useCallback } from "react";
|
|
3
3
|
const DEFAULT_COLORS = [
|
|
4
4
|
"#FF6B6B", "#4ECDC4", "#45B7D1", "#FFA07A",
|
|
5
5
|
"#98D8C8", "#F7DC6F", "#BB8FCE", "#85C1E2",
|
|
6
6
|
"#F8B739", "#52B788", "#E76F51", "#2A9D8F",
|
|
7
7
|
];
|
|
8
|
-
export const SpinnerWheel = ({ items, onSpinComplete, onButtonClick, spinning: externalSpinning, duration = 5000, size = 500, fontSize = 16, borderWidth = 8, borderColor = "#333", buttonText = "SPIN", buttonColor = "#333", buttonTextColor = "#fff", buttonIcon, buttonSize, buttonBorderColor = "#333", buttonBorderWidth = 4, disabled = false, winningIndex, autoSpinTrigger, }) => {
|
|
8
|
+
export const SpinnerWheel = ({ items, onSpinComplete, onButtonClick, spinning: externalSpinning, duration = 5000, size = 500, fontSize = 16, borderWidth = 8, borderColor = "#333", buttonText = "SPIN", buttonColor = "#333", buttonTextColor = "#fff", buttonIcon, buttonSize, buttonBorderColor = "#333", buttonBorderWidth = 4, disabled = false, winningIndex, autoSpinTrigger, textLayout = "horizontal", }) => {
|
|
9
9
|
const [rotation, setRotation] = useState(0);
|
|
10
10
|
const [spinning, setSpinning] = useState(false);
|
|
11
11
|
const animationRef = useRef();
|
|
@@ -122,25 +122,55 @@ export const SpinnerWheel = ({ items, onSpinComplete, onButtonClick, spinning: e
|
|
|
122
122
|
`A ${wheelRadius} ${wheelRadius} 0 0 1 ${x2} ${y2}`,
|
|
123
123
|
"Z",
|
|
124
124
|
].join(" ");
|
|
125
|
-
// Text position
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
125
|
+
// Text position and rendering based on layout
|
|
126
|
+
let textElement;
|
|
127
|
+
if (textLayout === "horizontal") {
|
|
128
|
+
// Horizontal layout: text starts from edge towards center
|
|
129
|
+
const outerTextRadius = wheelRadius * 0.85;
|
|
130
|
+
const innerTextRadius = wheelRadius * 0.35;
|
|
131
|
+
// Calculate start position (at the edge)
|
|
132
|
+
const startX = centerX + outerTextRadius * Math.cos(midAngle);
|
|
133
|
+
const startY = centerY + outerTextRadius * Math.sin(midAngle);
|
|
134
|
+
// Calculate end position (towards center)
|
|
135
|
+
const endX = centerX + innerTextRadius * Math.cos(midAngle);
|
|
136
|
+
const endY = centerY + innerTextRadius * Math.sin(midAngle);
|
|
137
|
+
// Text path for horizontal text
|
|
138
|
+
const textPathId = `textPath-${item.id}`;
|
|
139
|
+
// Truncate long labels
|
|
140
|
+
let displayLabel = item.label;
|
|
141
|
+
if (items.length > 30 && displayLabel.length > 8) {
|
|
142
|
+
displayLabel = displayLabel.substring(0, 7) + "...";
|
|
143
|
+
}
|
|
144
|
+
else if (items.length > 20 && displayLabel.length > 12) {
|
|
145
|
+
displayLabel = displayLabel.substring(0, 11) + "...";
|
|
146
|
+
}
|
|
147
|
+
else if (items.length > 12 && displayLabel.length > 15) {
|
|
148
|
+
displayLabel = displayLabel.substring(0, 14) + "...";
|
|
149
|
+
}
|
|
150
|
+
textElement = (_jsxs(_Fragment, { children: [_jsx("defs", { children: _jsx("path", { id: textPathId, d: `M ${startX} ${startY} L ${endX} ${endY}` }) }), _jsx("text", { fill: getTextColor(index), fontSize: dynamicFontSize, fontWeight: "bold", style: { userSelect: "none", pointerEvents: "none" }, children: _jsx("textPath", { href: `#${textPathId}`, startOffset: "50%", textAnchor: "middle", children: displayLabel }) })] }));
|
|
139
151
|
}
|
|
140
|
-
else
|
|
141
|
-
|
|
152
|
+
else {
|
|
153
|
+
// Radial layout (default): rotating text
|
|
154
|
+
const textRadius = items.length > 30 ? wheelRadius * 0.65
|
|
155
|
+
: items.length > 20 ? wheelRadius * 0.68
|
|
156
|
+
: wheelRadius * 0.7;
|
|
157
|
+
const textX = centerX + textRadius * Math.cos(midAngle);
|
|
158
|
+
const textY = centerY + textRadius * Math.sin(midAngle);
|
|
159
|
+
const textAngle = (midAngle * 180) / Math.PI + 90;
|
|
160
|
+
// Truncate long labels
|
|
161
|
+
let displayLabel = item.label;
|
|
162
|
+
if (items.length > 30 && displayLabel.length > 8) {
|
|
163
|
+
displayLabel = displayLabel.substring(0, 7) + "...";
|
|
164
|
+
}
|
|
165
|
+
else if (items.length > 20 && displayLabel.length > 12) {
|
|
166
|
+
displayLabel = displayLabel.substring(0, 11) + "...";
|
|
167
|
+
}
|
|
168
|
+
else if (items.length > 12 && displayLabel.length > 15) {
|
|
169
|
+
displayLabel = displayLabel.substring(0, 14) + "...";
|
|
170
|
+
}
|
|
171
|
+
textElement = (_jsx("text", { x: textX, y: textY, fill: getTextColor(index), fontSize: dynamicFontSize, fontWeight: "bold", textAnchor: "middle", dominantBaseline: "middle", transform: `rotate(${textAngle} ${textX} ${textY})`, style: { userSelect: "none", pointerEvents: "none" }, children: displayLabel }));
|
|
142
172
|
}
|
|
143
|
-
return (_jsxs("g", { children: [_jsx("path", { d: pathData, fill: getColor(index), stroke: borderColor, strokeWidth: 1 }),
|
|
173
|
+
return (_jsxs("g", { children: [_jsx("path", { d: pathData, fill: getColor(index), stroke: borderColor, strokeWidth: 1 }), textElement] }, item.id));
|
|
144
174
|
});
|
|
145
175
|
};
|
|
146
176
|
return (_jsxs("div", { style: { position: "relative", width: size, height: size, margin: "0 auto" }, children: [_jsxs("svg", { width: size, height: size, style: {
|
package/dist/types.d.ts
CHANGED
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,aAAa,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,aAAa,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IACnD,UAAU,CAAC,EAAE,QAAQ,GAAG,YAAY,CAAC;CACtC"}
|