@digilogiclabs/saas-factory-ui 2.3.0 → 2.5.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/dist/index.d.mts +61 -11
- package/dist/index.d.ts +61 -11
- package/dist/index.js +278 -90
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +278 -90
- package/dist/index.mjs.map +1 -1
- package/dist/web/index.d.mts +61 -11
- package/dist/web/index.d.ts +61 -11
- package/dist/web/index.js +384 -194
- package/dist/web/index.js.map +1 -1
- package/dist/web/index.mjs +327 -137
- package/dist/web/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -30719,7 +30719,6 @@ function DiceRoller({
|
|
|
30719
30719
|
}) {
|
|
30720
30720
|
const clampedCount = Math.max(1, Math.min(MAX_COUNT, count2));
|
|
30721
30721
|
const dark = useDarkMode(darkMode);
|
|
30722
|
-
const reducedMotion = usePrefersReducedMotion();
|
|
30723
30722
|
const [rolling, setRolling] = (0, import_react68.useState)(false);
|
|
30724
30723
|
const [results, setResults] = (0, import_react68.useState)(null);
|
|
30725
30724
|
const [history, setHistory] = (0, import_react68.useState)([]);
|
|
@@ -30856,6 +30855,14 @@ function DiceRoller({
|
|
|
30856
30855
|
[]
|
|
30857
30856
|
);
|
|
30858
30857
|
(0, import_react68.useEffect)(() => {
|
|
30858
|
+
if (timersRef.current.length > 0) {
|
|
30859
|
+
timersRef.current.forEach((t) => clearTimeout(t));
|
|
30860
|
+
timersRef.current = [];
|
|
30861
|
+
}
|
|
30862
|
+
if (rollingRef.current) {
|
|
30863
|
+
rollingRef.current = false;
|
|
30864
|
+
setRolling(false);
|
|
30865
|
+
}
|
|
30859
30866
|
const rawW = arenaRef.current?.offsetWidth ?? 0;
|
|
30860
30867
|
const rawH = arenaRef.current?.offsetHeight ?? 0;
|
|
30861
30868
|
const w = rawW > 20 ? rawW : arena.w > 20 ? arena.w : 400;
|
|
@@ -31012,7 +31019,6 @@ function DiceRoller({
|
|
|
31012
31019
|
defaultPositions,
|
|
31013
31020
|
effectiveSize,
|
|
31014
31021
|
half,
|
|
31015
|
-
reducedMotion,
|
|
31016
31022
|
onRoll,
|
|
31017
31023
|
writeCubeRotation,
|
|
31018
31024
|
writeDiePosition,
|
|
@@ -33025,6 +33031,9 @@ function CoinFlipper({
|
|
|
33025
33031
|
}, [flipping, onFlip, historyMax]);
|
|
33026
33032
|
(0, import_react70.useEffect)(() => {
|
|
33027
33033
|
if (flipRef) flipRef.current = flip;
|
|
33034
|
+
return () => {
|
|
33035
|
+
if (flipRef && flipRef.current === flip) flipRef.current = null;
|
|
33036
|
+
};
|
|
33028
33037
|
}, [flip, flipRef]);
|
|
33029
33038
|
(0, import_react70.useEffect)(() => {
|
|
33030
33039
|
if (autoFlip) {
|
|
@@ -33526,6 +33535,11 @@ var WEDGE_PALETTE = [
|
|
|
33526
33535
|
];
|
|
33527
33536
|
var DEFAULT_EASING = "cubic-bezier(0.25, 0.46, 0.45, 0.94)";
|
|
33528
33537
|
var DRAMATIC_EASING = "cubic-bezier(0.39, 0.575, 0.565, 1)";
|
|
33538
|
+
var PHYSICS_DECAY_K = 4;
|
|
33539
|
+
function physicsRampDown(t) {
|
|
33540
|
+
return (1 - Math.exp(-PHYSICS_DECAY_K * t)) / (1 - Math.exp(-PHYSICS_DECAY_K));
|
|
33541
|
+
}
|
|
33542
|
+
var REEL_LEAD_IN = 32;
|
|
33529
33543
|
var INTENSITY_CONFIG = {
|
|
33530
33544
|
subtle: {
|
|
33531
33545
|
particles: 14,
|
|
@@ -33597,6 +33611,9 @@ function ReelStage({
|
|
|
33597
33611
|
winner,
|
|
33598
33612
|
spinning,
|
|
33599
33613
|
reelY,
|
|
33614
|
+
displayY,
|
|
33615
|
+
cssTransition,
|
|
33616
|
+
tapeRef,
|
|
33600
33617
|
reelVisibleRows,
|
|
33601
33618
|
reelRowHeight,
|
|
33602
33619
|
size,
|
|
@@ -33620,17 +33637,16 @@ function ReelStage({
|
|
|
33620
33637
|
const stripHeight = reelVisibleRows * reelRowHeight;
|
|
33621
33638
|
const centerOffsetPx = (reelVisibleRows - 1) / 2 * reelRowHeight;
|
|
33622
33639
|
const P = pool.length;
|
|
33623
|
-
const leadIn = 32;
|
|
33624
33640
|
const tapeRows = Math.max(
|
|
33625
|
-
reelVisibleRows +
|
|
33626
|
-
Math.ceil((reelY + stripHeight) / reelRowHeight) +
|
|
33641
|
+
reelVisibleRows + REEL_LEAD_IN + 1,
|
|
33642
|
+
Math.ceil((reelY + stripHeight) / reelRowHeight) + REEL_LEAD_IN + 1
|
|
33627
33643
|
);
|
|
33628
33644
|
const tape = Array.from({ length: tapeRows }, (_, i) => {
|
|
33629
|
-
const poolIdx = P === 0 ? 0 : ((i -
|
|
33645
|
+
const poolIdx = P === 0 ? 0 : ((i - REEL_LEAD_IN) % P + P) % P;
|
|
33630
33646
|
return pool[poolIdx];
|
|
33631
33647
|
});
|
|
33632
|
-
const effectiveY =
|
|
33633
|
-
const transition = spinning ? `transform ${reducedMotion ? 250 : Math.round(spinDuration * intensityCfg.durationMult)}ms ${intensityCfg.easing}` : "none";
|
|
33648
|
+
const effectiveY = displayY + REEL_LEAD_IN * reelRowHeight;
|
|
33649
|
+
const transition = spinning && cssTransition ? `transform ${reducedMotion ? 250 : Math.round(spinDuration * intensityCfg.durationMult)}ms ${intensityCfg.easing}` : "none";
|
|
33634
33650
|
return /* @__PURE__ */ (0, import_jsx_runtime117.jsxs)(
|
|
33635
33651
|
"div",
|
|
33636
33652
|
{
|
|
@@ -33675,6 +33691,7 @@ function ReelStage({
|
|
|
33675
33691
|
) : /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(
|
|
33676
33692
|
"div",
|
|
33677
33693
|
{
|
|
33694
|
+
ref: tapeRef,
|
|
33678
33695
|
"aria-hidden": "true",
|
|
33679
33696
|
style: {
|
|
33680
33697
|
position: "absolute",
|
|
@@ -33890,6 +33907,9 @@ function RandomPlayerPicker({
|
|
|
33890
33907
|
showStats = true,
|
|
33891
33908
|
renderWinner,
|
|
33892
33909
|
spinRef,
|
|
33910
|
+
resetRef,
|
|
33911
|
+
onPoolChange,
|
|
33912
|
+
draftCompleteCaption = "Draft complete \u2014 reset to spin again",
|
|
33893
33913
|
intensity = "normal",
|
|
33894
33914
|
enableHaptics = false,
|
|
33895
33915
|
tickSoundUrl,
|
|
@@ -33904,6 +33924,7 @@ function RandomPlayerPicker({
|
|
|
33904
33924
|
const dark = useDarkMode(darkMode);
|
|
33905
33925
|
const reducedMotion = usePrefersReducedMotion();
|
|
33906
33926
|
const intensityCfg = INTENSITY_CONFIG[intensity];
|
|
33927
|
+
const physicsDriven = intensity === "dramatic" && !reducedMotion;
|
|
33907
33928
|
const [pool, setPool] = (0, import_react71.useState)(participants);
|
|
33908
33929
|
const [rotation, setRotation] = (0, import_react71.useState)(0);
|
|
33909
33930
|
const [reelY, setReelY] = (0, import_react71.useState)(0);
|
|
@@ -33916,10 +33937,17 @@ function RandomPlayerPicker({
|
|
|
33916
33937
|
const particleTimer = (0, import_react71.useRef)(null);
|
|
33917
33938
|
const popTimer = (0, import_react71.useRef)(null);
|
|
33918
33939
|
const tickInterval = (0, import_react71.useRef)(null);
|
|
33940
|
+
const pendingPopIdxRef = (0, import_react71.useRef)(null);
|
|
33941
|
+
const removeWinnersRef = (0, import_react71.useRef)(removeWinners);
|
|
33919
33942
|
const tickAudioRef = (0, import_react71.useRef)(null);
|
|
33920
33943
|
const winAudioRef = (0, import_react71.useRef)(null);
|
|
33921
33944
|
const containerRef = (0, import_react71.useRef)(null);
|
|
33922
33945
|
const prevIdsRef = (0, import_react71.useRef)("");
|
|
33946
|
+
const wheelSvgRef = (0, import_react71.useRef)(null);
|
|
33947
|
+
const reelTapeRef = (0, import_react71.useRef)(null);
|
|
33948
|
+
const rafRef = (0, import_react71.useRef)(null);
|
|
33949
|
+
const visualRotationRef = (0, import_react71.useRef)(null);
|
|
33950
|
+
const visualReelYRef = (0, import_react71.useRef)(null);
|
|
33923
33951
|
(0, import_react71.useEffect)(() => {
|
|
33924
33952
|
if (typeof window === "undefined" || !tickSoundUrl) {
|
|
33925
33953
|
tickAudioRef.current = null;
|
|
@@ -33954,10 +33982,33 @@ function RandomPlayerPicker({
|
|
|
33954
33982
|
winAudioRef.current = null;
|
|
33955
33983
|
};
|
|
33956
33984
|
}, [winSoundUrl, soundVolume]);
|
|
33985
|
+
(0, import_react71.useEffect)(() => {
|
|
33986
|
+
removeWinnersRef.current = removeWinners;
|
|
33987
|
+
}, [removeWinners]);
|
|
33957
33988
|
(0, import_react71.useEffect)(() => {
|
|
33958
33989
|
const ids = participants.map((p) => p.id).join("|");
|
|
33959
33990
|
const structuralChange = ids !== prevIdsRef.current;
|
|
33960
33991
|
if (structuralChange) {
|
|
33992
|
+
if (winnerTimer.current) {
|
|
33993
|
+
clearTimeout(winnerTimer.current);
|
|
33994
|
+
winnerTimer.current = null;
|
|
33995
|
+
}
|
|
33996
|
+
if (tickInterval.current) {
|
|
33997
|
+
clearTimeout(tickInterval.current);
|
|
33998
|
+
tickInterval.current = null;
|
|
33999
|
+
}
|
|
34000
|
+
if (popTimer.current) {
|
|
34001
|
+
clearTimeout(popTimer.current);
|
|
34002
|
+
popTimer.current = null;
|
|
34003
|
+
}
|
|
34004
|
+
if (rafRef.current) {
|
|
34005
|
+
cancelAnimationFrame(rafRef.current);
|
|
34006
|
+
rafRef.current = null;
|
|
34007
|
+
}
|
|
34008
|
+
visualRotationRef.current = null;
|
|
34009
|
+
visualReelYRef.current = null;
|
|
34010
|
+
pendingPopIdxRef.current = null;
|
|
34011
|
+
setSpinning(false);
|
|
33961
34012
|
setPool(participants);
|
|
33962
34013
|
setHistory([]);
|
|
33963
34014
|
setWinner(null);
|
|
@@ -34014,33 +34065,50 @@ function RandomPlayerPicker({
|
|
|
34014
34065
|
[dark]
|
|
34015
34066
|
);
|
|
34016
34067
|
const spin = (0, import_react71.useCallback)(() => {
|
|
34017
|
-
if (spinning
|
|
34068
|
+
if (spinning) return;
|
|
34069
|
+
let activePool = pool;
|
|
34070
|
+
if (popTimer.current) {
|
|
34071
|
+
clearTimeout(popTimer.current);
|
|
34072
|
+
popTimer.current = null;
|
|
34073
|
+
const pendingIdx = pendingPopIdxRef.current;
|
|
34074
|
+
pendingPopIdxRef.current = null;
|
|
34075
|
+
if (pendingIdx != null && pendingIdx >= 0 && pendingIdx < pool.length && removeWinnersRef.current) {
|
|
34076
|
+
activePool = pool.filter((_, i) => i !== pendingIdx);
|
|
34077
|
+
setPool(activePool);
|
|
34078
|
+
setWinner(null);
|
|
34079
|
+
}
|
|
34080
|
+
}
|
|
34081
|
+
const activeN = activePool.length;
|
|
34082
|
+
if (activeN < 2) return;
|
|
34083
|
+
const activeAnglePerWedge = 2 * Math.PI / activeN;
|
|
34084
|
+
const activeRotation = rotation;
|
|
34085
|
+
const activeReelY = reelY;
|
|
34018
34086
|
onSpinStart?.();
|
|
34019
|
-
const winnerIdx = weightedIndex(
|
|
34087
|
+
const winnerIdx = weightedIndex(activePool);
|
|
34020
34088
|
const minRev = minRevolutions + intensityCfg.revsBoost;
|
|
34021
34089
|
const maxRev = maxRevolutions + intensityCfg.revsBoost;
|
|
34022
34090
|
const minFullRevs = Math.ceil(minRev);
|
|
34023
34091
|
const maxFullRevs = Math.max(minFullRevs, Math.floor(maxRev));
|
|
34024
34092
|
const revolutions = minFullRevs + Math.floor(Math.random() * (maxFullRevs - minFullRevs + 1));
|
|
34025
|
-
const wedgeMidRad = winnerIdx *
|
|
34093
|
+
const wedgeMidRad = winnerIdx * activeAnglePerWedge + activeAnglePerWedge / 2;
|
|
34026
34094
|
const wedgeMidDeg = wedgeMidRad * 180 / Math.PI;
|
|
34027
|
-
const wheelWobble = (Math.random() - 0.5) * (
|
|
34028
|
-
const currentMod = (
|
|
34095
|
+
const wheelWobble = (Math.random() - 0.5) * (activeAnglePerWedge * 180 / Math.PI) * 0.6;
|
|
34096
|
+
const currentMod = (activeRotation % 360 + 360) % 360;
|
|
34029
34097
|
const targetMod = ((-wedgeMidDeg + wheelWobble) % 360 + 360) % 360;
|
|
34030
34098
|
let deltaMod = targetMod - currentMod;
|
|
34031
34099
|
if (deltaMod < 0) deltaMod += 360;
|
|
34032
|
-
const finalRotation =
|
|
34100
|
+
const finalRotation = activeRotation + deltaMod + revolutions * 360;
|
|
34033
34101
|
const centerOffsetRows = (reelVisibleRows - 1) / 2;
|
|
34034
34102
|
const centerOffsetPx = centerOffsetRows * reelRowHeight;
|
|
34035
34103
|
const currentCenterRow = Math.round(
|
|
34036
|
-
(
|
|
34104
|
+
(activeReelY + centerOffsetPx) / reelRowHeight
|
|
34037
34105
|
);
|
|
34038
|
-
const currentCenterMod = mod(currentCenterRow,
|
|
34039
|
-
const targetRowOffset = (winnerIdx - currentCenterMod +
|
|
34040
|
-
const reelRevRows = Math.round(revolutions *
|
|
34041
|
-
const finalReelY =
|
|
34042
|
-
const settledWinnerIdx = mode === "reel" ? reelIndexAtCenter(finalReelY, centerOffsetPx, reelRowHeight,
|
|
34043
|
-
const finalWinnerIdx = settledWinnerIdx >= 0 && settledWinnerIdx <
|
|
34106
|
+
const currentCenterMod = mod(currentCenterRow, activeN);
|
|
34107
|
+
const targetRowOffset = (winnerIdx - currentCenterMod + activeN) % activeN;
|
|
34108
|
+
const reelRevRows = Math.round(revolutions * activeN);
|
|
34109
|
+
const finalReelY = activeReelY + (reelRevRows + targetRowOffset) * reelRowHeight;
|
|
34110
|
+
const settledWinnerIdx = mode === "reel" ? reelIndexAtCenter(finalReelY, centerOffsetPx, reelRowHeight, activeN) : wheelIndexAtPointer(finalRotation, activeN);
|
|
34111
|
+
const finalWinnerIdx = settledWinnerIdx >= 0 && settledWinnerIdx < activePool.length ? settledWinnerIdx : winnerIdx;
|
|
34044
34112
|
const effectiveDuration = reducedMotion ? 250 : Math.round(spinDuration * intensityCfg.durationMult);
|
|
34045
34113
|
setSpinning(true);
|
|
34046
34114
|
setWinner(null);
|
|
@@ -34050,10 +34118,59 @@ function RandomPlayerPicker({
|
|
|
34050
34118
|
} else {
|
|
34051
34119
|
setRotation(finalRotation);
|
|
34052
34120
|
}
|
|
34053
|
-
|
|
34121
|
+
const fireTick = () => {
|
|
34122
|
+
onTick?.();
|
|
34123
|
+
const ta = tickAudioRef.current;
|
|
34124
|
+
if (ta) {
|
|
34125
|
+
try {
|
|
34126
|
+
ta.currentTime = 0;
|
|
34127
|
+
void ta.play().catch(() => {
|
|
34128
|
+
});
|
|
34129
|
+
} catch {
|
|
34130
|
+
}
|
|
34131
|
+
}
|
|
34132
|
+
};
|
|
34133
|
+
if (physicsDriven) {
|
|
34134
|
+
const from = mode === "reel" ? activeReelY : activeRotation;
|
|
34135
|
+
const to = mode === "reel" ? finalReelY : finalRotation;
|
|
34136
|
+
const crossUnit = mode === "reel" ? reelRowHeight : activeAnglePerWedge * 180 / Math.PI;
|
|
34137
|
+
if (mode === "reel") visualReelYRef.current = from;
|
|
34138
|
+
else visualRotationRef.current = from;
|
|
34139
|
+
let crossings = 0;
|
|
34140
|
+
const t0 = performance.now();
|
|
34141
|
+
const step = (now) => {
|
|
34142
|
+
const t = Math.min(1, (now - t0) / effectiveDuration);
|
|
34143
|
+
const value = from + (to - from) * physicsRampDown(t);
|
|
34144
|
+
if (mode === "reel") {
|
|
34145
|
+
visualReelYRef.current = value;
|
|
34146
|
+
const el = reelTapeRef.current;
|
|
34147
|
+
if (el) {
|
|
34148
|
+
el.style.transform = `translateY(${-(value + REEL_LEAD_IN * reelRowHeight)}px)`;
|
|
34149
|
+
}
|
|
34150
|
+
} else {
|
|
34151
|
+
visualRotationRef.current = value;
|
|
34152
|
+
const el = wheelSvgRef.current;
|
|
34153
|
+
if (el) el.style.transform = `rotate(${value}deg)`;
|
|
34154
|
+
}
|
|
34155
|
+
const crossed = Math.floor(Math.abs(value - from) / crossUnit);
|
|
34156
|
+
if (crossed > crossings) {
|
|
34157
|
+
crossings = crossed;
|
|
34158
|
+
fireTick();
|
|
34159
|
+
}
|
|
34160
|
+
if (t < 1) {
|
|
34161
|
+
rafRef.current = requestAnimationFrame(step);
|
|
34162
|
+
} else {
|
|
34163
|
+
rafRef.current = null;
|
|
34164
|
+
visualRotationRef.current = null;
|
|
34165
|
+
visualReelYRef.current = null;
|
|
34166
|
+
}
|
|
34167
|
+
};
|
|
34168
|
+
rafRef.current = requestAnimationFrame(step);
|
|
34169
|
+
}
|
|
34170
|
+
if (!physicsDriven && (onTick || tickAudioRef.current) && !reducedMotion) {
|
|
34054
34171
|
const wedges = mode === "reel" ? Math.max(1, reelRevRows + targetRowOffset) : (() => {
|
|
34055
34172
|
const totalDelta = deltaMod + revolutions * 360;
|
|
34056
|
-
const wedgeDeg =
|
|
34173
|
+
const wedgeDeg = activeAnglePerWedge * 180 / Math.PI;
|
|
34057
34174
|
return Math.max(
|
|
34058
34175
|
1,
|
|
34059
34176
|
Math.round(totalDelta / Math.max(wedgeDeg, 1))
|
|
@@ -34068,16 +34185,7 @@ function RandomPlayerPicker({
|
|
|
34068
34185
|
const eased = 1 - Math.pow(1 - progress, 3);
|
|
34069
34186
|
const interval = startInterval + (endInterval - startInterval) * eased;
|
|
34070
34187
|
tickInterval.current = setTimeout(() => {
|
|
34071
|
-
|
|
34072
|
-
const ta = tickAudioRef.current;
|
|
34073
|
-
if (ta) {
|
|
34074
|
-
try {
|
|
34075
|
-
ta.currentTime = 0;
|
|
34076
|
-
void ta.play().catch(() => {
|
|
34077
|
-
});
|
|
34078
|
-
} catch {
|
|
34079
|
-
}
|
|
34080
|
-
}
|
|
34188
|
+
fireTick();
|
|
34081
34189
|
elapsed += interval;
|
|
34082
34190
|
schedule();
|
|
34083
34191
|
}, interval);
|
|
@@ -34085,7 +34193,7 @@ function RandomPlayerPicker({
|
|
|
34085
34193
|
schedule();
|
|
34086
34194
|
}
|
|
34087
34195
|
winnerTimer.current = setTimeout(() => {
|
|
34088
|
-
const picked =
|
|
34196
|
+
const picked = activePool[finalWinnerIdx];
|
|
34089
34197
|
if (!picked) {
|
|
34090
34198
|
setSpinning(false);
|
|
34091
34199
|
return;
|
|
@@ -34131,9 +34239,13 @@ function RandomPlayerPicker({
|
|
|
34131
34239
|
particleTimer.current = setTimeout(() => setParticles([]), 2400);
|
|
34132
34240
|
}
|
|
34133
34241
|
if (removeWinners) {
|
|
34242
|
+
pendingPopIdxRef.current = finalWinnerIdx;
|
|
34134
34243
|
popTimer.current = setTimeout(() => {
|
|
34244
|
+
popTimer.current = null;
|
|
34245
|
+
pendingPopIdxRef.current = null;
|
|
34246
|
+
if (!removeWinnersRef.current) return;
|
|
34135
34247
|
setPool((p) => p.filter((_, i) => i !== finalWinnerIdx));
|
|
34136
|
-
setWinner(null);
|
|
34248
|
+
if (activePool.length - 1 >= 2) setWinner(null);
|
|
34137
34249
|
setRotation(0);
|
|
34138
34250
|
setReelY(0);
|
|
34139
34251
|
}, 2800);
|
|
@@ -34142,8 +34254,6 @@ function RandomPlayerPicker({
|
|
|
34142
34254
|
}, [
|
|
34143
34255
|
pool,
|
|
34144
34256
|
spinning,
|
|
34145
|
-
n,
|
|
34146
|
-
anglePerWedge,
|
|
34147
34257
|
rotation,
|
|
34148
34258
|
reelY,
|
|
34149
34259
|
mode,
|
|
@@ -34158,6 +34268,7 @@ function RandomPlayerPicker({
|
|
|
34158
34268
|
maxRevolutions,
|
|
34159
34269
|
history,
|
|
34160
34270
|
reducedMotion,
|
|
34271
|
+
physicsDriven,
|
|
34161
34272
|
enableHaptics,
|
|
34162
34273
|
intensityCfg.particles,
|
|
34163
34274
|
intensityCfg.durationMult,
|
|
@@ -34169,16 +34280,25 @@ function RandomPlayerPicker({
|
|
|
34169
34280
|
if (spinRef && spinRef.current === spin) spinRef.current = null;
|
|
34170
34281
|
};
|
|
34171
34282
|
}, [spin, spinRef]);
|
|
34283
|
+
const onPoolChangeRef = (0, import_react71.useRef)(onPoolChange);
|
|
34284
|
+
(0, import_react71.useEffect)(() => {
|
|
34285
|
+
onPoolChangeRef.current = onPoolChange;
|
|
34286
|
+
});
|
|
34287
|
+
(0, import_react71.useEffect)(() => {
|
|
34288
|
+
onPoolChangeRef.current?.(pool);
|
|
34289
|
+
}, [pool]);
|
|
34172
34290
|
(0, import_react71.useEffect)(
|
|
34173
34291
|
() => () => {
|
|
34174
34292
|
if (winnerTimer.current) clearTimeout(winnerTimer.current);
|
|
34175
34293
|
if (particleTimer.current) clearTimeout(particleTimer.current);
|
|
34176
34294
|
if (popTimer.current) clearTimeout(popTimer.current);
|
|
34177
34295
|
if (tickInterval.current) clearTimeout(tickInterval.current);
|
|
34296
|
+
if (rafRef.current) cancelAnimationFrame(rafRef.current);
|
|
34178
34297
|
},
|
|
34179
34298
|
[]
|
|
34180
34299
|
);
|
|
34181
34300
|
(0, import_react71.useEffect)(() => {
|
|
34301
|
+
if (trigger === "external") return;
|
|
34182
34302
|
const el = containerRef.current;
|
|
34183
34303
|
if (!el) return;
|
|
34184
34304
|
const h = (e) => {
|
|
@@ -34189,8 +34309,28 @@ function RandomPlayerPicker({
|
|
|
34189
34309
|
};
|
|
34190
34310
|
el.addEventListener("keydown", h);
|
|
34191
34311
|
return () => el.removeEventListener("keydown", h);
|
|
34192
|
-
}, [spin]);
|
|
34312
|
+
}, [spin, trigger]);
|
|
34193
34313
|
const reset = (0, import_react71.useCallback)(() => {
|
|
34314
|
+
if (winnerTimer.current) {
|
|
34315
|
+
clearTimeout(winnerTimer.current);
|
|
34316
|
+
winnerTimer.current = null;
|
|
34317
|
+
}
|
|
34318
|
+
if (tickInterval.current) {
|
|
34319
|
+
clearTimeout(tickInterval.current);
|
|
34320
|
+
tickInterval.current = null;
|
|
34321
|
+
}
|
|
34322
|
+
if (popTimer.current) {
|
|
34323
|
+
clearTimeout(popTimer.current);
|
|
34324
|
+
popTimer.current = null;
|
|
34325
|
+
}
|
|
34326
|
+
if (rafRef.current) {
|
|
34327
|
+
cancelAnimationFrame(rafRef.current);
|
|
34328
|
+
rafRef.current = null;
|
|
34329
|
+
}
|
|
34330
|
+
visualRotationRef.current = null;
|
|
34331
|
+
visualReelYRef.current = null;
|
|
34332
|
+
pendingPopIdxRef.current = null;
|
|
34333
|
+
setSpinning(false);
|
|
34194
34334
|
setPool(participants);
|
|
34195
34335
|
setHistory([]);
|
|
34196
34336
|
setWinner(null);
|
|
@@ -34198,9 +34338,17 @@ function RandomPlayerPicker({
|
|
|
34198
34338
|
setReelY(0);
|
|
34199
34339
|
setParticles([]);
|
|
34200
34340
|
}, [participants]);
|
|
34341
|
+
(0, import_react71.useEffect)(() => {
|
|
34342
|
+
if (resetRef) resetRef.current = reset;
|
|
34343
|
+
return () => {
|
|
34344
|
+
if (resetRef && resetRef.current === reset) resetRef.current = null;
|
|
34345
|
+
};
|
|
34346
|
+
}, [reset, resetRef]);
|
|
34201
34347
|
const isIdle = n < 2;
|
|
34348
|
+
const drainedByDraft = isIdle && participants.length >= 2;
|
|
34349
|
+
const effectiveIdleCaption = drainedByDraft ? draftCompleteCaption : placeholderCaption;
|
|
34202
34350
|
const placeholderDiscColor = dark ? "#2a3344" : "#e2e8f0";
|
|
34203
|
-
if (isIdle && placeholderMode === "message") {
|
|
34351
|
+
if (isIdle && !drainedByDraft && placeholderMode === "message") {
|
|
34204
34352
|
return /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(
|
|
34205
34353
|
"div",
|
|
34206
34354
|
{
|
|
@@ -34222,6 +34370,8 @@ function RandomPlayerPicker({
|
|
|
34222
34370
|
const shakeKeyframe = `rppShake_${intensity}`;
|
|
34223
34371
|
const glowKeyframe = `rppGlow_${intensity}`;
|
|
34224
34372
|
const pulseKeyframe = `rppPulse_${intensity}`;
|
|
34373
|
+
const displayRotation = visualRotationRef.current ?? rotation;
|
|
34374
|
+
const displayReelY = visualReelYRef.current ?? reelY;
|
|
34225
34375
|
return /* @__PURE__ */ (0, import_jsx_runtime117.jsxs)(
|
|
34226
34376
|
"div",
|
|
34227
34377
|
{
|
|
@@ -34268,13 +34418,33 @@ function RandomPlayerPicker({
|
|
|
34268
34418
|
[data-rpp-anim] { animation: none !important; }
|
|
34269
34419
|
}
|
|
34270
34420
|
` }),
|
|
34271
|
-
|
|
34421
|
+
isIdle && placeholderMode === "message" ? (
|
|
34422
|
+
/* Drained draft in "message" mode — compact box in the wheel's
|
|
34423
|
+
place; the winner card / stats / controls / history below stay
|
|
34424
|
+
mounted so the endgame isn't a dead end. */
|
|
34425
|
+
/* @__PURE__ */ (0, import_jsx_runtime117.jsx)(
|
|
34426
|
+
"div",
|
|
34427
|
+
{
|
|
34428
|
+
style: {
|
|
34429
|
+
padding: "2rem 1rem",
|
|
34430
|
+
textAlign: "center",
|
|
34431
|
+
color: c.dim,
|
|
34432
|
+
borderRadius: 12,
|
|
34433
|
+
border: `1px dashed ${c.border}`
|
|
34434
|
+
},
|
|
34435
|
+
children: effectiveIdleCaption
|
|
34436
|
+
}
|
|
34437
|
+
)
|
|
34438
|
+
) : mode === "reel" ? /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(
|
|
34272
34439
|
ReelStage,
|
|
34273
34440
|
{
|
|
34274
34441
|
pool,
|
|
34275
34442
|
winner,
|
|
34276
34443
|
spinning,
|
|
34277
34444
|
reelY,
|
|
34445
|
+
displayY: displayReelY,
|
|
34446
|
+
cssTransition: !physicsDriven,
|
|
34447
|
+
tapeRef: reelTapeRef,
|
|
34278
34448
|
reelVisibleRows,
|
|
34279
34449
|
reelRowHeight,
|
|
34280
34450
|
size,
|
|
@@ -34283,7 +34453,7 @@ function RandomPlayerPicker({
|
|
|
34283
34453
|
dark,
|
|
34284
34454
|
isIdle,
|
|
34285
34455
|
placeholderMode,
|
|
34286
|
-
placeholderCaption,
|
|
34456
|
+
placeholderCaption: effectiveIdleCaption,
|
|
34287
34457
|
placeholderDiscColor,
|
|
34288
34458
|
spinDuration,
|
|
34289
34459
|
intensityCfg,
|
|
@@ -34340,6 +34510,7 @@ function RandomPlayerPicker({
|
|
|
34340
34510
|
/* @__PURE__ */ (0, import_jsx_runtime117.jsxs)(
|
|
34341
34511
|
"svg",
|
|
34342
34512
|
{
|
|
34513
|
+
ref: wheelSvgRef,
|
|
34343
34514
|
width: size,
|
|
34344
34515
|
height: size,
|
|
34345
34516
|
"aria-hidden": "true",
|
|
@@ -34348,8 +34519,8 @@ function RandomPlayerPicker({
|
|
|
34348
34519
|
top: 36,
|
|
34349
34520
|
left: 0,
|
|
34350
34521
|
transformOrigin: `${cx}px ${cy}px`,
|
|
34351
|
-
transform: `rotate(${
|
|
34352
|
-
transition: spinning ? `transform ${reducedMotion ? 250 : Math.round(spinDuration * intensityCfg.durationMult)}ms ${intensityCfg.easing}` : "none",
|
|
34522
|
+
transform: `rotate(${displayRotation}deg)`,
|
|
34523
|
+
transition: spinning && !physicsDriven ? `transform ${reducedMotion ? 250 : Math.round(spinDuration * intensityCfg.durationMult)}ms ${intensityCfg.easing}` : "none",
|
|
34353
34524
|
filter: `drop-shadow(0 8px 28px ${dark ? "rgba(0,0,0,0.55)" : "rgba(0,0,0,0.15)"})`
|
|
34354
34525
|
},
|
|
34355
34526
|
children: [
|
|
@@ -34502,7 +34673,7 @@ function RandomPlayerPicker({
|
|
|
34502
34673
|
pointerEvents: "none",
|
|
34503
34674
|
zIndex: 3
|
|
34504
34675
|
},
|
|
34505
|
-
children:
|
|
34676
|
+
children: effectiveIdleCaption
|
|
34506
34677
|
}
|
|
34507
34678
|
),
|
|
34508
34679
|
particles.map((p) => /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(
|
|
@@ -38643,27 +38814,28 @@ function GameTimer({
|
|
|
38643
38814
|
const now = performance.now();
|
|
38644
38815
|
const delta = now - lastTickRef.current;
|
|
38645
38816
|
lastTickRef.current = now;
|
|
38646
|
-
|
|
38647
|
-
|
|
38648
|
-
|
|
38649
|
-
|
|
38650
|
-
|
|
38651
|
-
|
|
38652
|
-
|
|
38817
|
+
const next = elapsedRef.current + delta;
|
|
38818
|
+
if (mode === "countdown" && next >= durationMs) {
|
|
38819
|
+
elapsedRef.current = durationMs;
|
|
38820
|
+
setElapsedMs(durationMs);
|
|
38821
|
+
setRunning(false);
|
|
38822
|
+
setCompleted(true);
|
|
38823
|
+
onComplete?.();
|
|
38824
|
+
return;
|
|
38825
|
+
}
|
|
38826
|
+
if (mode === "countdown") {
|
|
38827
|
+
const secLeft = Math.ceil((durationMs - next) / 1e3);
|
|
38828
|
+
if (secLeft <= warningAt && !warningFiredRef.current) {
|
|
38829
|
+
warningFiredRef.current = true;
|
|
38830
|
+
onWarning?.();
|
|
38653
38831
|
}
|
|
38654
|
-
if (
|
|
38655
|
-
|
|
38656
|
-
|
|
38657
|
-
warningFiredRef.current = true;
|
|
38658
|
-
onWarning?.();
|
|
38659
|
-
}
|
|
38660
|
-
if (secLeft <= dangerAt && !dangerFiredRef.current) {
|
|
38661
|
-
dangerFiredRef.current = true;
|
|
38662
|
-
onDanger?.();
|
|
38663
|
-
}
|
|
38832
|
+
if (secLeft <= dangerAt && !dangerFiredRef.current) {
|
|
38833
|
+
dangerFiredRef.current = true;
|
|
38834
|
+
onDanger?.();
|
|
38664
38835
|
}
|
|
38665
|
-
|
|
38666
|
-
|
|
38836
|
+
}
|
|
38837
|
+
elapsedRef.current = next;
|
|
38838
|
+
setElapsedMs(next);
|
|
38667
38839
|
rafRef.current = requestAnimationFrame(tick);
|
|
38668
38840
|
}, [mode, durationMs, warningAt, dangerAt, onComplete, onWarning, onDanger]);
|
|
38669
38841
|
(0, import_react74.useEffect)(() => {
|
|
@@ -38684,6 +38856,7 @@ function GameTimer({
|
|
|
38684
38856
|
}, []);
|
|
38685
38857
|
const reset = (0, import_react74.useCallback)(() => {
|
|
38686
38858
|
setRunning(false);
|
|
38859
|
+
elapsedRef.current = 0;
|
|
38687
38860
|
setElapsedMs(0);
|
|
38688
38861
|
setCompleted(false);
|
|
38689
38862
|
setLaps([]);
|
|
@@ -40185,8 +40358,10 @@ function TeamGenerator({
|
|
|
40185
40358
|
minPlayersPerTeam = 2,
|
|
40186
40359
|
maxPlayers = Number.POSITIVE_INFINITY,
|
|
40187
40360
|
defaultNames = [],
|
|
40361
|
+
defaultTeams,
|
|
40188
40362
|
onNamesChange,
|
|
40189
40363
|
onGenerate,
|
|
40364
|
+
onTeamsChange,
|
|
40190
40365
|
onTeamCountChange,
|
|
40191
40366
|
labels,
|
|
40192
40367
|
ariaLabel = "Team generator",
|
|
@@ -40201,12 +40376,17 @@ function TeamGenerator({
|
|
|
40201
40376
|
const [names, setNames] = (0, import_react76.useState)(defaultNames);
|
|
40202
40377
|
const [input, setInput] = (0, import_react76.useState)("");
|
|
40203
40378
|
const [teamCount, setTeamCount] = (0, import_react76.useState)(initialCount);
|
|
40204
|
-
const [teams, setTeams] = (0, import_react76.useState)(
|
|
40379
|
+
const [teams, setTeams] = (0, import_react76.useState)(
|
|
40380
|
+
defaultTeams && defaultTeams.length > 0 ? defaultTeams : null
|
|
40381
|
+
);
|
|
40205
40382
|
const inputRef = (0, import_react76.useRef)(null);
|
|
40206
40383
|
const minNames = teamCount * minPlayersPerTeam;
|
|
40207
40384
|
(0, import_react76.useEffect)(() => {
|
|
40208
40385
|
onNamesChange?.(names);
|
|
40209
40386
|
}, [names, onNamesChange]);
|
|
40387
|
+
(0, import_react76.useEffect)(() => {
|
|
40388
|
+
onTeamsChange?.(teams);
|
|
40389
|
+
}, [teams, onTeamsChange]);
|
|
40210
40390
|
const addName = (0, import_react76.useCallback)(() => {
|
|
40211
40391
|
const trimmed = input.trim();
|
|
40212
40392
|
if (!trimmed) return;
|
|
@@ -40828,6 +41008,8 @@ function RoundRobinScheduler({
|
|
|
40828
41008
|
theme: theme5 = "slate",
|
|
40829
41009
|
darkMode,
|
|
40830
41010
|
minTeams = 3,
|
|
41011
|
+
maxTeams,
|
|
41012
|
+
nameMaxLength,
|
|
40831
41013
|
defaultTeams = [],
|
|
40832
41014
|
resumeRounds,
|
|
40833
41015
|
onTeamsChange,
|
|
@@ -40850,39 +41032,41 @@ function RoundRobinScheduler({
|
|
|
40850
41032
|
(0, import_react77.useEffect)(() => {
|
|
40851
41033
|
onTeamsChange?.(teams);
|
|
40852
41034
|
}, [teams, onTeamsChange]);
|
|
40853
|
-
const
|
|
40854
|
-
|
|
40855
|
-
if (names.length === 0) return;
|
|
40856
|
-
setTeams((prev) => {
|
|
40857
|
-
const existing = new Set(prev.map((t) => t.toLowerCase()));
|
|
40858
|
-
const next = [...prev];
|
|
40859
|
-
for (const name of names) {
|
|
40860
|
-
if (!existing.has(name.toLowerCase())) {
|
|
40861
|
-
existing.add(name.toLowerCase());
|
|
40862
|
-
next.push(name);
|
|
40863
|
-
}
|
|
40864
|
-
}
|
|
40865
|
-
return next;
|
|
40866
|
-
});
|
|
40867
|
-
setInput("");
|
|
40868
|
-
setRounds([]);
|
|
40869
|
-
}, []);
|
|
40870
|
-
const importTeams = (0, import_react77.useCallback)((incoming) => {
|
|
40871
|
-
if (incoming.length === 0) return;
|
|
40872
|
-
setTeams((prev) => {
|
|
41035
|
+
const mergeNames = (0, import_react77.useCallback)(
|
|
41036
|
+
(prev, incoming) => {
|
|
40873
41037
|
const existing = new Set(prev.map((t) => t.toLowerCase()));
|
|
40874
41038
|
const next = [...prev];
|
|
40875
|
-
for (const
|
|
41039
|
+
for (const rawName of incoming) {
|
|
41040
|
+
if (maxTeams != null && next.length >= maxTeams) break;
|
|
41041
|
+
const name = nameMaxLength != null ? rawName.slice(0, nameMaxLength) : rawName;
|
|
40876
41042
|
const key = name.toLowerCase();
|
|
40877
|
-
if (!existing.has(key)) {
|
|
41043
|
+
if (name.length > 0 && !existing.has(key)) {
|
|
40878
41044
|
existing.add(key);
|
|
40879
41045
|
next.push(name);
|
|
40880
41046
|
}
|
|
40881
41047
|
}
|
|
40882
41048
|
return next;
|
|
40883
|
-
}
|
|
40884
|
-
|
|
40885
|
-
|
|
41049
|
+
},
|
|
41050
|
+
[maxTeams, nameMaxLength]
|
|
41051
|
+
);
|
|
41052
|
+
const addTeams = (0, import_react77.useCallback)(
|
|
41053
|
+
(raw) => {
|
|
41054
|
+
const names = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
41055
|
+
if (names.length === 0) return;
|
|
41056
|
+
setTeams((prev) => mergeNames(prev, names));
|
|
41057
|
+
setInput("");
|
|
41058
|
+
setRounds([]);
|
|
41059
|
+
},
|
|
41060
|
+
[mergeNames]
|
|
41061
|
+
);
|
|
41062
|
+
const importTeams = (0, import_react77.useCallback)(
|
|
41063
|
+
(incoming) => {
|
|
41064
|
+
if (incoming.length === 0) return;
|
|
41065
|
+
setTeams((prev) => mergeNames(prev, incoming));
|
|
41066
|
+
setRounds([]);
|
|
41067
|
+
},
|
|
41068
|
+
[mergeNames]
|
|
41069
|
+
);
|
|
40886
41070
|
const handleKeyDown = (0, import_react77.useCallback)(
|
|
40887
41071
|
(e) => {
|
|
40888
41072
|
if (e.key === "Enter") {
|
|
@@ -41371,6 +41555,8 @@ function Scoreboard({
|
|
|
41371
41555
|
onTeamsChange,
|
|
41372
41556
|
onRoundAdvance,
|
|
41373
41557
|
onNewGame,
|
|
41558
|
+
onReset,
|
|
41559
|
+
nameMaxLength,
|
|
41374
41560
|
labels,
|
|
41375
41561
|
ariaLabel = "Scoreboard",
|
|
41376
41562
|
className
|
|
@@ -41445,7 +41631,8 @@ function Scoreboard({
|
|
|
41445
41631
|
setRound(1);
|
|
41446
41632
|
setHistory([]);
|
|
41447
41633
|
setHistoryOpen(false);
|
|
41448
|
-
|
|
41634
|
+
onReset?.();
|
|
41635
|
+
}, [onReset]);
|
|
41449
41636
|
const newGame = (0, import_react78.useCallback)(() => {
|
|
41450
41637
|
setTeams(createDefaultTeams(seedCount, L.teamNamePrefix));
|
|
41451
41638
|
setRound(1);
|
|
@@ -41667,6 +41854,7 @@ function Scoreboard({
|
|
|
41667
41854
|
{
|
|
41668
41855
|
type: "text",
|
|
41669
41856
|
value: team.name,
|
|
41857
|
+
maxLength: nameMaxLength,
|
|
41670
41858
|
onChange: (e) => updateName(index, e.target.value),
|
|
41671
41859
|
style: nameInputStyle,
|
|
41672
41860
|
"aria-label": `Team ${index + 1} name`
|