@digilogiclabs/saas-factory-ui 2.3.0 → 2.4.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 +20 -3
- package/dist/index.d.ts +20 -3
- package/dist/index.js +137 -65
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +137 -65
- package/dist/index.mjs.map +1 -1
- package/dist/web/index.d.mts +20 -3
- package/dist/web/index.d.ts +20 -3
- package/dist/web/index.js +243 -169
- package/dist/web/index.js.map +1 -1
- package/dist/web/index.mjs +186 -112
- package/dist/web/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/web/index.mjs
CHANGED
|
@@ -30226,10 +30226,10 @@ function TiltCard({
|
|
|
30226
30226
|
// src/components/ui/dice-roller/index.tsx
|
|
30227
30227
|
import {
|
|
30228
30228
|
useCallback as useCallback41,
|
|
30229
|
-
useEffect as
|
|
30229
|
+
useEffect as useEffect51,
|
|
30230
30230
|
useMemo as useMemo26,
|
|
30231
30231
|
useRef as useRef40,
|
|
30232
|
-
useState as
|
|
30232
|
+
useState as useState66
|
|
30233
30233
|
} from "react";
|
|
30234
30234
|
|
|
30235
30235
|
// src/hooks/web/use-dark-mode.ts
|
|
@@ -30279,21 +30279,6 @@ function useDarkMode(forced) {
|
|
|
30279
30279
|
return dark;
|
|
30280
30280
|
}
|
|
30281
30281
|
|
|
30282
|
-
// src/hooks/web/use-prefers-reduced-motion.ts
|
|
30283
|
-
import { useEffect as useEffect51, useState as useState66 } from "react";
|
|
30284
|
-
function usePrefersReducedMotion() {
|
|
30285
|
-
const [reduced, setReduced] = useState66(false);
|
|
30286
|
-
useEffect51(() => {
|
|
30287
|
-
if (typeof window === "undefined" || !window.matchMedia) return;
|
|
30288
|
-
const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
30289
|
-
setReduced(mq.matches);
|
|
30290
|
-
const read = () => setReduced(mq.matches);
|
|
30291
|
-
mq.addEventListener("change", read);
|
|
30292
|
-
return () => mq.removeEventListener("change", read);
|
|
30293
|
-
}, []);
|
|
30294
|
-
return reduced;
|
|
30295
|
-
}
|
|
30296
|
-
|
|
30297
30282
|
// src/components/ui/dice-roller/index.tsx
|
|
30298
30283
|
import { Fragment as Fragment30, jsx as jsx113, jsxs as jsxs86 } from "react/jsx-runtime";
|
|
30299
30284
|
var PRESETS = {
|
|
@@ -30512,8 +30497,8 @@ function simulateDice(arenaW, arenaH, diceSize, starts, numDice) {
|
|
|
30512
30497
|
return frames;
|
|
30513
30498
|
}
|
|
30514
30499
|
function useArenaBounds(ref) {
|
|
30515
|
-
const [bounds, setBounds] =
|
|
30516
|
-
|
|
30500
|
+
const [bounds, setBounds] = useState66({ w: 400, h: 260 });
|
|
30501
|
+
useEffect51(() => {
|
|
30517
30502
|
if (!ref.current) return;
|
|
30518
30503
|
const el = ref.current;
|
|
30519
30504
|
const update = () => setBounds({ w: el.offsetWidth, h: el.offsetHeight });
|
|
@@ -30568,10 +30553,9 @@ function DiceRoller({
|
|
|
30568
30553
|
}) {
|
|
30569
30554
|
const clampedCount = Math.max(1, Math.min(MAX_COUNT, count2));
|
|
30570
30555
|
const dark = useDarkMode(darkMode);
|
|
30571
|
-
const
|
|
30572
|
-
const [
|
|
30573
|
-
const [
|
|
30574
|
-
const [history, setHistory] = useState67([]);
|
|
30556
|
+
const [rolling, setRolling] = useState66(false);
|
|
30557
|
+
const [results, setResults] = useState66(null);
|
|
30558
|
+
const [history, setHistory] = useState66([]);
|
|
30575
30559
|
const dieWrapperRefs = useRef40([]);
|
|
30576
30560
|
const cubeRefs = useRef40([]);
|
|
30577
30561
|
const shadowRefs = useRef40([]);
|
|
@@ -30704,7 +30688,15 @@ function DiceRoller({
|
|
|
30704
30688
|
},
|
|
30705
30689
|
[]
|
|
30706
30690
|
);
|
|
30707
|
-
|
|
30691
|
+
useEffect51(() => {
|
|
30692
|
+
if (timersRef.current.length > 0) {
|
|
30693
|
+
timersRef.current.forEach((t) => clearTimeout(t));
|
|
30694
|
+
timersRef.current = [];
|
|
30695
|
+
}
|
|
30696
|
+
if (rollingRef.current) {
|
|
30697
|
+
rollingRef.current = false;
|
|
30698
|
+
setRolling(false);
|
|
30699
|
+
}
|
|
30708
30700
|
const rawW = arenaRef.current?.offsetWidth ?? 0;
|
|
30709
30701
|
const rawH = arenaRef.current?.offsetHeight ?? 0;
|
|
30710
30702
|
const w = rawW > 20 ? rawW : arena.w > 20 ? arena.w : 400;
|
|
@@ -30737,7 +30729,7 @@ function DiceRoller({
|
|
|
30737
30729
|
defaultPositions,
|
|
30738
30730
|
half
|
|
30739
30731
|
]);
|
|
30740
|
-
|
|
30732
|
+
useEffect51(() => {
|
|
30741
30733
|
setHistory([]);
|
|
30742
30734
|
}, [sides]);
|
|
30743
30735
|
const roll = useCallback41(() => {
|
|
@@ -30861,25 +30853,24 @@ function DiceRoller({
|
|
|
30861
30853
|
defaultPositions,
|
|
30862
30854
|
effectiveSize,
|
|
30863
30855
|
half,
|
|
30864
|
-
reducedMotion,
|
|
30865
30856
|
onRoll,
|
|
30866
30857
|
writeCubeRotation,
|
|
30867
30858
|
writeDiePosition,
|
|
30868
30859
|
showHistory,
|
|
30869
30860
|
historyMax
|
|
30870
30861
|
]);
|
|
30871
|
-
|
|
30862
|
+
useEffect51(() => {
|
|
30872
30863
|
if (rollRef) rollRef.current = roll;
|
|
30873
30864
|
return () => {
|
|
30874
30865
|
if (rollRef && rollRef.current === roll) rollRef.current = null;
|
|
30875
30866
|
};
|
|
30876
30867
|
}, [roll, rollRef]);
|
|
30877
|
-
|
|
30868
|
+
useEffect51(() => {
|
|
30878
30869
|
if (!autoRoll) return;
|
|
30879
30870
|
const t = setTimeout(roll, 300);
|
|
30880
30871
|
return () => clearTimeout(t);
|
|
30881
30872
|
}, []);
|
|
30882
|
-
|
|
30873
|
+
useEffect51(
|
|
30883
30874
|
() => () => {
|
|
30884
30875
|
timersRef.current.forEach((t) => clearTimeout(t));
|
|
30885
30876
|
timersRef.current = [];
|
|
@@ -31180,10 +31171,10 @@ DiceRoller.displayName = "DiceRoller";
|
|
|
31180
31171
|
|
|
31181
31172
|
// src/components/ui/bracket-generator/index.tsx
|
|
31182
31173
|
import {
|
|
31183
|
-
useState as
|
|
31174
|
+
useState as useState67,
|
|
31184
31175
|
useCallback as useCallback42,
|
|
31185
31176
|
useRef as useRef41,
|
|
31186
|
-
useEffect as
|
|
31177
|
+
useEffect as useEffect52,
|
|
31187
31178
|
useMemo as useMemo27,
|
|
31188
31179
|
useLayoutEffect as useLayoutEffect2
|
|
31189
31180
|
} from "react";
|
|
@@ -31406,7 +31397,7 @@ function computeLayout(rounds, firstRoundCount) {
|
|
|
31406
31397
|
};
|
|
31407
31398
|
}
|
|
31408
31399
|
function usePanZoom(viewportRef, contentSize, minZoom, maxZoom) {
|
|
31409
|
-
const [transform, setTransform] =
|
|
31400
|
+
const [transform, setTransform] = useState67({
|
|
31410
31401
|
x: 0,
|
|
31411
31402
|
y: 0,
|
|
31412
31403
|
scale: 1
|
|
@@ -31426,7 +31417,7 @@ function usePanZoom(viewportRef, contentSize, minZoom, maxZoom) {
|
|
|
31426
31417
|
},
|
|
31427
31418
|
[contentSize]
|
|
31428
31419
|
);
|
|
31429
|
-
|
|
31420
|
+
useEffect52(() => {
|
|
31430
31421
|
const el = viewportRef.current;
|
|
31431
31422
|
if (!el) return;
|
|
31432
31423
|
const onWheel = (e) => {
|
|
@@ -31657,12 +31648,12 @@ function BracketGenerator({
|
|
|
31657
31648
|
ariaLabel = "Tournament bracket"
|
|
31658
31649
|
}) {
|
|
31659
31650
|
const dark = useDarkMode(darkMode);
|
|
31660
|
-
const [bracketState, setBracketState] =
|
|
31651
|
+
const [bracketState, setBracketState] = useState67({
|
|
31661
31652
|
matches: [],
|
|
31662
31653
|
rounds: 0,
|
|
31663
31654
|
champion: null
|
|
31664
31655
|
});
|
|
31665
|
-
const [simulating, setSimulating] =
|
|
31656
|
+
const [simulating, setSimulating] = useState67(false);
|
|
31666
31657
|
const autoRef = useRef41(null);
|
|
31667
31658
|
const viewportRef = useRef41(null);
|
|
31668
31659
|
const effectiveParticipants = useMemo27(() => {
|
|
@@ -31717,7 +31708,7 @@ function BracketGenerator({
|
|
|
31717
31708
|
minZoom,
|
|
31718
31709
|
maxZoom
|
|
31719
31710
|
);
|
|
31720
|
-
const [miniMapHidden, setMiniMapHidden] =
|
|
31711
|
+
const [miniMapHidden, setMiniMapHidden] = useState67(false);
|
|
31721
31712
|
const miniMapDragRef = useRef41(false);
|
|
31722
31713
|
const resumeRef = useRef41(resumeMatches);
|
|
31723
31714
|
const hydrateFromResume = useCallback42(() => {
|
|
@@ -31851,7 +31842,7 @@ function BracketGenerator({
|
|
|
31851
31842
|
[]
|
|
31852
31843
|
);
|
|
31853
31844
|
const prevBracketStateRef = useRef41(bracketState);
|
|
31854
|
-
|
|
31845
|
+
useEffect52(() => {
|
|
31855
31846
|
const prev = prevBracketStateRef.current;
|
|
31856
31847
|
const next = bracketState;
|
|
31857
31848
|
if (prev === next) return;
|
|
@@ -31875,7 +31866,7 @@ function BracketGenerator({
|
|
|
31875
31866
|
[effectiveParticipants, seedingStrategy]
|
|
31876
31867
|
);
|
|
31877
31868
|
const seededKeyRef = useRef41(null);
|
|
31878
|
-
|
|
31869
|
+
useEffect52(() => {
|
|
31879
31870
|
if (seededKeyRef.current === structuralKey) {
|
|
31880
31871
|
setBracketState((prev) => {
|
|
31881
31872
|
if (!prev.matches.length) return prev;
|
|
@@ -31906,7 +31897,7 @@ function BracketGenerator({
|
|
|
31906
31897
|
seedBracket();
|
|
31907
31898
|
}
|
|
31908
31899
|
}, [structuralKey, effectiveParticipants, hydrateFromResume, seedBracket]);
|
|
31909
|
-
|
|
31900
|
+
useEffect52(() => {
|
|
31910
31901
|
return () => {
|
|
31911
31902
|
if (autoRef.current) clearTimeout(autoRef.current);
|
|
31912
31903
|
};
|
|
@@ -31917,7 +31908,7 @@ function BracketGenerator({
|
|
|
31917
31908
|
return () => clearTimeout(timer);
|
|
31918
31909
|
}
|
|
31919
31910
|
}, [contentSize.w]);
|
|
31920
|
-
|
|
31911
|
+
useEffect52(() => {
|
|
31921
31912
|
if (autoSimulate && bracketState.matches.length && !bracketState.champion) {
|
|
31922
31913
|
const timer = setTimeout(autoPlay, 500);
|
|
31923
31914
|
return () => clearTimeout(timer);
|
|
@@ -32638,10 +32629,10 @@ function BracketGenerator({
|
|
|
32638
32629
|
// src/components/ui/coin-flipper/index.tsx
|
|
32639
32630
|
import {
|
|
32640
32631
|
useCallback as useCallback43,
|
|
32641
|
-
useEffect as
|
|
32632
|
+
useEffect as useEffect53,
|
|
32642
32633
|
useMemo as useMemo28,
|
|
32643
32634
|
useRef as useRef42,
|
|
32644
|
-
useState as
|
|
32635
|
+
useState as useState68
|
|
32645
32636
|
} from "react";
|
|
32646
32637
|
import { jsx as jsx115, jsxs as jsxs88 } from "react/jsx-runtime";
|
|
32647
32638
|
var PRESETS3 = {
|
|
@@ -32826,10 +32817,10 @@ function CoinFlipper({
|
|
|
32826
32817
|
}) {
|
|
32827
32818
|
const dark = useDarkMode(darkMode);
|
|
32828
32819
|
const tc = typeof theme5 === "string" ? PRESETS3[theme5] || PRESETS3.gold : theme5;
|
|
32829
|
-
const [result, setResult] =
|
|
32830
|
-
const [flipping, setFlipping] =
|
|
32831
|
-
const [history, setHistory] =
|
|
32832
|
-
const [stats, setStats] =
|
|
32820
|
+
const [result, setResult] = useState68(null);
|
|
32821
|
+
const [flipping, setFlipping] = useState68(false);
|
|
32822
|
+
const [history, setHistory] = useState68([]);
|
|
32823
|
+
const [stats, setStats] = useState68({ heads: 0, tails: 0 });
|
|
32833
32824
|
const flipIdRef = useRef42(0);
|
|
32834
32825
|
const coinRef = useRef42(null);
|
|
32835
32826
|
const shadowRef = useRef42(null);
|
|
@@ -32885,16 +32876,19 @@ function CoinFlipper({
|
|
|
32885
32876
|
}, frames.length * stepMs);
|
|
32886
32877
|
timersRef.current.push(settleTimer);
|
|
32887
32878
|
}, [flipping, onFlip, historyMax]);
|
|
32888
|
-
|
|
32879
|
+
useEffect53(() => {
|
|
32889
32880
|
if (flipRef) flipRef.current = flip;
|
|
32881
|
+
return () => {
|
|
32882
|
+
if (flipRef && flipRef.current === flip) flipRef.current = null;
|
|
32883
|
+
};
|
|
32890
32884
|
}, [flip, flipRef]);
|
|
32891
|
-
|
|
32885
|
+
useEffect53(() => {
|
|
32892
32886
|
if (autoFlip) {
|
|
32893
32887
|
const timer = setTimeout(flip, 300);
|
|
32894
32888
|
return () => clearTimeout(timer);
|
|
32895
32889
|
}
|
|
32896
32890
|
}, []);
|
|
32897
|
-
|
|
32891
|
+
useEffect53(() => {
|
|
32898
32892
|
return () => {
|
|
32899
32893
|
timersRef.current.forEach(clearTimeout);
|
|
32900
32894
|
};
|
|
@@ -33337,6 +33331,23 @@ import {
|
|
|
33337
33331
|
useRef as useRef43,
|
|
33338
33332
|
useState as useState70
|
|
33339
33333
|
} from "react";
|
|
33334
|
+
|
|
33335
|
+
// src/hooks/web/use-prefers-reduced-motion.ts
|
|
33336
|
+
import { useEffect as useEffect54, useState as useState69 } from "react";
|
|
33337
|
+
function usePrefersReducedMotion() {
|
|
33338
|
+
const [reduced, setReduced] = useState69(false);
|
|
33339
|
+
useEffect54(() => {
|
|
33340
|
+
if (typeof window === "undefined" || !window.matchMedia) return;
|
|
33341
|
+
const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
33342
|
+
setReduced(mq.matches);
|
|
33343
|
+
const read = () => setReduced(mq.matches);
|
|
33344
|
+
mq.addEventListener("change", read);
|
|
33345
|
+
return () => mq.removeEventListener("change", read);
|
|
33346
|
+
}, []);
|
|
33347
|
+
return reduced;
|
|
33348
|
+
}
|
|
33349
|
+
|
|
33350
|
+
// src/components/ui/random-player-picker/index.tsx
|
|
33340
33351
|
import { Fragment as Fragment31, jsx as jsx116, jsxs as jsxs89 } from "react/jsx-runtime";
|
|
33341
33352
|
var PRESETS4 = {
|
|
33342
33353
|
blue: {
|
|
@@ -33784,6 +33795,8 @@ function RandomPlayerPicker({
|
|
|
33784
33795
|
const particleTimer = useRef43(null);
|
|
33785
33796
|
const popTimer = useRef43(null);
|
|
33786
33797
|
const tickInterval = useRef43(null);
|
|
33798
|
+
const pendingPopIdxRef = useRef43(null);
|
|
33799
|
+
const removeWinnersRef = useRef43(removeWinners);
|
|
33787
33800
|
const tickAudioRef = useRef43(null);
|
|
33788
33801
|
const winAudioRef = useRef43(null);
|
|
33789
33802
|
const containerRef = useRef43(null);
|
|
@@ -33822,10 +33835,27 @@ function RandomPlayerPicker({
|
|
|
33822
33835
|
winAudioRef.current = null;
|
|
33823
33836
|
};
|
|
33824
33837
|
}, [winSoundUrl, soundVolume]);
|
|
33838
|
+
useEffect55(() => {
|
|
33839
|
+
removeWinnersRef.current = removeWinners;
|
|
33840
|
+
}, [removeWinners]);
|
|
33825
33841
|
useEffect55(() => {
|
|
33826
33842
|
const ids = participants.map((p) => p.id).join("|");
|
|
33827
33843
|
const structuralChange = ids !== prevIdsRef.current;
|
|
33828
33844
|
if (structuralChange) {
|
|
33845
|
+
if (winnerTimer.current) {
|
|
33846
|
+
clearTimeout(winnerTimer.current);
|
|
33847
|
+
winnerTimer.current = null;
|
|
33848
|
+
}
|
|
33849
|
+
if (tickInterval.current) {
|
|
33850
|
+
clearTimeout(tickInterval.current);
|
|
33851
|
+
tickInterval.current = null;
|
|
33852
|
+
}
|
|
33853
|
+
if (popTimer.current) {
|
|
33854
|
+
clearTimeout(popTimer.current);
|
|
33855
|
+
popTimer.current = null;
|
|
33856
|
+
}
|
|
33857
|
+
pendingPopIdxRef.current = null;
|
|
33858
|
+
setSpinning(false);
|
|
33829
33859
|
setPool(participants);
|
|
33830
33860
|
setHistory([]);
|
|
33831
33861
|
setWinner(null);
|
|
@@ -33882,33 +33912,50 @@ function RandomPlayerPicker({
|
|
|
33882
33912
|
[dark]
|
|
33883
33913
|
);
|
|
33884
33914
|
const spin = useCallback44(() => {
|
|
33885
|
-
if (spinning
|
|
33915
|
+
if (spinning) return;
|
|
33916
|
+
let activePool = pool;
|
|
33917
|
+
if (popTimer.current) {
|
|
33918
|
+
clearTimeout(popTimer.current);
|
|
33919
|
+
popTimer.current = null;
|
|
33920
|
+
const pendingIdx = pendingPopIdxRef.current;
|
|
33921
|
+
pendingPopIdxRef.current = null;
|
|
33922
|
+
if (pendingIdx != null && pendingIdx >= 0 && pendingIdx < pool.length && removeWinnersRef.current) {
|
|
33923
|
+
activePool = pool.filter((_, i) => i !== pendingIdx);
|
|
33924
|
+
setPool(activePool);
|
|
33925
|
+
setWinner(null);
|
|
33926
|
+
}
|
|
33927
|
+
}
|
|
33928
|
+
const activeN = activePool.length;
|
|
33929
|
+
if (activeN < 2) return;
|
|
33930
|
+
const activeAnglePerWedge = 2 * Math.PI / activeN;
|
|
33931
|
+
const activeRotation = rotation;
|
|
33932
|
+
const activeReelY = reelY;
|
|
33886
33933
|
onSpinStart?.();
|
|
33887
|
-
const winnerIdx = weightedIndex(
|
|
33934
|
+
const winnerIdx = weightedIndex(activePool);
|
|
33888
33935
|
const minRev = minRevolutions + intensityCfg.revsBoost;
|
|
33889
33936
|
const maxRev = maxRevolutions + intensityCfg.revsBoost;
|
|
33890
33937
|
const minFullRevs = Math.ceil(minRev);
|
|
33891
33938
|
const maxFullRevs = Math.max(minFullRevs, Math.floor(maxRev));
|
|
33892
33939
|
const revolutions = minFullRevs + Math.floor(Math.random() * (maxFullRevs - minFullRevs + 1));
|
|
33893
|
-
const wedgeMidRad = winnerIdx *
|
|
33940
|
+
const wedgeMidRad = winnerIdx * activeAnglePerWedge + activeAnglePerWedge / 2;
|
|
33894
33941
|
const wedgeMidDeg = wedgeMidRad * 180 / Math.PI;
|
|
33895
|
-
const wheelWobble = (Math.random() - 0.5) * (
|
|
33896
|
-
const currentMod = (
|
|
33942
|
+
const wheelWobble = (Math.random() - 0.5) * (activeAnglePerWedge * 180 / Math.PI) * 0.6;
|
|
33943
|
+
const currentMod = (activeRotation % 360 + 360) % 360;
|
|
33897
33944
|
const targetMod = ((-wedgeMidDeg + wheelWobble) % 360 + 360) % 360;
|
|
33898
33945
|
let deltaMod = targetMod - currentMod;
|
|
33899
33946
|
if (deltaMod < 0) deltaMod += 360;
|
|
33900
|
-
const finalRotation =
|
|
33947
|
+
const finalRotation = activeRotation + deltaMod + revolutions * 360;
|
|
33901
33948
|
const centerOffsetRows = (reelVisibleRows - 1) / 2;
|
|
33902
33949
|
const centerOffsetPx = centerOffsetRows * reelRowHeight;
|
|
33903
33950
|
const currentCenterRow = Math.round(
|
|
33904
|
-
(
|
|
33951
|
+
(activeReelY + centerOffsetPx) / reelRowHeight
|
|
33905
33952
|
);
|
|
33906
|
-
const currentCenterMod = mod(currentCenterRow,
|
|
33907
|
-
const targetRowOffset = (winnerIdx - currentCenterMod +
|
|
33908
|
-
const reelRevRows = Math.round(revolutions *
|
|
33909
|
-
const finalReelY =
|
|
33910
|
-
const settledWinnerIdx = mode === "reel" ? reelIndexAtCenter(finalReelY, centerOffsetPx, reelRowHeight,
|
|
33911
|
-
const finalWinnerIdx = settledWinnerIdx >= 0 && settledWinnerIdx <
|
|
33953
|
+
const currentCenterMod = mod(currentCenterRow, activeN);
|
|
33954
|
+
const targetRowOffset = (winnerIdx - currentCenterMod + activeN) % activeN;
|
|
33955
|
+
const reelRevRows = Math.round(revolutions * activeN);
|
|
33956
|
+
const finalReelY = activeReelY + (reelRevRows + targetRowOffset) * reelRowHeight;
|
|
33957
|
+
const settledWinnerIdx = mode === "reel" ? reelIndexAtCenter(finalReelY, centerOffsetPx, reelRowHeight, activeN) : wheelIndexAtPointer(finalRotation, activeN);
|
|
33958
|
+
const finalWinnerIdx = settledWinnerIdx >= 0 && settledWinnerIdx < activePool.length ? settledWinnerIdx : winnerIdx;
|
|
33912
33959
|
const effectiveDuration = reducedMotion ? 250 : Math.round(spinDuration * intensityCfg.durationMult);
|
|
33913
33960
|
setSpinning(true);
|
|
33914
33961
|
setWinner(null);
|
|
@@ -33921,7 +33968,7 @@ function RandomPlayerPicker({
|
|
|
33921
33968
|
if ((onTick || tickAudioRef.current) && !reducedMotion) {
|
|
33922
33969
|
const wedges = mode === "reel" ? Math.max(1, reelRevRows + targetRowOffset) : (() => {
|
|
33923
33970
|
const totalDelta = deltaMod + revolutions * 360;
|
|
33924
|
-
const wedgeDeg =
|
|
33971
|
+
const wedgeDeg = activeAnglePerWedge * 180 / Math.PI;
|
|
33925
33972
|
return Math.max(
|
|
33926
33973
|
1,
|
|
33927
33974
|
Math.round(totalDelta / Math.max(wedgeDeg, 1))
|
|
@@ -33953,7 +34000,7 @@ function RandomPlayerPicker({
|
|
|
33953
34000
|
schedule();
|
|
33954
34001
|
}
|
|
33955
34002
|
winnerTimer.current = setTimeout(() => {
|
|
33956
|
-
const picked =
|
|
34003
|
+
const picked = activePool[finalWinnerIdx];
|
|
33957
34004
|
if (!picked) {
|
|
33958
34005
|
setSpinning(false);
|
|
33959
34006
|
return;
|
|
@@ -33999,7 +34046,11 @@ function RandomPlayerPicker({
|
|
|
33999
34046
|
particleTimer.current = setTimeout(() => setParticles([]), 2400);
|
|
34000
34047
|
}
|
|
34001
34048
|
if (removeWinners) {
|
|
34049
|
+
pendingPopIdxRef.current = finalWinnerIdx;
|
|
34002
34050
|
popTimer.current = setTimeout(() => {
|
|
34051
|
+
popTimer.current = null;
|
|
34052
|
+
pendingPopIdxRef.current = null;
|
|
34053
|
+
if (!removeWinnersRef.current) return;
|
|
34003
34054
|
setPool((p) => p.filter((_, i) => i !== finalWinnerIdx));
|
|
34004
34055
|
setWinner(null);
|
|
34005
34056
|
setRotation(0);
|
|
@@ -34010,8 +34061,6 @@ function RandomPlayerPicker({
|
|
|
34010
34061
|
}, [
|
|
34011
34062
|
pool,
|
|
34012
34063
|
spinning,
|
|
34013
|
-
n,
|
|
34014
|
-
anglePerWedge,
|
|
34015
34064
|
rotation,
|
|
34016
34065
|
reelY,
|
|
34017
34066
|
mode,
|
|
@@ -34047,6 +34096,7 @@ function RandomPlayerPicker({
|
|
|
34047
34096
|
[]
|
|
34048
34097
|
);
|
|
34049
34098
|
useEffect55(() => {
|
|
34099
|
+
if (trigger === "external") return;
|
|
34050
34100
|
const el = containerRef.current;
|
|
34051
34101
|
if (!el) return;
|
|
34052
34102
|
const h = (e) => {
|
|
@@ -34057,8 +34107,22 @@ function RandomPlayerPicker({
|
|
|
34057
34107
|
};
|
|
34058
34108
|
el.addEventListener("keydown", h);
|
|
34059
34109
|
return () => el.removeEventListener("keydown", h);
|
|
34060
|
-
}, [spin]);
|
|
34110
|
+
}, [spin, trigger]);
|
|
34061
34111
|
const reset = useCallback44(() => {
|
|
34112
|
+
if (winnerTimer.current) {
|
|
34113
|
+
clearTimeout(winnerTimer.current);
|
|
34114
|
+
winnerTimer.current = null;
|
|
34115
|
+
}
|
|
34116
|
+
if (tickInterval.current) {
|
|
34117
|
+
clearTimeout(tickInterval.current);
|
|
34118
|
+
tickInterval.current = null;
|
|
34119
|
+
}
|
|
34120
|
+
if (popTimer.current) {
|
|
34121
|
+
clearTimeout(popTimer.current);
|
|
34122
|
+
popTimer.current = null;
|
|
34123
|
+
}
|
|
34124
|
+
pendingPopIdxRef.current = null;
|
|
34125
|
+
setSpinning(false);
|
|
34062
34126
|
setPool(participants);
|
|
34063
34127
|
setHistory([]);
|
|
34064
34128
|
setWinner(null);
|
|
@@ -38531,27 +38595,28 @@ function GameTimer({
|
|
|
38531
38595
|
const now = performance.now();
|
|
38532
38596
|
const delta = now - lastTickRef.current;
|
|
38533
38597
|
lastTickRef.current = now;
|
|
38534
|
-
|
|
38535
|
-
|
|
38536
|
-
|
|
38537
|
-
|
|
38538
|
-
|
|
38539
|
-
|
|
38540
|
-
|
|
38598
|
+
const next = elapsedRef.current + delta;
|
|
38599
|
+
if (mode === "countdown" && next >= durationMs) {
|
|
38600
|
+
elapsedRef.current = durationMs;
|
|
38601
|
+
setElapsedMs(durationMs);
|
|
38602
|
+
setRunning(false);
|
|
38603
|
+
setCompleted(true);
|
|
38604
|
+
onComplete?.();
|
|
38605
|
+
return;
|
|
38606
|
+
}
|
|
38607
|
+
if (mode === "countdown") {
|
|
38608
|
+
const secLeft = Math.ceil((durationMs - next) / 1e3);
|
|
38609
|
+
if (secLeft <= warningAt && !warningFiredRef.current) {
|
|
38610
|
+
warningFiredRef.current = true;
|
|
38611
|
+
onWarning?.();
|
|
38541
38612
|
}
|
|
38542
|
-
if (
|
|
38543
|
-
|
|
38544
|
-
|
|
38545
|
-
warningFiredRef.current = true;
|
|
38546
|
-
onWarning?.();
|
|
38547
|
-
}
|
|
38548
|
-
if (secLeft <= dangerAt && !dangerFiredRef.current) {
|
|
38549
|
-
dangerFiredRef.current = true;
|
|
38550
|
-
onDanger?.();
|
|
38551
|
-
}
|
|
38613
|
+
if (secLeft <= dangerAt && !dangerFiredRef.current) {
|
|
38614
|
+
dangerFiredRef.current = true;
|
|
38615
|
+
onDanger?.();
|
|
38552
38616
|
}
|
|
38553
|
-
|
|
38554
|
-
|
|
38617
|
+
}
|
|
38618
|
+
elapsedRef.current = next;
|
|
38619
|
+
setElapsedMs(next);
|
|
38555
38620
|
rafRef.current = requestAnimationFrame(tick);
|
|
38556
38621
|
}, [mode, durationMs, warningAt, dangerAt, onComplete, onWarning, onDanger]);
|
|
38557
38622
|
useEffect59(() => {
|
|
@@ -38572,6 +38637,7 @@ function GameTimer({
|
|
|
38572
38637
|
}, []);
|
|
38573
38638
|
const reset = useCallback48(() => {
|
|
38574
38639
|
setRunning(false);
|
|
38640
|
+
elapsedRef.current = 0;
|
|
38575
38641
|
setElapsedMs(0);
|
|
38576
38642
|
setCompleted(false);
|
|
38577
38643
|
setLaps([]);
|
|
@@ -40528,6 +40594,8 @@ function RoundRobinScheduler({
|
|
|
40528
40594
|
theme: theme5 = "slate",
|
|
40529
40595
|
darkMode,
|
|
40530
40596
|
minTeams = 3,
|
|
40597
|
+
maxTeams,
|
|
40598
|
+
nameMaxLength,
|
|
40531
40599
|
defaultTeams = [],
|
|
40532
40600
|
resumeRounds,
|
|
40533
40601
|
onTeamsChange,
|
|
@@ -40550,39 +40618,41 @@ function RoundRobinScheduler({
|
|
|
40550
40618
|
useEffect62(() => {
|
|
40551
40619
|
onTeamsChange?.(teams);
|
|
40552
40620
|
}, [teams, onTeamsChange]);
|
|
40553
|
-
const
|
|
40554
|
-
|
|
40555
|
-
if (names.length === 0) return;
|
|
40556
|
-
setTeams((prev) => {
|
|
40557
|
-
const existing = new Set(prev.map((t) => t.toLowerCase()));
|
|
40558
|
-
const next = [...prev];
|
|
40559
|
-
for (const name of names) {
|
|
40560
|
-
if (!existing.has(name.toLowerCase())) {
|
|
40561
|
-
existing.add(name.toLowerCase());
|
|
40562
|
-
next.push(name);
|
|
40563
|
-
}
|
|
40564
|
-
}
|
|
40565
|
-
return next;
|
|
40566
|
-
});
|
|
40567
|
-
setInput("");
|
|
40568
|
-
setRounds([]);
|
|
40569
|
-
}, []);
|
|
40570
|
-
const importTeams = useCallback51((incoming) => {
|
|
40571
|
-
if (incoming.length === 0) return;
|
|
40572
|
-
setTeams((prev) => {
|
|
40621
|
+
const mergeNames = useCallback51(
|
|
40622
|
+
(prev, incoming) => {
|
|
40573
40623
|
const existing = new Set(prev.map((t) => t.toLowerCase()));
|
|
40574
40624
|
const next = [...prev];
|
|
40575
|
-
for (const
|
|
40625
|
+
for (const rawName of incoming) {
|
|
40626
|
+
if (maxTeams != null && next.length >= maxTeams) break;
|
|
40627
|
+
const name = nameMaxLength != null ? rawName.slice(0, nameMaxLength) : rawName;
|
|
40576
40628
|
const key = name.toLowerCase();
|
|
40577
|
-
if (!existing.has(key)) {
|
|
40629
|
+
if (name.length > 0 && !existing.has(key)) {
|
|
40578
40630
|
existing.add(key);
|
|
40579
40631
|
next.push(name);
|
|
40580
40632
|
}
|
|
40581
40633
|
}
|
|
40582
40634
|
return next;
|
|
40583
|
-
}
|
|
40584
|
-
|
|
40585
|
-
|
|
40635
|
+
},
|
|
40636
|
+
[maxTeams, nameMaxLength]
|
|
40637
|
+
);
|
|
40638
|
+
const addTeams = useCallback51(
|
|
40639
|
+
(raw) => {
|
|
40640
|
+
const names = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
40641
|
+
if (names.length === 0) return;
|
|
40642
|
+
setTeams((prev) => mergeNames(prev, names));
|
|
40643
|
+
setInput("");
|
|
40644
|
+
setRounds([]);
|
|
40645
|
+
},
|
|
40646
|
+
[mergeNames]
|
|
40647
|
+
);
|
|
40648
|
+
const importTeams = useCallback51(
|
|
40649
|
+
(incoming) => {
|
|
40650
|
+
if (incoming.length === 0) return;
|
|
40651
|
+
setTeams((prev) => mergeNames(prev, incoming));
|
|
40652
|
+
setRounds([]);
|
|
40653
|
+
},
|
|
40654
|
+
[mergeNames]
|
|
40655
|
+
);
|
|
40586
40656
|
const handleKeyDown = useCallback51(
|
|
40587
40657
|
(e) => {
|
|
40588
40658
|
if (e.key === "Enter") {
|
|
@@ -41076,6 +41146,8 @@ function Scoreboard({
|
|
|
41076
41146
|
onTeamsChange,
|
|
41077
41147
|
onRoundAdvance,
|
|
41078
41148
|
onNewGame,
|
|
41149
|
+
onReset,
|
|
41150
|
+
nameMaxLength,
|
|
41079
41151
|
labels,
|
|
41080
41152
|
ariaLabel = "Scoreboard",
|
|
41081
41153
|
className
|
|
@@ -41150,7 +41222,8 @@ function Scoreboard({
|
|
|
41150
41222
|
setRound(1);
|
|
41151
41223
|
setHistory([]);
|
|
41152
41224
|
setHistoryOpen(false);
|
|
41153
|
-
|
|
41225
|
+
onReset?.();
|
|
41226
|
+
}, [onReset]);
|
|
41154
41227
|
const newGame = useCallback52(() => {
|
|
41155
41228
|
setTeams(createDefaultTeams(seedCount, L.teamNamePrefix));
|
|
41156
41229
|
setRound(1);
|
|
@@ -41372,6 +41445,7 @@ function Scoreboard({
|
|
|
41372
41445
|
{
|
|
41373
41446
|
type: "text",
|
|
41374
41447
|
value: team.name,
|
|
41448
|
+
maxLength: nameMaxLength,
|
|
41375
41449
|
onChange: (e) => updateName(index, e.target.value),
|
|
41376
41450
|
style: nameInputStyle,
|
|
41377
41451
|
"aria-label": `Team ${index + 1} name`
|