@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.mjs
CHANGED
|
@@ -30665,7 +30665,6 @@ function DiceRoller({
|
|
|
30665
30665
|
}) {
|
|
30666
30666
|
const clampedCount = Math.max(1, Math.min(MAX_COUNT, count2));
|
|
30667
30667
|
const dark = useDarkMode(darkMode);
|
|
30668
|
-
const reducedMotion = usePrefersReducedMotion();
|
|
30669
30668
|
const [rolling, setRolling] = useState70(false);
|
|
30670
30669
|
const [results, setResults] = useState70(null);
|
|
30671
30670
|
const [history, setHistory] = useState70([]);
|
|
@@ -30802,6 +30801,14 @@ function DiceRoller({
|
|
|
30802
30801
|
[]
|
|
30803
30802
|
);
|
|
30804
30803
|
useEffect54(() => {
|
|
30804
|
+
if (timersRef.current.length > 0) {
|
|
30805
|
+
timersRef.current.forEach((t) => clearTimeout(t));
|
|
30806
|
+
timersRef.current = [];
|
|
30807
|
+
}
|
|
30808
|
+
if (rollingRef.current) {
|
|
30809
|
+
rollingRef.current = false;
|
|
30810
|
+
setRolling(false);
|
|
30811
|
+
}
|
|
30805
30812
|
const rawW = arenaRef.current?.offsetWidth ?? 0;
|
|
30806
30813
|
const rawH = arenaRef.current?.offsetHeight ?? 0;
|
|
30807
30814
|
const w = rawW > 20 ? rawW : arena.w > 20 ? arena.w : 400;
|
|
@@ -30958,7 +30965,6 @@ function DiceRoller({
|
|
|
30958
30965
|
defaultPositions,
|
|
30959
30966
|
effectiveSize,
|
|
30960
30967
|
half,
|
|
30961
|
-
reducedMotion,
|
|
30962
30968
|
onRoll,
|
|
30963
30969
|
writeCubeRotation,
|
|
30964
30970
|
writeDiePosition,
|
|
@@ -32984,6 +32990,9 @@ function CoinFlipper({
|
|
|
32984
32990
|
}, [flipping, onFlip, historyMax]);
|
|
32985
32991
|
useEffect56(() => {
|
|
32986
32992
|
if (flipRef) flipRef.current = flip;
|
|
32993
|
+
return () => {
|
|
32994
|
+
if (flipRef && flipRef.current === flip) flipRef.current = null;
|
|
32995
|
+
};
|
|
32987
32996
|
}, [flip, flipRef]);
|
|
32988
32997
|
useEffect56(() => {
|
|
32989
32998
|
if (autoFlip) {
|
|
@@ -33491,6 +33500,11 @@ var WEDGE_PALETTE = [
|
|
|
33491
33500
|
];
|
|
33492
33501
|
var DEFAULT_EASING = "cubic-bezier(0.25, 0.46, 0.45, 0.94)";
|
|
33493
33502
|
var DRAMATIC_EASING = "cubic-bezier(0.39, 0.575, 0.565, 1)";
|
|
33503
|
+
var PHYSICS_DECAY_K = 4;
|
|
33504
|
+
function physicsRampDown(t) {
|
|
33505
|
+
return (1 - Math.exp(-PHYSICS_DECAY_K * t)) / (1 - Math.exp(-PHYSICS_DECAY_K));
|
|
33506
|
+
}
|
|
33507
|
+
var REEL_LEAD_IN = 32;
|
|
33494
33508
|
var INTENSITY_CONFIG = {
|
|
33495
33509
|
subtle: {
|
|
33496
33510
|
particles: 14,
|
|
@@ -33562,6 +33576,9 @@ function ReelStage({
|
|
|
33562
33576
|
winner,
|
|
33563
33577
|
spinning,
|
|
33564
33578
|
reelY,
|
|
33579
|
+
displayY,
|
|
33580
|
+
cssTransition,
|
|
33581
|
+
tapeRef,
|
|
33565
33582
|
reelVisibleRows,
|
|
33566
33583
|
reelRowHeight,
|
|
33567
33584
|
size,
|
|
@@ -33585,17 +33602,16 @@ function ReelStage({
|
|
|
33585
33602
|
const stripHeight = reelVisibleRows * reelRowHeight;
|
|
33586
33603
|
const centerOffsetPx = (reelVisibleRows - 1) / 2 * reelRowHeight;
|
|
33587
33604
|
const P = pool.length;
|
|
33588
|
-
const leadIn = 32;
|
|
33589
33605
|
const tapeRows = Math.max(
|
|
33590
|
-
reelVisibleRows +
|
|
33591
|
-
Math.ceil((reelY + stripHeight) / reelRowHeight) +
|
|
33606
|
+
reelVisibleRows + REEL_LEAD_IN + 1,
|
|
33607
|
+
Math.ceil((reelY + stripHeight) / reelRowHeight) + REEL_LEAD_IN + 1
|
|
33592
33608
|
);
|
|
33593
33609
|
const tape = Array.from({ length: tapeRows }, (_, i) => {
|
|
33594
|
-
const poolIdx = P === 0 ? 0 : ((i -
|
|
33610
|
+
const poolIdx = P === 0 ? 0 : ((i - REEL_LEAD_IN) % P + P) % P;
|
|
33595
33611
|
return pool[poolIdx];
|
|
33596
33612
|
});
|
|
33597
|
-
const effectiveY =
|
|
33598
|
-
const transition = spinning ? `transform ${reducedMotion ? 250 : Math.round(spinDuration * intensityCfg.durationMult)}ms ${intensityCfg.easing}` : "none";
|
|
33613
|
+
const effectiveY = displayY + REEL_LEAD_IN * reelRowHeight;
|
|
33614
|
+
const transition = spinning && cssTransition ? `transform ${reducedMotion ? 250 : Math.round(spinDuration * intensityCfg.durationMult)}ms ${intensityCfg.easing}` : "none";
|
|
33599
33615
|
return /* @__PURE__ */ jsxs89(
|
|
33600
33616
|
"div",
|
|
33601
33617
|
{
|
|
@@ -33640,6 +33656,7 @@ function ReelStage({
|
|
|
33640
33656
|
) : /* @__PURE__ */ jsx117(
|
|
33641
33657
|
"div",
|
|
33642
33658
|
{
|
|
33659
|
+
ref: tapeRef,
|
|
33643
33660
|
"aria-hidden": "true",
|
|
33644
33661
|
style: {
|
|
33645
33662
|
position: "absolute",
|
|
@@ -33855,6 +33872,9 @@ function RandomPlayerPicker({
|
|
|
33855
33872
|
showStats = true,
|
|
33856
33873
|
renderWinner,
|
|
33857
33874
|
spinRef,
|
|
33875
|
+
resetRef,
|
|
33876
|
+
onPoolChange,
|
|
33877
|
+
draftCompleteCaption = "Draft complete \u2014 reset to spin again",
|
|
33858
33878
|
intensity = "normal",
|
|
33859
33879
|
enableHaptics = false,
|
|
33860
33880
|
tickSoundUrl,
|
|
@@ -33869,6 +33889,7 @@ function RandomPlayerPicker({
|
|
|
33869
33889
|
const dark = useDarkMode(darkMode);
|
|
33870
33890
|
const reducedMotion = usePrefersReducedMotion();
|
|
33871
33891
|
const intensityCfg = INTENSITY_CONFIG[intensity];
|
|
33892
|
+
const physicsDriven = intensity === "dramatic" && !reducedMotion;
|
|
33872
33893
|
const [pool, setPool] = useState73(participants);
|
|
33873
33894
|
const [rotation, setRotation] = useState73(0);
|
|
33874
33895
|
const [reelY, setReelY] = useState73(0);
|
|
@@ -33881,10 +33902,17 @@ function RandomPlayerPicker({
|
|
|
33881
33902
|
const particleTimer = useRef42(null);
|
|
33882
33903
|
const popTimer = useRef42(null);
|
|
33883
33904
|
const tickInterval = useRef42(null);
|
|
33905
|
+
const pendingPopIdxRef = useRef42(null);
|
|
33906
|
+
const removeWinnersRef = useRef42(removeWinners);
|
|
33884
33907
|
const tickAudioRef = useRef42(null);
|
|
33885
33908
|
const winAudioRef = useRef42(null);
|
|
33886
33909
|
const containerRef = useRef42(null);
|
|
33887
33910
|
const prevIdsRef = useRef42("");
|
|
33911
|
+
const wheelSvgRef = useRef42(null);
|
|
33912
|
+
const reelTapeRef = useRef42(null);
|
|
33913
|
+
const rafRef = useRef42(null);
|
|
33914
|
+
const visualRotationRef = useRef42(null);
|
|
33915
|
+
const visualReelYRef = useRef42(null);
|
|
33888
33916
|
useEffect57(() => {
|
|
33889
33917
|
if (typeof window === "undefined" || !tickSoundUrl) {
|
|
33890
33918
|
tickAudioRef.current = null;
|
|
@@ -33919,10 +33947,33 @@ function RandomPlayerPicker({
|
|
|
33919
33947
|
winAudioRef.current = null;
|
|
33920
33948
|
};
|
|
33921
33949
|
}, [winSoundUrl, soundVolume]);
|
|
33950
|
+
useEffect57(() => {
|
|
33951
|
+
removeWinnersRef.current = removeWinners;
|
|
33952
|
+
}, [removeWinners]);
|
|
33922
33953
|
useEffect57(() => {
|
|
33923
33954
|
const ids = participants.map((p) => p.id).join("|");
|
|
33924
33955
|
const structuralChange = ids !== prevIdsRef.current;
|
|
33925
33956
|
if (structuralChange) {
|
|
33957
|
+
if (winnerTimer.current) {
|
|
33958
|
+
clearTimeout(winnerTimer.current);
|
|
33959
|
+
winnerTimer.current = null;
|
|
33960
|
+
}
|
|
33961
|
+
if (tickInterval.current) {
|
|
33962
|
+
clearTimeout(tickInterval.current);
|
|
33963
|
+
tickInterval.current = null;
|
|
33964
|
+
}
|
|
33965
|
+
if (popTimer.current) {
|
|
33966
|
+
clearTimeout(popTimer.current);
|
|
33967
|
+
popTimer.current = null;
|
|
33968
|
+
}
|
|
33969
|
+
if (rafRef.current) {
|
|
33970
|
+
cancelAnimationFrame(rafRef.current);
|
|
33971
|
+
rafRef.current = null;
|
|
33972
|
+
}
|
|
33973
|
+
visualRotationRef.current = null;
|
|
33974
|
+
visualReelYRef.current = null;
|
|
33975
|
+
pendingPopIdxRef.current = null;
|
|
33976
|
+
setSpinning(false);
|
|
33926
33977
|
setPool(participants);
|
|
33927
33978
|
setHistory([]);
|
|
33928
33979
|
setWinner(null);
|
|
@@ -33979,33 +34030,50 @@ function RandomPlayerPicker({
|
|
|
33979
34030
|
[dark]
|
|
33980
34031
|
);
|
|
33981
34032
|
const spin = useCallback47(() => {
|
|
33982
|
-
if (spinning
|
|
34033
|
+
if (spinning) return;
|
|
34034
|
+
let activePool = pool;
|
|
34035
|
+
if (popTimer.current) {
|
|
34036
|
+
clearTimeout(popTimer.current);
|
|
34037
|
+
popTimer.current = null;
|
|
34038
|
+
const pendingIdx = pendingPopIdxRef.current;
|
|
34039
|
+
pendingPopIdxRef.current = null;
|
|
34040
|
+
if (pendingIdx != null && pendingIdx >= 0 && pendingIdx < pool.length && removeWinnersRef.current) {
|
|
34041
|
+
activePool = pool.filter((_, i) => i !== pendingIdx);
|
|
34042
|
+
setPool(activePool);
|
|
34043
|
+
setWinner(null);
|
|
34044
|
+
}
|
|
34045
|
+
}
|
|
34046
|
+
const activeN = activePool.length;
|
|
34047
|
+
if (activeN < 2) return;
|
|
34048
|
+
const activeAnglePerWedge = 2 * Math.PI / activeN;
|
|
34049
|
+
const activeRotation = rotation;
|
|
34050
|
+
const activeReelY = reelY;
|
|
33983
34051
|
onSpinStart?.();
|
|
33984
|
-
const winnerIdx = weightedIndex(
|
|
34052
|
+
const winnerIdx = weightedIndex(activePool);
|
|
33985
34053
|
const minRev = minRevolutions + intensityCfg.revsBoost;
|
|
33986
34054
|
const maxRev = maxRevolutions + intensityCfg.revsBoost;
|
|
33987
34055
|
const minFullRevs = Math.ceil(minRev);
|
|
33988
34056
|
const maxFullRevs = Math.max(minFullRevs, Math.floor(maxRev));
|
|
33989
34057
|
const revolutions = minFullRevs + Math.floor(Math.random() * (maxFullRevs - minFullRevs + 1));
|
|
33990
|
-
const wedgeMidRad = winnerIdx *
|
|
34058
|
+
const wedgeMidRad = winnerIdx * activeAnglePerWedge + activeAnglePerWedge / 2;
|
|
33991
34059
|
const wedgeMidDeg = wedgeMidRad * 180 / Math.PI;
|
|
33992
|
-
const wheelWobble = (Math.random() - 0.5) * (
|
|
33993
|
-
const currentMod = (
|
|
34060
|
+
const wheelWobble = (Math.random() - 0.5) * (activeAnglePerWedge * 180 / Math.PI) * 0.6;
|
|
34061
|
+
const currentMod = (activeRotation % 360 + 360) % 360;
|
|
33994
34062
|
const targetMod = ((-wedgeMidDeg + wheelWobble) % 360 + 360) % 360;
|
|
33995
34063
|
let deltaMod = targetMod - currentMod;
|
|
33996
34064
|
if (deltaMod < 0) deltaMod += 360;
|
|
33997
|
-
const finalRotation =
|
|
34065
|
+
const finalRotation = activeRotation + deltaMod + revolutions * 360;
|
|
33998
34066
|
const centerOffsetRows = (reelVisibleRows - 1) / 2;
|
|
33999
34067
|
const centerOffsetPx = centerOffsetRows * reelRowHeight;
|
|
34000
34068
|
const currentCenterRow = Math.round(
|
|
34001
|
-
(
|
|
34069
|
+
(activeReelY + centerOffsetPx) / reelRowHeight
|
|
34002
34070
|
);
|
|
34003
|
-
const currentCenterMod = mod(currentCenterRow,
|
|
34004
|
-
const targetRowOffset = (winnerIdx - currentCenterMod +
|
|
34005
|
-
const reelRevRows = Math.round(revolutions *
|
|
34006
|
-
const finalReelY =
|
|
34007
|
-
const settledWinnerIdx = mode === "reel" ? reelIndexAtCenter(finalReelY, centerOffsetPx, reelRowHeight,
|
|
34008
|
-
const finalWinnerIdx = settledWinnerIdx >= 0 && settledWinnerIdx <
|
|
34071
|
+
const currentCenterMod = mod(currentCenterRow, activeN);
|
|
34072
|
+
const targetRowOffset = (winnerIdx - currentCenterMod + activeN) % activeN;
|
|
34073
|
+
const reelRevRows = Math.round(revolutions * activeN);
|
|
34074
|
+
const finalReelY = activeReelY + (reelRevRows + targetRowOffset) * reelRowHeight;
|
|
34075
|
+
const settledWinnerIdx = mode === "reel" ? reelIndexAtCenter(finalReelY, centerOffsetPx, reelRowHeight, activeN) : wheelIndexAtPointer(finalRotation, activeN);
|
|
34076
|
+
const finalWinnerIdx = settledWinnerIdx >= 0 && settledWinnerIdx < activePool.length ? settledWinnerIdx : winnerIdx;
|
|
34009
34077
|
const effectiveDuration = reducedMotion ? 250 : Math.round(spinDuration * intensityCfg.durationMult);
|
|
34010
34078
|
setSpinning(true);
|
|
34011
34079
|
setWinner(null);
|
|
@@ -34015,10 +34083,59 @@ function RandomPlayerPicker({
|
|
|
34015
34083
|
} else {
|
|
34016
34084
|
setRotation(finalRotation);
|
|
34017
34085
|
}
|
|
34018
|
-
|
|
34086
|
+
const fireTick = () => {
|
|
34087
|
+
onTick?.();
|
|
34088
|
+
const ta = tickAudioRef.current;
|
|
34089
|
+
if (ta) {
|
|
34090
|
+
try {
|
|
34091
|
+
ta.currentTime = 0;
|
|
34092
|
+
void ta.play().catch(() => {
|
|
34093
|
+
});
|
|
34094
|
+
} catch {
|
|
34095
|
+
}
|
|
34096
|
+
}
|
|
34097
|
+
};
|
|
34098
|
+
if (physicsDriven) {
|
|
34099
|
+
const from = mode === "reel" ? activeReelY : activeRotation;
|
|
34100
|
+
const to = mode === "reel" ? finalReelY : finalRotation;
|
|
34101
|
+
const crossUnit = mode === "reel" ? reelRowHeight : activeAnglePerWedge * 180 / Math.PI;
|
|
34102
|
+
if (mode === "reel") visualReelYRef.current = from;
|
|
34103
|
+
else visualRotationRef.current = from;
|
|
34104
|
+
let crossings = 0;
|
|
34105
|
+
const t0 = performance.now();
|
|
34106
|
+
const step = (now) => {
|
|
34107
|
+
const t = Math.min(1, (now - t0) / effectiveDuration);
|
|
34108
|
+
const value = from + (to - from) * physicsRampDown(t);
|
|
34109
|
+
if (mode === "reel") {
|
|
34110
|
+
visualReelYRef.current = value;
|
|
34111
|
+
const el = reelTapeRef.current;
|
|
34112
|
+
if (el) {
|
|
34113
|
+
el.style.transform = `translateY(${-(value + REEL_LEAD_IN * reelRowHeight)}px)`;
|
|
34114
|
+
}
|
|
34115
|
+
} else {
|
|
34116
|
+
visualRotationRef.current = value;
|
|
34117
|
+
const el = wheelSvgRef.current;
|
|
34118
|
+
if (el) el.style.transform = `rotate(${value}deg)`;
|
|
34119
|
+
}
|
|
34120
|
+
const crossed = Math.floor(Math.abs(value - from) / crossUnit);
|
|
34121
|
+
if (crossed > crossings) {
|
|
34122
|
+
crossings = crossed;
|
|
34123
|
+
fireTick();
|
|
34124
|
+
}
|
|
34125
|
+
if (t < 1) {
|
|
34126
|
+
rafRef.current = requestAnimationFrame(step);
|
|
34127
|
+
} else {
|
|
34128
|
+
rafRef.current = null;
|
|
34129
|
+
visualRotationRef.current = null;
|
|
34130
|
+
visualReelYRef.current = null;
|
|
34131
|
+
}
|
|
34132
|
+
};
|
|
34133
|
+
rafRef.current = requestAnimationFrame(step);
|
|
34134
|
+
}
|
|
34135
|
+
if (!physicsDriven && (onTick || tickAudioRef.current) && !reducedMotion) {
|
|
34019
34136
|
const wedges = mode === "reel" ? Math.max(1, reelRevRows + targetRowOffset) : (() => {
|
|
34020
34137
|
const totalDelta = deltaMod + revolutions * 360;
|
|
34021
|
-
const wedgeDeg =
|
|
34138
|
+
const wedgeDeg = activeAnglePerWedge * 180 / Math.PI;
|
|
34022
34139
|
return Math.max(
|
|
34023
34140
|
1,
|
|
34024
34141
|
Math.round(totalDelta / Math.max(wedgeDeg, 1))
|
|
@@ -34033,16 +34150,7 @@ function RandomPlayerPicker({
|
|
|
34033
34150
|
const eased = 1 - Math.pow(1 - progress, 3);
|
|
34034
34151
|
const interval = startInterval + (endInterval - startInterval) * eased;
|
|
34035
34152
|
tickInterval.current = setTimeout(() => {
|
|
34036
|
-
|
|
34037
|
-
const ta = tickAudioRef.current;
|
|
34038
|
-
if (ta) {
|
|
34039
|
-
try {
|
|
34040
|
-
ta.currentTime = 0;
|
|
34041
|
-
void ta.play().catch(() => {
|
|
34042
|
-
});
|
|
34043
|
-
} catch {
|
|
34044
|
-
}
|
|
34045
|
-
}
|
|
34153
|
+
fireTick();
|
|
34046
34154
|
elapsed += interval;
|
|
34047
34155
|
schedule();
|
|
34048
34156
|
}, interval);
|
|
@@ -34050,7 +34158,7 @@ function RandomPlayerPicker({
|
|
|
34050
34158
|
schedule();
|
|
34051
34159
|
}
|
|
34052
34160
|
winnerTimer.current = setTimeout(() => {
|
|
34053
|
-
const picked =
|
|
34161
|
+
const picked = activePool[finalWinnerIdx];
|
|
34054
34162
|
if (!picked) {
|
|
34055
34163
|
setSpinning(false);
|
|
34056
34164
|
return;
|
|
@@ -34096,9 +34204,13 @@ function RandomPlayerPicker({
|
|
|
34096
34204
|
particleTimer.current = setTimeout(() => setParticles([]), 2400);
|
|
34097
34205
|
}
|
|
34098
34206
|
if (removeWinners) {
|
|
34207
|
+
pendingPopIdxRef.current = finalWinnerIdx;
|
|
34099
34208
|
popTimer.current = setTimeout(() => {
|
|
34209
|
+
popTimer.current = null;
|
|
34210
|
+
pendingPopIdxRef.current = null;
|
|
34211
|
+
if (!removeWinnersRef.current) return;
|
|
34100
34212
|
setPool((p) => p.filter((_, i) => i !== finalWinnerIdx));
|
|
34101
|
-
setWinner(null);
|
|
34213
|
+
if (activePool.length - 1 >= 2) setWinner(null);
|
|
34102
34214
|
setRotation(0);
|
|
34103
34215
|
setReelY(0);
|
|
34104
34216
|
}, 2800);
|
|
@@ -34107,8 +34219,6 @@ function RandomPlayerPicker({
|
|
|
34107
34219
|
}, [
|
|
34108
34220
|
pool,
|
|
34109
34221
|
spinning,
|
|
34110
|
-
n,
|
|
34111
|
-
anglePerWedge,
|
|
34112
34222
|
rotation,
|
|
34113
34223
|
reelY,
|
|
34114
34224
|
mode,
|
|
@@ -34123,6 +34233,7 @@ function RandomPlayerPicker({
|
|
|
34123
34233
|
maxRevolutions,
|
|
34124
34234
|
history,
|
|
34125
34235
|
reducedMotion,
|
|
34236
|
+
physicsDriven,
|
|
34126
34237
|
enableHaptics,
|
|
34127
34238
|
intensityCfg.particles,
|
|
34128
34239
|
intensityCfg.durationMult,
|
|
@@ -34134,16 +34245,25 @@ function RandomPlayerPicker({
|
|
|
34134
34245
|
if (spinRef && spinRef.current === spin) spinRef.current = null;
|
|
34135
34246
|
};
|
|
34136
34247
|
}, [spin, spinRef]);
|
|
34248
|
+
const onPoolChangeRef = useRef42(onPoolChange);
|
|
34249
|
+
useEffect57(() => {
|
|
34250
|
+
onPoolChangeRef.current = onPoolChange;
|
|
34251
|
+
});
|
|
34252
|
+
useEffect57(() => {
|
|
34253
|
+
onPoolChangeRef.current?.(pool);
|
|
34254
|
+
}, [pool]);
|
|
34137
34255
|
useEffect57(
|
|
34138
34256
|
() => () => {
|
|
34139
34257
|
if (winnerTimer.current) clearTimeout(winnerTimer.current);
|
|
34140
34258
|
if (particleTimer.current) clearTimeout(particleTimer.current);
|
|
34141
34259
|
if (popTimer.current) clearTimeout(popTimer.current);
|
|
34142
34260
|
if (tickInterval.current) clearTimeout(tickInterval.current);
|
|
34261
|
+
if (rafRef.current) cancelAnimationFrame(rafRef.current);
|
|
34143
34262
|
},
|
|
34144
34263
|
[]
|
|
34145
34264
|
);
|
|
34146
34265
|
useEffect57(() => {
|
|
34266
|
+
if (trigger === "external") return;
|
|
34147
34267
|
const el = containerRef.current;
|
|
34148
34268
|
if (!el) return;
|
|
34149
34269
|
const h = (e) => {
|
|
@@ -34154,8 +34274,28 @@ function RandomPlayerPicker({
|
|
|
34154
34274
|
};
|
|
34155
34275
|
el.addEventListener("keydown", h);
|
|
34156
34276
|
return () => el.removeEventListener("keydown", h);
|
|
34157
|
-
}, [spin]);
|
|
34277
|
+
}, [spin, trigger]);
|
|
34158
34278
|
const reset = useCallback47(() => {
|
|
34279
|
+
if (winnerTimer.current) {
|
|
34280
|
+
clearTimeout(winnerTimer.current);
|
|
34281
|
+
winnerTimer.current = null;
|
|
34282
|
+
}
|
|
34283
|
+
if (tickInterval.current) {
|
|
34284
|
+
clearTimeout(tickInterval.current);
|
|
34285
|
+
tickInterval.current = null;
|
|
34286
|
+
}
|
|
34287
|
+
if (popTimer.current) {
|
|
34288
|
+
clearTimeout(popTimer.current);
|
|
34289
|
+
popTimer.current = null;
|
|
34290
|
+
}
|
|
34291
|
+
if (rafRef.current) {
|
|
34292
|
+
cancelAnimationFrame(rafRef.current);
|
|
34293
|
+
rafRef.current = null;
|
|
34294
|
+
}
|
|
34295
|
+
visualRotationRef.current = null;
|
|
34296
|
+
visualReelYRef.current = null;
|
|
34297
|
+
pendingPopIdxRef.current = null;
|
|
34298
|
+
setSpinning(false);
|
|
34159
34299
|
setPool(participants);
|
|
34160
34300
|
setHistory([]);
|
|
34161
34301
|
setWinner(null);
|
|
@@ -34163,9 +34303,17 @@ function RandomPlayerPicker({
|
|
|
34163
34303
|
setReelY(0);
|
|
34164
34304
|
setParticles([]);
|
|
34165
34305
|
}, [participants]);
|
|
34306
|
+
useEffect57(() => {
|
|
34307
|
+
if (resetRef) resetRef.current = reset;
|
|
34308
|
+
return () => {
|
|
34309
|
+
if (resetRef && resetRef.current === reset) resetRef.current = null;
|
|
34310
|
+
};
|
|
34311
|
+
}, [reset, resetRef]);
|
|
34166
34312
|
const isIdle = n < 2;
|
|
34313
|
+
const drainedByDraft = isIdle && participants.length >= 2;
|
|
34314
|
+
const effectiveIdleCaption = drainedByDraft ? draftCompleteCaption : placeholderCaption;
|
|
34167
34315
|
const placeholderDiscColor = dark ? "#2a3344" : "#e2e8f0";
|
|
34168
|
-
if (isIdle && placeholderMode === "message") {
|
|
34316
|
+
if (isIdle && !drainedByDraft && placeholderMode === "message") {
|
|
34169
34317
|
return /* @__PURE__ */ jsx117(
|
|
34170
34318
|
"div",
|
|
34171
34319
|
{
|
|
@@ -34187,6 +34335,8 @@ function RandomPlayerPicker({
|
|
|
34187
34335
|
const shakeKeyframe = `rppShake_${intensity}`;
|
|
34188
34336
|
const glowKeyframe = `rppGlow_${intensity}`;
|
|
34189
34337
|
const pulseKeyframe = `rppPulse_${intensity}`;
|
|
34338
|
+
const displayRotation = visualRotationRef.current ?? rotation;
|
|
34339
|
+
const displayReelY = visualReelYRef.current ?? reelY;
|
|
34190
34340
|
return /* @__PURE__ */ jsxs89(
|
|
34191
34341
|
"div",
|
|
34192
34342
|
{
|
|
@@ -34233,13 +34383,33 @@ function RandomPlayerPicker({
|
|
|
34233
34383
|
[data-rpp-anim] { animation: none !important; }
|
|
34234
34384
|
}
|
|
34235
34385
|
` }),
|
|
34236
|
-
|
|
34386
|
+
isIdle && placeholderMode === "message" ? (
|
|
34387
|
+
/* Drained draft in "message" mode — compact box in the wheel's
|
|
34388
|
+
place; the winner card / stats / controls / history below stay
|
|
34389
|
+
mounted so the endgame isn't a dead end. */
|
|
34390
|
+
/* @__PURE__ */ jsx117(
|
|
34391
|
+
"div",
|
|
34392
|
+
{
|
|
34393
|
+
style: {
|
|
34394
|
+
padding: "2rem 1rem",
|
|
34395
|
+
textAlign: "center",
|
|
34396
|
+
color: c.dim,
|
|
34397
|
+
borderRadius: 12,
|
|
34398
|
+
border: `1px dashed ${c.border}`
|
|
34399
|
+
},
|
|
34400
|
+
children: effectiveIdleCaption
|
|
34401
|
+
}
|
|
34402
|
+
)
|
|
34403
|
+
) : mode === "reel" ? /* @__PURE__ */ jsx117(
|
|
34237
34404
|
ReelStage,
|
|
34238
34405
|
{
|
|
34239
34406
|
pool,
|
|
34240
34407
|
winner,
|
|
34241
34408
|
spinning,
|
|
34242
34409
|
reelY,
|
|
34410
|
+
displayY: displayReelY,
|
|
34411
|
+
cssTransition: !physicsDriven,
|
|
34412
|
+
tapeRef: reelTapeRef,
|
|
34243
34413
|
reelVisibleRows,
|
|
34244
34414
|
reelRowHeight,
|
|
34245
34415
|
size,
|
|
@@ -34248,7 +34418,7 @@ function RandomPlayerPicker({
|
|
|
34248
34418
|
dark,
|
|
34249
34419
|
isIdle,
|
|
34250
34420
|
placeholderMode,
|
|
34251
|
-
placeholderCaption,
|
|
34421
|
+
placeholderCaption: effectiveIdleCaption,
|
|
34252
34422
|
placeholderDiscColor,
|
|
34253
34423
|
spinDuration,
|
|
34254
34424
|
intensityCfg,
|
|
@@ -34305,6 +34475,7 @@ function RandomPlayerPicker({
|
|
|
34305
34475
|
/* @__PURE__ */ jsxs89(
|
|
34306
34476
|
"svg",
|
|
34307
34477
|
{
|
|
34478
|
+
ref: wheelSvgRef,
|
|
34308
34479
|
width: size,
|
|
34309
34480
|
height: size,
|
|
34310
34481
|
"aria-hidden": "true",
|
|
@@ -34313,8 +34484,8 @@ function RandomPlayerPicker({
|
|
|
34313
34484
|
top: 36,
|
|
34314
34485
|
left: 0,
|
|
34315
34486
|
transformOrigin: `${cx}px ${cy}px`,
|
|
34316
|
-
transform: `rotate(${
|
|
34317
|
-
transition: spinning ? `transform ${reducedMotion ? 250 : Math.round(spinDuration * intensityCfg.durationMult)}ms ${intensityCfg.easing}` : "none",
|
|
34487
|
+
transform: `rotate(${displayRotation}deg)`,
|
|
34488
|
+
transition: spinning && !physicsDriven ? `transform ${reducedMotion ? 250 : Math.round(spinDuration * intensityCfg.durationMult)}ms ${intensityCfg.easing}` : "none",
|
|
34318
34489
|
filter: `drop-shadow(0 8px 28px ${dark ? "rgba(0,0,0,0.55)" : "rgba(0,0,0,0.15)"})`
|
|
34319
34490
|
},
|
|
34320
34491
|
children: [
|
|
@@ -34467,7 +34638,7 @@ function RandomPlayerPicker({
|
|
|
34467
34638
|
pointerEvents: "none",
|
|
34468
34639
|
zIndex: 3
|
|
34469
34640
|
},
|
|
34470
|
-
children:
|
|
34641
|
+
children: effectiveIdleCaption
|
|
34471
34642
|
}
|
|
34472
34643
|
),
|
|
34473
34644
|
particles.map((p) => /* @__PURE__ */ jsx117(
|
|
@@ -38628,27 +38799,28 @@ function GameTimer({
|
|
|
38628
38799
|
const now = performance.now();
|
|
38629
38800
|
const delta = now - lastTickRef.current;
|
|
38630
38801
|
lastTickRef.current = now;
|
|
38631
|
-
|
|
38632
|
-
|
|
38633
|
-
|
|
38634
|
-
|
|
38635
|
-
|
|
38636
|
-
|
|
38637
|
-
|
|
38802
|
+
const next = elapsedRef.current + delta;
|
|
38803
|
+
if (mode === "countdown" && next >= durationMs) {
|
|
38804
|
+
elapsedRef.current = durationMs;
|
|
38805
|
+
setElapsedMs(durationMs);
|
|
38806
|
+
setRunning(false);
|
|
38807
|
+
setCompleted(true);
|
|
38808
|
+
onComplete?.();
|
|
38809
|
+
return;
|
|
38810
|
+
}
|
|
38811
|
+
if (mode === "countdown") {
|
|
38812
|
+
const secLeft = Math.ceil((durationMs - next) / 1e3);
|
|
38813
|
+
if (secLeft <= warningAt && !warningFiredRef.current) {
|
|
38814
|
+
warningFiredRef.current = true;
|
|
38815
|
+
onWarning?.();
|
|
38638
38816
|
}
|
|
38639
|
-
if (
|
|
38640
|
-
|
|
38641
|
-
|
|
38642
|
-
warningFiredRef.current = true;
|
|
38643
|
-
onWarning?.();
|
|
38644
|
-
}
|
|
38645
|
-
if (secLeft <= dangerAt && !dangerFiredRef.current) {
|
|
38646
|
-
dangerFiredRef.current = true;
|
|
38647
|
-
onDanger?.();
|
|
38648
|
-
}
|
|
38817
|
+
if (secLeft <= dangerAt && !dangerFiredRef.current) {
|
|
38818
|
+
dangerFiredRef.current = true;
|
|
38819
|
+
onDanger?.();
|
|
38649
38820
|
}
|
|
38650
|
-
|
|
38651
|
-
|
|
38821
|
+
}
|
|
38822
|
+
elapsedRef.current = next;
|
|
38823
|
+
setElapsedMs(next);
|
|
38652
38824
|
rafRef.current = requestAnimationFrame(tick);
|
|
38653
38825
|
}, [mode, durationMs, warningAt, dangerAt, onComplete, onWarning, onDanger]);
|
|
38654
38826
|
useEffect61(() => {
|
|
@@ -38669,6 +38841,7 @@ function GameTimer({
|
|
|
38669
38841
|
}, []);
|
|
38670
38842
|
const reset = useCallback51(() => {
|
|
38671
38843
|
setRunning(false);
|
|
38844
|
+
elapsedRef.current = 0;
|
|
38672
38845
|
setElapsedMs(0);
|
|
38673
38846
|
setCompleted(false);
|
|
38674
38847
|
setLaps([]);
|
|
@@ -40182,8 +40355,10 @@ function TeamGenerator({
|
|
|
40182
40355
|
minPlayersPerTeam = 2,
|
|
40183
40356
|
maxPlayers = Number.POSITIVE_INFINITY,
|
|
40184
40357
|
defaultNames = [],
|
|
40358
|
+
defaultTeams,
|
|
40185
40359
|
onNamesChange,
|
|
40186
40360
|
onGenerate,
|
|
40361
|
+
onTeamsChange,
|
|
40187
40362
|
onTeamCountChange,
|
|
40188
40363
|
labels,
|
|
40189
40364
|
ariaLabel = "Team generator",
|
|
@@ -40198,12 +40373,17 @@ function TeamGenerator({
|
|
|
40198
40373
|
const [names, setNames] = useState79(defaultNames);
|
|
40199
40374
|
const [input, setInput] = useState79("");
|
|
40200
40375
|
const [teamCount, setTeamCount] = useState79(initialCount);
|
|
40201
|
-
const [teams, setTeams] = useState79(
|
|
40376
|
+
const [teams, setTeams] = useState79(
|
|
40377
|
+
defaultTeams && defaultTeams.length > 0 ? defaultTeams : null
|
|
40378
|
+
);
|
|
40202
40379
|
const inputRef = useRef48(null);
|
|
40203
40380
|
const minNames = teamCount * minPlayersPerTeam;
|
|
40204
40381
|
useEffect63(() => {
|
|
40205
40382
|
onNamesChange?.(names);
|
|
40206
40383
|
}, [names, onNamesChange]);
|
|
40384
|
+
useEffect63(() => {
|
|
40385
|
+
onTeamsChange?.(teams);
|
|
40386
|
+
}, [teams, onTeamsChange]);
|
|
40207
40387
|
const addName = useCallback53(() => {
|
|
40208
40388
|
const trimmed = input.trim();
|
|
40209
40389
|
if (!trimmed) return;
|
|
@@ -40830,6 +41010,8 @@ function RoundRobinScheduler({
|
|
|
40830
41010
|
theme: theme5 = "slate",
|
|
40831
41011
|
darkMode,
|
|
40832
41012
|
minTeams = 3,
|
|
41013
|
+
maxTeams,
|
|
41014
|
+
nameMaxLength,
|
|
40833
41015
|
defaultTeams = [],
|
|
40834
41016
|
resumeRounds,
|
|
40835
41017
|
onTeamsChange,
|
|
@@ -40852,39 +41034,41 @@ function RoundRobinScheduler({
|
|
|
40852
41034
|
useEffect64(() => {
|
|
40853
41035
|
onTeamsChange?.(teams);
|
|
40854
41036
|
}, [teams, onTeamsChange]);
|
|
40855
|
-
const
|
|
40856
|
-
|
|
40857
|
-
if (names.length === 0) return;
|
|
40858
|
-
setTeams((prev) => {
|
|
40859
|
-
const existing = new Set(prev.map((t) => t.toLowerCase()));
|
|
40860
|
-
const next = [...prev];
|
|
40861
|
-
for (const name of names) {
|
|
40862
|
-
if (!existing.has(name.toLowerCase())) {
|
|
40863
|
-
existing.add(name.toLowerCase());
|
|
40864
|
-
next.push(name);
|
|
40865
|
-
}
|
|
40866
|
-
}
|
|
40867
|
-
return next;
|
|
40868
|
-
});
|
|
40869
|
-
setInput("");
|
|
40870
|
-
setRounds([]);
|
|
40871
|
-
}, []);
|
|
40872
|
-
const importTeams = useCallback54((incoming) => {
|
|
40873
|
-
if (incoming.length === 0) return;
|
|
40874
|
-
setTeams((prev) => {
|
|
41037
|
+
const mergeNames = useCallback54(
|
|
41038
|
+
(prev, incoming) => {
|
|
40875
41039
|
const existing = new Set(prev.map((t) => t.toLowerCase()));
|
|
40876
41040
|
const next = [...prev];
|
|
40877
|
-
for (const
|
|
41041
|
+
for (const rawName of incoming) {
|
|
41042
|
+
if (maxTeams != null && next.length >= maxTeams) break;
|
|
41043
|
+
const name = nameMaxLength != null ? rawName.slice(0, nameMaxLength) : rawName;
|
|
40878
41044
|
const key = name.toLowerCase();
|
|
40879
|
-
if (!existing.has(key)) {
|
|
41045
|
+
if (name.length > 0 && !existing.has(key)) {
|
|
40880
41046
|
existing.add(key);
|
|
40881
41047
|
next.push(name);
|
|
40882
41048
|
}
|
|
40883
41049
|
}
|
|
40884
41050
|
return next;
|
|
40885
|
-
}
|
|
40886
|
-
|
|
40887
|
-
|
|
41051
|
+
},
|
|
41052
|
+
[maxTeams, nameMaxLength]
|
|
41053
|
+
);
|
|
41054
|
+
const addTeams = useCallback54(
|
|
41055
|
+
(raw) => {
|
|
41056
|
+
const names = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
41057
|
+
if (names.length === 0) return;
|
|
41058
|
+
setTeams((prev) => mergeNames(prev, names));
|
|
41059
|
+
setInput("");
|
|
41060
|
+
setRounds([]);
|
|
41061
|
+
},
|
|
41062
|
+
[mergeNames]
|
|
41063
|
+
);
|
|
41064
|
+
const importTeams = useCallback54(
|
|
41065
|
+
(incoming) => {
|
|
41066
|
+
if (incoming.length === 0) return;
|
|
41067
|
+
setTeams((prev) => mergeNames(prev, incoming));
|
|
41068
|
+
setRounds([]);
|
|
41069
|
+
},
|
|
41070
|
+
[mergeNames]
|
|
41071
|
+
);
|
|
40888
41072
|
const handleKeyDown = useCallback54(
|
|
40889
41073
|
(e) => {
|
|
40890
41074
|
if (e.key === "Enter") {
|
|
@@ -41378,6 +41562,8 @@ function Scoreboard({
|
|
|
41378
41562
|
onTeamsChange,
|
|
41379
41563
|
onRoundAdvance,
|
|
41380
41564
|
onNewGame,
|
|
41565
|
+
onReset,
|
|
41566
|
+
nameMaxLength,
|
|
41381
41567
|
labels,
|
|
41382
41568
|
ariaLabel = "Scoreboard",
|
|
41383
41569
|
className
|
|
@@ -41452,7 +41638,8 @@ function Scoreboard({
|
|
|
41452
41638
|
setRound(1);
|
|
41453
41639
|
setHistory([]);
|
|
41454
41640
|
setHistoryOpen(false);
|
|
41455
|
-
|
|
41641
|
+
onReset?.();
|
|
41642
|
+
}, [onReset]);
|
|
41456
41643
|
const newGame = useCallback55(() => {
|
|
41457
41644
|
setTeams(createDefaultTeams(seedCount, L.teamNamePrefix));
|
|
41458
41645
|
setRound(1);
|
|
@@ -41674,6 +41861,7 @@ function Scoreboard({
|
|
|
41674
41861
|
{
|
|
41675
41862
|
type: "text",
|
|
41676
41863
|
value: team.name,
|
|
41864
|
+
maxLength: nameMaxLength,
|
|
41677
41865
|
onChange: (e) => updateName(index, e.target.value),
|
|
41678
41866
|
style: nameInputStyle,
|
|
41679
41867
|
"aria-label": `Team ${index + 1} name`
|