@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.js
CHANGED
|
@@ -282,6 +282,7 @@ __export(index_exports, {
|
|
|
282
282
|
CommentSystem: () => CommentSystem,
|
|
283
283
|
Container: () => Container,
|
|
284
284
|
ConversionKPI: () => ConversionKPI,
|
|
285
|
+
D6_FACE_PLACEMENTS: () => D6_FACE_PLACEMENTS,
|
|
285
286
|
DISTANCES: () => DISTANCES,
|
|
286
287
|
DURATIONS: () => DURATIONS,
|
|
287
288
|
DashboardGrid: () => DashboardGrid,
|
|
@@ -571,6 +572,7 @@ __export(index_exports, {
|
|
|
571
572
|
createOptimizedStore: () => createOptimizedStore,
|
|
572
573
|
createSlideVariant: () => createSlideVariant,
|
|
573
574
|
createTransition: () => createTransition,
|
|
575
|
+
d6LandingRotation: () => d6LandingRotation,
|
|
574
576
|
dailyTrendingConfig: () => dailyTrendingConfig,
|
|
575
577
|
detectAuthProvider: () => detectAuthProvider,
|
|
576
578
|
detectDelimiter: () => detectDelimiter,
|
|
@@ -30515,14 +30517,35 @@ var PIP_POS = {
|
|
|
30515
30517
|
[75, 75]
|
|
30516
30518
|
]
|
|
30517
30519
|
};
|
|
30518
|
-
var
|
|
30520
|
+
var D6_FACE_PLACEMENTS = {
|
|
30519
30521
|
1: { x: 0, y: 0 },
|
|
30520
|
-
2: { x: 0, y:
|
|
30521
|
-
3: { x:
|
|
30522
|
-
4: { x:
|
|
30523
|
-
5: { x:
|
|
30524
|
-
6: { x:
|
|
30522
|
+
2: { x: 0, y: 90 },
|
|
30523
|
+
3: { x: 90, y: 0 },
|
|
30524
|
+
4: { x: -90, y: 0 },
|
|
30525
|
+
5: { x: 0, y: -90 },
|
|
30526
|
+
6: { x: 0, y: 180 }
|
|
30525
30527
|
};
|
|
30528
|
+
function d6LandingRotation(value) {
|
|
30529
|
+
const p = D6_FACE_PLACEMENTS[value];
|
|
30530
|
+
return {
|
|
30531
|
+
x: Math.abs(p.x) === 180 ? 180 : -p.x || 0,
|
|
30532
|
+
y: Math.abs(p.y) === 180 ? 180 : -p.y || 0
|
|
30533
|
+
};
|
|
30534
|
+
}
|
|
30535
|
+
var POLY_SHAPES = {
|
|
30536
|
+
4: { points: "50,8 93,86 7,86", textY: 62, fontScale: 0.3 },
|
|
30537
|
+
8: { points: "50,4 96,50 50,96 4,50", textY: 51, fontScale: 0.33 },
|
|
30538
|
+
10: { points: "50,4 93,44 50,96 7,44", textY: 47, fontScale: 0.3 },
|
|
30539
|
+
12: { points: "50,4 95,37 78,93 22,93 5,37", textY: 54, fontScale: 0.33 },
|
|
30540
|
+
20: {
|
|
30541
|
+
points: "50,3 91,26 91,74 50,97 9,74 9,26",
|
|
30542
|
+
textY: 51,
|
|
30543
|
+
fontScale: 0.33
|
|
30544
|
+
}
|
|
30545
|
+
};
|
|
30546
|
+
function formatDieValue(v, sides) {
|
|
30547
|
+
return sides > 8 && (v === 6 || v === 9) ? `${v}.` : String(v);
|
|
30548
|
+
}
|
|
30526
30549
|
var MAX_COUNT = 6;
|
|
30527
30550
|
var TUMBLE_MS = 950;
|
|
30528
30551
|
var SETTLE_MS = 1100;
|
|
@@ -30674,6 +30697,7 @@ function Face({ value, pipColor, pipShadow, pipDiameter }) {
|
|
|
30674
30697
|
}
|
|
30675
30698
|
function DiceRoller({
|
|
30676
30699
|
count: count2 = 2,
|
|
30700
|
+
sides = 6,
|
|
30677
30701
|
theme: theme5 = "red",
|
|
30678
30702
|
darkMode,
|
|
30679
30703
|
size = 80,
|
|
@@ -30695,13 +30719,13 @@ function DiceRoller({
|
|
|
30695
30719
|
}) {
|
|
30696
30720
|
const clampedCount = Math.max(1, Math.min(MAX_COUNT, count2));
|
|
30697
30721
|
const dark = useDarkMode(darkMode);
|
|
30698
|
-
const reducedMotion = usePrefersReducedMotion();
|
|
30699
30722
|
const [rolling, setRolling] = (0, import_react68.useState)(false);
|
|
30700
30723
|
const [results, setResults] = (0, import_react68.useState)(null);
|
|
30701
30724
|
const [history, setHistory] = (0, import_react68.useState)([]);
|
|
30702
30725
|
const dieWrapperRefs = (0, import_react68.useRef)([]);
|
|
30703
30726
|
const cubeRefs = (0, import_react68.useRef)([]);
|
|
30704
30727
|
const shadowRefs = (0, import_react68.useRef)([]);
|
|
30728
|
+
const numeralRefs = (0, import_react68.useRef)([]);
|
|
30705
30729
|
const currentPositionsRef = (0, import_react68.useRef)(null);
|
|
30706
30730
|
const rollingRef = (0, import_react68.useRef)(false);
|
|
30707
30731
|
const dieSetters = (0, import_react68.useRef)([]);
|
|
@@ -30731,6 +30755,15 @@ function DiceRoller({
|
|
|
30731
30755
|
}
|
|
30732
30756
|
return shadowSetters.current[i];
|
|
30733
30757
|
};
|
|
30758
|
+
const numeralSetters = (0, import_react68.useRef)([]);
|
|
30759
|
+
const getNumeralSetter = (i) => {
|
|
30760
|
+
if (!numeralSetters.current[i]) {
|
|
30761
|
+
numeralSetters.current[i] = (el) => {
|
|
30762
|
+
numeralRefs.current[i] = el;
|
|
30763
|
+
};
|
|
30764
|
+
}
|
|
30765
|
+
return numeralSetters.current[i];
|
|
30766
|
+
};
|
|
30734
30767
|
const timersRef = (0, import_react68.useRef)([]);
|
|
30735
30768
|
const rafHandleRef = (0, import_react68.useRef)(null);
|
|
30736
30769
|
const arenaRef = (0, import_react68.useRef)(null);
|
|
@@ -30783,15 +30816,15 @@ function DiceRoller({
|
|
|
30783
30816
|
const pipDiameter = Math.round(effectiveSize * 0.2);
|
|
30784
30817
|
const faceRadius = Math.round(effectiveSize * 0.07);
|
|
30785
30818
|
const arenaHeight = arenaHeightProp ?? Math.max(effectiveSize * 3, 200);
|
|
30786
|
-
const
|
|
30787
|
-
() =>
|
|
30788
|
-
|
|
30789
|
-
|
|
30790
|
-
|
|
30791
|
-
|
|
30792
|
-
`
|
|
30793
|
-
|
|
30794
|
-
|
|
30819
|
+
const faceTransformFor = (0, import_react68.useCallback)(
|
|
30820
|
+
(value) => {
|
|
30821
|
+
const p = D6_FACE_PLACEMENTS[value];
|
|
30822
|
+
const parts = [];
|
|
30823
|
+
if (p.x !== 0) parts.push(`rotateX(${p.x}deg)`);
|
|
30824
|
+
if (p.y !== 0) parts.push(`rotateY(${p.y}deg)`);
|
|
30825
|
+
parts.push(`translateZ(${half}px)`);
|
|
30826
|
+
return parts.join(" ");
|
|
30827
|
+
},
|
|
30795
30828
|
[half]
|
|
30796
30829
|
);
|
|
30797
30830
|
const defaultPositions = (0, import_react68.useCallback)(
|
|
@@ -30813,15 +30846,23 @@ function DiceRoller({
|
|
|
30813
30846
|
[half]
|
|
30814
30847
|
);
|
|
30815
30848
|
const writeCubeRotation = (0, import_react68.useCallback)(
|
|
30816
|
-
(i, rx, ry, rz, withTransition) => {
|
|
30849
|
+
(i, rx, ry, rz, withTransition, tiltZ = 0) => {
|
|
30817
30850
|
const el = cubeRefs.current[i];
|
|
30818
30851
|
if (!el) return;
|
|
30819
30852
|
el.style.transition = withTransition ? "transform 0.95s cubic-bezier(0.12,0.8,0.22,1)" : "none";
|
|
30820
|
-
el.style.transform = `rotateX(${rx}deg) rotateY(${ry}deg) rotateZ(${rz}deg)`;
|
|
30853
|
+
el.style.transform = `${tiltZ ? `rotateZ(${tiltZ}deg) ` : ""}rotateX(${rx}deg) rotateY(${ry}deg) rotateZ(${rz}deg)`;
|
|
30821
30854
|
},
|
|
30822
30855
|
[]
|
|
30823
30856
|
);
|
|
30824
30857
|
(0, import_react68.useEffect)(() => {
|
|
30858
|
+
if (timersRef.current.length > 0) {
|
|
30859
|
+
timersRef.current.forEach((t) => clearTimeout(t));
|
|
30860
|
+
timersRef.current = [];
|
|
30861
|
+
}
|
|
30862
|
+
if (rollingRef.current) {
|
|
30863
|
+
rollingRef.current = false;
|
|
30864
|
+
setRolling(false);
|
|
30865
|
+
}
|
|
30825
30866
|
const rawW = arenaRef.current?.offsetWidth ?? 0;
|
|
30826
30867
|
const rawH = arenaRef.current?.offsetHeight ?? 0;
|
|
30827
30868
|
const w = rawW > 20 ? rawW : arena.w > 20 ? arena.w : 400;
|
|
@@ -30847,19 +30888,23 @@ function DiceRoller({
|
|
|
30847
30888
|
setResults(null);
|
|
30848
30889
|
}, [
|
|
30849
30890
|
clampedCount,
|
|
30891
|
+
sides,
|
|
30850
30892
|
effectiveSize,
|
|
30851
30893
|
arena.w,
|
|
30852
30894
|
arenaHeight,
|
|
30853
30895
|
defaultPositions,
|
|
30854
30896
|
half
|
|
30855
30897
|
]);
|
|
30898
|
+
(0, import_react68.useEffect)(() => {
|
|
30899
|
+
setHistory([]);
|
|
30900
|
+
}, [sides]);
|
|
30856
30901
|
const roll = (0, import_react68.useCallback)(() => {
|
|
30857
30902
|
if (rollingRef.current) return;
|
|
30858
30903
|
rollingRef.current = true;
|
|
30859
30904
|
setRolling(true);
|
|
30860
30905
|
const vals = Array.from(
|
|
30861
30906
|
{ length: clampedCount },
|
|
30862
|
-
() => Math.floor(Math.random() *
|
|
30907
|
+
() => Math.floor(Math.random() * sides) + 1
|
|
30863
30908
|
);
|
|
30864
30909
|
timersRef.current.forEach((t) => clearTimeout(t));
|
|
30865
30910
|
timersRef.current = [];
|
|
@@ -30874,13 +30919,17 @@ function DiceRoller({
|
|
|
30874
30919
|
const starts = currentPositionsRef.current ?? defaultPositions(w, h, clampedCount);
|
|
30875
30920
|
const frames = simulateDice(w, h, effectiveSize, starts, clampedCount);
|
|
30876
30921
|
for (let i = 0; i < clampedCount; i++) {
|
|
30877
|
-
|
|
30878
|
-
|
|
30879
|
-
|
|
30880
|
-
|
|
30881
|
-
|
|
30882
|
-
|
|
30883
|
-
|
|
30922
|
+
if (sides === 6) {
|
|
30923
|
+
writeCubeRotation(
|
|
30924
|
+
i,
|
|
30925
|
+
(Math.random() - 0.5) * 720,
|
|
30926
|
+
(Math.random() - 0.5) * 720,
|
|
30927
|
+
(Math.random() - 0.5) * 720,
|
|
30928
|
+
false
|
|
30929
|
+
);
|
|
30930
|
+
} else {
|
|
30931
|
+
writeCubeRotation(i, 0, 0, (Math.random() - 0.5) * 720, false);
|
|
30932
|
+
}
|
|
30884
30933
|
const el = dieWrapperRefs.current[i];
|
|
30885
30934
|
if (el) el.style.transition = "none";
|
|
30886
30935
|
const shadow = shadowRefs.current[i];
|
|
@@ -30913,18 +30962,47 @@ function DiceRoller({
|
|
|
30913
30962
|
}, capturedStep * stepMs);
|
|
30914
30963
|
timersRef.current.push(timer);
|
|
30915
30964
|
}
|
|
30965
|
+
if (sides !== 6) {
|
|
30966
|
+
const flickerTicks = 8;
|
|
30967
|
+
const flickerStep = Math.max(60, Math.floor(TUMBLE_MS / flickerTicks));
|
|
30968
|
+
for (let f = 1; f <= flickerTicks; f++) {
|
|
30969
|
+
const t = setTimeout(() => {
|
|
30970
|
+
for (let i = 0; i < clampedCount; i++) {
|
|
30971
|
+
const el = numeralRefs.current[i];
|
|
30972
|
+
if (el)
|
|
30973
|
+
el.textContent = formatDieValue(
|
|
30974
|
+
Math.floor(Math.random() * sides) + 1,
|
|
30975
|
+
sides
|
|
30976
|
+
);
|
|
30977
|
+
}
|
|
30978
|
+
}, f * flickerStep);
|
|
30979
|
+
timersRef.current.push(t);
|
|
30980
|
+
}
|
|
30981
|
+
}
|
|
30916
30982
|
requestAnimationFrame(() => {
|
|
30917
30983
|
requestAnimationFrame(() => {
|
|
30918
30984
|
for (let i = 0; i < clampedCount; i++) {
|
|
30919
|
-
const
|
|
30920
|
-
|
|
30921
|
-
|
|
30922
|
-
|
|
30923
|
-
|
|
30985
|
+
const tilt = (Math.random() - 0.5) * 18;
|
|
30986
|
+
if (sides === 6) {
|
|
30987
|
+
const tgt = d6LandingRotation(vals[i]);
|
|
30988
|
+
const lx = tgt.x + (Math.random() > 0.5 ? 720 : -720);
|
|
30989
|
+
const ly = tgt.y;
|
|
30990
|
+
const lz = (Math.random() > 0.5 ? 1 : -1) * 360;
|
|
30991
|
+
writeCubeRotation(i, lx, ly, lz, true, tilt);
|
|
30992
|
+
} else {
|
|
30993
|
+
const lz = (Math.random() > 0.5 ? 1 : -1) * 720 + tilt;
|
|
30994
|
+
writeCubeRotation(i, 0, 0, lz, true);
|
|
30995
|
+
}
|
|
30924
30996
|
}
|
|
30925
30997
|
});
|
|
30926
30998
|
});
|
|
30927
30999
|
const settleTimer = setTimeout(() => {
|
|
31000
|
+
if (sides !== 6) {
|
|
31001
|
+
for (let i = 0; i < clampedCount; i++) {
|
|
31002
|
+
const el = numeralRefs.current[i];
|
|
31003
|
+
if (el) el.textContent = formatDieValue(vals[i], sides);
|
|
31004
|
+
}
|
|
31005
|
+
}
|
|
30928
31006
|
setResults(vals);
|
|
30929
31007
|
setRolling(false);
|
|
30930
31008
|
rollingRef.current = false;
|
|
@@ -30937,10 +31015,10 @@ function DiceRoller({
|
|
|
30937
31015
|
timersRef.current.push(settleTimer);
|
|
30938
31016
|
}, [
|
|
30939
31017
|
clampedCount,
|
|
31018
|
+
sides,
|
|
30940
31019
|
defaultPositions,
|
|
30941
31020
|
effectiveSize,
|
|
30942
31021
|
half,
|
|
30943
|
-
reducedMotion,
|
|
30944
31022
|
onRoll,
|
|
30945
31023
|
writeCubeRotation,
|
|
30946
31024
|
writeDiePosition,
|
|
@@ -30982,7 +31060,7 @@ function DiceRoller({
|
|
|
30982
31060
|
{
|
|
30983
31061
|
ref: arenaRef,
|
|
30984
31062
|
role: "img",
|
|
30985
|
-
"aria-label": ariaLabel ?? `${clampedCount} dice`,
|
|
31063
|
+
"aria-label": ariaLabel ?? `${clampedCount} ${sides === 6 ? "dice" : `${sides}-sided dice`}`,
|
|
30986
31064
|
"aria-live": "polite",
|
|
30987
31065
|
style: {
|
|
30988
31066
|
position: "relative",
|
|
@@ -31045,7 +31123,7 @@ function DiceRoller({
|
|
|
31045
31123
|
transformStyle: "preserve-3d",
|
|
31046
31124
|
willChange: "transform"
|
|
31047
31125
|
},
|
|
31048
|
-
children: [1, 2, 3, 4, 5, 6].map((val
|
|
31126
|
+
children: sides === 6 ? [1, 2, 3, 4, 5, 6].map((val) => /* @__PURE__ */ (0, import_jsx_runtime114.jsx)(
|
|
31049
31127
|
"div",
|
|
31050
31128
|
{
|
|
31051
31129
|
style: {
|
|
@@ -31055,7 +31133,7 @@ function DiceRoller({
|
|
|
31055
31133
|
background: colors.face,
|
|
31056
31134
|
border: `1px solid ${colors.faceBorder}`,
|
|
31057
31135
|
borderRadius: faceRadius,
|
|
31058
|
-
transform:
|
|
31136
|
+
transform: faceTransformFor(val),
|
|
31059
31137
|
transition: "background 0.3s, border-color 0.3s"
|
|
31060
31138
|
},
|
|
31061
31139
|
children: /* @__PURE__ */ (0, import_jsx_runtime114.jsx)(
|
|
@@ -31069,7 +31147,64 @@ function DiceRoller({
|
|
|
31069
31147
|
)
|
|
31070
31148
|
},
|
|
31071
31149
|
val
|
|
31072
|
-
))
|
|
31150
|
+
)) : (() => {
|
|
31151
|
+
const shape = POLY_SHAPES[sides];
|
|
31152
|
+
const shown = results?.[d] ?? sides;
|
|
31153
|
+
return /* @__PURE__ */ (0, import_jsx_runtime114.jsxs)(
|
|
31154
|
+
"svg",
|
|
31155
|
+
{
|
|
31156
|
+
viewBox: "0 0 100 100",
|
|
31157
|
+
width: "100%",
|
|
31158
|
+
height: "100%",
|
|
31159
|
+
style: { display: "block", overflow: "visible" },
|
|
31160
|
+
"aria-hidden": "true",
|
|
31161
|
+
children: [
|
|
31162
|
+
/* @__PURE__ */ (0, import_jsx_runtime114.jsx)(
|
|
31163
|
+
"polygon",
|
|
31164
|
+
{
|
|
31165
|
+
points: shape.points,
|
|
31166
|
+
fill: colors.face,
|
|
31167
|
+
stroke: colors.faceBorder,
|
|
31168
|
+
strokeWidth: 2.5,
|
|
31169
|
+
strokeLinejoin: "round",
|
|
31170
|
+
style: { transition: "fill 0.3s, stroke 0.3s" }
|
|
31171
|
+
}
|
|
31172
|
+
),
|
|
31173
|
+
/* @__PURE__ */ (0, import_jsx_runtime114.jsx)(
|
|
31174
|
+
"polygon",
|
|
31175
|
+
{
|
|
31176
|
+
points: shape.points,
|
|
31177
|
+
fill: "none",
|
|
31178
|
+
stroke: colors.faceBorder,
|
|
31179
|
+
strokeWidth: 1.5,
|
|
31180
|
+
strokeLinejoin: "round",
|
|
31181
|
+
opacity: 0.55,
|
|
31182
|
+
transform: "translate(50 50) scale(0.76) translate(-50 -50)"
|
|
31183
|
+
}
|
|
31184
|
+
),
|
|
31185
|
+
/* @__PURE__ */ (0, import_jsx_runtime114.jsx)(
|
|
31186
|
+
"text",
|
|
31187
|
+
{
|
|
31188
|
+
ref: getNumeralSetter(d),
|
|
31189
|
+
x: 50,
|
|
31190
|
+
y: shape.textY,
|
|
31191
|
+
textAnchor: "middle",
|
|
31192
|
+
dominantBaseline: "central",
|
|
31193
|
+
fontSize: 100 * shape.fontScale,
|
|
31194
|
+
fontWeight: 800,
|
|
31195
|
+
fill: colors.pip,
|
|
31196
|
+
style: {
|
|
31197
|
+
fontVariantNumeric: "tabular-nums",
|
|
31198
|
+
transition: "fill 0.3s",
|
|
31199
|
+
userSelect: "none"
|
|
31200
|
+
},
|
|
31201
|
+
children: formatDieValue(shown, sides)
|
|
31202
|
+
}
|
|
31203
|
+
)
|
|
31204
|
+
]
|
|
31205
|
+
}
|
|
31206
|
+
);
|
|
31207
|
+
})()
|
|
31073
31208
|
}
|
|
31074
31209
|
)
|
|
31075
31210
|
] }, d);
|
|
@@ -32896,6 +33031,9 @@ function CoinFlipper({
|
|
|
32896
33031
|
}, [flipping, onFlip, historyMax]);
|
|
32897
33032
|
(0, import_react70.useEffect)(() => {
|
|
32898
33033
|
if (flipRef) flipRef.current = flip;
|
|
33034
|
+
return () => {
|
|
33035
|
+
if (flipRef && flipRef.current === flip) flipRef.current = null;
|
|
33036
|
+
};
|
|
32899
33037
|
}, [flip, flipRef]);
|
|
32900
33038
|
(0, import_react70.useEffect)(() => {
|
|
32901
33039
|
if (autoFlip) {
|
|
@@ -33787,6 +33925,8 @@ function RandomPlayerPicker({
|
|
|
33787
33925
|
const particleTimer = (0, import_react71.useRef)(null);
|
|
33788
33926
|
const popTimer = (0, import_react71.useRef)(null);
|
|
33789
33927
|
const tickInterval = (0, import_react71.useRef)(null);
|
|
33928
|
+
const pendingPopIdxRef = (0, import_react71.useRef)(null);
|
|
33929
|
+
const removeWinnersRef = (0, import_react71.useRef)(removeWinners);
|
|
33790
33930
|
const tickAudioRef = (0, import_react71.useRef)(null);
|
|
33791
33931
|
const winAudioRef = (0, import_react71.useRef)(null);
|
|
33792
33932
|
const containerRef = (0, import_react71.useRef)(null);
|
|
@@ -33825,10 +33965,27 @@ function RandomPlayerPicker({
|
|
|
33825
33965
|
winAudioRef.current = null;
|
|
33826
33966
|
};
|
|
33827
33967
|
}, [winSoundUrl, soundVolume]);
|
|
33968
|
+
(0, import_react71.useEffect)(() => {
|
|
33969
|
+
removeWinnersRef.current = removeWinners;
|
|
33970
|
+
}, [removeWinners]);
|
|
33828
33971
|
(0, import_react71.useEffect)(() => {
|
|
33829
33972
|
const ids = participants.map((p) => p.id).join("|");
|
|
33830
33973
|
const structuralChange = ids !== prevIdsRef.current;
|
|
33831
33974
|
if (structuralChange) {
|
|
33975
|
+
if (winnerTimer.current) {
|
|
33976
|
+
clearTimeout(winnerTimer.current);
|
|
33977
|
+
winnerTimer.current = null;
|
|
33978
|
+
}
|
|
33979
|
+
if (tickInterval.current) {
|
|
33980
|
+
clearTimeout(tickInterval.current);
|
|
33981
|
+
tickInterval.current = null;
|
|
33982
|
+
}
|
|
33983
|
+
if (popTimer.current) {
|
|
33984
|
+
clearTimeout(popTimer.current);
|
|
33985
|
+
popTimer.current = null;
|
|
33986
|
+
}
|
|
33987
|
+
pendingPopIdxRef.current = null;
|
|
33988
|
+
setSpinning(false);
|
|
33832
33989
|
setPool(participants);
|
|
33833
33990
|
setHistory([]);
|
|
33834
33991
|
setWinner(null);
|
|
@@ -33885,33 +34042,50 @@ function RandomPlayerPicker({
|
|
|
33885
34042
|
[dark]
|
|
33886
34043
|
);
|
|
33887
34044
|
const spin = (0, import_react71.useCallback)(() => {
|
|
33888
|
-
if (spinning
|
|
34045
|
+
if (spinning) return;
|
|
34046
|
+
let activePool = pool;
|
|
34047
|
+
if (popTimer.current) {
|
|
34048
|
+
clearTimeout(popTimer.current);
|
|
34049
|
+
popTimer.current = null;
|
|
34050
|
+
const pendingIdx = pendingPopIdxRef.current;
|
|
34051
|
+
pendingPopIdxRef.current = null;
|
|
34052
|
+
if (pendingIdx != null && pendingIdx >= 0 && pendingIdx < pool.length && removeWinnersRef.current) {
|
|
34053
|
+
activePool = pool.filter((_, i) => i !== pendingIdx);
|
|
34054
|
+
setPool(activePool);
|
|
34055
|
+
setWinner(null);
|
|
34056
|
+
}
|
|
34057
|
+
}
|
|
34058
|
+
const activeN = activePool.length;
|
|
34059
|
+
if (activeN < 2) return;
|
|
34060
|
+
const activeAnglePerWedge = 2 * Math.PI / activeN;
|
|
34061
|
+
const activeRotation = rotation;
|
|
34062
|
+
const activeReelY = reelY;
|
|
33889
34063
|
onSpinStart?.();
|
|
33890
|
-
const winnerIdx = weightedIndex(
|
|
34064
|
+
const winnerIdx = weightedIndex(activePool);
|
|
33891
34065
|
const minRev = minRevolutions + intensityCfg.revsBoost;
|
|
33892
34066
|
const maxRev = maxRevolutions + intensityCfg.revsBoost;
|
|
33893
34067
|
const minFullRevs = Math.ceil(minRev);
|
|
33894
34068
|
const maxFullRevs = Math.max(minFullRevs, Math.floor(maxRev));
|
|
33895
34069
|
const revolutions = minFullRevs + Math.floor(Math.random() * (maxFullRevs - minFullRevs + 1));
|
|
33896
|
-
const wedgeMidRad = winnerIdx *
|
|
34070
|
+
const wedgeMidRad = winnerIdx * activeAnglePerWedge + activeAnglePerWedge / 2;
|
|
33897
34071
|
const wedgeMidDeg = wedgeMidRad * 180 / Math.PI;
|
|
33898
|
-
const wheelWobble = (Math.random() - 0.5) * (
|
|
33899
|
-
const currentMod = (
|
|
34072
|
+
const wheelWobble = (Math.random() - 0.5) * (activeAnglePerWedge * 180 / Math.PI) * 0.6;
|
|
34073
|
+
const currentMod = (activeRotation % 360 + 360) % 360;
|
|
33900
34074
|
const targetMod = ((-wedgeMidDeg + wheelWobble) % 360 + 360) % 360;
|
|
33901
34075
|
let deltaMod = targetMod - currentMod;
|
|
33902
34076
|
if (deltaMod < 0) deltaMod += 360;
|
|
33903
|
-
const finalRotation =
|
|
34077
|
+
const finalRotation = activeRotation + deltaMod + revolutions * 360;
|
|
33904
34078
|
const centerOffsetRows = (reelVisibleRows - 1) / 2;
|
|
33905
34079
|
const centerOffsetPx = centerOffsetRows * reelRowHeight;
|
|
33906
34080
|
const currentCenterRow = Math.round(
|
|
33907
|
-
(
|
|
34081
|
+
(activeReelY + centerOffsetPx) / reelRowHeight
|
|
33908
34082
|
);
|
|
33909
|
-
const currentCenterMod = mod(currentCenterRow,
|
|
33910
|
-
const targetRowOffset = (winnerIdx - currentCenterMod +
|
|
33911
|
-
const reelRevRows = Math.round(revolutions *
|
|
33912
|
-
const finalReelY =
|
|
33913
|
-
const settledWinnerIdx = mode === "reel" ? reelIndexAtCenter(finalReelY, centerOffsetPx, reelRowHeight,
|
|
33914
|
-
const finalWinnerIdx = settledWinnerIdx >= 0 && settledWinnerIdx <
|
|
34083
|
+
const currentCenterMod = mod(currentCenterRow, activeN);
|
|
34084
|
+
const targetRowOffset = (winnerIdx - currentCenterMod + activeN) % activeN;
|
|
34085
|
+
const reelRevRows = Math.round(revolutions * activeN);
|
|
34086
|
+
const finalReelY = activeReelY + (reelRevRows + targetRowOffset) * reelRowHeight;
|
|
34087
|
+
const settledWinnerIdx = mode === "reel" ? reelIndexAtCenter(finalReelY, centerOffsetPx, reelRowHeight, activeN) : wheelIndexAtPointer(finalRotation, activeN);
|
|
34088
|
+
const finalWinnerIdx = settledWinnerIdx >= 0 && settledWinnerIdx < activePool.length ? settledWinnerIdx : winnerIdx;
|
|
33915
34089
|
const effectiveDuration = reducedMotion ? 250 : Math.round(spinDuration * intensityCfg.durationMult);
|
|
33916
34090
|
setSpinning(true);
|
|
33917
34091
|
setWinner(null);
|
|
@@ -33924,7 +34098,7 @@ function RandomPlayerPicker({
|
|
|
33924
34098
|
if ((onTick || tickAudioRef.current) && !reducedMotion) {
|
|
33925
34099
|
const wedges = mode === "reel" ? Math.max(1, reelRevRows + targetRowOffset) : (() => {
|
|
33926
34100
|
const totalDelta = deltaMod + revolutions * 360;
|
|
33927
|
-
const wedgeDeg =
|
|
34101
|
+
const wedgeDeg = activeAnglePerWedge * 180 / Math.PI;
|
|
33928
34102
|
return Math.max(
|
|
33929
34103
|
1,
|
|
33930
34104
|
Math.round(totalDelta / Math.max(wedgeDeg, 1))
|
|
@@ -33956,7 +34130,7 @@ function RandomPlayerPicker({
|
|
|
33956
34130
|
schedule();
|
|
33957
34131
|
}
|
|
33958
34132
|
winnerTimer.current = setTimeout(() => {
|
|
33959
|
-
const picked =
|
|
34133
|
+
const picked = activePool[finalWinnerIdx];
|
|
33960
34134
|
if (!picked) {
|
|
33961
34135
|
setSpinning(false);
|
|
33962
34136
|
return;
|
|
@@ -34002,7 +34176,11 @@ function RandomPlayerPicker({
|
|
|
34002
34176
|
particleTimer.current = setTimeout(() => setParticles([]), 2400);
|
|
34003
34177
|
}
|
|
34004
34178
|
if (removeWinners) {
|
|
34179
|
+
pendingPopIdxRef.current = finalWinnerIdx;
|
|
34005
34180
|
popTimer.current = setTimeout(() => {
|
|
34181
|
+
popTimer.current = null;
|
|
34182
|
+
pendingPopIdxRef.current = null;
|
|
34183
|
+
if (!removeWinnersRef.current) return;
|
|
34006
34184
|
setPool((p) => p.filter((_, i) => i !== finalWinnerIdx));
|
|
34007
34185
|
setWinner(null);
|
|
34008
34186
|
setRotation(0);
|
|
@@ -34013,8 +34191,6 @@ function RandomPlayerPicker({
|
|
|
34013
34191
|
}, [
|
|
34014
34192
|
pool,
|
|
34015
34193
|
spinning,
|
|
34016
|
-
n,
|
|
34017
|
-
anglePerWedge,
|
|
34018
34194
|
rotation,
|
|
34019
34195
|
reelY,
|
|
34020
34196
|
mode,
|
|
@@ -34050,6 +34226,7 @@ function RandomPlayerPicker({
|
|
|
34050
34226
|
[]
|
|
34051
34227
|
);
|
|
34052
34228
|
(0, import_react71.useEffect)(() => {
|
|
34229
|
+
if (trigger === "external") return;
|
|
34053
34230
|
const el = containerRef.current;
|
|
34054
34231
|
if (!el) return;
|
|
34055
34232
|
const h = (e) => {
|
|
@@ -34060,8 +34237,22 @@ function RandomPlayerPicker({
|
|
|
34060
34237
|
};
|
|
34061
34238
|
el.addEventListener("keydown", h);
|
|
34062
34239
|
return () => el.removeEventListener("keydown", h);
|
|
34063
|
-
}, [spin]);
|
|
34240
|
+
}, [spin, trigger]);
|
|
34064
34241
|
const reset = (0, import_react71.useCallback)(() => {
|
|
34242
|
+
if (winnerTimer.current) {
|
|
34243
|
+
clearTimeout(winnerTimer.current);
|
|
34244
|
+
winnerTimer.current = null;
|
|
34245
|
+
}
|
|
34246
|
+
if (tickInterval.current) {
|
|
34247
|
+
clearTimeout(tickInterval.current);
|
|
34248
|
+
tickInterval.current = null;
|
|
34249
|
+
}
|
|
34250
|
+
if (popTimer.current) {
|
|
34251
|
+
clearTimeout(popTimer.current);
|
|
34252
|
+
popTimer.current = null;
|
|
34253
|
+
}
|
|
34254
|
+
pendingPopIdxRef.current = null;
|
|
34255
|
+
setSpinning(false);
|
|
34065
34256
|
setPool(participants);
|
|
34066
34257
|
setHistory([]);
|
|
34067
34258
|
setWinner(null);
|
|
@@ -38514,27 +38705,28 @@ function GameTimer({
|
|
|
38514
38705
|
const now = performance.now();
|
|
38515
38706
|
const delta = now - lastTickRef.current;
|
|
38516
38707
|
lastTickRef.current = now;
|
|
38517
|
-
|
|
38518
|
-
|
|
38519
|
-
|
|
38520
|
-
|
|
38521
|
-
|
|
38522
|
-
|
|
38523
|
-
|
|
38708
|
+
const next = elapsedRef.current + delta;
|
|
38709
|
+
if (mode === "countdown" && next >= durationMs) {
|
|
38710
|
+
elapsedRef.current = durationMs;
|
|
38711
|
+
setElapsedMs(durationMs);
|
|
38712
|
+
setRunning(false);
|
|
38713
|
+
setCompleted(true);
|
|
38714
|
+
onComplete?.();
|
|
38715
|
+
return;
|
|
38716
|
+
}
|
|
38717
|
+
if (mode === "countdown") {
|
|
38718
|
+
const secLeft = Math.ceil((durationMs - next) / 1e3);
|
|
38719
|
+
if (secLeft <= warningAt && !warningFiredRef.current) {
|
|
38720
|
+
warningFiredRef.current = true;
|
|
38721
|
+
onWarning?.();
|
|
38524
38722
|
}
|
|
38525
|
-
if (
|
|
38526
|
-
|
|
38527
|
-
|
|
38528
|
-
warningFiredRef.current = true;
|
|
38529
|
-
onWarning?.();
|
|
38530
|
-
}
|
|
38531
|
-
if (secLeft <= dangerAt && !dangerFiredRef.current) {
|
|
38532
|
-
dangerFiredRef.current = true;
|
|
38533
|
-
onDanger?.();
|
|
38534
|
-
}
|
|
38723
|
+
if (secLeft <= dangerAt && !dangerFiredRef.current) {
|
|
38724
|
+
dangerFiredRef.current = true;
|
|
38725
|
+
onDanger?.();
|
|
38535
38726
|
}
|
|
38536
|
-
|
|
38537
|
-
|
|
38727
|
+
}
|
|
38728
|
+
elapsedRef.current = next;
|
|
38729
|
+
setElapsedMs(next);
|
|
38538
38730
|
rafRef.current = requestAnimationFrame(tick);
|
|
38539
38731
|
}, [mode, durationMs, warningAt, dangerAt, onComplete, onWarning, onDanger]);
|
|
38540
38732
|
(0, import_react74.useEffect)(() => {
|
|
@@ -38555,6 +38747,7 @@ function GameTimer({
|
|
|
38555
38747
|
}, []);
|
|
38556
38748
|
const reset = (0, import_react74.useCallback)(() => {
|
|
38557
38749
|
setRunning(false);
|
|
38750
|
+
elapsedRef.current = 0;
|
|
38558
38751
|
setElapsedMs(0);
|
|
38559
38752
|
setCompleted(false);
|
|
38560
38753
|
setLaps([]);
|
|
@@ -40699,6 +40892,8 @@ function RoundRobinScheduler({
|
|
|
40699
40892
|
theme: theme5 = "slate",
|
|
40700
40893
|
darkMode,
|
|
40701
40894
|
minTeams = 3,
|
|
40895
|
+
maxTeams,
|
|
40896
|
+
nameMaxLength,
|
|
40702
40897
|
defaultTeams = [],
|
|
40703
40898
|
resumeRounds,
|
|
40704
40899
|
onTeamsChange,
|
|
@@ -40721,39 +40916,41 @@ function RoundRobinScheduler({
|
|
|
40721
40916
|
(0, import_react77.useEffect)(() => {
|
|
40722
40917
|
onTeamsChange?.(teams);
|
|
40723
40918
|
}, [teams, onTeamsChange]);
|
|
40724
|
-
const
|
|
40725
|
-
|
|
40726
|
-
if (names.length === 0) return;
|
|
40727
|
-
setTeams((prev) => {
|
|
40919
|
+
const mergeNames = (0, import_react77.useCallback)(
|
|
40920
|
+
(prev, incoming) => {
|
|
40728
40921
|
const existing = new Set(prev.map((t) => t.toLowerCase()));
|
|
40729
40922
|
const next = [...prev];
|
|
40730
|
-
for (const
|
|
40731
|
-
if (
|
|
40732
|
-
|
|
40733
|
-
next.push(name);
|
|
40734
|
-
}
|
|
40735
|
-
}
|
|
40736
|
-
return next;
|
|
40737
|
-
});
|
|
40738
|
-
setInput("");
|
|
40739
|
-
setRounds([]);
|
|
40740
|
-
}, []);
|
|
40741
|
-
const importTeams = (0, import_react77.useCallback)((incoming) => {
|
|
40742
|
-
if (incoming.length === 0) return;
|
|
40743
|
-
setTeams((prev) => {
|
|
40744
|
-
const existing = new Set(prev.map((t) => t.toLowerCase()));
|
|
40745
|
-
const next = [...prev];
|
|
40746
|
-
for (const name of incoming) {
|
|
40923
|
+
for (const rawName of incoming) {
|
|
40924
|
+
if (maxTeams != null && next.length >= maxTeams) break;
|
|
40925
|
+
const name = nameMaxLength != null ? rawName.slice(0, nameMaxLength) : rawName;
|
|
40747
40926
|
const key = name.toLowerCase();
|
|
40748
|
-
if (!existing.has(key)) {
|
|
40927
|
+
if (name.length > 0 && !existing.has(key)) {
|
|
40749
40928
|
existing.add(key);
|
|
40750
40929
|
next.push(name);
|
|
40751
40930
|
}
|
|
40752
40931
|
}
|
|
40753
40932
|
return next;
|
|
40754
|
-
}
|
|
40755
|
-
|
|
40756
|
-
|
|
40933
|
+
},
|
|
40934
|
+
[maxTeams, nameMaxLength]
|
|
40935
|
+
);
|
|
40936
|
+
const addTeams = (0, import_react77.useCallback)(
|
|
40937
|
+
(raw) => {
|
|
40938
|
+
const names = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
40939
|
+
if (names.length === 0) return;
|
|
40940
|
+
setTeams((prev) => mergeNames(prev, names));
|
|
40941
|
+
setInput("");
|
|
40942
|
+
setRounds([]);
|
|
40943
|
+
},
|
|
40944
|
+
[mergeNames]
|
|
40945
|
+
);
|
|
40946
|
+
const importTeams = (0, import_react77.useCallback)(
|
|
40947
|
+
(incoming) => {
|
|
40948
|
+
if (incoming.length === 0) return;
|
|
40949
|
+
setTeams((prev) => mergeNames(prev, incoming));
|
|
40950
|
+
setRounds([]);
|
|
40951
|
+
},
|
|
40952
|
+
[mergeNames]
|
|
40953
|
+
);
|
|
40757
40954
|
const handleKeyDown = (0, import_react77.useCallback)(
|
|
40758
40955
|
(e) => {
|
|
40759
40956
|
if (e.key === "Enter") {
|
|
@@ -41242,6 +41439,8 @@ function Scoreboard({
|
|
|
41242
41439
|
onTeamsChange,
|
|
41243
41440
|
onRoundAdvance,
|
|
41244
41441
|
onNewGame,
|
|
41442
|
+
onReset,
|
|
41443
|
+
nameMaxLength,
|
|
41245
41444
|
labels,
|
|
41246
41445
|
ariaLabel = "Scoreboard",
|
|
41247
41446
|
className
|
|
@@ -41316,7 +41515,8 @@ function Scoreboard({
|
|
|
41316
41515
|
setRound(1);
|
|
41317
41516
|
setHistory([]);
|
|
41318
41517
|
setHistoryOpen(false);
|
|
41319
|
-
|
|
41518
|
+
onReset?.();
|
|
41519
|
+
}, [onReset]);
|
|
41320
41520
|
const newGame = (0, import_react78.useCallback)(() => {
|
|
41321
41521
|
setTeams(createDefaultTeams(seedCount, L.teamNamePrefix));
|
|
41322
41522
|
setRound(1);
|
|
@@ -41538,6 +41738,7 @@ function Scoreboard({
|
|
|
41538
41738
|
{
|
|
41539
41739
|
type: "text",
|
|
41540
41740
|
value: team.name,
|
|
41741
|
+
maxLength: nameMaxLength,
|
|
41541
41742
|
onChange: (e) => updateName(index, e.target.value),
|
|
41542
41743
|
style: nameInputStyle,
|
|
41543
41744
|
"aria-label": `Team ${index + 1} name`
|