@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/web/index.js
CHANGED
|
@@ -264,6 +264,7 @@ __export(index_exports, {
|
|
|
264
264
|
CommentSystem: () => CommentSystem,
|
|
265
265
|
Container: () => Container,
|
|
266
266
|
ConversionKPI: () => ConversionKPI,
|
|
267
|
+
D6_FACE_PLACEMENTS: () => D6_FACE_PLACEMENTS,
|
|
267
268
|
DashboardGrid: () => DashboardGrid,
|
|
268
269
|
DashboardLayout: () => DashboardLayout,
|
|
269
270
|
DashboardWidget: () => DashboardWidget,
|
|
@@ -504,6 +505,7 @@ __export(index_exports, {
|
|
|
504
505
|
createFormStore: () => createFormStore,
|
|
505
506
|
createOptimizedStore: () => createOptimizedStore,
|
|
506
507
|
cssAnimations: () => cssAnimations,
|
|
508
|
+
d6LandingRotation: () => d6LandingRotation,
|
|
507
509
|
dailyTrendingConfig: () => dailyTrendingConfig,
|
|
508
510
|
detectAuthProvider: () => detectAuthProvider,
|
|
509
511
|
detectDelimiter: () => detectDelimiter,
|
|
@@ -30223,7 +30225,7 @@ function TiltCard({
|
|
|
30223
30225
|
}
|
|
30224
30226
|
|
|
30225
30227
|
// src/components/ui/dice-roller/index.tsx
|
|
30226
|
-
var
|
|
30228
|
+
var import_react69 = require("react");
|
|
30227
30229
|
|
|
30228
30230
|
// src/hooks/web/use-dark-mode.ts
|
|
30229
30231
|
var import_react68 = require("react");
|
|
@@ -30272,21 +30274,6 @@ function useDarkMode(forced) {
|
|
|
30272
30274
|
return dark;
|
|
30273
30275
|
}
|
|
30274
30276
|
|
|
30275
|
-
// src/hooks/web/use-prefers-reduced-motion.ts
|
|
30276
|
-
var import_react69 = require("react");
|
|
30277
|
-
function usePrefersReducedMotion() {
|
|
30278
|
-
const [reduced, setReduced] = (0, import_react69.useState)(false);
|
|
30279
|
-
(0, import_react69.useEffect)(() => {
|
|
30280
|
-
if (typeof window === "undefined" || !window.matchMedia) return;
|
|
30281
|
-
const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
30282
|
-
setReduced(mq.matches);
|
|
30283
|
-
const read = () => setReduced(mq.matches);
|
|
30284
|
-
mq.addEventListener("change", read);
|
|
30285
|
-
return () => mq.removeEventListener("change", read);
|
|
30286
|
-
}, []);
|
|
30287
|
-
return reduced;
|
|
30288
|
-
}
|
|
30289
|
-
|
|
30290
30277
|
// src/components/ui/dice-roller/index.tsx
|
|
30291
30278
|
var import_jsx_runtime113 = require("react/jsx-runtime");
|
|
30292
30279
|
var PRESETS = {
|
|
@@ -30359,14 +30346,35 @@ var PIP_POS = {
|
|
|
30359
30346
|
[75, 75]
|
|
30360
30347
|
]
|
|
30361
30348
|
};
|
|
30362
|
-
var
|
|
30349
|
+
var D6_FACE_PLACEMENTS = {
|
|
30363
30350
|
1: { x: 0, y: 0 },
|
|
30364
|
-
2: { x: 0, y:
|
|
30365
|
-
3: { x:
|
|
30366
|
-
4: { x:
|
|
30367
|
-
5: { x:
|
|
30368
|
-
6: { x:
|
|
30351
|
+
2: { x: 0, y: 90 },
|
|
30352
|
+
3: { x: 90, y: 0 },
|
|
30353
|
+
4: { x: -90, y: 0 },
|
|
30354
|
+
5: { x: 0, y: -90 },
|
|
30355
|
+
6: { x: 0, y: 180 }
|
|
30356
|
+
};
|
|
30357
|
+
function d6LandingRotation(value) {
|
|
30358
|
+
const p = D6_FACE_PLACEMENTS[value];
|
|
30359
|
+
return {
|
|
30360
|
+
x: Math.abs(p.x) === 180 ? 180 : -p.x || 0,
|
|
30361
|
+
y: Math.abs(p.y) === 180 ? 180 : -p.y || 0
|
|
30362
|
+
};
|
|
30363
|
+
}
|
|
30364
|
+
var POLY_SHAPES = {
|
|
30365
|
+
4: { points: "50,8 93,86 7,86", textY: 62, fontScale: 0.3 },
|
|
30366
|
+
8: { points: "50,4 96,50 50,96 4,50", textY: 51, fontScale: 0.33 },
|
|
30367
|
+
10: { points: "50,4 93,44 50,96 7,44", textY: 47, fontScale: 0.3 },
|
|
30368
|
+
12: { points: "50,4 95,37 78,93 22,93 5,37", textY: 54, fontScale: 0.33 },
|
|
30369
|
+
20: {
|
|
30370
|
+
points: "50,3 91,26 91,74 50,97 9,74 9,26",
|
|
30371
|
+
textY: 51,
|
|
30372
|
+
fontScale: 0.33
|
|
30373
|
+
}
|
|
30369
30374
|
};
|
|
30375
|
+
function formatDieValue(v, sides) {
|
|
30376
|
+
return sides > 8 && (v === 6 || v === 9) ? `${v}.` : String(v);
|
|
30377
|
+
}
|
|
30370
30378
|
var MAX_COUNT = 6;
|
|
30371
30379
|
var TUMBLE_MS = 950;
|
|
30372
30380
|
var SETTLE_MS = 1100;
|
|
@@ -30484,8 +30492,8 @@ function simulateDice(arenaW, arenaH, diceSize, starts, numDice) {
|
|
|
30484
30492
|
return frames;
|
|
30485
30493
|
}
|
|
30486
30494
|
function useArenaBounds(ref) {
|
|
30487
|
-
const [bounds, setBounds] = (0,
|
|
30488
|
-
(0,
|
|
30495
|
+
const [bounds, setBounds] = (0, import_react69.useState)({ w: 400, h: 260 });
|
|
30496
|
+
(0, import_react69.useEffect)(() => {
|
|
30489
30497
|
if (!ref.current) return;
|
|
30490
30498
|
const el = ref.current;
|
|
30491
30499
|
const update = () => setBounds({ w: el.offsetWidth, h: el.offsetHeight });
|
|
@@ -30518,6 +30526,7 @@ function Face({ value, pipColor, pipShadow, pipDiameter }) {
|
|
|
30518
30526
|
}
|
|
30519
30527
|
function DiceRoller({
|
|
30520
30528
|
count: count2 = 2,
|
|
30529
|
+
sides = 6,
|
|
30521
30530
|
theme: theme5 = "red",
|
|
30522
30531
|
darkMode,
|
|
30523
30532
|
size = 80,
|
|
@@ -30539,18 +30548,18 @@ function DiceRoller({
|
|
|
30539
30548
|
}) {
|
|
30540
30549
|
const clampedCount = Math.max(1, Math.min(MAX_COUNT, count2));
|
|
30541
30550
|
const dark = useDarkMode(darkMode);
|
|
30542
|
-
const
|
|
30543
|
-
const [
|
|
30544
|
-
const [
|
|
30545
|
-
const
|
|
30546
|
-
const
|
|
30547
|
-
const
|
|
30548
|
-
const
|
|
30549
|
-
const currentPositionsRef = (0,
|
|
30550
|
-
const rollingRef = (0,
|
|
30551
|
-
const dieSetters = (0,
|
|
30552
|
-
const cubeSetters = (0,
|
|
30553
|
-
const shadowSetters = (0,
|
|
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)([]);
|
|
30554
30563
|
const getDieSetter = (i) => {
|
|
30555
30564
|
if (!dieSetters.current[i]) {
|
|
30556
30565
|
dieSetters.current[i] = (el) => {
|
|
@@ -30575,11 +30584,20 @@ function DiceRoller({
|
|
|
30575
30584
|
}
|
|
30576
30585
|
return shadowSetters.current[i];
|
|
30577
30586
|
};
|
|
30578
|
-
const
|
|
30579
|
-
const
|
|
30580
|
-
|
|
30587
|
+
const numeralSetters = (0, import_react69.useRef)([]);
|
|
30588
|
+
const getNumeralSetter = (i) => {
|
|
30589
|
+
if (!numeralSetters.current[i]) {
|
|
30590
|
+
numeralSetters.current[i] = (el) => {
|
|
30591
|
+
numeralRefs.current[i] = el;
|
|
30592
|
+
};
|
|
30593
|
+
}
|
|
30594
|
+
return numeralSetters.current[i];
|
|
30595
|
+
};
|
|
30596
|
+
const timersRef = (0, import_react69.useRef)([]);
|
|
30597
|
+
const rafHandleRef = (0, import_react69.useRef)(null);
|
|
30598
|
+
const arenaRef = (0, import_react69.useRef)(null);
|
|
30581
30599
|
const arena = useArenaBounds(arenaRef);
|
|
30582
|
-
const colors = (0,
|
|
30600
|
+
const colors = (0, import_react69.useMemo)(() => {
|
|
30583
30601
|
if (typeof theme5 === "object") {
|
|
30584
30602
|
const pipL = theme5.pip;
|
|
30585
30603
|
const pipD = theme5.pipDark ?? theme5.pip;
|
|
@@ -30616,7 +30634,7 @@ function DiceRoller({
|
|
|
30616
30634
|
chipDim: dark ? "#3a4358" : "#94a3b8"
|
|
30617
30635
|
};
|
|
30618
30636
|
}, [theme5, dark]);
|
|
30619
|
-
const effectiveSize = (0,
|
|
30637
|
+
const effectiveSize = (0, import_react69.useMemo)(() => {
|
|
30620
30638
|
if (!responsive2) return size;
|
|
30621
30639
|
const needed = clampedCount * size * 1.6;
|
|
30622
30640
|
if (arena.w >= needed || arena.w === 0) return size;
|
|
@@ -30627,25 +30645,25 @@ function DiceRoller({
|
|
|
30627
30645
|
const pipDiameter = Math.round(effectiveSize * 0.2);
|
|
30628
30646
|
const faceRadius = Math.round(effectiveSize * 0.07);
|
|
30629
30647
|
const arenaHeight = arenaHeightProp ?? Math.max(effectiveSize * 3, 200);
|
|
30630
|
-
const
|
|
30631
|
-
() =>
|
|
30632
|
-
|
|
30633
|
-
|
|
30634
|
-
|
|
30635
|
-
|
|
30636
|
-
`
|
|
30637
|
-
|
|
30638
|
-
|
|
30648
|
+
const faceTransformFor = (0, import_react69.useCallback)(
|
|
30649
|
+
(value) => {
|
|
30650
|
+
const p = D6_FACE_PLACEMENTS[value];
|
|
30651
|
+
const parts = [];
|
|
30652
|
+
if (p.x !== 0) parts.push(`rotateX(${p.x}deg)`);
|
|
30653
|
+
if (p.y !== 0) parts.push(`rotateY(${p.y}deg)`);
|
|
30654
|
+
parts.push(`translateZ(${half}px)`);
|
|
30655
|
+
return parts.join(" ");
|
|
30656
|
+
},
|
|
30639
30657
|
[half]
|
|
30640
30658
|
);
|
|
30641
|
-
const defaultPositions = (0,
|
|
30659
|
+
const defaultPositions = (0, import_react69.useCallback)(
|
|
30642
30660
|
(w, h, n) => Array.from({ length: n }, (_, i) => ({
|
|
30643
30661
|
x: (i + 1) * w / (n + 1),
|
|
30644
30662
|
y: h / 2
|
|
30645
30663
|
})),
|
|
30646
30664
|
[]
|
|
30647
30665
|
);
|
|
30648
|
-
const writeDiePosition = (0,
|
|
30666
|
+
const writeDiePosition = (0, import_react69.useCallback)(
|
|
30649
30667
|
(i, x, y, sx = 1, sy = 1) => {
|
|
30650
30668
|
const el = dieWrapperRefs.current[i];
|
|
30651
30669
|
if (!el) return;
|
|
@@ -30656,16 +30674,24 @@ function DiceRoller({
|
|
|
30656
30674
|
},
|
|
30657
30675
|
[half]
|
|
30658
30676
|
);
|
|
30659
|
-
const writeCubeRotation = (0,
|
|
30660
|
-
(i, rx, ry, rz, withTransition) => {
|
|
30677
|
+
const writeCubeRotation = (0, import_react69.useCallback)(
|
|
30678
|
+
(i, rx, ry, rz, withTransition, tiltZ = 0) => {
|
|
30661
30679
|
const el = cubeRefs.current[i];
|
|
30662
30680
|
if (!el) return;
|
|
30663
30681
|
el.style.transition = withTransition ? "transform 0.95s cubic-bezier(0.12,0.8,0.22,1)" : "none";
|
|
30664
|
-
el.style.transform = `rotateX(${rx}deg) rotateY(${ry}deg) rotateZ(${rz}deg)`;
|
|
30682
|
+
el.style.transform = `${tiltZ ? `rotateZ(${tiltZ}deg) ` : ""}rotateX(${rx}deg) rotateY(${ry}deg) rotateZ(${rz}deg)`;
|
|
30665
30683
|
},
|
|
30666
30684
|
[]
|
|
30667
30685
|
);
|
|
30668
|
-
(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
|
+
}
|
|
30669
30695
|
const rawW = arenaRef.current?.offsetWidth ?? 0;
|
|
30670
30696
|
const rawH = arenaRef.current?.offsetHeight ?? 0;
|
|
30671
30697
|
const w = rawW > 20 ? rawW : arena.w > 20 ? arena.w : 400;
|
|
@@ -30691,19 +30717,23 @@ function DiceRoller({
|
|
|
30691
30717
|
setResults(null);
|
|
30692
30718
|
}, [
|
|
30693
30719
|
clampedCount,
|
|
30720
|
+
sides,
|
|
30694
30721
|
effectiveSize,
|
|
30695
30722
|
arena.w,
|
|
30696
30723
|
arenaHeight,
|
|
30697
30724
|
defaultPositions,
|
|
30698
30725
|
half
|
|
30699
30726
|
]);
|
|
30700
|
-
|
|
30727
|
+
(0, import_react69.useEffect)(() => {
|
|
30728
|
+
setHistory([]);
|
|
30729
|
+
}, [sides]);
|
|
30730
|
+
const roll = (0, import_react69.useCallback)(() => {
|
|
30701
30731
|
if (rollingRef.current) return;
|
|
30702
30732
|
rollingRef.current = true;
|
|
30703
30733
|
setRolling(true);
|
|
30704
30734
|
const vals = Array.from(
|
|
30705
30735
|
{ length: clampedCount },
|
|
30706
|
-
() => Math.floor(Math.random() *
|
|
30736
|
+
() => Math.floor(Math.random() * sides) + 1
|
|
30707
30737
|
);
|
|
30708
30738
|
timersRef.current.forEach((t) => clearTimeout(t));
|
|
30709
30739
|
timersRef.current = [];
|
|
@@ -30718,13 +30748,17 @@ function DiceRoller({
|
|
|
30718
30748
|
const starts = currentPositionsRef.current ?? defaultPositions(w, h, clampedCount);
|
|
30719
30749
|
const frames = simulateDice(w, h, effectiveSize, starts, clampedCount);
|
|
30720
30750
|
for (let i = 0; i < clampedCount; i++) {
|
|
30721
|
-
|
|
30722
|
-
|
|
30723
|
-
|
|
30724
|
-
|
|
30725
|
-
|
|
30726
|
-
|
|
30727
|
-
|
|
30751
|
+
if (sides === 6) {
|
|
30752
|
+
writeCubeRotation(
|
|
30753
|
+
i,
|
|
30754
|
+
(Math.random() - 0.5) * 720,
|
|
30755
|
+
(Math.random() - 0.5) * 720,
|
|
30756
|
+
(Math.random() - 0.5) * 720,
|
|
30757
|
+
false
|
|
30758
|
+
);
|
|
30759
|
+
} else {
|
|
30760
|
+
writeCubeRotation(i, 0, 0, (Math.random() - 0.5) * 720, false);
|
|
30761
|
+
}
|
|
30728
30762
|
const el = dieWrapperRefs.current[i];
|
|
30729
30763
|
if (el) el.style.transition = "none";
|
|
30730
30764
|
const shadow = shadowRefs.current[i];
|
|
@@ -30757,18 +30791,47 @@ function DiceRoller({
|
|
|
30757
30791
|
}, capturedStep * stepMs);
|
|
30758
30792
|
timersRef.current.push(timer);
|
|
30759
30793
|
}
|
|
30794
|
+
if (sides !== 6) {
|
|
30795
|
+
const flickerTicks = 8;
|
|
30796
|
+
const flickerStep = Math.max(60, Math.floor(TUMBLE_MS / flickerTicks));
|
|
30797
|
+
for (let f = 1; f <= flickerTicks; f++) {
|
|
30798
|
+
const t = setTimeout(() => {
|
|
30799
|
+
for (let i = 0; i < clampedCount; i++) {
|
|
30800
|
+
const el = numeralRefs.current[i];
|
|
30801
|
+
if (el)
|
|
30802
|
+
el.textContent = formatDieValue(
|
|
30803
|
+
Math.floor(Math.random() * sides) + 1,
|
|
30804
|
+
sides
|
|
30805
|
+
);
|
|
30806
|
+
}
|
|
30807
|
+
}, f * flickerStep);
|
|
30808
|
+
timersRef.current.push(t);
|
|
30809
|
+
}
|
|
30810
|
+
}
|
|
30760
30811
|
requestAnimationFrame(() => {
|
|
30761
30812
|
requestAnimationFrame(() => {
|
|
30762
30813
|
for (let i = 0; i < clampedCount; i++) {
|
|
30763
|
-
const
|
|
30764
|
-
|
|
30765
|
-
|
|
30766
|
-
|
|
30767
|
-
|
|
30814
|
+
const tilt = (Math.random() - 0.5) * 18;
|
|
30815
|
+
if (sides === 6) {
|
|
30816
|
+
const tgt = d6LandingRotation(vals[i]);
|
|
30817
|
+
const lx = tgt.x + (Math.random() > 0.5 ? 720 : -720);
|
|
30818
|
+
const ly = tgt.y;
|
|
30819
|
+
const lz = (Math.random() > 0.5 ? 1 : -1) * 360;
|
|
30820
|
+
writeCubeRotation(i, lx, ly, lz, true, tilt);
|
|
30821
|
+
} else {
|
|
30822
|
+
const lz = (Math.random() > 0.5 ? 1 : -1) * 720 + tilt;
|
|
30823
|
+
writeCubeRotation(i, 0, 0, lz, true);
|
|
30824
|
+
}
|
|
30768
30825
|
}
|
|
30769
30826
|
});
|
|
30770
30827
|
});
|
|
30771
30828
|
const settleTimer = setTimeout(() => {
|
|
30829
|
+
if (sides !== 6) {
|
|
30830
|
+
for (let i = 0; i < clampedCount; i++) {
|
|
30831
|
+
const el = numeralRefs.current[i];
|
|
30832
|
+
if (el) el.textContent = formatDieValue(vals[i], sides);
|
|
30833
|
+
}
|
|
30834
|
+
}
|
|
30772
30835
|
setResults(vals);
|
|
30773
30836
|
setRolling(false);
|
|
30774
30837
|
rollingRef.current = false;
|
|
@@ -30781,28 +30844,28 @@ function DiceRoller({
|
|
|
30781
30844
|
timersRef.current.push(settleTimer);
|
|
30782
30845
|
}, [
|
|
30783
30846
|
clampedCount,
|
|
30847
|
+
sides,
|
|
30784
30848
|
defaultPositions,
|
|
30785
30849
|
effectiveSize,
|
|
30786
30850
|
half,
|
|
30787
|
-
reducedMotion,
|
|
30788
30851
|
onRoll,
|
|
30789
30852
|
writeCubeRotation,
|
|
30790
30853
|
writeDiePosition,
|
|
30791
30854
|
showHistory,
|
|
30792
30855
|
historyMax
|
|
30793
30856
|
]);
|
|
30794
|
-
(0,
|
|
30857
|
+
(0, import_react69.useEffect)(() => {
|
|
30795
30858
|
if (rollRef) rollRef.current = roll;
|
|
30796
30859
|
return () => {
|
|
30797
30860
|
if (rollRef && rollRef.current === roll) rollRef.current = null;
|
|
30798
30861
|
};
|
|
30799
30862
|
}, [roll, rollRef]);
|
|
30800
|
-
(0,
|
|
30863
|
+
(0, import_react69.useEffect)(() => {
|
|
30801
30864
|
if (!autoRoll) return;
|
|
30802
30865
|
const t = setTimeout(roll, 300);
|
|
30803
30866
|
return () => clearTimeout(t);
|
|
30804
30867
|
}, []);
|
|
30805
|
-
(0,
|
|
30868
|
+
(0, import_react69.useEffect)(
|
|
30806
30869
|
() => () => {
|
|
30807
30870
|
timersRef.current.forEach((t) => clearTimeout(t));
|
|
30808
30871
|
timersRef.current = [];
|
|
@@ -30826,7 +30889,7 @@ function DiceRoller({
|
|
|
30826
30889
|
{
|
|
30827
30890
|
ref: arenaRef,
|
|
30828
30891
|
role: "img",
|
|
30829
|
-
"aria-label": ariaLabel ?? `${clampedCount} dice`,
|
|
30892
|
+
"aria-label": ariaLabel ?? `${clampedCount} ${sides === 6 ? "dice" : `${sides}-sided dice`}`,
|
|
30830
30893
|
"aria-live": "polite",
|
|
30831
30894
|
style: {
|
|
30832
30895
|
position: "relative",
|
|
@@ -30889,7 +30952,7 @@ function DiceRoller({
|
|
|
30889
30952
|
transformStyle: "preserve-3d",
|
|
30890
30953
|
willChange: "transform"
|
|
30891
30954
|
},
|
|
30892
|
-
children: [1, 2, 3, 4, 5, 6].map((val
|
|
30955
|
+
children: sides === 6 ? [1, 2, 3, 4, 5, 6].map((val) => /* @__PURE__ */ (0, import_jsx_runtime113.jsx)(
|
|
30893
30956
|
"div",
|
|
30894
30957
|
{
|
|
30895
30958
|
style: {
|
|
@@ -30899,7 +30962,7 @@ function DiceRoller({
|
|
|
30899
30962
|
background: colors.face,
|
|
30900
30963
|
border: `1px solid ${colors.faceBorder}`,
|
|
30901
30964
|
borderRadius: faceRadius,
|
|
30902
|
-
transform:
|
|
30965
|
+
transform: faceTransformFor(val),
|
|
30903
30966
|
transition: "background 0.3s, border-color 0.3s"
|
|
30904
30967
|
},
|
|
30905
30968
|
children: /* @__PURE__ */ (0, import_jsx_runtime113.jsx)(
|
|
@@ -30913,7 +30976,64 @@ function DiceRoller({
|
|
|
30913
30976
|
)
|
|
30914
30977
|
},
|
|
30915
30978
|
val
|
|
30916
|
-
))
|
|
30979
|
+
)) : (() => {
|
|
30980
|
+
const shape = POLY_SHAPES[sides];
|
|
30981
|
+
const shown = results?.[d] ?? sides;
|
|
30982
|
+
return /* @__PURE__ */ (0, import_jsx_runtime113.jsxs)(
|
|
30983
|
+
"svg",
|
|
30984
|
+
{
|
|
30985
|
+
viewBox: "0 0 100 100",
|
|
30986
|
+
width: "100%",
|
|
30987
|
+
height: "100%",
|
|
30988
|
+
style: { display: "block", overflow: "visible" },
|
|
30989
|
+
"aria-hidden": "true",
|
|
30990
|
+
children: [
|
|
30991
|
+
/* @__PURE__ */ (0, import_jsx_runtime113.jsx)(
|
|
30992
|
+
"polygon",
|
|
30993
|
+
{
|
|
30994
|
+
points: shape.points,
|
|
30995
|
+
fill: colors.face,
|
|
30996
|
+
stroke: colors.faceBorder,
|
|
30997
|
+
strokeWidth: 2.5,
|
|
30998
|
+
strokeLinejoin: "round",
|
|
30999
|
+
style: { transition: "fill 0.3s, stroke 0.3s" }
|
|
31000
|
+
}
|
|
31001
|
+
),
|
|
31002
|
+
/* @__PURE__ */ (0, import_jsx_runtime113.jsx)(
|
|
31003
|
+
"polygon",
|
|
31004
|
+
{
|
|
31005
|
+
points: shape.points,
|
|
31006
|
+
fill: "none",
|
|
31007
|
+
stroke: colors.faceBorder,
|
|
31008
|
+
strokeWidth: 1.5,
|
|
31009
|
+
strokeLinejoin: "round",
|
|
31010
|
+
opacity: 0.55,
|
|
31011
|
+
transform: "translate(50 50) scale(0.76) translate(-50 -50)"
|
|
31012
|
+
}
|
|
31013
|
+
),
|
|
31014
|
+
/* @__PURE__ */ (0, import_jsx_runtime113.jsx)(
|
|
31015
|
+
"text",
|
|
31016
|
+
{
|
|
31017
|
+
ref: getNumeralSetter(d),
|
|
31018
|
+
x: 50,
|
|
31019
|
+
y: shape.textY,
|
|
31020
|
+
textAnchor: "middle",
|
|
31021
|
+
dominantBaseline: "central",
|
|
31022
|
+
fontSize: 100 * shape.fontScale,
|
|
31023
|
+
fontWeight: 800,
|
|
31024
|
+
fill: colors.pip,
|
|
31025
|
+
style: {
|
|
31026
|
+
fontVariantNumeric: "tabular-nums",
|
|
31027
|
+
transition: "fill 0.3s",
|
|
31028
|
+
userSelect: "none"
|
|
31029
|
+
},
|
|
31030
|
+
children: formatDieValue(shown, sides)
|
|
31031
|
+
}
|
|
31032
|
+
)
|
|
31033
|
+
]
|
|
31034
|
+
}
|
|
31035
|
+
);
|
|
31036
|
+
})()
|
|
30917
31037
|
}
|
|
30918
31038
|
)
|
|
30919
31039
|
] }, d);
|
|
@@ -31045,7 +31165,7 @@ function DiceRoller({
|
|
|
31045
31165
|
DiceRoller.displayName = "DiceRoller";
|
|
31046
31166
|
|
|
31047
31167
|
// src/components/ui/bracket-generator/index.tsx
|
|
31048
|
-
var
|
|
31168
|
+
var import_react70 = require("react");
|
|
31049
31169
|
|
|
31050
31170
|
// src/utils/array.ts
|
|
31051
31171
|
function shuffle(arr) {
|
|
@@ -31265,15 +31385,15 @@ function computeLayout(rounds, firstRoundCount) {
|
|
|
31265
31385
|
};
|
|
31266
31386
|
}
|
|
31267
31387
|
function usePanZoom(viewportRef, contentSize, minZoom, maxZoom) {
|
|
31268
|
-
const [transform, setTransform] = (0,
|
|
31388
|
+
const [transform, setTransform] = (0, import_react70.useState)({
|
|
31269
31389
|
x: 0,
|
|
31270
31390
|
y: 0,
|
|
31271
31391
|
scale: 1
|
|
31272
31392
|
});
|
|
31273
|
-
const isDragging = (0,
|
|
31274
|
-
const dragStart = (0,
|
|
31275
|
-
const pinchRef = (0,
|
|
31276
|
-
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)(
|
|
31277
31397
|
(t, vw, vh) => {
|
|
31278
31398
|
const sw = contentSize.w * t.scale;
|
|
31279
31399
|
const sh = contentSize.h * t.scale;
|
|
@@ -31285,7 +31405,7 @@ function usePanZoom(viewportRef, contentSize, minZoom, maxZoom) {
|
|
|
31285
31405
|
},
|
|
31286
31406
|
[contentSize]
|
|
31287
31407
|
);
|
|
31288
|
-
(0,
|
|
31408
|
+
(0, import_react70.useEffect)(() => {
|
|
31289
31409
|
const el = viewportRef.current;
|
|
31290
31410
|
if (!el) return;
|
|
31291
31411
|
const onWheel = (e) => {
|
|
@@ -31310,7 +31430,7 @@ function usePanZoom(viewportRef, contentSize, minZoom, maxZoom) {
|
|
|
31310
31430
|
el.addEventListener("wheel", onWheel, { passive: false });
|
|
31311
31431
|
return () => el.removeEventListener("wheel", onWheel);
|
|
31312
31432
|
}, [viewportRef, minZoom, maxZoom, clampTransform]);
|
|
31313
|
-
const onPointerDown = (0,
|
|
31433
|
+
const onPointerDown = (0, import_react70.useCallback)(
|
|
31314
31434
|
(e) => {
|
|
31315
31435
|
if (e.target.closest("[data-no-pan]")) return;
|
|
31316
31436
|
isDragging.current = true;
|
|
@@ -31323,7 +31443,7 @@ function usePanZoom(viewportRef, contentSize, minZoom, maxZoom) {
|
|
|
31323
31443
|
},
|
|
31324
31444
|
[transform]
|
|
31325
31445
|
);
|
|
31326
|
-
const onPointerMove = (0,
|
|
31446
|
+
const onPointerMove = (0, import_react70.useCallback)(
|
|
31327
31447
|
(e) => {
|
|
31328
31448
|
if (!isDragging.current) return;
|
|
31329
31449
|
const el = viewportRef.current;
|
|
@@ -31343,11 +31463,11 @@ function usePanZoom(viewportRef, contentSize, minZoom, maxZoom) {
|
|
|
31343
31463
|
},
|
|
31344
31464
|
[viewportRef, clampTransform]
|
|
31345
31465
|
);
|
|
31346
|
-
const onPointerUp = (0,
|
|
31466
|
+
const onPointerUp = (0, import_react70.useCallback)((e) => {
|
|
31347
31467
|
isDragging.current = false;
|
|
31348
31468
|
e.currentTarget.style.cursor = "grab";
|
|
31349
31469
|
}, []);
|
|
31350
|
-
const onTouchStart = (0,
|
|
31470
|
+
const onTouchStart = (0, import_react70.useCallback)(
|
|
31351
31471
|
(e) => {
|
|
31352
31472
|
if (e.touches.length === 2) {
|
|
31353
31473
|
const [t1, t2] = [e.touches[0], e.touches[1]];
|
|
@@ -31367,7 +31487,7 @@ function usePanZoom(viewportRef, contentSize, minZoom, maxZoom) {
|
|
|
31367
31487
|
},
|
|
31368
31488
|
[transform]
|
|
31369
31489
|
);
|
|
31370
|
-
const onTouchMove = (0,
|
|
31490
|
+
const onTouchMove = (0, import_react70.useCallback)(
|
|
31371
31491
|
(e) => {
|
|
31372
31492
|
if (e.touches.length === 2 && pinchRef.current) {
|
|
31373
31493
|
e.preventDefault();
|
|
@@ -31404,10 +31524,10 @@ function usePanZoom(viewportRef, contentSize, minZoom, maxZoom) {
|
|
|
31404
31524
|
},
|
|
31405
31525
|
[viewportRef, minZoom, maxZoom, clampTransform]
|
|
31406
31526
|
);
|
|
31407
|
-
const onTouchEnd = (0,
|
|
31527
|
+
const onTouchEnd = (0, import_react70.useCallback)(() => {
|
|
31408
31528
|
pinchRef.current = null;
|
|
31409
31529
|
}, []);
|
|
31410
|
-
const zoomBy = (0,
|
|
31530
|
+
const zoomBy = (0, import_react70.useCallback)(
|
|
31411
31531
|
(factor) => {
|
|
31412
31532
|
const el = viewportRef.current;
|
|
31413
31533
|
if (!el) return;
|
|
@@ -31426,7 +31546,7 @@ function usePanZoom(viewportRef, contentSize, minZoom, maxZoom) {
|
|
|
31426
31546
|
},
|
|
31427
31547
|
[viewportRef, minZoom, maxZoom, clampTransform]
|
|
31428
31548
|
);
|
|
31429
|
-
const fit = (0,
|
|
31549
|
+
const fit = (0, import_react70.useCallback)(() => {
|
|
31430
31550
|
const el = viewportRef.current;
|
|
31431
31551
|
if (!el || !contentSize.w) return;
|
|
31432
31552
|
const rect = el.getBoundingClientRect();
|
|
@@ -31440,10 +31560,10 @@ function usePanZoom(viewportRef, contentSize, minZoom, maxZoom) {
|
|
|
31440
31560
|
scale
|
|
31441
31561
|
});
|
|
31442
31562
|
}, [viewportRef, contentSize, minZoom, maxZoom]);
|
|
31443
|
-
const reset = (0,
|
|
31563
|
+
const reset = (0, import_react70.useCallback)(() => {
|
|
31444
31564
|
setTransform({ x: 40, y: 40, scale: 1 });
|
|
31445
31565
|
}, []);
|
|
31446
|
-
const panTo = (0,
|
|
31566
|
+
const panTo = (0, import_react70.useCallback)(
|
|
31447
31567
|
(contentX, contentY) => {
|
|
31448
31568
|
const el = viewportRef.current;
|
|
31449
31569
|
if (!el) return;
|
|
@@ -31516,15 +31636,15 @@ function BracketGenerator({
|
|
|
31516
31636
|
ariaLabel = "Tournament bracket"
|
|
31517
31637
|
}) {
|
|
31518
31638
|
const dark = useDarkMode(darkMode);
|
|
31519
|
-
const [bracketState, setBracketState] = (0,
|
|
31639
|
+
const [bracketState, setBracketState] = (0, import_react70.useState)({
|
|
31520
31640
|
matches: [],
|
|
31521
31641
|
rounds: 0,
|
|
31522
31642
|
champion: null
|
|
31523
31643
|
});
|
|
31524
|
-
const [simulating, setSimulating] = (0,
|
|
31525
|
-
const autoRef = (0,
|
|
31526
|
-
const viewportRef = (0,
|
|
31527
|
-
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)(() => {
|
|
31528
31648
|
if (participantsProp?.length) {
|
|
31529
31649
|
return padToSize(
|
|
31530
31650
|
participantsProp.map((p, i) => ({
|
|
@@ -31543,7 +31663,7 @@ function BracketGenerator({
|
|
|
31543
31663
|
}));
|
|
31544
31664
|
}, [participantsProp, size]);
|
|
31545
31665
|
const preset = typeof theme5 === "string" ? PRESETS2[theme5] || PRESETS2.blue : null;
|
|
31546
|
-
const themeColors = (0,
|
|
31666
|
+
const themeColors = (0, import_react70.useMemo)(() => {
|
|
31547
31667
|
if (typeof theme5 === "object") return theme5;
|
|
31548
31668
|
const p = preset;
|
|
31549
31669
|
return {
|
|
@@ -31552,9 +31672,9 @@ function BracketGenerator({
|
|
|
31552
31672
|
color: p.color
|
|
31553
31673
|
};
|
|
31554
31674
|
}, [theme5, dark, preset]);
|
|
31555
|
-
const colors = (0,
|
|
31675
|
+
const colors = (0, import_react70.useMemo)(() => buildColorTokens(dark), [dark]);
|
|
31556
31676
|
const roundLabels = ROUND_NAMES[effectiveParticipants.length] || [];
|
|
31557
|
-
const layout = (0,
|
|
31677
|
+
const layout = (0, import_react70.useMemo)(() => {
|
|
31558
31678
|
if (!bracketState.rounds)
|
|
31559
31679
|
return {
|
|
31560
31680
|
positions: {},
|
|
@@ -31563,7 +31683,7 @@ function BracketGenerator({
|
|
|
31563
31683
|
};
|
|
31564
31684
|
return computeLayout(bracketState.rounds, effectiveParticipants.length / 2);
|
|
31565
31685
|
}, [bracketState.rounds, effectiveParticipants.length]);
|
|
31566
|
-
const contentSize = (0,
|
|
31686
|
+
const contentSize = (0, import_react70.useMemo)(
|
|
31567
31687
|
() => ({
|
|
31568
31688
|
w: layout.totalW + CANVAS_PAD * 2,
|
|
31569
31689
|
h: layout.totalH + CANVAS_PAD * 2
|
|
@@ -31576,10 +31696,10 @@ function BracketGenerator({
|
|
|
31576
31696
|
minZoom,
|
|
31577
31697
|
maxZoom
|
|
31578
31698
|
);
|
|
31579
|
-
const [miniMapHidden, setMiniMapHidden] = (0,
|
|
31580
|
-
const miniMapDragRef = (0,
|
|
31581
|
-
const resumeRef = (0,
|
|
31582
|
-
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)(() => {
|
|
31583
31703
|
const resume = resumeRef.current;
|
|
31584
31704
|
if (!resume?.length) return null;
|
|
31585
31705
|
const n = effectiveParticipants.length;
|
|
@@ -31619,7 +31739,7 @@ function BracketGenerator({
|
|
|
31619
31739
|
const finalMatch = matches.find((m) => m.round === rounds - 1);
|
|
31620
31740
|
return { matches, rounds, champion: finalMatch?.winner ?? null };
|
|
31621
31741
|
}, [effectiveParticipants]);
|
|
31622
|
-
const seedBracket = (0,
|
|
31742
|
+
const seedBracket = (0, import_react70.useCallback)(() => {
|
|
31623
31743
|
const { matches, rounds } = buildBracket(
|
|
31624
31744
|
effectiveParticipants,
|
|
31625
31745
|
seedingStrategy
|
|
@@ -31642,7 +31762,7 @@ function BracketGenerator({
|
|
|
31642
31762
|
};
|
|
31643
31763
|
setBracketState(newState);
|
|
31644
31764
|
}, [effectiveParticipants, seedingStrategy]);
|
|
31645
|
-
const simulateNext = (0,
|
|
31765
|
+
const simulateNext = (0, import_react70.useCallback)(() => {
|
|
31646
31766
|
setBracketState((prev) => {
|
|
31647
31767
|
const next = prev.matches.find((m) => m.a && m.b && !m.winner);
|
|
31648
31768
|
if (!next) return prev;
|
|
@@ -31657,7 +31777,7 @@ function BracketGenerator({
|
|
|
31657
31777
|
return { ...prev, matches, champion };
|
|
31658
31778
|
});
|
|
31659
31779
|
}, []);
|
|
31660
|
-
const autoPlay = (0,
|
|
31780
|
+
const autoPlay = (0, import_react70.useCallback)(() => {
|
|
31661
31781
|
if (simulating) {
|
|
31662
31782
|
if (autoRef.current) clearTimeout(autoRef.current);
|
|
31663
31783
|
setSimulating(false);
|
|
@@ -31686,7 +31806,7 @@ function BracketGenerator({
|
|
|
31686
31806
|
};
|
|
31687
31807
|
tick();
|
|
31688
31808
|
}, [simulating, simulateDelayMs]);
|
|
31689
|
-
const advanceWinner = (0,
|
|
31809
|
+
const advanceWinner = (0, import_react70.useCallback)(
|
|
31690
31810
|
(matchId, winnerSlot) => {
|
|
31691
31811
|
setBracketState((prev) => {
|
|
31692
31812
|
const m = prev.matches.find((x) => x.id === matchId);
|
|
@@ -31709,8 +31829,8 @@ function BracketGenerator({
|
|
|
31709
31829
|
},
|
|
31710
31830
|
[]
|
|
31711
31831
|
);
|
|
31712
|
-
const prevBracketStateRef = (0,
|
|
31713
|
-
(0,
|
|
31832
|
+
const prevBracketStateRef = (0, import_react70.useRef)(bracketState);
|
|
31833
|
+
(0, import_react70.useEffect)(() => {
|
|
31714
31834
|
const prev = prevBracketStateRef.current;
|
|
31715
31835
|
const next = bracketState;
|
|
31716
31836
|
if (prev === next) return;
|
|
@@ -31729,12 +31849,12 @@ function BracketGenerator({
|
|
|
31729
31849
|
onStateChange?.(next);
|
|
31730
31850
|
prevBracketStateRef.current = next;
|
|
31731
31851
|
}, [bracketState, onMatchComplete, onChampion, onStateChange]);
|
|
31732
|
-
const structuralKey = (0,
|
|
31852
|
+
const structuralKey = (0, import_react70.useMemo)(
|
|
31733
31853
|
() => `${seedingStrategy}|${effectiveParticipants.map((p) => `${p.id}:${p.seed ?? ""}`).join(",")}`,
|
|
31734
31854
|
[effectiveParticipants, seedingStrategy]
|
|
31735
31855
|
);
|
|
31736
|
-
const seededKeyRef = (0,
|
|
31737
|
-
(0,
|
|
31856
|
+
const seededKeyRef = (0, import_react70.useRef)(null);
|
|
31857
|
+
(0, import_react70.useEffect)(() => {
|
|
31738
31858
|
if (seededKeyRef.current === structuralKey) {
|
|
31739
31859
|
setBracketState((prev) => {
|
|
31740
31860
|
if (!prev.matches.length) return prev;
|
|
@@ -31765,24 +31885,24 @@ function BracketGenerator({
|
|
|
31765
31885
|
seedBracket();
|
|
31766
31886
|
}
|
|
31767
31887
|
}, [structuralKey, effectiveParticipants, hydrateFromResume, seedBracket]);
|
|
31768
|
-
(0,
|
|
31888
|
+
(0, import_react70.useEffect)(() => {
|
|
31769
31889
|
return () => {
|
|
31770
31890
|
if (autoRef.current) clearTimeout(autoRef.current);
|
|
31771
31891
|
};
|
|
31772
31892
|
}, []);
|
|
31773
|
-
(0,
|
|
31893
|
+
(0, import_react70.useLayoutEffect)(() => {
|
|
31774
31894
|
if (contentSize.w > 0) {
|
|
31775
31895
|
const timer = setTimeout(fit, 50);
|
|
31776
31896
|
return () => clearTimeout(timer);
|
|
31777
31897
|
}
|
|
31778
31898
|
}, [contentSize.w]);
|
|
31779
|
-
(0,
|
|
31899
|
+
(0, import_react70.useEffect)(() => {
|
|
31780
31900
|
if (autoSimulate && bracketState.matches.length && !bracketState.champion) {
|
|
31781
31901
|
const timer = setTimeout(autoPlay, 500);
|
|
31782
31902
|
return () => clearTimeout(timer);
|
|
31783
31903
|
}
|
|
31784
31904
|
}, [autoSimulate]);
|
|
31785
|
-
const stats = (0,
|
|
31905
|
+
const stats = (0, import_react70.useMemo)(() => {
|
|
31786
31906
|
const done = bracketState.matches.filter((m) => m.winner).length;
|
|
31787
31907
|
const total = bracketState.matches.length;
|
|
31788
31908
|
const remaining = bracketState.matches.filter(
|
|
@@ -31790,7 +31910,7 @@ function BracketGenerator({
|
|
|
31790
31910
|
).length;
|
|
31791
31911
|
return { done, total, remaining };
|
|
31792
31912
|
}, [bracketState]);
|
|
31793
|
-
const connectorPaths = (0,
|
|
31913
|
+
const connectorPaths = (0, import_react70.useMemo)(() => {
|
|
31794
31914
|
const paths = [];
|
|
31795
31915
|
bracketState.matches.forEach((m) => {
|
|
31796
31916
|
if (m.round >= bracketState.rounds - 1) return;
|
|
@@ -32495,7 +32615,7 @@ function BracketGenerator({
|
|
|
32495
32615
|
}
|
|
32496
32616
|
|
|
32497
32617
|
// src/components/ui/coin-flipper/index.tsx
|
|
32498
|
-
var
|
|
32618
|
+
var import_react71 = require("react");
|
|
32499
32619
|
var import_jsx_runtime115 = require("react/jsx-runtime");
|
|
32500
32620
|
var PRESETS3 = {
|
|
32501
32621
|
gold: {
|
|
@@ -32679,15 +32799,15 @@ function CoinFlipper({
|
|
|
32679
32799
|
}) {
|
|
32680
32800
|
const dark = useDarkMode(darkMode);
|
|
32681
32801
|
const tc = typeof theme5 === "string" ? PRESETS3[theme5] || PRESETS3.gold : theme5;
|
|
32682
|
-
const [result, setResult] = (0,
|
|
32683
|
-
const [flipping, setFlipping] = (0,
|
|
32684
|
-
const [history, setHistory] = (0,
|
|
32685
|
-
const [stats, setStats] = (0,
|
|
32686
|
-
const flipIdRef = (0,
|
|
32687
|
-
const coinRef = (0,
|
|
32688
|
-
const shadowRef = (0,
|
|
32689
|
-
const timersRef = (0,
|
|
32690
|
-
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)(
|
|
32691
32811
|
() => ({
|
|
32692
32812
|
bg: dark ? "#0f1117" : "#f8fafc",
|
|
32693
32813
|
surface: dark ? "#1a1d27" : "#ffffff",
|
|
@@ -32698,7 +32818,7 @@ function CoinFlipper({
|
|
|
32698
32818
|
}),
|
|
32699
32819
|
[dark]
|
|
32700
32820
|
);
|
|
32701
|
-
const flip = (0,
|
|
32821
|
+
const flip = (0, import_react71.useCallback)(() => {
|
|
32702
32822
|
if (flipping) return;
|
|
32703
32823
|
setFlipping(true);
|
|
32704
32824
|
const outcome = Math.random() < 0.5 ? "heads" : "tails";
|
|
@@ -32738,16 +32858,19 @@ function CoinFlipper({
|
|
|
32738
32858
|
}, frames.length * stepMs);
|
|
32739
32859
|
timersRef.current.push(settleTimer);
|
|
32740
32860
|
}, [flipping, onFlip, historyMax]);
|
|
32741
|
-
(0,
|
|
32861
|
+
(0, import_react71.useEffect)(() => {
|
|
32742
32862
|
if (flipRef) flipRef.current = flip;
|
|
32863
|
+
return () => {
|
|
32864
|
+
if (flipRef && flipRef.current === flip) flipRef.current = null;
|
|
32865
|
+
};
|
|
32743
32866
|
}, [flip, flipRef]);
|
|
32744
|
-
(0,
|
|
32867
|
+
(0, import_react71.useEffect)(() => {
|
|
32745
32868
|
if (autoFlip) {
|
|
32746
32869
|
const timer = setTimeout(flip, 300);
|
|
32747
32870
|
return () => clearTimeout(timer);
|
|
32748
32871
|
}
|
|
32749
32872
|
}, []);
|
|
32750
|
-
(0,
|
|
32873
|
+
(0, import_react71.useEffect)(() => {
|
|
32751
32874
|
return () => {
|
|
32752
32875
|
timersRef.current.forEach(clearTimeout);
|
|
32753
32876
|
};
|
|
@@ -33184,6 +33307,23 @@ function CoinFlipper({
|
|
|
33184
33307
|
|
|
33185
33308
|
// src/components/ui/random-player-picker/index.tsx
|
|
33186
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
|
|
33187
33327
|
var import_jsx_runtime116 = require("react/jsx-runtime");
|
|
33188
33328
|
var PRESETS4 = {
|
|
33189
33329
|
blue: {
|
|
@@ -33631,6 +33771,8 @@ function RandomPlayerPicker({
|
|
|
33631
33771
|
const particleTimer = (0, import_react73.useRef)(null);
|
|
33632
33772
|
const popTimer = (0, import_react73.useRef)(null);
|
|
33633
33773
|
const tickInterval = (0, import_react73.useRef)(null);
|
|
33774
|
+
const pendingPopIdxRef = (0, import_react73.useRef)(null);
|
|
33775
|
+
const removeWinnersRef = (0, import_react73.useRef)(removeWinners);
|
|
33634
33776
|
const tickAudioRef = (0, import_react73.useRef)(null);
|
|
33635
33777
|
const winAudioRef = (0, import_react73.useRef)(null);
|
|
33636
33778
|
const containerRef = (0, import_react73.useRef)(null);
|
|
@@ -33669,10 +33811,27 @@ function RandomPlayerPicker({
|
|
|
33669
33811
|
winAudioRef.current = null;
|
|
33670
33812
|
};
|
|
33671
33813
|
}, [winSoundUrl, soundVolume]);
|
|
33814
|
+
(0, import_react73.useEffect)(() => {
|
|
33815
|
+
removeWinnersRef.current = removeWinners;
|
|
33816
|
+
}, [removeWinners]);
|
|
33672
33817
|
(0, import_react73.useEffect)(() => {
|
|
33673
33818
|
const ids = participants.map((p) => p.id).join("|");
|
|
33674
33819
|
const structuralChange = ids !== prevIdsRef.current;
|
|
33675
33820
|
if (structuralChange) {
|
|
33821
|
+
if (winnerTimer.current) {
|
|
33822
|
+
clearTimeout(winnerTimer.current);
|
|
33823
|
+
winnerTimer.current = null;
|
|
33824
|
+
}
|
|
33825
|
+
if (tickInterval.current) {
|
|
33826
|
+
clearTimeout(tickInterval.current);
|
|
33827
|
+
tickInterval.current = null;
|
|
33828
|
+
}
|
|
33829
|
+
if (popTimer.current) {
|
|
33830
|
+
clearTimeout(popTimer.current);
|
|
33831
|
+
popTimer.current = null;
|
|
33832
|
+
}
|
|
33833
|
+
pendingPopIdxRef.current = null;
|
|
33834
|
+
setSpinning(false);
|
|
33676
33835
|
setPool(participants);
|
|
33677
33836
|
setHistory([]);
|
|
33678
33837
|
setWinner(null);
|
|
@@ -33729,33 +33888,50 @@ function RandomPlayerPicker({
|
|
|
33729
33888
|
[dark]
|
|
33730
33889
|
);
|
|
33731
33890
|
const spin = (0, import_react73.useCallback)(() => {
|
|
33732
|
-
if (spinning
|
|
33891
|
+
if (spinning) return;
|
|
33892
|
+
let activePool = pool;
|
|
33893
|
+
if (popTimer.current) {
|
|
33894
|
+
clearTimeout(popTimer.current);
|
|
33895
|
+
popTimer.current = null;
|
|
33896
|
+
const pendingIdx = pendingPopIdxRef.current;
|
|
33897
|
+
pendingPopIdxRef.current = null;
|
|
33898
|
+
if (pendingIdx != null && pendingIdx >= 0 && pendingIdx < pool.length && removeWinnersRef.current) {
|
|
33899
|
+
activePool = pool.filter((_, i) => i !== pendingIdx);
|
|
33900
|
+
setPool(activePool);
|
|
33901
|
+
setWinner(null);
|
|
33902
|
+
}
|
|
33903
|
+
}
|
|
33904
|
+
const activeN = activePool.length;
|
|
33905
|
+
if (activeN < 2) return;
|
|
33906
|
+
const activeAnglePerWedge = 2 * Math.PI / activeN;
|
|
33907
|
+
const activeRotation = rotation;
|
|
33908
|
+
const activeReelY = reelY;
|
|
33733
33909
|
onSpinStart?.();
|
|
33734
|
-
const winnerIdx = weightedIndex(
|
|
33910
|
+
const winnerIdx = weightedIndex(activePool);
|
|
33735
33911
|
const minRev = minRevolutions + intensityCfg.revsBoost;
|
|
33736
33912
|
const maxRev = maxRevolutions + intensityCfg.revsBoost;
|
|
33737
33913
|
const minFullRevs = Math.ceil(minRev);
|
|
33738
33914
|
const maxFullRevs = Math.max(minFullRevs, Math.floor(maxRev));
|
|
33739
33915
|
const revolutions = minFullRevs + Math.floor(Math.random() * (maxFullRevs - minFullRevs + 1));
|
|
33740
|
-
const wedgeMidRad = winnerIdx *
|
|
33916
|
+
const wedgeMidRad = winnerIdx * activeAnglePerWedge + activeAnglePerWedge / 2;
|
|
33741
33917
|
const wedgeMidDeg = wedgeMidRad * 180 / Math.PI;
|
|
33742
|
-
const wheelWobble = (Math.random() - 0.5) * (
|
|
33743
|
-
const currentMod = (
|
|
33918
|
+
const wheelWobble = (Math.random() - 0.5) * (activeAnglePerWedge * 180 / Math.PI) * 0.6;
|
|
33919
|
+
const currentMod = (activeRotation % 360 + 360) % 360;
|
|
33744
33920
|
const targetMod = ((-wedgeMidDeg + wheelWobble) % 360 + 360) % 360;
|
|
33745
33921
|
let deltaMod = targetMod - currentMod;
|
|
33746
33922
|
if (deltaMod < 0) deltaMod += 360;
|
|
33747
|
-
const finalRotation =
|
|
33923
|
+
const finalRotation = activeRotation + deltaMod + revolutions * 360;
|
|
33748
33924
|
const centerOffsetRows = (reelVisibleRows - 1) / 2;
|
|
33749
33925
|
const centerOffsetPx = centerOffsetRows * reelRowHeight;
|
|
33750
33926
|
const currentCenterRow = Math.round(
|
|
33751
|
-
(
|
|
33927
|
+
(activeReelY + centerOffsetPx) / reelRowHeight
|
|
33752
33928
|
);
|
|
33753
|
-
const currentCenterMod = mod(currentCenterRow,
|
|
33754
|
-
const targetRowOffset = (winnerIdx - currentCenterMod +
|
|
33755
|
-
const reelRevRows = Math.round(revolutions *
|
|
33756
|
-
const finalReelY =
|
|
33757
|
-
const settledWinnerIdx = mode === "reel" ? reelIndexAtCenter(finalReelY, centerOffsetPx, reelRowHeight,
|
|
33758
|
-
const finalWinnerIdx = settledWinnerIdx >= 0 && settledWinnerIdx <
|
|
33929
|
+
const currentCenterMod = mod(currentCenterRow, activeN);
|
|
33930
|
+
const targetRowOffset = (winnerIdx - currentCenterMod + activeN) % activeN;
|
|
33931
|
+
const reelRevRows = Math.round(revolutions * activeN);
|
|
33932
|
+
const finalReelY = activeReelY + (reelRevRows + targetRowOffset) * reelRowHeight;
|
|
33933
|
+
const settledWinnerIdx = mode === "reel" ? reelIndexAtCenter(finalReelY, centerOffsetPx, reelRowHeight, activeN) : wheelIndexAtPointer(finalRotation, activeN);
|
|
33934
|
+
const finalWinnerIdx = settledWinnerIdx >= 0 && settledWinnerIdx < activePool.length ? settledWinnerIdx : winnerIdx;
|
|
33759
33935
|
const effectiveDuration = reducedMotion ? 250 : Math.round(spinDuration * intensityCfg.durationMult);
|
|
33760
33936
|
setSpinning(true);
|
|
33761
33937
|
setWinner(null);
|
|
@@ -33768,7 +33944,7 @@ function RandomPlayerPicker({
|
|
|
33768
33944
|
if ((onTick || tickAudioRef.current) && !reducedMotion) {
|
|
33769
33945
|
const wedges = mode === "reel" ? Math.max(1, reelRevRows + targetRowOffset) : (() => {
|
|
33770
33946
|
const totalDelta = deltaMod + revolutions * 360;
|
|
33771
|
-
const wedgeDeg =
|
|
33947
|
+
const wedgeDeg = activeAnglePerWedge * 180 / Math.PI;
|
|
33772
33948
|
return Math.max(
|
|
33773
33949
|
1,
|
|
33774
33950
|
Math.round(totalDelta / Math.max(wedgeDeg, 1))
|
|
@@ -33800,7 +33976,7 @@ function RandomPlayerPicker({
|
|
|
33800
33976
|
schedule();
|
|
33801
33977
|
}
|
|
33802
33978
|
winnerTimer.current = setTimeout(() => {
|
|
33803
|
-
const picked =
|
|
33979
|
+
const picked = activePool[finalWinnerIdx];
|
|
33804
33980
|
if (!picked) {
|
|
33805
33981
|
setSpinning(false);
|
|
33806
33982
|
return;
|
|
@@ -33846,7 +34022,11 @@ function RandomPlayerPicker({
|
|
|
33846
34022
|
particleTimer.current = setTimeout(() => setParticles([]), 2400);
|
|
33847
34023
|
}
|
|
33848
34024
|
if (removeWinners) {
|
|
34025
|
+
pendingPopIdxRef.current = finalWinnerIdx;
|
|
33849
34026
|
popTimer.current = setTimeout(() => {
|
|
34027
|
+
popTimer.current = null;
|
|
34028
|
+
pendingPopIdxRef.current = null;
|
|
34029
|
+
if (!removeWinnersRef.current) return;
|
|
33850
34030
|
setPool((p) => p.filter((_, i) => i !== finalWinnerIdx));
|
|
33851
34031
|
setWinner(null);
|
|
33852
34032
|
setRotation(0);
|
|
@@ -33857,8 +34037,6 @@ function RandomPlayerPicker({
|
|
|
33857
34037
|
}, [
|
|
33858
34038
|
pool,
|
|
33859
34039
|
spinning,
|
|
33860
|
-
n,
|
|
33861
|
-
anglePerWedge,
|
|
33862
34040
|
rotation,
|
|
33863
34041
|
reelY,
|
|
33864
34042
|
mode,
|
|
@@ -33894,6 +34072,7 @@ function RandomPlayerPicker({
|
|
|
33894
34072
|
[]
|
|
33895
34073
|
);
|
|
33896
34074
|
(0, import_react73.useEffect)(() => {
|
|
34075
|
+
if (trigger === "external") return;
|
|
33897
34076
|
const el = containerRef.current;
|
|
33898
34077
|
if (!el) return;
|
|
33899
34078
|
const h = (e) => {
|
|
@@ -33904,8 +34083,22 @@ function RandomPlayerPicker({
|
|
|
33904
34083
|
};
|
|
33905
34084
|
el.addEventListener("keydown", h);
|
|
33906
34085
|
return () => el.removeEventListener("keydown", h);
|
|
33907
|
-
}, [spin]);
|
|
34086
|
+
}, [spin, trigger]);
|
|
33908
34087
|
const reset = (0, import_react73.useCallback)(() => {
|
|
34088
|
+
if (winnerTimer.current) {
|
|
34089
|
+
clearTimeout(winnerTimer.current);
|
|
34090
|
+
winnerTimer.current = null;
|
|
34091
|
+
}
|
|
34092
|
+
if (tickInterval.current) {
|
|
34093
|
+
clearTimeout(tickInterval.current);
|
|
34094
|
+
tickInterval.current = null;
|
|
34095
|
+
}
|
|
34096
|
+
if (popTimer.current) {
|
|
34097
|
+
clearTimeout(popTimer.current);
|
|
34098
|
+
popTimer.current = null;
|
|
34099
|
+
}
|
|
34100
|
+
pendingPopIdxRef.current = null;
|
|
34101
|
+
setSpinning(false);
|
|
33909
34102
|
setPool(participants);
|
|
33910
34103
|
setHistory([]);
|
|
33911
34104
|
setWinner(null);
|
|
@@ -38358,27 +38551,28 @@ function GameTimer({
|
|
|
38358
38551
|
const now = performance.now();
|
|
38359
38552
|
const delta = now - lastTickRef.current;
|
|
38360
38553
|
lastTickRef.current = now;
|
|
38361
|
-
|
|
38362
|
-
|
|
38363
|
-
|
|
38364
|
-
|
|
38365
|
-
|
|
38366
|
-
|
|
38367
|
-
|
|
38554
|
+
const next = elapsedRef.current + delta;
|
|
38555
|
+
if (mode === "countdown" && next >= durationMs) {
|
|
38556
|
+
elapsedRef.current = durationMs;
|
|
38557
|
+
setElapsedMs(durationMs);
|
|
38558
|
+
setRunning(false);
|
|
38559
|
+
setCompleted(true);
|
|
38560
|
+
onComplete?.();
|
|
38561
|
+
return;
|
|
38562
|
+
}
|
|
38563
|
+
if (mode === "countdown") {
|
|
38564
|
+
const secLeft = Math.ceil((durationMs - next) / 1e3);
|
|
38565
|
+
if (secLeft <= warningAt && !warningFiredRef.current) {
|
|
38566
|
+
warningFiredRef.current = true;
|
|
38567
|
+
onWarning?.();
|
|
38368
38568
|
}
|
|
38369
|
-
if (
|
|
38370
|
-
|
|
38371
|
-
|
|
38372
|
-
warningFiredRef.current = true;
|
|
38373
|
-
onWarning?.();
|
|
38374
|
-
}
|
|
38375
|
-
if (secLeft <= dangerAt && !dangerFiredRef.current) {
|
|
38376
|
-
dangerFiredRef.current = true;
|
|
38377
|
-
onDanger?.();
|
|
38378
|
-
}
|
|
38569
|
+
if (secLeft <= dangerAt && !dangerFiredRef.current) {
|
|
38570
|
+
dangerFiredRef.current = true;
|
|
38571
|
+
onDanger?.();
|
|
38379
38572
|
}
|
|
38380
|
-
|
|
38381
|
-
|
|
38573
|
+
}
|
|
38574
|
+
elapsedRef.current = next;
|
|
38575
|
+
setElapsedMs(next);
|
|
38382
38576
|
rafRef.current = requestAnimationFrame(tick);
|
|
38383
38577
|
}, [mode, durationMs, warningAt, dangerAt, onComplete, onWarning, onDanger]);
|
|
38384
38578
|
(0, import_react76.useEffect)(() => {
|
|
@@ -38399,6 +38593,7 @@ function GameTimer({
|
|
|
38399
38593
|
}, []);
|
|
38400
38594
|
const reset = (0, import_react76.useCallback)(() => {
|
|
38401
38595
|
setRunning(false);
|
|
38596
|
+
elapsedRef.current = 0;
|
|
38402
38597
|
setElapsedMs(0);
|
|
38403
38598
|
setCompleted(false);
|
|
38404
38599
|
setLaps([]);
|
|
@@ -40338,6 +40533,8 @@ function RoundRobinScheduler({
|
|
|
40338
40533
|
theme: theme5 = "slate",
|
|
40339
40534
|
darkMode,
|
|
40340
40535
|
minTeams = 3,
|
|
40536
|
+
maxTeams,
|
|
40537
|
+
nameMaxLength,
|
|
40341
40538
|
defaultTeams = [],
|
|
40342
40539
|
resumeRounds,
|
|
40343
40540
|
onTeamsChange,
|
|
@@ -40360,39 +40557,41 @@ function RoundRobinScheduler({
|
|
|
40360
40557
|
(0, import_react79.useEffect)(() => {
|
|
40361
40558
|
onTeamsChange?.(teams);
|
|
40362
40559
|
}, [teams, onTeamsChange]);
|
|
40363
|
-
const
|
|
40364
|
-
|
|
40365
|
-
if (names.length === 0) return;
|
|
40366
|
-
setTeams((prev) => {
|
|
40367
|
-
const existing = new Set(prev.map((t) => t.toLowerCase()));
|
|
40368
|
-
const next = [...prev];
|
|
40369
|
-
for (const name of names) {
|
|
40370
|
-
if (!existing.has(name.toLowerCase())) {
|
|
40371
|
-
existing.add(name.toLowerCase());
|
|
40372
|
-
next.push(name);
|
|
40373
|
-
}
|
|
40374
|
-
}
|
|
40375
|
-
return next;
|
|
40376
|
-
});
|
|
40377
|
-
setInput("");
|
|
40378
|
-
setRounds([]);
|
|
40379
|
-
}, []);
|
|
40380
|
-
const importTeams = (0, import_react79.useCallback)((incoming) => {
|
|
40381
|
-
if (incoming.length === 0) return;
|
|
40382
|
-
setTeams((prev) => {
|
|
40560
|
+
const mergeNames = (0, import_react79.useCallback)(
|
|
40561
|
+
(prev, incoming) => {
|
|
40383
40562
|
const existing = new Set(prev.map((t) => t.toLowerCase()));
|
|
40384
40563
|
const next = [...prev];
|
|
40385
|
-
for (const
|
|
40564
|
+
for (const rawName of incoming) {
|
|
40565
|
+
if (maxTeams != null && next.length >= maxTeams) break;
|
|
40566
|
+
const name = nameMaxLength != null ? rawName.slice(0, nameMaxLength) : rawName;
|
|
40386
40567
|
const key = name.toLowerCase();
|
|
40387
|
-
if (!existing.has(key)) {
|
|
40568
|
+
if (name.length > 0 && !existing.has(key)) {
|
|
40388
40569
|
existing.add(key);
|
|
40389
40570
|
next.push(name);
|
|
40390
40571
|
}
|
|
40391
40572
|
}
|
|
40392
40573
|
return next;
|
|
40393
|
-
}
|
|
40394
|
-
|
|
40395
|
-
|
|
40574
|
+
},
|
|
40575
|
+
[maxTeams, nameMaxLength]
|
|
40576
|
+
);
|
|
40577
|
+
const addTeams = (0, import_react79.useCallback)(
|
|
40578
|
+
(raw) => {
|
|
40579
|
+
const names = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
40580
|
+
if (names.length === 0) return;
|
|
40581
|
+
setTeams((prev) => mergeNames(prev, names));
|
|
40582
|
+
setInput("");
|
|
40583
|
+
setRounds([]);
|
|
40584
|
+
},
|
|
40585
|
+
[mergeNames]
|
|
40586
|
+
);
|
|
40587
|
+
const importTeams = (0, import_react79.useCallback)(
|
|
40588
|
+
(incoming) => {
|
|
40589
|
+
if (incoming.length === 0) return;
|
|
40590
|
+
setTeams((prev) => mergeNames(prev, incoming));
|
|
40591
|
+
setRounds([]);
|
|
40592
|
+
},
|
|
40593
|
+
[mergeNames]
|
|
40594
|
+
);
|
|
40396
40595
|
const handleKeyDown = (0, import_react79.useCallback)(
|
|
40397
40596
|
(e) => {
|
|
40398
40597
|
if (e.key === "Enter") {
|
|
@@ -40881,6 +41080,8 @@ function Scoreboard({
|
|
|
40881
41080
|
onTeamsChange,
|
|
40882
41081
|
onRoundAdvance,
|
|
40883
41082
|
onNewGame,
|
|
41083
|
+
onReset,
|
|
41084
|
+
nameMaxLength,
|
|
40884
41085
|
labels,
|
|
40885
41086
|
ariaLabel = "Scoreboard",
|
|
40886
41087
|
className
|
|
@@ -40955,7 +41156,8 @@ function Scoreboard({
|
|
|
40955
41156
|
setRound(1);
|
|
40956
41157
|
setHistory([]);
|
|
40957
41158
|
setHistoryOpen(false);
|
|
40958
|
-
|
|
41159
|
+
onReset?.();
|
|
41160
|
+
}, [onReset]);
|
|
40959
41161
|
const newGame = (0, import_react80.useCallback)(() => {
|
|
40960
41162
|
setTeams(createDefaultTeams(seedCount, L.teamNamePrefix));
|
|
40961
41163
|
setRound(1);
|
|
@@ -41177,6 +41379,7 @@ function Scoreboard({
|
|
|
41177
41379
|
{
|
|
41178
41380
|
type: "text",
|
|
41179
41381
|
value: team.name,
|
|
41382
|
+
maxLength: nameMaxLength,
|
|
41180
41383
|
onChange: (e) => updateName(index, e.target.value),
|
|
41181
41384
|
style: nameInputStyle,
|
|
41182
41385
|
"aria-label": `Team ${index + 1} name`
|