@digilogiclabs/saas-factory-ui 2.2.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 +62 -6
- package/dist/index.d.ts +62 -6
- package/dist/index.js +300 -99
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +300 -99
- package/dist/index.mjs.map +1 -1
- package/dist/web/index.d.mts +62 -6
- package/dist/web/index.d.ts +62 -6
- package/dist/web/index.js +402 -199
- package/dist/web/index.js.map +1 -1
- package/dist/web/index.mjs +348 -145
- package/dist/web/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -30463,14 +30463,35 @@ var PIP_POS = {
|
|
|
30463
30463
|
[75, 75]
|
|
30464
30464
|
]
|
|
30465
30465
|
};
|
|
30466
|
-
var
|
|
30466
|
+
var D6_FACE_PLACEMENTS = {
|
|
30467
30467
|
1: { x: 0, y: 0 },
|
|
30468
|
-
2: { x: 0, y:
|
|
30469
|
-
3: { x:
|
|
30470
|
-
4: { x:
|
|
30471
|
-
5: { x:
|
|
30472
|
-
6: { x:
|
|
30468
|
+
2: { x: 0, y: 90 },
|
|
30469
|
+
3: { x: 90, y: 0 },
|
|
30470
|
+
4: { x: -90, y: 0 },
|
|
30471
|
+
5: { x: 0, y: -90 },
|
|
30472
|
+
6: { x: 0, y: 180 }
|
|
30473
30473
|
};
|
|
30474
|
+
function d6LandingRotation(value) {
|
|
30475
|
+
const p = D6_FACE_PLACEMENTS[value];
|
|
30476
|
+
return {
|
|
30477
|
+
x: Math.abs(p.x) === 180 ? 180 : -p.x || 0,
|
|
30478
|
+
y: Math.abs(p.y) === 180 ? 180 : -p.y || 0
|
|
30479
|
+
};
|
|
30480
|
+
}
|
|
30481
|
+
var POLY_SHAPES = {
|
|
30482
|
+
4: { points: "50,8 93,86 7,86", textY: 62, fontScale: 0.3 },
|
|
30483
|
+
8: { points: "50,4 96,50 50,96 4,50", textY: 51, fontScale: 0.33 },
|
|
30484
|
+
10: { points: "50,4 93,44 50,96 7,44", textY: 47, fontScale: 0.3 },
|
|
30485
|
+
12: { points: "50,4 95,37 78,93 22,93 5,37", textY: 54, fontScale: 0.33 },
|
|
30486
|
+
20: {
|
|
30487
|
+
points: "50,3 91,26 91,74 50,97 9,74 9,26",
|
|
30488
|
+
textY: 51,
|
|
30489
|
+
fontScale: 0.33
|
|
30490
|
+
}
|
|
30491
|
+
};
|
|
30492
|
+
function formatDieValue(v, sides) {
|
|
30493
|
+
return sides > 8 && (v === 6 || v === 9) ? `${v}.` : String(v);
|
|
30494
|
+
}
|
|
30474
30495
|
var MAX_COUNT = 6;
|
|
30475
30496
|
var TUMBLE_MS = 950;
|
|
30476
30497
|
var SETTLE_MS = 1100;
|
|
@@ -30622,6 +30643,7 @@ function Face({ value, pipColor, pipShadow, pipDiameter }) {
|
|
|
30622
30643
|
}
|
|
30623
30644
|
function DiceRoller({
|
|
30624
30645
|
count: count2 = 2,
|
|
30646
|
+
sides = 6,
|
|
30625
30647
|
theme: theme5 = "red",
|
|
30626
30648
|
darkMode,
|
|
30627
30649
|
size = 80,
|
|
@@ -30643,13 +30665,13 @@ function DiceRoller({
|
|
|
30643
30665
|
}) {
|
|
30644
30666
|
const clampedCount = Math.max(1, Math.min(MAX_COUNT, count2));
|
|
30645
30667
|
const dark = useDarkMode(darkMode);
|
|
30646
|
-
const reducedMotion = usePrefersReducedMotion();
|
|
30647
30668
|
const [rolling, setRolling] = useState70(false);
|
|
30648
30669
|
const [results, setResults] = useState70(null);
|
|
30649
30670
|
const [history, setHistory] = useState70([]);
|
|
30650
30671
|
const dieWrapperRefs = useRef39([]);
|
|
30651
30672
|
const cubeRefs = useRef39([]);
|
|
30652
30673
|
const shadowRefs = useRef39([]);
|
|
30674
|
+
const numeralRefs = useRef39([]);
|
|
30653
30675
|
const currentPositionsRef = useRef39(null);
|
|
30654
30676
|
const rollingRef = useRef39(false);
|
|
30655
30677
|
const dieSetters = useRef39([]);
|
|
@@ -30679,6 +30701,15 @@ function DiceRoller({
|
|
|
30679
30701
|
}
|
|
30680
30702
|
return shadowSetters.current[i];
|
|
30681
30703
|
};
|
|
30704
|
+
const numeralSetters = useRef39([]);
|
|
30705
|
+
const getNumeralSetter = (i) => {
|
|
30706
|
+
if (!numeralSetters.current[i]) {
|
|
30707
|
+
numeralSetters.current[i] = (el) => {
|
|
30708
|
+
numeralRefs.current[i] = el;
|
|
30709
|
+
};
|
|
30710
|
+
}
|
|
30711
|
+
return numeralSetters.current[i];
|
|
30712
|
+
};
|
|
30682
30713
|
const timersRef = useRef39([]);
|
|
30683
30714
|
const rafHandleRef = useRef39(null);
|
|
30684
30715
|
const arenaRef = useRef39(null);
|
|
@@ -30731,15 +30762,15 @@ function DiceRoller({
|
|
|
30731
30762
|
const pipDiameter = Math.round(effectiveSize * 0.2);
|
|
30732
30763
|
const faceRadius = Math.round(effectiveSize * 0.07);
|
|
30733
30764
|
const arenaHeight = arenaHeightProp ?? Math.max(effectiveSize * 3, 200);
|
|
30734
|
-
const
|
|
30735
|
-
() =>
|
|
30736
|
-
|
|
30737
|
-
|
|
30738
|
-
|
|
30739
|
-
|
|
30740
|
-
`
|
|
30741
|
-
|
|
30742
|
-
|
|
30765
|
+
const faceTransformFor = useCallback44(
|
|
30766
|
+
(value) => {
|
|
30767
|
+
const p = D6_FACE_PLACEMENTS[value];
|
|
30768
|
+
const parts = [];
|
|
30769
|
+
if (p.x !== 0) parts.push(`rotateX(${p.x}deg)`);
|
|
30770
|
+
if (p.y !== 0) parts.push(`rotateY(${p.y}deg)`);
|
|
30771
|
+
parts.push(`translateZ(${half}px)`);
|
|
30772
|
+
return parts.join(" ");
|
|
30773
|
+
},
|
|
30743
30774
|
[half]
|
|
30744
30775
|
);
|
|
30745
30776
|
const defaultPositions = useCallback44(
|
|
@@ -30761,15 +30792,23 @@ function DiceRoller({
|
|
|
30761
30792
|
[half]
|
|
30762
30793
|
);
|
|
30763
30794
|
const writeCubeRotation = useCallback44(
|
|
30764
|
-
(i, rx, ry, rz, withTransition) => {
|
|
30795
|
+
(i, rx, ry, rz, withTransition, tiltZ = 0) => {
|
|
30765
30796
|
const el = cubeRefs.current[i];
|
|
30766
30797
|
if (!el) return;
|
|
30767
30798
|
el.style.transition = withTransition ? "transform 0.95s cubic-bezier(0.12,0.8,0.22,1)" : "none";
|
|
30768
|
-
el.style.transform = `rotateX(${rx}deg) rotateY(${ry}deg) rotateZ(${rz}deg)`;
|
|
30799
|
+
el.style.transform = `${tiltZ ? `rotateZ(${tiltZ}deg) ` : ""}rotateX(${rx}deg) rotateY(${ry}deg) rotateZ(${rz}deg)`;
|
|
30769
30800
|
},
|
|
30770
30801
|
[]
|
|
30771
30802
|
);
|
|
30772
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
|
+
}
|
|
30773
30812
|
const rawW = arenaRef.current?.offsetWidth ?? 0;
|
|
30774
30813
|
const rawH = arenaRef.current?.offsetHeight ?? 0;
|
|
30775
30814
|
const w = rawW > 20 ? rawW : arena.w > 20 ? arena.w : 400;
|
|
@@ -30795,19 +30834,23 @@ function DiceRoller({
|
|
|
30795
30834
|
setResults(null);
|
|
30796
30835
|
}, [
|
|
30797
30836
|
clampedCount,
|
|
30837
|
+
sides,
|
|
30798
30838
|
effectiveSize,
|
|
30799
30839
|
arena.w,
|
|
30800
30840
|
arenaHeight,
|
|
30801
30841
|
defaultPositions,
|
|
30802
30842
|
half
|
|
30803
30843
|
]);
|
|
30844
|
+
useEffect54(() => {
|
|
30845
|
+
setHistory([]);
|
|
30846
|
+
}, [sides]);
|
|
30804
30847
|
const roll = useCallback44(() => {
|
|
30805
30848
|
if (rollingRef.current) return;
|
|
30806
30849
|
rollingRef.current = true;
|
|
30807
30850
|
setRolling(true);
|
|
30808
30851
|
const vals = Array.from(
|
|
30809
30852
|
{ length: clampedCount },
|
|
30810
|
-
() => Math.floor(Math.random() *
|
|
30853
|
+
() => Math.floor(Math.random() * sides) + 1
|
|
30811
30854
|
);
|
|
30812
30855
|
timersRef.current.forEach((t) => clearTimeout(t));
|
|
30813
30856
|
timersRef.current = [];
|
|
@@ -30822,13 +30865,17 @@ function DiceRoller({
|
|
|
30822
30865
|
const starts = currentPositionsRef.current ?? defaultPositions(w, h, clampedCount);
|
|
30823
30866
|
const frames = simulateDice(w, h, effectiveSize, starts, clampedCount);
|
|
30824
30867
|
for (let i = 0; i < clampedCount; i++) {
|
|
30825
|
-
|
|
30826
|
-
|
|
30827
|
-
|
|
30828
|
-
|
|
30829
|
-
|
|
30830
|
-
|
|
30831
|
-
|
|
30868
|
+
if (sides === 6) {
|
|
30869
|
+
writeCubeRotation(
|
|
30870
|
+
i,
|
|
30871
|
+
(Math.random() - 0.5) * 720,
|
|
30872
|
+
(Math.random() - 0.5) * 720,
|
|
30873
|
+
(Math.random() - 0.5) * 720,
|
|
30874
|
+
false
|
|
30875
|
+
);
|
|
30876
|
+
} else {
|
|
30877
|
+
writeCubeRotation(i, 0, 0, (Math.random() - 0.5) * 720, false);
|
|
30878
|
+
}
|
|
30832
30879
|
const el = dieWrapperRefs.current[i];
|
|
30833
30880
|
if (el) el.style.transition = "none";
|
|
30834
30881
|
const shadow = shadowRefs.current[i];
|
|
@@ -30861,18 +30908,47 @@ function DiceRoller({
|
|
|
30861
30908
|
}, capturedStep * stepMs);
|
|
30862
30909
|
timersRef.current.push(timer);
|
|
30863
30910
|
}
|
|
30911
|
+
if (sides !== 6) {
|
|
30912
|
+
const flickerTicks = 8;
|
|
30913
|
+
const flickerStep = Math.max(60, Math.floor(TUMBLE_MS / flickerTicks));
|
|
30914
|
+
for (let f = 1; f <= flickerTicks; f++) {
|
|
30915
|
+
const t = setTimeout(() => {
|
|
30916
|
+
for (let i = 0; i < clampedCount; i++) {
|
|
30917
|
+
const el = numeralRefs.current[i];
|
|
30918
|
+
if (el)
|
|
30919
|
+
el.textContent = formatDieValue(
|
|
30920
|
+
Math.floor(Math.random() * sides) + 1,
|
|
30921
|
+
sides
|
|
30922
|
+
);
|
|
30923
|
+
}
|
|
30924
|
+
}, f * flickerStep);
|
|
30925
|
+
timersRef.current.push(t);
|
|
30926
|
+
}
|
|
30927
|
+
}
|
|
30864
30928
|
requestAnimationFrame(() => {
|
|
30865
30929
|
requestAnimationFrame(() => {
|
|
30866
30930
|
for (let i = 0; i < clampedCount; i++) {
|
|
30867
|
-
const
|
|
30868
|
-
|
|
30869
|
-
|
|
30870
|
-
|
|
30871
|
-
|
|
30931
|
+
const tilt = (Math.random() - 0.5) * 18;
|
|
30932
|
+
if (sides === 6) {
|
|
30933
|
+
const tgt = d6LandingRotation(vals[i]);
|
|
30934
|
+
const lx = tgt.x + (Math.random() > 0.5 ? 720 : -720);
|
|
30935
|
+
const ly = tgt.y;
|
|
30936
|
+
const lz = (Math.random() > 0.5 ? 1 : -1) * 360;
|
|
30937
|
+
writeCubeRotation(i, lx, ly, lz, true, tilt);
|
|
30938
|
+
} else {
|
|
30939
|
+
const lz = (Math.random() > 0.5 ? 1 : -1) * 720 + tilt;
|
|
30940
|
+
writeCubeRotation(i, 0, 0, lz, true);
|
|
30941
|
+
}
|
|
30872
30942
|
}
|
|
30873
30943
|
});
|
|
30874
30944
|
});
|
|
30875
30945
|
const settleTimer = setTimeout(() => {
|
|
30946
|
+
if (sides !== 6) {
|
|
30947
|
+
for (let i = 0; i < clampedCount; i++) {
|
|
30948
|
+
const el = numeralRefs.current[i];
|
|
30949
|
+
if (el) el.textContent = formatDieValue(vals[i], sides);
|
|
30950
|
+
}
|
|
30951
|
+
}
|
|
30876
30952
|
setResults(vals);
|
|
30877
30953
|
setRolling(false);
|
|
30878
30954
|
rollingRef.current = false;
|
|
@@ -30885,10 +30961,10 @@ function DiceRoller({
|
|
|
30885
30961
|
timersRef.current.push(settleTimer);
|
|
30886
30962
|
}, [
|
|
30887
30963
|
clampedCount,
|
|
30964
|
+
sides,
|
|
30888
30965
|
defaultPositions,
|
|
30889
30966
|
effectiveSize,
|
|
30890
30967
|
half,
|
|
30891
|
-
reducedMotion,
|
|
30892
30968
|
onRoll,
|
|
30893
30969
|
writeCubeRotation,
|
|
30894
30970
|
writeDiePosition,
|
|
@@ -30930,7 +31006,7 @@ function DiceRoller({
|
|
|
30930
31006
|
{
|
|
30931
31007
|
ref: arenaRef,
|
|
30932
31008
|
role: "img",
|
|
30933
|
-
"aria-label": ariaLabel ?? `${clampedCount} dice`,
|
|
31009
|
+
"aria-label": ariaLabel ?? `${clampedCount} ${sides === 6 ? "dice" : `${sides}-sided dice`}`,
|
|
30934
31010
|
"aria-live": "polite",
|
|
30935
31011
|
style: {
|
|
30936
31012
|
position: "relative",
|
|
@@ -30993,7 +31069,7 @@ function DiceRoller({
|
|
|
30993
31069
|
transformStyle: "preserve-3d",
|
|
30994
31070
|
willChange: "transform"
|
|
30995
31071
|
},
|
|
30996
|
-
children: [1, 2, 3, 4, 5, 6].map((val
|
|
31072
|
+
children: sides === 6 ? [1, 2, 3, 4, 5, 6].map((val) => /* @__PURE__ */ jsx114(
|
|
30997
31073
|
"div",
|
|
30998
31074
|
{
|
|
30999
31075
|
style: {
|
|
@@ -31003,7 +31079,7 @@ function DiceRoller({
|
|
|
31003
31079
|
background: colors.face,
|
|
31004
31080
|
border: `1px solid ${colors.faceBorder}`,
|
|
31005
31081
|
borderRadius: faceRadius,
|
|
31006
|
-
transform:
|
|
31082
|
+
transform: faceTransformFor(val),
|
|
31007
31083
|
transition: "background 0.3s, border-color 0.3s"
|
|
31008
31084
|
},
|
|
31009
31085
|
children: /* @__PURE__ */ jsx114(
|
|
@@ -31017,7 +31093,64 @@ function DiceRoller({
|
|
|
31017
31093
|
)
|
|
31018
31094
|
},
|
|
31019
31095
|
val
|
|
31020
|
-
))
|
|
31096
|
+
)) : (() => {
|
|
31097
|
+
const shape = POLY_SHAPES[sides];
|
|
31098
|
+
const shown = results?.[d] ?? sides;
|
|
31099
|
+
return /* @__PURE__ */ jsxs86(
|
|
31100
|
+
"svg",
|
|
31101
|
+
{
|
|
31102
|
+
viewBox: "0 0 100 100",
|
|
31103
|
+
width: "100%",
|
|
31104
|
+
height: "100%",
|
|
31105
|
+
style: { display: "block", overflow: "visible" },
|
|
31106
|
+
"aria-hidden": "true",
|
|
31107
|
+
children: [
|
|
31108
|
+
/* @__PURE__ */ jsx114(
|
|
31109
|
+
"polygon",
|
|
31110
|
+
{
|
|
31111
|
+
points: shape.points,
|
|
31112
|
+
fill: colors.face,
|
|
31113
|
+
stroke: colors.faceBorder,
|
|
31114
|
+
strokeWidth: 2.5,
|
|
31115
|
+
strokeLinejoin: "round",
|
|
31116
|
+
style: { transition: "fill 0.3s, stroke 0.3s" }
|
|
31117
|
+
}
|
|
31118
|
+
),
|
|
31119
|
+
/* @__PURE__ */ jsx114(
|
|
31120
|
+
"polygon",
|
|
31121
|
+
{
|
|
31122
|
+
points: shape.points,
|
|
31123
|
+
fill: "none",
|
|
31124
|
+
stroke: colors.faceBorder,
|
|
31125
|
+
strokeWidth: 1.5,
|
|
31126
|
+
strokeLinejoin: "round",
|
|
31127
|
+
opacity: 0.55,
|
|
31128
|
+
transform: "translate(50 50) scale(0.76) translate(-50 -50)"
|
|
31129
|
+
}
|
|
31130
|
+
),
|
|
31131
|
+
/* @__PURE__ */ jsx114(
|
|
31132
|
+
"text",
|
|
31133
|
+
{
|
|
31134
|
+
ref: getNumeralSetter(d),
|
|
31135
|
+
x: 50,
|
|
31136
|
+
y: shape.textY,
|
|
31137
|
+
textAnchor: "middle",
|
|
31138
|
+
dominantBaseline: "central",
|
|
31139
|
+
fontSize: 100 * shape.fontScale,
|
|
31140
|
+
fontWeight: 800,
|
|
31141
|
+
fill: colors.pip,
|
|
31142
|
+
style: {
|
|
31143
|
+
fontVariantNumeric: "tabular-nums",
|
|
31144
|
+
transition: "fill 0.3s",
|
|
31145
|
+
userSelect: "none"
|
|
31146
|
+
},
|
|
31147
|
+
children: formatDieValue(shown, sides)
|
|
31148
|
+
}
|
|
31149
|
+
)
|
|
31150
|
+
]
|
|
31151
|
+
}
|
|
31152
|
+
);
|
|
31153
|
+
})()
|
|
31021
31154
|
}
|
|
31022
31155
|
)
|
|
31023
31156
|
] }, d);
|
|
@@ -32857,6 +32990,9 @@ function CoinFlipper({
|
|
|
32857
32990
|
}, [flipping, onFlip, historyMax]);
|
|
32858
32991
|
useEffect56(() => {
|
|
32859
32992
|
if (flipRef) flipRef.current = flip;
|
|
32993
|
+
return () => {
|
|
32994
|
+
if (flipRef && flipRef.current === flip) flipRef.current = null;
|
|
32995
|
+
};
|
|
32860
32996
|
}, [flip, flipRef]);
|
|
32861
32997
|
useEffect56(() => {
|
|
32862
32998
|
if (autoFlip) {
|
|
@@ -33754,6 +33890,8 @@ function RandomPlayerPicker({
|
|
|
33754
33890
|
const particleTimer = useRef42(null);
|
|
33755
33891
|
const popTimer = useRef42(null);
|
|
33756
33892
|
const tickInterval = useRef42(null);
|
|
33893
|
+
const pendingPopIdxRef = useRef42(null);
|
|
33894
|
+
const removeWinnersRef = useRef42(removeWinners);
|
|
33757
33895
|
const tickAudioRef = useRef42(null);
|
|
33758
33896
|
const winAudioRef = useRef42(null);
|
|
33759
33897
|
const containerRef = useRef42(null);
|
|
@@ -33792,10 +33930,27 @@ function RandomPlayerPicker({
|
|
|
33792
33930
|
winAudioRef.current = null;
|
|
33793
33931
|
};
|
|
33794
33932
|
}, [winSoundUrl, soundVolume]);
|
|
33933
|
+
useEffect57(() => {
|
|
33934
|
+
removeWinnersRef.current = removeWinners;
|
|
33935
|
+
}, [removeWinners]);
|
|
33795
33936
|
useEffect57(() => {
|
|
33796
33937
|
const ids = participants.map((p) => p.id).join("|");
|
|
33797
33938
|
const structuralChange = ids !== prevIdsRef.current;
|
|
33798
33939
|
if (structuralChange) {
|
|
33940
|
+
if (winnerTimer.current) {
|
|
33941
|
+
clearTimeout(winnerTimer.current);
|
|
33942
|
+
winnerTimer.current = null;
|
|
33943
|
+
}
|
|
33944
|
+
if (tickInterval.current) {
|
|
33945
|
+
clearTimeout(tickInterval.current);
|
|
33946
|
+
tickInterval.current = null;
|
|
33947
|
+
}
|
|
33948
|
+
if (popTimer.current) {
|
|
33949
|
+
clearTimeout(popTimer.current);
|
|
33950
|
+
popTimer.current = null;
|
|
33951
|
+
}
|
|
33952
|
+
pendingPopIdxRef.current = null;
|
|
33953
|
+
setSpinning(false);
|
|
33799
33954
|
setPool(participants);
|
|
33800
33955
|
setHistory([]);
|
|
33801
33956
|
setWinner(null);
|
|
@@ -33852,33 +34007,50 @@ function RandomPlayerPicker({
|
|
|
33852
34007
|
[dark]
|
|
33853
34008
|
);
|
|
33854
34009
|
const spin = useCallback47(() => {
|
|
33855
|
-
if (spinning
|
|
34010
|
+
if (spinning) return;
|
|
34011
|
+
let activePool = pool;
|
|
34012
|
+
if (popTimer.current) {
|
|
34013
|
+
clearTimeout(popTimer.current);
|
|
34014
|
+
popTimer.current = null;
|
|
34015
|
+
const pendingIdx = pendingPopIdxRef.current;
|
|
34016
|
+
pendingPopIdxRef.current = null;
|
|
34017
|
+
if (pendingIdx != null && pendingIdx >= 0 && pendingIdx < pool.length && removeWinnersRef.current) {
|
|
34018
|
+
activePool = pool.filter((_, i) => i !== pendingIdx);
|
|
34019
|
+
setPool(activePool);
|
|
34020
|
+
setWinner(null);
|
|
34021
|
+
}
|
|
34022
|
+
}
|
|
34023
|
+
const activeN = activePool.length;
|
|
34024
|
+
if (activeN < 2) return;
|
|
34025
|
+
const activeAnglePerWedge = 2 * Math.PI / activeN;
|
|
34026
|
+
const activeRotation = rotation;
|
|
34027
|
+
const activeReelY = reelY;
|
|
33856
34028
|
onSpinStart?.();
|
|
33857
|
-
const winnerIdx = weightedIndex(
|
|
34029
|
+
const winnerIdx = weightedIndex(activePool);
|
|
33858
34030
|
const minRev = minRevolutions + intensityCfg.revsBoost;
|
|
33859
34031
|
const maxRev = maxRevolutions + intensityCfg.revsBoost;
|
|
33860
34032
|
const minFullRevs = Math.ceil(minRev);
|
|
33861
34033
|
const maxFullRevs = Math.max(minFullRevs, Math.floor(maxRev));
|
|
33862
34034
|
const revolutions = minFullRevs + Math.floor(Math.random() * (maxFullRevs - minFullRevs + 1));
|
|
33863
|
-
const wedgeMidRad = winnerIdx *
|
|
34035
|
+
const wedgeMidRad = winnerIdx * activeAnglePerWedge + activeAnglePerWedge / 2;
|
|
33864
34036
|
const wedgeMidDeg = wedgeMidRad * 180 / Math.PI;
|
|
33865
|
-
const wheelWobble = (Math.random() - 0.5) * (
|
|
33866
|
-
const currentMod = (
|
|
34037
|
+
const wheelWobble = (Math.random() - 0.5) * (activeAnglePerWedge * 180 / Math.PI) * 0.6;
|
|
34038
|
+
const currentMod = (activeRotation % 360 + 360) % 360;
|
|
33867
34039
|
const targetMod = ((-wedgeMidDeg + wheelWobble) % 360 + 360) % 360;
|
|
33868
34040
|
let deltaMod = targetMod - currentMod;
|
|
33869
34041
|
if (deltaMod < 0) deltaMod += 360;
|
|
33870
|
-
const finalRotation =
|
|
34042
|
+
const finalRotation = activeRotation + deltaMod + revolutions * 360;
|
|
33871
34043
|
const centerOffsetRows = (reelVisibleRows - 1) / 2;
|
|
33872
34044
|
const centerOffsetPx = centerOffsetRows * reelRowHeight;
|
|
33873
34045
|
const currentCenterRow = Math.round(
|
|
33874
|
-
(
|
|
34046
|
+
(activeReelY + centerOffsetPx) / reelRowHeight
|
|
33875
34047
|
);
|
|
33876
|
-
const currentCenterMod = mod(currentCenterRow,
|
|
33877
|
-
const targetRowOffset = (winnerIdx - currentCenterMod +
|
|
33878
|
-
const reelRevRows = Math.round(revolutions *
|
|
33879
|
-
const finalReelY =
|
|
33880
|
-
const settledWinnerIdx = mode === "reel" ? reelIndexAtCenter(finalReelY, centerOffsetPx, reelRowHeight,
|
|
33881
|
-
const finalWinnerIdx = settledWinnerIdx >= 0 && settledWinnerIdx <
|
|
34048
|
+
const currentCenterMod = mod(currentCenterRow, activeN);
|
|
34049
|
+
const targetRowOffset = (winnerIdx - currentCenterMod + activeN) % activeN;
|
|
34050
|
+
const reelRevRows = Math.round(revolutions * activeN);
|
|
34051
|
+
const finalReelY = activeReelY + (reelRevRows + targetRowOffset) * reelRowHeight;
|
|
34052
|
+
const settledWinnerIdx = mode === "reel" ? reelIndexAtCenter(finalReelY, centerOffsetPx, reelRowHeight, activeN) : wheelIndexAtPointer(finalRotation, activeN);
|
|
34053
|
+
const finalWinnerIdx = settledWinnerIdx >= 0 && settledWinnerIdx < activePool.length ? settledWinnerIdx : winnerIdx;
|
|
33882
34054
|
const effectiveDuration = reducedMotion ? 250 : Math.round(spinDuration * intensityCfg.durationMult);
|
|
33883
34055
|
setSpinning(true);
|
|
33884
34056
|
setWinner(null);
|
|
@@ -33891,7 +34063,7 @@ function RandomPlayerPicker({
|
|
|
33891
34063
|
if ((onTick || tickAudioRef.current) && !reducedMotion) {
|
|
33892
34064
|
const wedges = mode === "reel" ? Math.max(1, reelRevRows + targetRowOffset) : (() => {
|
|
33893
34065
|
const totalDelta = deltaMod + revolutions * 360;
|
|
33894
|
-
const wedgeDeg =
|
|
34066
|
+
const wedgeDeg = activeAnglePerWedge * 180 / Math.PI;
|
|
33895
34067
|
return Math.max(
|
|
33896
34068
|
1,
|
|
33897
34069
|
Math.round(totalDelta / Math.max(wedgeDeg, 1))
|
|
@@ -33923,7 +34095,7 @@ function RandomPlayerPicker({
|
|
|
33923
34095
|
schedule();
|
|
33924
34096
|
}
|
|
33925
34097
|
winnerTimer.current = setTimeout(() => {
|
|
33926
|
-
const picked =
|
|
34098
|
+
const picked = activePool[finalWinnerIdx];
|
|
33927
34099
|
if (!picked) {
|
|
33928
34100
|
setSpinning(false);
|
|
33929
34101
|
return;
|
|
@@ -33969,7 +34141,11 @@ function RandomPlayerPicker({
|
|
|
33969
34141
|
particleTimer.current = setTimeout(() => setParticles([]), 2400);
|
|
33970
34142
|
}
|
|
33971
34143
|
if (removeWinners) {
|
|
34144
|
+
pendingPopIdxRef.current = finalWinnerIdx;
|
|
33972
34145
|
popTimer.current = setTimeout(() => {
|
|
34146
|
+
popTimer.current = null;
|
|
34147
|
+
pendingPopIdxRef.current = null;
|
|
34148
|
+
if (!removeWinnersRef.current) return;
|
|
33973
34149
|
setPool((p) => p.filter((_, i) => i !== finalWinnerIdx));
|
|
33974
34150
|
setWinner(null);
|
|
33975
34151
|
setRotation(0);
|
|
@@ -33980,8 +34156,6 @@ function RandomPlayerPicker({
|
|
|
33980
34156
|
}, [
|
|
33981
34157
|
pool,
|
|
33982
34158
|
spinning,
|
|
33983
|
-
n,
|
|
33984
|
-
anglePerWedge,
|
|
33985
34159
|
rotation,
|
|
33986
34160
|
reelY,
|
|
33987
34161
|
mode,
|
|
@@ -34017,6 +34191,7 @@ function RandomPlayerPicker({
|
|
|
34017
34191
|
[]
|
|
34018
34192
|
);
|
|
34019
34193
|
useEffect57(() => {
|
|
34194
|
+
if (trigger === "external") return;
|
|
34020
34195
|
const el = containerRef.current;
|
|
34021
34196
|
if (!el) return;
|
|
34022
34197
|
const h = (e) => {
|
|
@@ -34027,8 +34202,22 @@ function RandomPlayerPicker({
|
|
|
34027
34202
|
};
|
|
34028
34203
|
el.addEventListener("keydown", h);
|
|
34029
34204
|
return () => el.removeEventListener("keydown", h);
|
|
34030
|
-
}, [spin]);
|
|
34205
|
+
}, [spin, trigger]);
|
|
34031
34206
|
const reset = useCallback47(() => {
|
|
34207
|
+
if (winnerTimer.current) {
|
|
34208
|
+
clearTimeout(winnerTimer.current);
|
|
34209
|
+
winnerTimer.current = null;
|
|
34210
|
+
}
|
|
34211
|
+
if (tickInterval.current) {
|
|
34212
|
+
clearTimeout(tickInterval.current);
|
|
34213
|
+
tickInterval.current = null;
|
|
34214
|
+
}
|
|
34215
|
+
if (popTimer.current) {
|
|
34216
|
+
clearTimeout(popTimer.current);
|
|
34217
|
+
popTimer.current = null;
|
|
34218
|
+
}
|
|
34219
|
+
pendingPopIdxRef.current = null;
|
|
34220
|
+
setSpinning(false);
|
|
34032
34221
|
setPool(participants);
|
|
34033
34222
|
setHistory([]);
|
|
34034
34223
|
setWinner(null);
|
|
@@ -38501,27 +38690,28 @@ function GameTimer({
|
|
|
38501
38690
|
const now = performance.now();
|
|
38502
38691
|
const delta = now - lastTickRef.current;
|
|
38503
38692
|
lastTickRef.current = now;
|
|
38504
|
-
|
|
38505
|
-
|
|
38506
|
-
|
|
38507
|
-
|
|
38508
|
-
|
|
38509
|
-
|
|
38510
|
-
|
|
38693
|
+
const next = elapsedRef.current + delta;
|
|
38694
|
+
if (mode === "countdown" && next >= durationMs) {
|
|
38695
|
+
elapsedRef.current = durationMs;
|
|
38696
|
+
setElapsedMs(durationMs);
|
|
38697
|
+
setRunning(false);
|
|
38698
|
+
setCompleted(true);
|
|
38699
|
+
onComplete?.();
|
|
38700
|
+
return;
|
|
38701
|
+
}
|
|
38702
|
+
if (mode === "countdown") {
|
|
38703
|
+
const secLeft = Math.ceil((durationMs - next) / 1e3);
|
|
38704
|
+
if (secLeft <= warningAt && !warningFiredRef.current) {
|
|
38705
|
+
warningFiredRef.current = true;
|
|
38706
|
+
onWarning?.();
|
|
38511
38707
|
}
|
|
38512
|
-
if (
|
|
38513
|
-
|
|
38514
|
-
|
|
38515
|
-
warningFiredRef.current = true;
|
|
38516
|
-
onWarning?.();
|
|
38517
|
-
}
|
|
38518
|
-
if (secLeft <= dangerAt && !dangerFiredRef.current) {
|
|
38519
|
-
dangerFiredRef.current = true;
|
|
38520
|
-
onDanger?.();
|
|
38521
|
-
}
|
|
38708
|
+
if (secLeft <= dangerAt && !dangerFiredRef.current) {
|
|
38709
|
+
dangerFiredRef.current = true;
|
|
38710
|
+
onDanger?.();
|
|
38522
38711
|
}
|
|
38523
|
-
|
|
38524
|
-
|
|
38712
|
+
}
|
|
38713
|
+
elapsedRef.current = next;
|
|
38714
|
+
setElapsedMs(next);
|
|
38525
38715
|
rafRef.current = requestAnimationFrame(tick);
|
|
38526
38716
|
}, [mode, durationMs, warningAt, dangerAt, onComplete, onWarning, onDanger]);
|
|
38527
38717
|
useEffect61(() => {
|
|
@@ -38542,6 +38732,7 @@ function GameTimer({
|
|
|
38542
38732
|
}, []);
|
|
38543
38733
|
const reset = useCallback51(() => {
|
|
38544
38734
|
setRunning(false);
|
|
38735
|
+
elapsedRef.current = 0;
|
|
38545
38736
|
setElapsedMs(0);
|
|
38546
38737
|
setCompleted(false);
|
|
38547
38738
|
setLaps([]);
|
|
@@ -40703,6 +40894,8 @@ function RoundRobinScheduler({
|
|
|
40703
40894
|
theme: theme5 = "slate",
|
|
40704
40895
|
darkMode,
|
|
40705
40896
|
minTeams = 3,
|
|
40897
|
+
maxTeams,
|
|
40898
|
+
nameMaxLength,
|
|
40706
40899
|
defaultTeams = [],
|
|
40707
40900
|
resumeRounds,
|
|
40708
40901
|
onTeamsChange,
|
|
@@ -40725,39 +40918,41 @@ function RoundRobinScheduler({
|
|
|
40725
40918
|
useEffect64(() => {
|
|
40726
40919
|
onTeamsChange?.(teams);
|
|
40727
40920
|
}, [teams, onTeamsChange]);
|
|
40728
|
-
const
|
|
40729
|
-
|
|
40730
|
-
if (names.length === 0) return;
|
|
40731
|
-
setTeams((prev) => {
|
|
40921
|
+
const mergeNames = useCallback54(
|
|
40922
|
+
(prev, incoming) => {
|
|
40732
40923
|
const existing = new Set(prev.map((t) => t.toLowerCase()));
|
|
40733
40924
|
const next = [...prev];
|
|
40734
|
-
for (const
|
|
40735
|
-
if (
|
|
40736
|
-
|
|
40737
|
-
next.push(name);
|
|
40738
|
-
}
|
|
40739
|
-
}
|
|
40740
|
-
return next;
|
|
40741
|
-
});
|
|
40742
|
-
setInput("");
|
|
40743
|
-
setRounds([]);
|
|
40744
|
-
}, []);
|
|
40745
|
-
const importTeams = useCallback54((incoming) => {
|
|
40746
|
-
if (incoming.length === 0) return;
|
|
40747
|
-
setTeams((prev) => {
|
|
40748
|
-
const existing = new Set(prev.map((t) => t.toLowerCase()));
|
|
40749
|
-
const next = [...prev];
|
|
40750
|
-
for (const name of incoming) {
|
|
40925
|
+
for (const rawName of incoming) {
|
|
40926
|
+
if (maxTeams != null && next.length >= maxTeams) break;
|
|
40927
|
+
const name = nameMaxLength != null ? rawName.slice(0, nameMaxLength) : rawName;
|
|
40751
40928
|
const key = name.toLowerCase();
|
|
40752
|
-
if (!existing.has(key)) {
|
|
40929
|
+
if (name.length > 0 && !existing.has(key)) {
|
|
40753
40930
|
existing.add(key);
|
|
40754
40931
|
next.push(name);
|
|
40755
40932
|
}
|
|
40756
40933
|
}
|
|
40757
40934
|
return next;
|
|
40758
|
-
}
|
|
40759
|
-
|
|
40760
|
-
|
|
40935
|
+
},
|
|
40936
|
+
[maxTeams, nameMaxLength]
|
|
40937
|
+
);
|
|
40938
|
+
const addTeams = useCallback54(
|
|
40939
|
+
(raw) => {
|
|
40940
|
+
const names = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
40941
|
+
if (names.length === 0) return;
|
|
40942
|
+
setTeams((prev) => mergeNames(prev, names));
|
|
40943
|
+
setInput("");
|
|
40944
|
+
setRounds([]);
|
|
40945
|
+
},
|
|
40946
|
+
[mergeNames]
|
|
40947
|
+
);
|
|
40948
|
+
const importTeams = useCallback54(
|
|
40949
|
+
(incoming) => {
|
|
40950
|
+
if (incoming.length === 0) return;
|
|
40951
|
+
setTeams((prev) => mergeNames(prev, incoming));
|
|
40952
|
+
setRounds([]);
|
|
40953
|
+
},
|
|
40954
|
+
[mergeNames]
|
|
40955
|
+
);
|
|
40761
40956
|
const handleKeyDown = useCallback54(
|
|
40762
40957
|
(e) => {
|
|
40763
40958
|
if (e.key === "Enter") {
|
|
@@ -41251,6 +41446,8 @@ function Scoreboard({
|
|
|
41251
41446
|
onTeamsChange,
|
|
41252
41447
|
onRoundAdvance,
|
|
41253
41448
|
onNewGame,
|
|
41449
|
+
onReset,
|
|
41450
|
+
nameMaxLength,
|
|
41254
41451
|
labels,
|
|
41255
41452
|
ariaLabel = "Scoreboard",
|
|
41256
41453
|
className
|
|
@@ -41325,7 +41522,8 @@ function Scoreboard({
|
|
|
41325
41522
|
setRound(1);
|
|
41326
41523
|
setHistory([]);
|
|
41327
41524
|
setHistoryOpen(false);
|
|
41328
|
-
|
|
41525
|
+
onReset?.();
|
|
41526
|
+
}, [onReset]);
|
|
41329
41527
|
const newGame = useCallback55(() => {
|
|
41330
41528
|
setTeams(createDefaultTeams(seedCount, L.teamNamePrefix));
|
|
41331
41529
|
setRound(1);
|
|
@@ -41547,6 +41745,7 @@ function Scoreboard({
|
|
|
41547
41745
|
{
|
|
41548
41746
|
type: "text",
|
|
41549
41747
|
value: team.name,
|
|
41748
|
+
maxLength: nameMaxLength,
|
|
41550
41749
|
onChange: (e) => updateName(index, e.target.value),
|
|
41551
41750
|
style: nameInputStyle,
|
|
41552
41751
|
"aria-label": `Team ${index + 1} name`
|
|
@@ -44885,6 +45084,7 @@ export {
|
|
|
44885
45084
|
CommentSystem,
|
|
44886
45085
|
Container,
|
|
44887
45086
|
ConversionKPI,
|
|
45087
|
+
D6_FACE_PLACEMENTS,
|
|
44888
45088
|
DISTANCES,
|
|
44889
45089
|
DURATIONS,
|
|
44890
45090
|
DashboardGrid,
|
|
@@ -45174,6 +45374,7 @@ export {
|
|
|
45174
45374
|
createOptimizedStore,
|
|
45175
45375
|
createSlideVariant,
|
|
45176
45376
|
createTransition,
|
|
45377
|
+
d6LandingRotation,
|
|
45177
45378
|
dailyTrendingConfig,
|
|
45178
45379
|
detectAuthProvider,
|
|
45179
45380
|
detectDelimiter,
|