@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/web/index.js
CHANGED
|
@@ -30225,7 +30225,7 @@ function TiltCard({
|
|
|
30225
30225
|
}
|
|
30226
30226
|
|
|
30227
30227
|
// src/components/ui/dice-roller/index.tsx
|
|
30228
|
-
var
|
|
30228
|
+
var import_react69 = require("react");
|
|
30229
30229
|
|
|
30230
30230
|
// src/hooks/web/use-dark-mode.ts
|
|
30231
30231
|
var import_react68 = require("react");
|
|
@@ -30274,21 +30274,6 @@ function useDarkMode(forced) {
|
|
|
30274
30274
|
return dark;
|
|
30275
30275
|
}
|
|
30276
30276
|
|
|
30277
|
-
// src/hooks/web/use-prefers-reduced-motion.ts
|
|
30278
|
-
var import_react69 = require("react");
|
|
30279
|
-
function usePrefersReducedMotion() {
|
|
30280
|
-
const [reduced, setReduced] = (0, import_react69.useState)(false);
|
|
30281
|
-
(0, import_react69.useEffect)(() => {
|
|
30282
|
-
if (typeof window === "undefined" || !window.matchMedia) return;
|
|
30283
|
-
const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
30284
|
-
setReduced(mq.matches);
|
|
30285
|
-
const read = () => setReduced(mq.matches);
|
|
30286
|
-
mq.addEventListener("change", read);
|
|
30287
|
-
return () => mq.removeEventListener("change", read);
|
|
30288
|
-
}, []);
|
|
30289
|
-
return reduced;
|
|
30290
|
-
}
|
|
30291
|
-
|
|
30292
30277
|
// src/components/ui/dice-roller/index.tsx
|
|
30293
30278
|
var import_jsx_runtime113 = require("react/jsx-runtime");
|
|
30294
30279
|
var PRESETS = {
|
|
@@ -30507,8 +30492,8 @@ function simulateDice(arenaW, arenaH, diceSize, starts, numDice) {
|
|
|
30507
30492
|
return frames;
|
|
30508
30493
|
}
|
|
30509
30494
|
function useArenaBounds(ref) {
|
|
30510
|
-
const [bounds, setBounds] = (0,
|
|
30511
|
-
(0,
|
|
30495
|
+
const [bounds, setBounds] = (0, import_react69.useState)({ w: 400, h: 260 });
|
|
30496
|
+
(0, import_react69.useEffect)(() => {
|
|
30512
30497
|
if (!ref.current) return;
|
|
30513
30498
|
const el = ref.current;
|
|
30514
30499
|
const update = () => setBounds({ w: el.offsetWidth, h: el.offsetHeight });
|
|
@@ -30563,19 +30548,18 @@ function DiceRoller({
|
|
|
30563
30548
|
}) {
|
|
30564
30549
|
const clampedCount = Math.max(1, Math.min(MAX_COUNT, count2));
|
|
30565
30550
|
const dark = useDarkMode(darkMode);
|
|
30566
|
-
const
|
|
30567
|
-
const [
|
|
30568
|
-
const [
|
|
30569
|
-
const
|
|
30570
|
-
const
|
|
30571
|
-
const
|
|
30572
|
-
const
|
|
30573
|
-
const
|
|
30574
|
-
const
|
|
30575
|
-
const
|
|
30576
|
-
const
|
|
30577
|
-
const
|
|
30578
|
-
const shadowSetters = (0, import_react70.useRef)([]);
|
|
30551
|
+
const [rolling, setRolling] = (0, import_react69.useState)(false);
|
|
30552
|
+
const [results, setResults] = (0, import_react69.useState)(null);
|
|
30553
|
+
const [history, setHistory] = (0, import_react69.useState)([]);
|
|
30554
|
+
const dieWrapperRefs = (0, import_react69.useRef)([]);
|
|
30555
|
+
const cubeRefs = (0, import_react69.useRef)([]);
|
|
30556
|
+
const shadowRefs = (0, import_react69.useRef)([]);
|
|
30557
|
+
const numeralRefs = (0, import_react69.useRef)([]);
|
|
30558
|
+
const currentPositionsRef = (0, import_react69.useRef)(null);
|
|
30559
|
+
const rollingRef = (0, import_react69.useRef)(false);
|
|
30560
|
+
const dieSetters = (0, import_react69.useRef)([]);
|
|
30561
|
+
const cubeSetters = (0, import_react69.useRef)([]);
|
|
30562
|
+
const shadowSetters = (0, import_react69.useRef)([]);
|
|
30579
30563
|
const getDieSetter = (i) => {
|
|
30580
30564
|
if (!dieSetters.current[i]) {
|
|
30581
30565
|
dieSetters.current[i] = (el) => {
|
|
@@ -30600,7 +30584,7 @@ function DiceRoller({
|
|
|
30600
30584
|
}
|
|
30601
30585
|
return shadowSetters.current[i];
|
|
30602
30586
|
};
|
|
30603
|
-
const numeralSetters = (0,
|
|
30587
|
+
const numeralSetters = (0, import_react69.useRef)([]);
|
|
30604
30588
|
const getNumeralSetter = (i) => {
|
|
30605
30589
|
if (!numeralSetters.current[i]) {
|
|
30606
30590
|
numeralSetters.current[i] = (el) => {
|
|
@@ -30609,11 +30593,11 @@ function DiceRoller({
|
|
|
30609
30593
|
}
|
|
30610
30594
|
return numeralSetters.current[i];
|
|
30611
30595
|
};
|
|
30612
|
-
const timersRef = (0,
|
|
30613
|
-
const rafHandleRef = (0,
|
|
30614
|
-
const arenaRef = (0,
|
|
30596
|
+
const timersRef = (0, import_react69.useRef)([]);
|
|
30597
|
+
const rafHandleRef = (0, import_react69.useRef)(null);
|
|
30598
|
+
const arenaRef = (0, import_react69.useRef)(null);
|
|
30615
30599
|
const arena = useArenaBounds(arenaRef);
|
|
30616
|
-
const colors = (0,
|
|
30600
|
+
const colors = (0, import_react69.useMemo)(() => {
|
|
30617
30601
|
if (typeof theme5 === "object") {
|
|
30618
30602
|
const pipL = theme5.pip;
|
|
30619
30603
|
const pipD = theme5.pipDark ?? theme5.pip;
|
|
@@ -30650,7 +30634,7 @@ function DiceRoller({
|
|
|
30650
30634
|
chipDim: dark ? "#3a4358" : "#94a3b8"
|
|
30651
30635
|
};
|
|
30652
30636
|
}, [theme5, dark]);
|
|
30653
|
-
const effectiveSize = (0,
|
|
30637
|
+
const effectiveSize = (0, import_react69.useMemo)(() => {
|
|
30654
30638
|
if (!responsive2) return size;
|
|
30655
30639
|
const needed = clampedCount * size * 1.6;
|
|
30656
30640
|
if (arena.w >= needed || arena.w === 0) return size;
|
|
@@ -30661,7 +30645,7 @@ function DiceRoller({
|
|
|
30661
30645
|
const pipDiameter = Math.round(effectiveSize * 0.2);
|
|
30662
30646
|
const faceRadius = Math.round(effectiveSize * 0.07);
|
|
30663
30647
|
const arenaHeight = arenaHeightProp ?? Math.max(effectiveSize * 3, 200);
|
|
30664
|
-
const faceTransformFor = (0,
|
|
30648
|
+
const faceTransformFor = (0, import_react69.useCallback)(
|
|
30665
30649
|
(value) => {
|
|
30666
30650
|
const p = D6_FACE_PLACEMENTS[value];
|
|
30667
30651
|
const parts = [];
|
|
@@ -30672,14 +30656,14 @@ function DiceRoller({
|
|
|
30672
30656
|
},
|
|
30673
30657
|
[half]
|
|
30674
30658
|
);
|
|
30675
|
-
const defaultPositions = (0,
|
|
30659
|
+
const defaultPositions = (0, import_react69.useCallback)(
|
|
30676
30660
|
(w, h, n) => Array.from({ length: n }, (_, i) => ({
|
|
30677
30661
|
x: (i + 1) * w / (n + 1),
|
|
30678
30662
|
y: h / 2
|
|
30679
30663
|
})),
|
|
30680
30664
|
[]
|
|
30681
30665
|
);
|
|
30682
|
-
const writeDiePosition = (0,
|
|
30666
|
+
const writeDiePosition = (0, import_react69.useCallback)(
|
|
30683
30667
|
(i, x, y, sx = 1, sy = 1) => {
|
|
30684
30668
|
const el = dieWrapperRefs.current[i];
|
|
30685
30669
|
if (!el) return;
|
|
@@ -30690,7 +30674,7 @@ function DiceRoller({
|
|
|
30690
30674
|
},
|
|
30691
30675
|
[half]
|
|
30692
30676
|
);
|
|
30693
|
-
const writeCubeRotation = (0,
|
|
30677
|
+
const writeCubeRotation = (0, import_react69.useCallback)(
|
|
30694
30678
|
(i, rx, ry, rz, withTransition, tiltZ = 0) => {
|
|
30695
30679
|
const el = cubeRefs.current[i];
|
|
30696
30680
|
if (!el) return;
|
|
@@ -30699,7 +30683,15 @@ function DiceRoller({
|
|
|
30699
30683
|
},
|
|
30700
30684
|
[]
|
|
30701
30685
|
);
|
|
30702
|
-
(0,
|
|
30686
|
+
(0, import_react69.useEffect)(() => {
|
|
30687
|
+
if (timersRef.current.length > 0) {
|
|
30688
|
+
timersRef.current.forEach((t) => clearTimeout(t));
|
|
30689
|
+
timersRef.current = [];
|
|
30690
|
+
}
|
|
30691
|
+
if (rollingRef.current) {
|
|
30692
|
+
rollingRef.current = false;
|
|
30693
|
+
setRolling(false);
|
|
30694
|
+
}
|
|
30703
30695
|
const rawW = arenaRef.current?.offsetWidth ?? 0;
|
|
30704
30696
|
const rawH = arenaRef.current?.offsetHeight ?? 0;
|
|
30705
30697
|
const w = rawW > 20 ? rawW : arena.w > 20 ? arena.w : 400;
|
|
@@ -30732,10 +30724,10 @@ function DiceRoller({
|
|
|
30732
30724
|
defaultPositions,
|
|
30733
30725
|
half
|
|
30734
30726
|
]);
|
|
30735
|
-
(0,
|
|
30727
|
+
(0, import_react69.useEffect)(() => {
|
|
30736
30728
|
setHistory([]);
|
|
30737
30729
|
}, [sides]);
|
|
30738
|
-
const roll = (0,
|
|
30730
|
+
const roll = (0, import_react69.useCallback)(() => {
|
|
30739
30731
|
if (rollingRef.current) return;
|
|
30740
30732
|
rollingRef.current = true;
|
|
30741
30733
|
setRolling(true);
|
|
@@ -30856,25 +30848,24 @@ function DiceRoller({
|
|
|
30856
30848
|
defaultPositions,
|
|
30857
30849
|
effectiveSize,
|
|
30858
30850
|
half,
|
|
30859
|
-
reducedMotion,
|
|
30860
30851
|
onRoll,
|
|
30861
30852
|
writeCubeRotation,
|
|
30862
30853
|
writeDiePosition,
|
|
30863
30854
|
showHistory,
|
|
30864
30855
|
historyMax
|
|
30865
30856
|
]);
|
|
30866
|
-
(0,
|
|
30857
|
+
(0, import_react69.useEffect)(() => {
|
|
30867
30858
|
if (rollRef) rollRef.current = roll;
|
|
30868
30859
|
return () => {
|
|
30869
30860
|
if (rollRef && rollRef.current === roll) rollRef.current = null;
|
|
30870
30861
|
};
|
|
30871
30862
|
}, [roll, rollRef]);
|
|
30872
|
-
(0,
|
|
30863
|
+
(0, import_react69.useEffect)(() => {
|
|
30873
30864
|
if (!autoRoll) return;
|
|
30874
30865
|
const t = setTimeout(roll, 300);
|
|
30875
30866
|
return () => clearTimeout(t);
|
|
30876
30867
|
}, []);
|
|
30877
|
-
(0,
|
|
30868
|
+
(0, import_react69.useEffect)(
|
|
30878
30869
|
() => () => {
|
|
30879
30870
|
timersRef.current.forEach((t) => clearTimeout(t));
|
|
30880
30871
|
timersRef.current = [];
|
|
@@ -31174,7 +31165,7 @@ function DiceRoller({
|
|
|
31174
31165
|
DiceRoller.displayName = "DiceRoller";
|
|
31175
31166
|
|
|
31176
31167
|
// src/components/ui/bracket-generator/index.tsx
|
|
31177
|
-
var
|
|
31168
|
+
var import_react70 = require("react");
|
|
31178
31169
|
|
|
31179
31170
|
// src/utils/array.ts
|
|
31180
31171
|
function shuffle(arr) {
|
|
@@ -31394,15 +31385,15 @@ function computeLayout(rounds, firstRoundCount) {
|
|
|
31394
31385
|
};
|
|
31395
31386
|
}
|
|
31396
31387
|
function usePanZoom(viewportRef, contentSize, minZoom, maxZoom) {
|
|
31397
|
-
const [transform, setTransform] = (0,
|
|
31388
|
+
const [transform, setTransform] = (0, import_react70.useState)({
|
|
31398
31389
|
x: 0,
|
|
31399
31390
|
y: 0,
|
|
31400
31391
|
scale: 1
|
|
31401
31392
|
});
|
|
31402
|
-
const isDragging = (0,
|
|
31403
|
-
const dragStart = (0,
|
|
31404
|
-
const pinchRef = (0,
|
|
31405
|
-
const clampTransform = (0,
|
|
31393
|
+
const isDragging = (0, import_react70.useRef)(false);
|
|
31394
|
+
const dragStart = (0, import_react70.useRef)({ x: 0, y: 0 });
|
|
31395
|
+
const pinchRef = (0, import_react70.useRef)(null);
|
|
31396
|
+
const clampTransform = (0, import_react70.useCallback)(
|
|
31406
31397
|
(t, vw, vh) => {
|
|
31407
31398
|
const sw = contentSize.w * t.scale;
|
|
31408
31399
|
const sh = contentSize.h * t.scale;
|
|
@@ -31414,7 +31405,7 @@ function usePanZoom(viewportRef, contentSize, minZoom, maxZoom) {
|
|
|
31414
31405
|
},
|
|
31415
31406
|
[contentSize]
|
|
31416
31407
|
);
|
|
31417
|
-
(0,
|
|
31408
|
+
(0, import_react70.useEffect)(() => {
|
|
31418
31409
|
const el = viewportRef.current;
|
|
31419
31410
|
if (!el) return;
|
|
31420
31411
|
const onWheel = (e) => {
|
|
@@ -31439,7 +31430,7 @@ function usePanZoom(viewportRef, contentSize, minZoom, maxZoom) {
|
|
|
31439
31430
|
el.addEventListener("wheel", onWheel, { passive: false });
|
|
31440
31431
|
return () => el.removeEventListener("wheel", onWheel);
|
|
31441
31432
|
}, [viewportRef, minZoom, maxZoom, clampTransform]);
|
|
31442
|
-
const onPointerDown = (0,
|
|
31433
|
+
const onPointerDown = (0, import_react70.useCallback)(
|
|
31443
31434
|
(e) => {
|
|
31444
31435
|
if (e.target.closest("[data-no-pan]")) return;
|
|
31445
31436
|
isDragging.current = true;
|
|
@@ -31452,7 +31443,7 @@ function usePanZoom(viewportRef, contentSize, minZoom, maxZoom) {
|
|
|
31452
31443
|
},
|
|
31453
31444
|
[transform]
|
|
31454
31445
|
);
|
|
31455
|
-
const onPointerMove = (0,
|
|
31446
|
+
const onPointerMove = (0, import_react70.useCallback)(
|
|
31456
31447
|
(e) => {
|
|
31457
31448
|
if (!isDragging.current) return;
|
|
31458
31449
|
const el = viewportRef.current;
|
|
@@ -31472,11 +31463,11 @@ function usePanZoom(viewportRef, contentSize, minZoom, maxZoom) {
|
|
|
31472
31463
|
},
|
|
31473
31464
|
[viewportRef, clampTransform]
|
|
31474
31465
|
);
|
|
31475
|
-
const onPointerUp = (0,
|
|
31466
|
+
const onPointerUp = (0, import_react70.useCallback)((e) => {
|
|
31476
31467
|
isDragging.current = false;
|
|
31477
31468
|
e.currentTarget.style.cursor = "grab";
|
|
31478
31469
|
}, []);
|
|
31479
|
-
const onTouchStart = (0,
|
|
31470
|
+
const onTouchStart = (0, import_react70.useCallback)(
|
|
31480
31471
|
(e) => {
|
|
31481
31472
|
if (e.touches.length === 2) {
|
|
31482
31473
|
const [t1, t2] = [e.touches[0], e.touches[1]];
|
|
@@ -31496,7 +31487,7 @@ function usePanZoom(viewportRef, contentSize, minZoom, maxZoom) {
|
|
|
31496
31487
|
},
|
|
31497
31488
|
[transform]
|
|
31498
31489
|
);
|
|
31499
|
-
const onTouchMove = (0,
|
|
31490
|
+
const onTouchMove = (0, import_react70.useCallback)(
|
|
31500
31491
|
(e) => {
|
|
31501
31492
|
if (e.touches.length === 2 && pinchRef.current) {
|
|
31502
31493
|
e.preventDefault();
|
|
@@ -31533,10 +31524,10 @@ function usePanZoom(viewportRef, contentSize, minZoom, maxZoom) {
|
|
|
31533
31524
|
},
|
|
31534
31525
|
[viewportRef, minZoom, maxZoom, clampTransform]
|
|
31535
31526
|
);
|
|
31536
|
-
const onTouchEnd = (0,
|
|
31527
|
+
const onTouchEnd = (0, import_react70.useCallback)(() => {
|
|
31537
31528
|
pinchRef.current = null;
|
|
31538
31529
|
}, []);
|
|
31539
|
-
const zoomBy = (0,
|
|
31530
|
+
const zoomBy = (0, import_react70.useCallback)(
|
|
31540
31531
|
(factor) => {
|
|
31541
31532
|
const el = viewportRef.current;
|
|
31542
31533
|
if (!el) return;
|
|
@@ -31555,7 +31546,7 @@ function usePanZoom(viewportRef, contentSize, minZoom, maxZoom) {
|
|
|
31555
31546
|
},
|
|
31556
31547
|
[viewportRef, minZoom, maxZoom, clampTransform]
|
|
31557
31548
|
);
|
|
31558
|
-
const fit = (0,
|
|
31549
|
+
const fit = (0, import_react70.useCallback)(() => {
|
|
31559
31550
|
const el = viewportRef.current;
|
|
31560
31551
|
if (!el || !contentSize.w) return;
|
|
31561
31552
|
const rect = el.getBoundingClientRect();
|
|
@@ -31569,10 +31560,10 @@ function usePanZoom(viewportRef, contentSize, minZoom, maxZoom) {
|
|
|
31569
31560
|
scale
|
|
31570
31561
|
});
|
|
31571
31562
|
}, [viewportRef, contentSize, minZoom, maxZoom]);
|
|
31572
|
-
const reset = (0,
|
|
31563
|
+
const reset = (0, import_react70.useCallback)(() => {
|
|
31573
31564
|
setTransform({ x: 40, y: 40, scale: 1 });
|
|
31574
31565
|
}, []);
|
|
31575
|
-
const panTo = (0,
|
|
31566
|
+
const panTo = (0, import_react70.useCallback)(
|
|
31576
31567
|
(contentX, contentY) => {
|
|
31577
31568
|
const el = viewportRef.current;
|
|
31578
31569
|
if (!el) return;
|
|
@@ -31645,15 +31636,15 @@ function BracketGenerator({
|
|
|
31645
31636
|
ariaLabel = "Tournament bracket"
|
|
31646
31637
|
}) {
|
|
31647
31638
|
const dark = useDarkMode(darkMode);
|
|
31648
|
-
const [bracketState, setBracketState] = (0,
|
|
31639
|
+
const [bracketState, setBracketState] = (0, import_react70.useState)({
|
|
31649
31640
|
matches: [],
|
|
31650
31641
|
rounds: 0,
|
|
31651
31642
|
champion: null
|
|
31652
31643
|
});
|
|
31653
|
-
const [simulating, setSimulating] = (0,
|
|
31654
|
-
const autoRef = (0,
|
|
31655
|
-
const viewportRef = (0,
|
|
31656
|
-
const effectiveParticipants = (0,
|
|
31644
|
+
const [simulating, setSimulating] = (0, import_react70.useState)(false);
|
|
31645
|
+
const autoRef = (0, import_react70.useRef)(null);
|
|
31646
|
+
const viewportRef = (0, import_react70.useRef)(null);
|
|
31647
|
+
const effectiveParticipants = (0, import_react70.useMemo)(() => {
|
|
31657
31648
|
if (participantsProp?.length) {
|
|
31658
31649
|
return padToSize(
|
|
31659
31650
|
participantsProp.map((p, i) => ({
|
|
@@ -31672,7 +31663,7 @@ function BracketGenerator({
|
|
|
31672
31663
|
}));
|
|
31673
31664
|
}, [participantsProp, size]);
|
|
31674
31665
|
const preset = typeof theme5 === "string" ? PRESETS2[theme5] || PRESETS2.blue : null;
|
|
31675
|
-
const themeColors = (0,
|
|
31666
|
+
const themeColors = (0, import_react70.useMemo)(() => {
|
|
31676
31667
|
if (typeof theme5 === "object") return theme5;
|
|
31677
31668
|
const p = preset;
|
|
31678
31669
|
return {
|
|
@@ -31681,9 +31672,9 @@ function BracketGenerator({
|
|
|
31681
31672
|
color: p.color
|
|
31682
31673
|
};
|
|
31683
31674
|
}, [theme5, dark, preset]);
|
|
31684
|
-
const colors = (0,
|
|
31675
|
+
const colors = (0, import_react70.useMemo)(() => buildColorTokens(dark), [dark]);
|
|
31685
31676
|
const roundLabels = ROUND_NAMES[effectiveParticipants.length] || [];
|
|
31686
|
-
const layout = (0,
|
|
31677
|
+
const layout = (0, import_react70.useMemo)(() => {
|
|
31687
31678
|
if (!bracketState.rounds)
|
|
31688
31679
|
return {
|
|
31689
31680
|
positions: {},
|
|
@@ -31692,7 +31683,7 @@ function BracketGenerator({
|
|
|
31692
31683
|
};
|
|
31693
31684
|
return computeLayout(bracketState.rounds, effectiveParticipants.length / 2);
|
|
31694
31685
|
}, [bracketState.rounds, effectiveParticipants.length]);
|
|
31695
|
-
const contentSize = (0,
|
|
31686
|
+
const contentSize = (0, import_react70.useMemo)(
|
|
31696
31687
|
() => ({
|
|
31697
31688
|
w: layout.totalW + CANVAS_PAD * 2,
|
|
31698
31689
|
h: layout.totalH + CANVAS_PAD * 2
|
|
@@ -31705,10 +31696,10 @@ function BracketGenerator({
|
|
|
31705
31696
|
minZoom,
|
|
31706
31697
|
maxZoom
|
|
31707
31698
|
);
|
|
31708
|
-
const [miniMapHidden, setMiniMapHidden] = (0,
|
|
31709
|
-
const miniMapDragRef = (0,
|
|
31710
|
-
const resumeRef = (0,
|
|
31711
|
-
const hydrateFromResume = (0,
|
|
31699
|
+
const [miniMapHidden, setMiniMapHidden] = (0, import_react70.useState)(false);
|
|
31700
|
+
const miniMapDragRef = (0, import_react70.useRef)(false);
|
|
31701
|
+
const resumeRef = (0, import_react70.useRef)(resumeMatches);
|
|
31702
|
+
const hydrateFromResume = (0, import_react70.useCallback)(() => {
|
|
31712
31703
|
const resume = resumeRef.current;
|
|
31713
31704
|
if (!resume?.length) return null;
|
|
31714
31705
|
const n = effectiveParticipants.length;
|
|
@@ -31748,7 +31739,7 @@ function BracketGenerator({
|
|
|
31748
31739
|
const finalMatch = matches.find((m) => m.round === rounds - 1);
|
|
31749
31740
|
return { matches, rounds, champion: finalMatch?.winner ?? null };
|
|
31750
31741
|
}, [effectiveParticipants]);
|
|
31751
|
-
const seedBracket = (0,
|
|
31742
|
+
const seedBracket = (0, import_react70.useCallback)(() => {
|
|
31752
31743
|
const { matches, rounds } = buildBracket(
|
|
31753
31744
|
effectiveParticipants,
|
|
31754
31745
|
seedingStrategy
|
|
@@ -31771,7 +31762,7 @@ function BracketGenerator({
|
|
|
31771
31762
|
};
|
|
31772
31763
|
setBracketState(newState);
|
|
31773
31764
|
}, [effectiveParticipants, seedingStrategy]);
|
|
31774
|
-
const simulateNext = (0,
|
|
31765
|
+
const simulateNext = (0, import_react70.useCallback)(() => {
|
|
31775
31766
|
setBracketState((prev) => {
|
|
31776
31767
|
const next = prev.matches.find((m) => m.a && m.b && !m.winner);
|
|
31777
31768
|
if (!next) return prev;
|
|
@@ -31786,7 +31777,7 @@ function BracketGenerator({
|
|
|
31786
31777
|
return { ...prev, matches, champion };
|
|
31787
31778
|
});
|
|
31788
31779
|
}, []);
|
|
31789
|
-
const autoPlay = (0,
|
|
31780
|
+
const autoPlay = (0, import_react70.useCallback)(() => {
|
|
31790
31781
|
if (simulating) {
|
|
31791
31782
|
if (autoRef.current) clearTimeout(autoRef.current);
|
|
31792
31783
|
setSimulating(false);
|
|
@@ -31815,7 +31806,7 @@ function BracketGenerator({
|
|
|
31815
31806
|
};
|
|
31816
31807
|
tick();
|
|
31817
31808
|
}, [simulating, simulateDelayMs]);
|
|
31818
|
-
const advanceWinner = (0,
|
|
31809
|
+
const advanceWinner = (0, import_react70.useCallback)(
|
|
31819
31810
|
(matchId, winnerSlot) => {
|
|
31820
31811
|
setBracketState((prev) => {
|
|
31821
31812
|
const m = prev.matches.find((x) => x.id === matchId);
|
|
@@ -31838,8 +31829,8 @@ function BracketGenerator({
|
|
|
31838
31829
|
},
|
|
31839
31830
|
[]
|
|
31840
31831
|
);
|
|
31841
|
-
const prevBracketStateRef = (0,
|
|
31842
|
-
(0,
|
|
31832
|
+
const prevBracketStateRef = (0, import_react70.useRef)(bracketState);
|
|
31833
|
+
(0, import_react70.useEffect)(() => {
|
|
31843
31834
|
const prev = prevBracketStateRef.current;
|
|
31844
31835
|
const next = bracketState;
|
|
31845
31836
|
if (prev === next) return;
|
|
@@ -31858,12 +31849,12 @@ function BracketGenerator({
|
|
|
31858
31849
|
onStateChange?.(next);
|
|
31859
31850
|
prevBracketStateRef.current = next;
|
|
31860
31851
|
}, [bracketState, onMatchComplete, onChampion, onStateChange]);
|
|
31861
|
-
const structuralKey = (0,
|
|
31852
|
+
const structuralKey = (0, import_react70.useMemo)(
|
|
31862
31853
|
() => `${seedingStrategy}|${effectiveParticipants.map((p) => `${p.id}:${p.seed ?? ""}`).join(",")}`,
|
|
31863
31854
|
[effectiveParticipants, seedingStrategy]
|
|
31864
31855
|
);
|
|
31865
|
-
const seededKeyRef = (0,
|
|
31866
|
-
(0,
|
|
31856
|
+
const seededKeyRef = (0, import_react70.useRef)(null);
|
|
31857
|
+
(0, import_react70.useEffect)(() => {
|
|
31867
31858
|
if (seededKeyRef.current === structuralKey) {
|
|
31868
31859
|
setBracketState((prev) => {
|
|
31869
31860
|
if (!prev.matches.length) return prev;
|
|
@@ -31894,24 +31885,24 @@ function BracketGenerator({
|
|
|
31894
31885
|
seedBracket();
|
|
31895
31886
|
}
|
|
31896
31887
|
}, [structuralKey, effectiveParticipants, hydrateFromResume, seedBracket]);
|
|
31897
|
-
(0,
|
|
31888
|
+
(0, import_react70.useEffect)(() => {
|
|
31898
31889
|
return () => {
|
|
31899
31890
|
if (autoRef.current) clearTimeout(autoRef.current);
|
|
31900
31891
|
};
|
|
31901
31892
|
}, []);
|
|
31902
|
-
(0,
|
|
31893
|
+
(0, import_react70.useLayoutEffect)(() => {
|
|
31903
31894
|
if (contentSize.w > 0) {
|
|
31904
31895
|
const timer = setTimeout(fit, 50);
|
|
31905
31896
|
return () => clearTimeout(timer);
|
|
31906
31897
|
}
|
|
31907
31898
|
}, [contentSize.w]);
|
|
31908
|
-
(0,
|
|
31899
|
+
(0, import_react70.useEffect)(() => {
|
|
31909
31900
|
if (autoSimulate && bracketState.matches.length && !bracketState.champion) {
|
|
31910
31901
|
const timer = setTimeout(autoPlay, 500);
|
|
31911
31902
|
return () => clearTimeout(timer);
|
|
31912
31903
|
}
|
|
31913
31904
|
}, [autoSimulate]);
|
|
31914
|
-
const stats = (0,
|
|
31905
|
+
const stats = (0, import_react70.useMemo)(() => {
|
|
31915
31906
|
const done = bracketState.matches.filter((m) => m.winner).length;
|
|
31916
31907
|
const total = bracketState.matches.length;
|
|
31917
31908
|
const remaining = bracketState.matches.filter(
|
|
@@ -31919,7 +31910,7 @@ function BracketGenerator({
|
|
|
31919
31910
|
).length;
|
|
31920
31911
|
return { done, total, remaining };
|
|
31921
31912
|
}, [bracketState]);
|
|
31922
|
-
const connectorPaths = (0,
|
|
31913
|
+
const connectorPaths = (0, import_react70.useMemo)(() => {
|
|
31923
31914
|
const paths = [];
|
|
31924
31915
|
bracketState.matches.forEach((m) => {
|
|
31925
31916
|
if (m.round >= bracketState.rounds - 1) return;
|
|
@@ -32624,7 +32615,7 @@ function BracketGenerator({
|
|
|
32624
32615
|
}
|
|
32625
32616
|
|
|
32626
32617
|
// src/components/ui/coin-flipper/index.tsx
|
|
32627
|
-
var
|
|
32618
|
+
var import_react71 = require("react");
|
|
32628
32619
|
var import_jsx_runtime115 = require("react/jsx-runtime");
|
|
32629
32620
|
var PRESETS3 = {
|
|
32630
32621
|
gold: {
|
|
@@ -32808,15 +32799,15 @@ function CoinFlipper({
|
|
|
32808
32799
|
}) {
|
|
32809
32800
|
const dark = useDarkMode(darkMode);
|
|
32810
32801
|
const tc = typeof theme5 === "string" ? PRESETS3[theme5] || PRESETS3.gold : theme5;
|
|
32811
|
-
const [result, setResult] = (0,
|
|
32812
|
-
const [flipping, setFlipping] = (0,
|
|
32813
|
-
const [history, setHistory] = (0,
|
|
32814
|
-
const [stats, setStats] = (0,
|
|
32815
|
-
const flipIdRef = (0,
|
|
32816
|
-
const coinRef = (0,
|
|
32817
|
-
const shadowRef = (0,
|
|
32818
|
-
const timersRef = (0,
|
|
32819
|
-
const colors = (0,
|
|
32802
|
+
const [result, setResult] = (0, import_react71.useState)(null);
|
|
32803
|
+
const [flipping, setFlipping] = (0, import_react71.useState)(false);
|
|
32804
|
+
const [history, setHistory] = (0, import_react71.useState)([]);
|
|
32805
|
+
const [stats, setStats] = (0, import_react71.useState)({ heads: 0, tails: 0 });
|
|
32806
|
+
const flipIdRef = (0, import_react71.useRef)(0);
|
|
32807
|
+
const coinRef = (0, import_react71.useRef)(null);
|
|
32808
|
+
const shadowRef = (0, import_react71.useRef)(null);
|
|
32809
|
+
const timersRef = (0, import_react71.useRef)([]);
|
|
32810
|
+
const colors = (0, import_react71.useMemo)(
|
|
32820
32811
|
() => ({
|
|
32821
32812
|
bg: dark ? "#0f1117" : "#f8fafc",
|
|
32822
32813
|
surface: dark ? "#1a1d27" : "#ffffff",
|
|
@@ -32827,7 +32818,7 @@ function CoinFlipper({
|
|
|
32827
32818
|
}),
|
|
32828
32819
|
[dark]
|
|
32829
32820
|
);
|
|
32830
|
-
const flip = (0,
|
|
32821
|
+
const flip = (0, import_react71.useCallback)(() => {
|
|
32831
32822
|
if (flipping) return;
|
|
32832
32823
|
setFlipping(true);
|
|
32833
32824
|
const outcome = Math.random() < 0.5 ? "heads" : "tails";
|
|
@@ -32867,16 +32858,19 @@ function CoinFlipper({
|
|
|
32867
32858
|
}, frames.length * stepMs);
|
|
32868
32859
|
timersRef.current.push(settleTimer);
|
|
32869
32860
|
}, [flipping, onFlip, historyMax]);
|
|
32870
|
-
(0,
|
|
32861
|
+
(0, import_react71.useEffect)(() => {
|
|
32871
32862
|
if (flipRef) flipRef.current = flip;
|
|
32863
|
+
return () => {
|
|
32864
|
+
if (flipRef && flipRef.current === flip) flipRef.current = null;
|
|
32865
|
+
};
|
|
32872
32866
|
}, [flip, flipRef]);
|
|
32873
|
-
(0,
|
|
32867
|
+
(0, import_react71.useEffect)(() => {
|
|
32874
32868
|
if (autoFlip) {
|
|
32875
32869
|
const timer = setTimeout(flip, 300);
|
|
32876
32870
|
return () => clearTimeout(timer);
|
|
32877
32871
|
}
|
|
32878
32872
|
}, []);
|
|
32879
|
-
(0,
|
|
32873
|
+
(0, import_react71.useEffect)(() => {
|
|
32880
32874
|
return () => {
|
|
32881
32875
|
timersRef.current.forEach(clearTimeout);
|
|
32882
32876
|
};
|
|
@@ -33313,6 +33307,23 @@ function CoinFlipper({
|
|
|
33313
33307
|
|
|
33314
33308
|
// src/components/ui/random-player-picker/index.tsx
|
|
33315
33309
|
var import_react73 = require("react");
|
|
33310
|
+
|
|
33311
|
+
// src/hooks/web/use-prefers-reduced-motion.ts
|
|
33312
|
+
var import_react72 = require("react");
|
|
33313
|
+
function usePrefersReducedMotion() {
|
|
33314
|
+
const [reduced, setReduced] = (0, import_react72.useState)(false);
|
|
33315
|
+
(0, import_react72.useEffect)(() => {
|
|
33316
|
+
if (typeof window === "undefined" || !window.matchMedia) return;
|
|
33317
|
+
const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
33318
|
+
setReduced(mq.matches);
|
|
33319
|
+
const read = () => setReduced(mq.matches);
|
|
33320
|
+
mq.addEventListener("change", read);
|
|
33321
|
+
return () => mq.removeEventListener("change", read);
|
|
33322
|
+
}, []);
|
|
33323
|
+
return reduced;
|
|
33324
|
+
}
|
|
33325
|
+
|
|
33326
|
+
// src/components/ui/random-player-picker/index.tsx
|
|
33316
33327
|
var import_jsx_runtime116 = require("react/jsx-runtime");
|
|
33317
33328
|
var PRESETS4 = {
|
|
33318
33329
|
blue: {
|
|
@@ -33370,6 +33381,11 @@ var WEDGE_PALETTE = [
|
|
|
33370
33381
|
];
|
|
33371
33382
|
var DEFAULT_EASING = "cubic-bezier(0.25, 0.46, 0.45, 0.94)";
|
|
33372
33383
|
var DRAMATIC_EASING = "cubic-bezier(0.39, 0.575, 0.565, 1)";
|
|
33384
|
+
var PHYSICS_DECAY_K = 4;
|
|
33385
|
+
function physicsRampDown(t) {
|
|
33386
|
+
return (1 - Math.exp(-PHYSICS_DECAY_K * t)) / (1 - Math.exp(-PHYSICS_DECAY_K));
|
|
33387
|
+
}
|
|
33388
|
+
var REEL_LEAD_IN = 32;
|
|
33373
33389
|
var INTENSITY_CONFIG = {
|
|
33374
33390
|
subtle: {
|
|
33375
33391
|
particles: 14,
|
|
@@ -33441,6 +33457,9 @@ function ReelStage({
|
|
|
33441
33457
|
winner,
|
|
33442
33458
|
spinning,
|
|
33443
33459
|
reelY,
|
|
33460
|
+
displayY,
|
|
33461
|
+
cssTransition,
|
|
33462
|
+
tapeRef,
|
|
33444
33463
|
reelVisibleRows,
|
|
33445
33464
|
reelRowHeight,
|
|
33446
33465
|
size,
|
|
@@ -33464,17 +33483,16 @@ function ReelStage({
|
|
|
33464
33483
|
const stripHeight = reelVisibleRows * reelRowHeight;
|
|
33465
33484
|
const centerOffsetPx = (reelVisibleRows - 1) / 2 * reelRowHeight;
|
|
33466
33485
|
const P = pool.length;
|
|
33467
|
-
const leadIn = 32;
|
|
33468
33486
|
const tapeRows = Math.max(
|
|
33469
|
-
reelVisibleRows +
|
|
33470
|
-
Math.ceil((reelY + stripHeight) / reelRowHeight) +
|
|
33487
|
+
reelVisibleRows + REEL_LEAD_IN + 1,
|
|
33488
|
+
Math.ceil((reelY + stripHeight) / reelRowHeight) + REEL_LEAD_IN + 1
|
|
33471
33489
|
);
|
|
33472
33490
|
const tape = Array.from({ length: tapeRows }, (_, i) => {
|
|
33473
|
-
const poolIdx = P === 0 ? 0 : ((i -
|
|
33491
|
+
const poolIdx = P === 0 ? 0 : ((i - REEL_LEAD_IN) % P + P) % P;
|
|
33474
33492
|
return pool[poolIdx];
|
|
33475
33493
|
});
|
|
33476
|
-
const effectiveY =
|
|
33477
|
-
const transition = spinning ? `transform ${reducedMotion ? 250 : Math.round(spinDuration * intensityCfg.durationMult)}ms ${intensityCfg.easing}` : "none";
|
|
33494
|
+
const effectiveY = displayY + REEL_LEAD_IN * reelRowHeight;
|
|
33495
|
+
const transition = spinning && cssTransition ? `transform ${reducedMotion ? 250 : Math.round(spinDuration * intensityCfg.durationMult)}ms ${intensityCfg.easing}` : "none";
|
|
33478
33496
|
return /* @__PURE__ */ (0, import_jsx_runtime116.jsxs)(
|
|
33479
33497
|
"div",
|
|
33480
33498
|
{
|
|
@@ -33519,6 +33537,7 @@ function ReelStage({
|
|
|
33519
33537
|
) : /* @__PURE__ */ (0, import_jsx_runtime116.jsx)(
|
|
33520
33538
|
"div",
|
|
33521
33539
|
{
|
|
33540
|
+
ref: tapeRef,
|
|
33522
33541
|
"aria-hidden": "true",
|
|
33523
33542
|
style: {
|
|
33524
33543
|
position: "absolute",
|
|
@@ -33734,6 +33753,9 @@ function RandomPlayerPicker({
|
|
|
33734
33753
|
showStats = true,
|
|
33735
33754
|
renderWinner,
|
|
33736
33755
|
spinRef,
|
|
33756
|
+
resetRef,
|
|
33757
|
+
onPoolChange,
|
|
33758
|
+
draftCompleteCaption = "Draft complete \u2014 reset to spin again",
|
|
33737
33759
|
intensity = "normal",
|
|
33738
33760
|
enableHaptics = false,
|
|
33739
33761
|
tickSoundUrl,
|
|
@@ -33748,6 +33770,7 @@ function RandomPlayerPicker({
|
|
|
33748
33770
|
const dark = useDarkMode(darkMode);
|
|
33749
33771
|
const reducedMotion = usePrefersReducedMotion();
|
|
33750
33772
|
const intensityCfg = INTENSITY_CONFIG[intensity];
|
|
33773
|
+
const physicsDriven = intensity === "dramatic" && !reducedMotion;
|
|
33751
33774
|
const [pool, setPool] = (0, import_react73.useState)(participants);
|
|
33752
33775
|
const [rotation, setRotation] = (0, import_react73.useState)(0);
|
|
33753
33776
|
const [reelY, setReelY] = (0, import_react73.useState)(0);
|
|
@@ -33760,10 +33783,17 @@ function RandomPlayerPicker({
|
|
|
33760
33783
|
const particleTimer = (0, import_react73.useRef)(null);
|
|
33761
33784
|
const popTimer = (0, import_react73.useRef)(null);
|
|
33762
33785
|
const tickInterval = (0, import_react73.useRef)(null);
|
|
33786
|
+
const pendingPopIdxRef = (0, import_react73.useRef)(null);
|
|
33787
|
+
const removeWinnersRef = (0, import_react73.useRef)(removeWinners);
|
|
33763
33788
|
const tickAudioRef = (0, import_react73.useRef)(null);
|
|
33764
33789
|
const winAudioRef = (0, import_react73.useRef)(null);
|
|
33765
33790
|
const containerRef = (0, import_react73.useRef)(null);
|
|
33766
33791
|
const prevIdsRef = (0, import_react73.useRef)("");
|
|
33792
|
+
const wheelSvgRef = (0, import_react73.useRef)(null);
|
|
33793
|
+
const reelTapeRef = (0, import_react73.useRef)(null);
|
|
33794
|
+
const rafRef = (0, import_react73.useRef)(null);
|
|
33795
|
+
const visualRotationRef = (0, import_react73.useRef)(null);
|
|
33796
|
+
const visualReelYRef = (0, import_react73.useRef)(null);
|
|
33767
33797
|
(0, import_react73.useEffect)(() => {
|
|
33768
33798
|
if (typeof window === "undefined" || !tickSoundUrl) {
|
|
33769
33799
|
tickAudioRef.current = null;
|
|
@@ -33798,10 +33828,33 @@ function RandomPlayerPicker({
|
|
|
33798
33828
|
winAudioRef.current = null;
|
|
33799
33829
|
};
|
|
33800
33830
|
}, [winSoundUrl, soundVolume]);
|
|
33831
|
+
(0, import_react73.useEffect)(() => {
|
|
33832
|
+
removeWinnersRef.current = removeWinners;
|
|
33833
|
+
}, [removeWinners]);
|
|
33801
33834
|
(0, import_react73.useEffect)(() => {
|
|
33802
33835
|
const ids = participants.map((p) => p.id).join("|");
|
|
33803
33836
|
const structuralChange = ids !== prevIdsRef.current;
|
|
33804
33837
|
if (structuralChange) {
|
|
33838
|
+
if (winnerTimer.current) {
|
|
33839
|
+
clearTimeout(winnerTimer.current);
|
|
33840
|
+
winnerTimer.current = null;
|
|
33841
|
+
}
|
|
33842
|
+
if (tickInterval.current) {
|
|
33843
|
+
clearTimeout(tickInterval.current);
|
|
33844
|
+
tickInterval.current = null;
|
|
33845
|
+
}
|
|
33846
|
+
if (popTimer.current) {
|
|
33847
|
+
clearTimeout(popTimer.current);
|
|
33848
|
+
popTimer.current = null;
|
|
33849
|
+
}
|
|
33850
|
+
if (rafRef.current) {
|
|
33851
|
+
cancelAnimationFrame(rafRef.current);
|
|
33852
|
+
rafRef.current = null;
|
|
33853
|
+
}
|
|
33854
|
+
visualRotationRef.current = null;
|
|
33855
|
+
visualReelYRef.current = null;
|
|
33856
|
+
pendingPopIdxRef.current = null;
|
|
33857
|
+
setSpinning(false);
|
|
33805
33858
|
setPool(participants);
|
|
33806
33859
|
setHistory([]);
|
|
33807
33860
|
setWinner(null);
|
|
@@ -33858,33 +33911,50 @@ function RandomPlayerPicker({
|
|
|
33858
33911
|
[dark]
|
|
33859
33912
|
);
|
|
33860
33913
|
const spin = (0, import_react73.useCallback)(() => {
|
|
33861
|
-
if (spinning
|
|
33914
|
+
if (spinning) return;
|
|
33915
|
+
let activePool = pool;
|
|
33916
|
+
if (popTimer.current) {
|
|
33917
|
+
clearTimeout(popTimer.current);
|
|
33918
|
+
popTimer.current = null;
|
|
33919
|
+
const pendingIdx = pendingPopIdxRef.current;
|
|
33920
|
+
pendingPopIdxRef.current = null;
|
|
33921
|
+
if (pendingIdx != null && pendingIdx >= 0 && pendingIdx < pool.length && removeWinnersRef.current) {
|
|
33922
|
+
activePool = pool.filter((_, i) => i !== pendingIdx);
|
|
33923
|
+
setPool(activePool);
|
|
33924
|
+
setWinner(null);
|
|
33925
|
+
}
|
|
33926
|
+
}
|
|
33927
|
+
const activeN = activePool.length;
|
|
33928
|
+
if (activeN < 2) return;
|
|
33929
|
+
const activeAnglePerWedge = 2 * Math.PI / activeN;
|
|
33930
|
+
const activeRotation = rotation;
|
|
33931
|
+
const activeReelY = reelY;
|
|
33862
33932
|
onSpinStart?.();
|
|
33863
|
-
const winnerIdx = weightedIndex(
|
|
33933
|
+
const winnerIdx = weightedIndex(activePool);
|
|
33864
33934
|
const minRev = minRevolutions + intensityCfg.revsBoost;
|
|
33865
33935
|
const maxRev = maxRevolutions + intensityCfg.revsBoost;
|
|
33866
33936
|
const minFullRevs = Math.ceil(minRev);
|
|
33867
33937
|
const maxFullRevs = Math.max(minFullRevs, Math.floor(maxRev));
|
|
33868
33938
|
const revolutions = minFullRevs + Math.floor(Math.random() * (maxFullRevs - minFullRevs + 1));
|
|
33869
|
-
const wedgeMidRad = winnerIdx *
|
|
33939
|
+
const wedgeMidRad = winnerIdx * activeAnglePerWedge + activeAnglePerWedge / 2;
|
|
33870
33940
|
const wedgeMidDeg = wedgeMidRad * 180 / Math.PI;
|
|
33871
|
-
const wheelWobble = (Math.random() - 0.5) * (
|
|
33872
|
-
const currentMod = (
|
|
33941
|
+
const wheelWobble = (Math.random() - 0.5) * (activeAnglePerWedge * 180 / Math.PI) * 0.6;
|
|
33942
|
+
const currentMod = (activeRotation % 360 + 360) % 360;
|
|
33873
33943
|
const targetMod = ((-wedgeMidDeg + wheelWobble) % 360 + 360) % 360;
|
|
33874
33944
|
let deltaMod = targetMod - currentMod;
|
|
33875
33945
|
if (deltaMod < 0) deltaMod += 360;
|
|
33876
|
-
const finalRotation =
|
|
33946
|
+
const finalRotation = activeRotation + deltaMod + revolutions * 360;
|
|
33877
33947
|
const centerOffsetRows = (reelVisibleRows - 1) / 2;
|
|
33878
33948
|
const centerOffsetPx = centerOffsetRows * reelRowHeight;
|
|
33879
33949
|
const currentCenterRow = Math.round(
|
|
33880
|
-
(
|
|
33950
|
+
(activeReelY + centerOffsetPx) / reelRowHeight
|
|
33881
33951
|
);
|
|
33882
|
-
const currentCenterMod = mod(currentCenterRow,
|
|
33883
|
-
const targetRowOffset = (winnerIdx - currentCenterMod +
|
|
33884
|
-
const reelRevRows = Math.round(revolutions *
|
|
33885
|
-
const finalReelY =
|
|
33886
|
-
const settledWinnerIdx = mode === "reel" ? reelIndexAtCenter(finalReelY, centerOffsetPx, reelRowHeight,
|
|
33887
|
-
const finalWinnerIdx = settledWinnerIdx >= 0 && settledWinnerIdx <
|
|
33952
|
+
const currentCenterMod = mod(currentCenterRow, activeN);
|
|
33953
|
+
const targetRowOffset = (winnerIdx - currentCenterMod + activeN) % activeN;
|
|
33954
|
+
const reelRevRows = Math.round(revolutions * activeN);
|
|
33955
|
+
const finalReelY = activeReelY + (reelRevRows + targetRowOffset) * reelRowHeight;
|
|
33956
|
+
const settledWinnerIdx = mode === "reel" ? reelIndexAtCenter(finalReelY, centerOffsetPx, reelRowHeight, activeN) : wheelIndexAtPointer(finalRotation, activeN);
|
|
33957
|
+
const finalWinnerIdx = settledWinnerIdx >= 0 && settledWinnerIdx < activePool.length ? settledWinnerIdx : winnerIdx;
|
|
33888
33958
|
const effectiveDuration = reducedMotion ? 250 : Math.round(spinDuration * intensityCfg.durationMult);
|
|
33889
33959
|
setSpinning(true);
|
|
33890
33960
|
setWinner(null);
|
|
@@ -33894,10 +33964,59 @@ function RandomPlayerPicker({
|
|
|
33894
33964
|
} else {
|
|
33895
33965
|
setRotation(finalRotation);
|
|
33896
33966
|
}
|
|
33897
|
-
|
|
33967
|
+
const fireTick = () => {
|
|
33968
|
+
onTick?.();
|
|
33969
|
+
const ta = tickAudioRef.current;
|
|
33970
|
+
if (ta) {
|
|
33971
|
+
try {
|
|
33972
|
+
ta.currentTime = 0;
|
|
33973
|
+
void ta.play().catch(() => {
|
|
33974
|
+
});
|
|
33975
|
+
} catch {
|
|
33976
|
+
}
|
|
33977
|
+
}
|
|
33978
|
+
};
|
|
33979
|
+
if (physicsDriven) {
|
|
33980
|
+
const from = mode === "reel" ? activeReelY : activeRotation;
|
|
33981
|
+
const to = mode === "reel" ? finalReelY : finalRotation;
|
|
33982
|
+
const crossUnit = mode === "reel" ? reelRowHeight : activeAnglePerWedge * 180 / Math.PI;
|
|
33983
|
+
if (mode === "reel") visualReelYRef.current = from;
|
|
33984
|
+
else visualRotationRef.current = from;
|
|
33985
|
+
let crossings = 0;
|
|
33986
|
+
const t0 = performance.now();
|
|
33987
|
+
const step = (now) => {
|
|
33988
|
+
const t = Math.min(1, (now - t0) / effectiveDuration);
|
|
33989
|
+
const value = from + (to - from) * physicsRampDown(t);
|
|
33990
|
+
if (mode === "reel") {
|
|
33991
|
+
visualReelYRef.current = value;
|
|
33992
|
+
const el = reelTapeRef.current;
|
|
33993
|
+
if (el) {
|
|
33994
|
+
el.style.transform = `translateY(${-(value + REEL_LEAD_IN * reelRowHeight)}px)`;
|
|
33995
|
+
}
|
|
33996
|
+
} else {
|
|
33997
|
+
visualRotationRef.current = value;
|
|
33998
|
+
const el = wheelSvgRef.current;
|
|
33999
|
+
if (el) el.style.transform = `rotate(${value}deg)`;
|
|
34000
|
+
}
|
|
34001
|
+
const crossed = Math.floor(Math.abs(value - from) / crossUnit);
|
|
34002
|
+
if (crossed > crossings) {
|
|
34003
|
+
crossings = crossed;
|
|
34004
|
+
fireTick();
|
|
34005
|
+
}
|
|
34006
|
+
if (t < 1) {
|
|
34007
|
+
rafRef.current = requestAnimationFrame(step);
|
|
34008
|
+
} else {
|
|
34009
|
+
rafRef.current = null;
|
|
34010
|
+
visualRotationRef.current = null;
|
|
34011
|
+
visualReelYRef.current = null;
|
|
34012
|
+
}
|
|
34013
|
+
};
|
|
34014
|
+
rafRef.current = requestAnimationFrame(step);
|
|
34015
|
+
}
|
|
34016
|
+
if (!physicsDriven && (onTick || tickAudioRef.current) && !reducedMotion) {
|
|
33898
34017
|
const wedges = mode === "reel" ? Math.max(1, reelRevRows + targetRowOffset) : (() => {
|
|
33899
34018
|
const totalDelta = deltaMod + revolutions * 360;
|
|
33900
|
-
const wedgeDeg =
|
|
34019
|
+
const wedgeDeg = activeAnglePerWedge * 180 / Math.PI;
|
|
33901
34020
|
return Math.max(
|
|
33902
34021
|
1,
|
|
33903
34022
|
Math.round(totalDelta / Math.max(wedgeDeg, 1))
|
|
@@ -33912,16 +34031,7 @@ function RandomPlayerPicker({
|
|
|
33912
34031
|
const eased = 1 - Math.pow(1 - progress, 3);
|
|
33913
34032
|
const interval = startInterval + (endInterval - startInterval) * eased;
|
|
33914
34033
|
tickInterval.current = setTimeout(() => {
|
|
33915
|
-
|
|
33916
|
-
const ta = tickAudioRef.current;
|
|
33917
|
-
if (ta) {
|
|
33918
|
-
try {
|
|
33919
|
-
ta.currentTime = 0;
|
|
33920
|
-
void ta.play().catch(() => {
|
|
33921
|
-
});
|
|
33922
|
-
} catch {
|
|
33923
|
-
}
|
|
33924
|
-
}
|
|
34034
|
+
fireTick();
|
|
33925
34035
|
elapsed += interval;
|
|
33926
34036
|
schedule();
|
|
33927
34037
|
}, interval);
|
|
@@ -33929,7 +34039,7 @@ function RandomPlayerPicker({
|
|
|
33929
34039
|
schedule();
|
|
33930
34040
|
}
|
|
33931
34041
|
winnerTimer.current = setTimeout(() => {
|
|
33932
|
-
const picked =
|
|
34042
|
+
const picked = activePool[finalWinnerIdx];
|
|
33933
34043
|
if (!picked) {
|
|
33934
34044
|
setSpinning(false);
|
|
33935
34045
|
return;
|
|
@@ -33975,9 +34085,13 @@ function RandomPlayerPicker({
|
|
|
33975
34085
|
particleTimer.current = setTimeout(() => setParticles([]), 2400);
|
|
33976
34086
|
}
|
|
33977
34087
|
if (removeWinners) {
|
|
34088
|
+
pendingPopIdxRef.current = finalWinnerIdx;
|
|
33978
34089
|
popTimer.current = setTimeout(() => {
|
|
34090
|
+
popTimer.current = null;
|
|
34091
|
+
pendingPopIdxRef.current = null;
|
|
34092
|
+
if (!removeWinnersRef.current) return;
|
|
33979
34093
|
setPool((p) => p.filter((_, i) => i !== finalWinnerIdx));
|
|
33980
|
-
setWinner(null);
|
|
34094
|
+
if (activePool.length - 1 >= 2) setWinner(null);
|
|
33981
34095
|
setRotation(0);
|
|
33982
34096
|
setReelY(0);
|
|
33983
34097
|
}, 2800);
|
|
@@ -33986,8 +34100,6 @@ function RandomPlayerPicker({
|
|
|
33986
34100
|
}, [
|
|
33987
34101
|
pool,
|
|
33988
34102
|
spinning,
|
|
33989
|
-
n,
|
|
33990
|
-
anglePerWedge,
|
|
33991
34103
|
rotation,
|
|
33992
34104
|
reelY,
|
|
33993
34105
|
mode,
|
|
@@ -34002,6 +34114,7 @@ function RandomPlayerPicker({
|
|
|
34002
34114
|
maxRevolutions,
|
|
34003
34115
|
history,
|
|
34004
34116
|
reducedMotion,
|
|
34117
|
+
physicsDriven,
|
|
34005
34118
|
enableHaptics,
|
|
34006
34119
|
intensityCfg.particles,
|
|
34007
34120
|
intensityCfg.durationMult,
|
|
@@ -34013,16 +34126,25 @@ function RandomPlayerPicker({
|
|
|
34013
34126
|
if (spinRef && spinRef.current === spin) spinRef.current = null;
|
|
34014
34127
|
};
|
|
34015
34128
|
}, [spin, spinRef]);
|
|
34129
|
+
const onPoolChangeRef = (0, import_react73.useRef)(onPoolChange);
|
|
34130
|
+
(0, import_react73.useEffect)(() => {
|
|
34131
|
+
onPoolChangeRef.current = onPoolChange;
|
|
34132
|
+
});
|
|
34133
|
+
(0, import_react73.useEffect)(() => {
|
|
34134
|
+
onPoolChangeRef.current?.(pool);
|
|
34135
|
+
}, [pool]);
|
|
34016
34136
|
(0, import_react73.useEffect)(
|
|
34017
34137
|
() => () => {
|
|
34018
34138
|
if (winnerTimer.current) clearTimeout(winnerTimer.current);
|
|
34019
34139
|
if (particleTimer.current) clearTimeout(particleTimer.current);
|
|
34020
34140
|
if (popTimer.current) clearTimeout(popTimer.current);
|
|
34021
34141
|
if (tickInterval.current) clearTimeout(tickInterval.current);
|
|
34142
|
+
if (rafRef.current) cancelAnimationFrame(rafRef.current);
|
|
34022
34143
|
},
|
|
34023
34144
|
[]
|
|
34024
34145
|
);
|
|
34025
34146
|
(0, import_react73.useEffect)(() => {
|
|
34147
|
+
if (trigger === "external") return;
|
|
34026
34148
|
const el = containerRef.current;
|
|
34027
34149
|
if (!el) return;
|
|
34028
34150
|
const h = (e) => {
|
|
@@ -34033,8 +34155,28 @@ function RandomPlayerPicker({
|
|
|
34033
34155
|
};
|
|
34034
34156
|
el.addEventListener("keydown", h);
|
|
34035
34157
|
return () => el.removeEventListener("keydown", h);
|
|
34036
|
-
}, [spin]);
|
|
34158
|
+
}, [spin, trigger]);
|
|
34037
34159
|
const reset = (0, import_react73.useCallback)(() => {
|
|
34160
|
+
if (winnerTimer.current) {
|
|
34161
|
+
clearTimeout(winnerTimer.current);
|
|
34162
|
+
winnerTimer.current = null;
|
|
34163
|
+
}
|
|
34164
|
+
if (tickInterval.current) {
|
|
34165
|
+
clearTimeout(tickInterval.current);
|
|
34166
|
+
tickInterval.current = null;
|
|
34167
|
+
}
|
|
34168
|
+
if (popTimer.current) {
|
|
34169
|
+
clearTimeout(popTimer.current);
|
|
34170
|
+
popTimer.current = null;
|
|
34171
|
+
}
|
|
34172
|
+
if (rafRef.current) {
|
|
34173
|
+
cancelAnimationFrame(rafRef.current);
|
|
34174
|
+
rafRef.current = null;
|
|
34175
|
+
}
|
|
34176
|
+
visualRotationRef.current = null;
|
|
34177
|
+
visualReelYRef.current = null;
|
|
34178
|
+
pendingPopIdxRef.current = null;
|
|
34179
|
+
setSpinning(false);
|
|
34038
34180
|
setPool(participants);
|
|
34039
34181
|
setHistory([]);
|
|
34040
34182
|
setWinner(null);
|
|
@@ -34042,9 +34184,17 @@ function RandomPlayerPicker({
|
|
|
34042
34184
|
setReelY(0);
|
|
34043
34185
|
setParticles([]);
|
|
34044
34186
|
}, [participants]);
|
|
34187
|
+
(0, import_react73.useEffect)(() => {
|
|
34188
|
+
if (resetRef) resetRef.current = reset;
|
|
34189
|
+
return () => {
|
|
34190
|
+
if (resetRef && resetRef.current === reset) resetRef.current = null;
|
|
34191
|
+
};
|
|
34192
|
+
}, [reset, resetRef]);
|
|
34045
34193
|
const isIdle = n < 2;
|
|
34194
|
+
const drainedByDraft = isIdle && participants.length >= 2;
|
|
34195
|
+
const effectiveIdleCaption = drainedByDraft ? draftCompleteCaption : placeholderCaption;
|
|
34046
34196
|
const placeholderDiscColor = dark ? "#2a3344" : "#e2e8f0";
|
|
34047
|
-
if (isIdle && placeholderMode === "message") {
|
|
34197
|
+
if (isIdle && !drainedByDraft && placeholderMode === "message") {
|
|
34048
34198
|
return /* @__PURE__ */ (0, import_jsx_runtime116.jsx)(
|
|
34049
34199
|
"div",
|
|
34050
34200
|
{
|
|
@@ -34066,6 +34216,8 @@ function RandomPlayerPicker({
|
|
|
34066
34216
|
const shakeKeyframe = `rppShake_${intensity}`;
|
|
34067
34217
|
const glowKeyframe = `rppGlow_${intensity}`;
|
|
34068
34218
|
const pulseKeyframe = `rppPulse_${intensity}`;
|
|
34219
|
+
const displayRotation = visualRotationRef.current ?? rotation;
|
|
34220
|
+
const displayReelY = visualReelYRef.current ?? reelY;
|
|
34069
34221
|
return /* @__PURE__ */ (0, import_jsx_runtime116.jsxs)(
|
|
34070
34222
|
"div",
|
|
34071
34223
|
{
|
|
@@ -34112,13 +34264,33 @@ function RandomPlayerPicker({
|
|
|
34112
34264
|
[data-rpp-anim] { animation: none !important; }
|
|
34113
34265
|
}
|
|
34114
34266
|
` }),
|
|
34115
|
-
|
|
34267
|
+
isIdle && placeholderMode === "message" ? (
|
|
34268
|
+
/* Drained draft in "message" mode — compact box in the wheel's
|
|
34269
|
+
place; the winner card / stats / controls / history below stay
|
|
34270
|
+
mounted so the endgame isn't a dead end. */
|
|
34271
|
+
/* @__PURE__ */ (0, import_jsx_runtime116.jsx)(
|
|
34272
|
+
"div",
|
|
34273
|
+
{
|
|
34274
|
+
style: {
|
|
34275
|
+
padding: "2rem 1rem",
|
|
34276
|
+
textAlign: "center",
|
|
34277
|
+
color: c.dim,
|
|
34278
|
+
borderRadius: 12,
|
|
34279
|
+
border: `1px dashed ${c.border}`
|
|
34280
|
+
},
|
|
34281
|
+
children: effectiveIdleCaption
|
|
34282
|
+
}
|
|
34283
|
+
)
|
|
34284
|
+
) : mode === "reel" ? /* @__PURE__ */ (0, import_jsx_runtime116.jsx)(
|
|
34116
34285
|
ReelStage,
|
|
34117
34286
|
{
|
|
34118
34287
|
pool,
|
|
34119
34288
|
winner,
|
|
34120
34289
|
spinning,
|
|
34121
34290
|
reelY,
|
|
34291
|
+
displayY: displayReelY,
|
|
34292
|
+
cssTransition: !physicsDriven,
|
|
34293
|
+
tapeRef: reelTapeRef,
|
|
34122
34294
|
reelVisibleRows,
|
|
34123
34295
|
reelRowHeight,
|
|
34124
34296
|
size,
|
|
@@ -34127,7 +34299,7 @@ function RandomPlayerPicker({
|
|
|
34127
34299
|
dark,
|
|
34128
34300
|
isIdle,
|
|
34129
34301
|
placeholderMode,
|
|
34130
|
-
placeholderCaption,
|
|
34302
|
+
placeholderCaption: effectiveIdleCaption,
|
|
34131
34303
|
placeholderDiscColor,
|
|
34132
34304
|
spinDuration,
|
|
34133
34305
|
intensityCfg,
|
|
@@ -34184,6 +34356,7 @@ function RandomPlayerPicker({
|
|
|
34184
34356
|
/* @__PURE__ */ (0, import_jsx_runtime116.jsxs)(
|
|
34185
34357
|
"svg",
|
|
34186
34358
|
{
|
|
34359
|
+
ref: wheelSvgRef,
|
|
34187
34360
|
width: size,
|
|
34188
34361
|
height: size,
|
|
34189
34362
|
"aria-hidden": "true",
|
|
@@ -34192,8 +34365,8 @@ function RandomPlayerPicker({
|
|
|
34192
34365
|
top: 36,
|
|
34193
34366
|
left: 0,
|
|
34194
34367
|
transformOrigin: `${cx}px ${cy}px`,
|
|
34195
|
-
transform: `rotate(${
|
|
34196
|
-
transition: spinning ? `transform ${reducedMotion ? 250 : Math.round(spinDuration * intensityCfg.durationMult)}ms ${intensityCfg.easing}` : "none",
|
|
34368
|
+
transform: `rotate(${displayRotation}deg)`,
|
|
34369
|
+
transition: spinning && !physicsDriven ? `transform ${reducedMotion ? 250 : Math.round(spinDuration * intensityCfg.durationMult)}ms ${intensityCfg.easing}` : "none",
|
|
34197
34370
|
filter: `drop-shadow(0 8px 28px ${dark ? "rgba(0,0,0,0.55)" : "rgba(0,0,0,0.15)"})`
|
|
34198
34371
|
},
|
|
34199
34372
|
children: [
|
|
@@ -34346,7 +34519,7 @@ function RandomPlayerPicker({
|
|
|
34346
34519
|
pointerEvents: "none",
|
|
34347
34520
|
zIndex: 3
|
|
34348
34521
|
},
|
|
34349
|
-
children:
|
|
34522
|
+
children: effectiveIdleCaption
|
|
34350
34523
|
}
|
|
34351
34524
|
),
|
|
34352
34525
|
particles.map((p) => /* @__PURE__ */ (0, import_jsx_runtime116.jsx)(
|
|
@@ -38487,27 +38660,28 @@ function GameTimer({
|
|
|
38487
38660
|
const now = performance.now();
|
|
38488
38661
|
const delta = now - lastTickRef.current;
|
|
38489
38662
|
lastTickRef.current = now;
|
|
38490
|
-
|
|
38491
|
-
|
|
38492
|
-
|
|
38493
|
-
|
|
38494
|
-
|
|
38495
|
-
|
|
38496
|
-
|
|
38663
|
+
const next = elapsedRef.current + delta;
|
|
38664
|
+
if (mode === "countdown" && next >= durationMs) {
|
|
38665
|
+
elapsedRef.current = durationMs;
|
|
38666
|
+
setElapsedMs(durationMs);
|
|
38667
|
+
setRunning(false);
|
|
38668
|
+
setCompleted(true);
|
|
38669
|
+
onComplete?.();
|
|
38670
|
+
return;
|
|
38671
|
+
}
|
|
38672
|
+
if (mode === "countdown") {
|
|
38673
|
+
const secLeft = Math.ceil((durationMs - next) / 1e3);
|
|
38674
|
+
if (secLeft <= warningAt && !warningFiredRef.current) {
|
|
38675
|
+
warningFiredRef.current = true;
|
|
38676
|
+
onWarning?.();
|
|
38497
38677
|
}
|
|
38498
|
-
if (
|
|
38499
|
-
|
|
38500
|
-
|
|
38501
|
-
warningFiredRef.current = true;
|
|
38502
|
-
onWarning?.();
|
|
38503
|
-
}
|
|
38504
|
-
if (secLeft <= dangerAt && !dangerFiredRef.current) {
|
|
38505
|
-
dangerFiredRef.current = true;
|
|
38506
|
-
onDanger?.();
|
|
38507
|
-
}
|
|
38678
|
+
if (secLeft <= dangerAt && !dangerFiredRef.current) {
|
|
38679
|
+
dangerFiredRef.current = true;
|
|
38680
|
+
onDanger?.();
|
|
38508
38681
|
}
|
|
38509
|
-
|
|
38510
|
-
|
|
38682
|
+
}
|
|
38683
|
+
elapsedRef.current = next;
|
|
38684
|
+
setElapsedMs(next);
|
|
38511
38685
|
rafRef.current = requestAnimationFrame(tick);
|
|
38512
38686
|
}, [mode, durationMs, warningAt, dangerAt, onComplete, onWarning, onDanger]);
|
|
38513
38687
|
(0, import_react76.useEffect)(() => {
|
|
@@ -38528,6 +38702,7 @@ function GameTimer({
|
|
|
38528
38702
|
}, []);
|
|
38529
38703
|
const reset = (0, import_react76.useCallback)(() => {
|
|
38530
38704
|
setRunning(false);
|
|
38705
|
+
elapsedRef.current = 0;
|
|
38531
38706
|
setElapsedMs(0);
|
|
38532
38707
|
setCompleted(false);
|
|
38533
38708
|
setLaps([]);
|
|
@@ -39824,8 +39999,10 @@ function TeamGenerator({
|
|
|
39824
39999
|
minPlayersPerTeam = 2,
|
|
39825
40000
|
maxPlayers = Number.POSITIVE_INFINITY,
|
|
39826
40001
|
defaultNames = [],
|
|
40002
|
+
defaultTeams,
|
|
39827
40003
|
onNamesChange,
|
|
39828
40004
|
onGenerate,
|
|
40005
|
+
onTeamsChange,
|
|
39829
40006
|
onTeamCountChange,
|
|
39830
40007
|
labels,
|
|
39831
40008
|
ariaLabel = "Team generator",
|
|
@@ -39840,12 +40017,17 @@ function TeamGenerator({
|
|
|
39840
40017
|
const [names, setNames] = (0, import_react78.useState)(defaultNames);
|
|
39841
40018
|
const [input, setInput] = (0, import_react78.useState)("");
|
|
39842
40019
|
const [teamCount, setTeamCount] = (0, import_react78.useState)(initialCount);
|
|
39843
|
-
const [teams, setTeams] = (0, import_react78.useState)(
|
|
40020
|
+
const [teams, setTeams] = (0, import_react78.useState)(
|
|
40021
|
+
defaultTeams && defaultTeams.length > 0 ? defaultTeams : null
|
|
40022
|
+
);
|
|
39844
40023
|
const inputRef = (0, import_react78.useRef)(null);
|
|
39845
40024
|
const minNames = teamCount * minPlayersPerTeam;
|
|
39846
40025
|
(0, import_react78.useEffect)(() => {
|
|
39847
40026
|
onNamesChange?.(names);
|
|
39848
40027
|
}, [names, onNamesChange]);
|
|
40028
|
+
(0, import_react78.useEffect)(() => {
|
|
40029
|
+
onTeamsChange?.(teams);
|
|
40030
|
+
}, [teams, onTeamsChange]);
|
|
39849
40031
|
const addName = (0, import_react78.useCallback)(() => {
|
|
39850
40032
|
const trimmed = input.trim();
|
|
39851
40033
|
if (!trimmed) return;
|
|
@@ -40467,6 +40649,8 @@ function RoundRobinScheduler({
|
|
|
40467
40649
|
theme: theme5 = "slate",
|
|
40468
40650
|
darkMode,
|
|
40469
40651
|
minTeams = 3,
|
|
40652
|
+
maxTeams,
|
|
40653
|
+
nameMaxLength,
|
|
40470
40654
|
defaultTeams = [],
|
|
40471
40655
|
resumeRounds,
|
|
40472
40656
|
onTeamsChange,
|
|
@@ -40489,39 +40673,41 @@ function RoundRobinScheduler({
|
|
|
40489
40673
|
(0, import_react79.useEffect)(() => {
|
|
40490
40674
|
onTeamsChange?.(teams);
|
|
40491
40675
|
}, [teams, onTeamsChange]);
|
|
40492
|
-
const
|
|
40493
|
-
|
|
40494
|
-
if (names.length === 0) return;
|
|
40495
|
-
setTeams((prev) => {
|
|
40496
|
-
const existing = new Set(prev.map((t) => t.toLowerCase()));
|
|
40497
|
-
const next = [...prev];
|
|
40498
|
-
for (const name of names) {
|
|
40499
|
-
if (!existing.has(name.toLowerCase())) {
|
|
40500
|
-
existing.add(name.toLowerCase());
|
|
40501
|
-
next.push(name);
|
|
40502
|
-
}
|
|
40503
|
-
}
|
|
40504
|
-
return next;
|
|
40505
|
-
});
|
|
40506
|
-
setInput("");
|
|
40507
|
-
setRounds([]);
|
|
40508
|
-
}, []);
|
|
40509
|
-
const importTeams = (0, import_react79.useCallback)((incoming) => {
|
|
40510
|
-
if (incoming.length === 0) return;
|
|
40511
|
-
setTeams((prev) => {
|
|
40676
|
+
const mergeNames = (0, import_react79.useCallback)(
|
|
40677
|
+
(prev, incoming) => {
|
|
40512
40678
|
const existing = new Set(prev.map((t) => t.toLowerCase()));
|
|
40513
40679
|
const next = [...prev];
|
|
40514
|
-
for (const
|
|
40680
|
+
for (const rawName of incoming) {
|
|
40681
|
+
if (maxTeams != null && next.length >= maxTeams) break;
|
|
40682
|
+
const name = nameMaxLength != null ? rawName.slice(0, nameMaxLength) : rawName;
|
|
40515
40683
|
const key = name.toLowerCase();
|
|
40516
|
-
if (!existing.has(key)) {
|
|
40684
|
+
if (name.length > 0 && !existing.has(key)) {
|
|
40517
40685
|
existing.add(key);
|
|
40518
40686
|
next.push(name);
|
|
40519
40687
|
}
|
|
40520
40688
|
}
|
|
40521
40689
|
return next;
|
|
40522
|
-
}
|
|
40523
|
-
|
|
40524
|
-
|
|
40690
|
+
},
|
|
40691
|
+
[maxTeams, nameMaxLength]
|
|
40692
|
+
);
|
|
40693
|
+
const addTeams = (0, import_react79.useCallback)(
|
|
40694
|
+
(raw) => {
|
|
40695
|
+
const names = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
40696
|
+
if (names.length === 0) return;
|
|
40697
|
+
setTeams((prev) => mergeNames(prev, names));
|
|
40698
|
+
setInput("");
|
|
40699
|
+
setRounds([]);
|
|
40700
|
+
},
|
|
40701
|
+
[mergeNames]
|
|
40702
|
+
);
|
|
40703
|
+
const importTeams = (0, import_react79.useCallback)(
|
|
40704
|
+
(incoming) => {
|
|
40705
|
+
if (incoming.length === 0) return;
|
|
40706
|
+
setTeams((prev) => mergeNames(prev, incoming));
|
|
40707
|
+
setRounds([]);
|
|
40708
|
+
},
|
|
40709
|
+
[mergeNames]
|
|
40710
|
+
);
|
|
40525
40711
|
const handleKeyDown = (0, import_react79.useCallback)(
|
|
40526
40712
|
(e) => {
|
|
40527
40713
|
if (e.key === "Enter") {
|
|
@@ -41010,6 +41196,8 @@ function Scoreboard({
|
|
|
41010
41196
|
onTeamsChange,
|
|
41011
41197
|
onRoundAdvance,
|
|
41012
41198
|
onNewGame,
|
|
41199
|
+
onReset,
|
|
41200
|
+
nameMaxLength,
|
|
41013
41201
|
labels,
|
|
41014
41202
|
ariaLabel = "Scoreboard",
|
|
41015
41203
|
className
|
|
@@ -41084,7 +41272,8 @@ function Scoreboard({
|
|
|
41084
41272
|
setRound(1);
|
|
41085
41273
|
setHistory([]);
|
|
41086
41274
|
setHistoryOpen(false);
|
|
41087
|
-
|
|
41275
|
+
onReset?.();
|
|
41276
|
+
}, [onReset]);
|
|
41088
41277
|
const newGame = (0, import_react80.useCallback)(() => {
|
|
41089
41278
|
setTeams(createDefaultTeams(seedCount, L.teamNamePrefix));
|
|
41090
41279
|
setRound(1);
|
|
@@ -41306,6 +41495,7 @@ function Scoreboard({
|
|
|
41306
41495
|
{
|
|
41307
41496
|
type: "text",
|
|
41308
41497
|
value: team.name,
|
|
41498
|
+
maxLength: nameMaxLength,
|
|
41309
41499
|
onChange: (e) => updateName(index, e.target.value),
|
|
41310
41500
|
style: nameInputStyle,
|
|
41311
41501
|
"aria-label": `Team ${index + 1} name`
|