@digilogiclabs/saas-factory-ui 2.1.0 → 2.3.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/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,
@@ -30359,14 +30361,35 @@ var PIP_POS = {
30359
30361
  [75, 75]
30360
30362
  ]
30361
30363
  };
30362
- var SHOW_ROT = {
30364
+ var D6_FACE_PLACEMENTS = {
30363
30365
  1: { x: 0, y: 0 },
30364
- 2: { x: 0, y: 180 },
30365
- 3: { x: 0, y: -90 },
30366
- 4: { x: 0, y: 90 },
30367
- 5: { x: -90, y: 0 },
30368
- 6: { x: 90, y: 0 }
30366
+ 2: { x: 0, y: 90 },
30367
+ 3: { x: 90, y: 0 },
30368
+ 4: { x: -90, y: 0 },
30369
+ 5: { x: 0, y: -90 },
30370
+ 6: { x: 0, y: 180 }
30369
30371
  };
30372
+ function d6LandingRotation(value) {
30373
+ const p = D6_FACE_PLACEMENTS[value];
30374
+ return {
30375
+ x: Math.abs(p.x) === 180 ? 180 : -p.x || 0,
30376
+ y: Math.abs(p.y) === 180 ? 180 : -p.y || 0
30377
+ };
30378
+ }
30379
+ var POLY_SHAPES = {
30380
+ 4: { points: "50,8 93,86 7,86", textY: 62, fontScale: 0.3 },
30381
+ 8: { points: "50,4 96,50 50,96 4,50", textY: 51, fontScale: 0.33 },
30382
+ 10: { points: "50,4 93,44 50,96 7,44", textY: 47, fontScale: 0.3 },
30383
+ 12: { points: "50,4 95,37 78,93 22,93 5,37", textY: 54, fontScale: 0.33 },
30384
+ 20: {
30385
+ points: "50,3 91,26 91,74 50,97 9,74 9,26",
30386
+ textY: 51,
30387
+ fontScale: 0.33
30388
+ }
30389
+ };
30390
+ function formatDieValue(v, sides) {
30391
+ return sides > 8 && (v === 6 || v === 9) ? `${v}.` : String(v);
30392
+ }
30370
30393
  var MAX_COUNT = 6;
30371
30394
  var TUMBLE_MS = 950;
30372
30395
  var SETTLE_MS = 1100;
@@ -30518,6 +30541,7 @@ function Face({ value, pipColor, pipShadow, pipDiameter }) {
30518
30541
  }
30519
30542
  function DiceRoller({
30520
30543
  count: count2 = 2,
30544
+ sides = 6,
30521
30545
  theme: theme5 = "red",
30522
30546
  darkMode,
30523
30547
  size = 80,
@@ -30546,6 +30570,7 @@ function DiceRoller({
30546
30570
  const dieWrapperRefs = (0, import_react70.useRef)([]);
30547
30571
  const cubeRefs = (0, import_react70.useRef)([]);
30548
30572
  const shadowRefs = (0, import_react70.useRef)([]);
30573
+ const numeralRefs = (0, import_react70.useRef)([]);
30549
30574
  const currentPositionsRef = (0, import_react70.useRef)(null);
30550
30575
  const rollingRef = (0, import_react70.useRef)(false);
30551
30576
  const dieSetters = (0, import_react70.useRef)([]);
@@ -30575,6 +30600,15 @@ function DiceRoller({
30575
30600
  }
30576
30601
  return shadowSetters.current[i];
30577
30602
  };
30603
+ const numeralSetters = (0, import_react70.useRef)([]);
30604
+ const getNumeralSetter = (i) => {
30605
+ if (!numeralSetters.current[i]) {
30606
+ numeralSetters.current[i] = (el) => {
30607
+ numeralRefs.current[i] = el;
30608
+ };
30609
+ }
30610
+ return numeralSetters.current[i];
30611
+ };
30578
30612
  const timersRef = (0, import_react70.useRef)([]);
30579
30613
  const rafHandleRef = (0, import_react70.useRef)(null);
30580
30614
  const arenaRef = (0, import_react70.useRef)(null);
@@ -30627,15 +30661,15 @@ function DiceRoller({
30627
30661
  const pipDiameter = Math.round(effectiveSize * 0.2);
30628
30662
  const faceRadius = Math.round(effectiveSize * 0.07);
30629
30663
  const arenaHeight = arenaHeightProp ?? Math.max(effectiveSize * 3, 200);
30630
- const faceTransforms = (0, import_react70.useMemo)(
30631
- () => [
30632
- `translateZ(${half}px)`,
30633
- `rotateY(180deg) translateZ(${half}px)`,
30634
- `rotateY(-90deg) translateZ(${half}px)`,
30635
- `rotateY(90deg) translateZ(${half}px)`,
30636
- `rotateX(90deg) translateZ(${half}px)`,
30637
- `rotateX(-90deg) translateZ(${half}px)`
30638
- ],
30664
+ const faceTransformFor = (0, import_react70.useCallback)(
30665
+ (value) => {
30666
+ const p = D6_FACE_PLACEMENTS[value];
30667
+ const parts = [];
30668
+ if (p.x !== 0) parts.push(`rotateX(${p.x}deg)`);
30669
+ if (p.y !== 0) parts.push(`rotateY(${p.y}deg)`);
30670
+ parts.push(`translateZ(${half}px)`);
30671
+ return parts.join(" ");
30672
+ },
30639
30673
  [half]
30640
30674
  );
30641
30675
  const defaultPositions = (0, import_react70.useCallback)(
@@ -30657,11 +30691,11 @@ function DiceRoller({
30657
30691
  [half]
30658
30692
  );
30659
30693
  const writeCubeRotation = (0, import_react70.useCallback)(
30660
- (i, rx, ry, rz, withTransition) => {
30694
+ (i, rx, ry, rz, withTransition, tiltZ = 0) => {
30661
30695
  const el = cubeRefs.current[i];
30662
30696
  if (!el) return;
30663
30697
  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)`;
30698
+ el.style.transform = `${tiltZ ? `rotateZ(${tiltZ}deg) ` : ""}rotateX(${rx}deg) rotateY(${ry}deg) rotateZ(${rz}deg)`;
30665
30699
  },
30666
30700
  []
30667
30701
  );
@@ -30691,19 +30725,23 @@ function DiceRoller({
30691
30725
  setResults(null);
30692
30726
  }, [
30693
30727
  clampedCount,
30728
+ sides,
30694
30729
  effectiveSize,
30695
30730
  arena.w,
30696
30731
  arenaHeight,
30697
30732
  defaultPositions,
30698
30733
  half
30699
30734
  ]);
30735
+ (0, import_react70.useEffect)(() => {
30736
+ setHistory([]);
30737
+ }, [sides]);
30700
30738
  const roll = (0, import_react70.useCallback)(() => {
30701
30739
  if (rollingRef.current) return;
30702
30740
  rollingRef.current = true;
30703
30741
  setRolling(true);
30704
30742
  const vals = Array.from(
30705
30743
  { length: clampedCount },
30706
- () => Math.floor(Math.random() * 6) + 1
30744
+ () => Math.floor(Math.random() * sides) + 1
30707
30745
  );
30708
30746
  timersRef.current.forEach((t) => clearTimeout(t));
30709
30747
  timersRef.current = [];
@@ -30718,13 +30756,17 @@ function DiceRoller({
30718
30756
  const starts = currentPositionsRef.current ?? defaultPositions(w, h, clampedCount);
30719
30757
  const frames = simulateDice(w, h, effectiveSize, starts, clampedCount);
30720
30758
  for (let i = 0; i < clampedCount; i++) {
30721
- writeCubeRotation(
30722
- i,
30723
- (Math.random() - 0.5) * 720,
30724
- (Math.random() - 0.5) * 720,
30725
- (Math.random() - 0.5) * 720,
30726
- false
30727
- );
30759
+ if (sides === 6) {
30760
+ writeCubeRotation(
30761
+ i,
30762
+ (Math.random() - 0.5) * 720,
30763
+ (Math.random() - 0.5) * 720,
30764
+ (Math.random() - 0.5) * 720,
30765
+ false
30766
+ );
30767
+ } else {
30768
+ writeCubeRotation(i, 0, 0, (Math.random() - 0.5) * 720, false);
30769
+ }
30728
30770
  const el = dieWrapperRefs.current[i];
30729
30771
  if (el) el.style.transition = "none";
30730
30772
  const shadow = shadowRefs.current[i];
@@ -30757,18 +30799,47 @@ function DiceRoller({
30757
30799
  }, capturedStep * stepMs);
30758
30800
  timersRef.current.push(timer);
30759
30801
  }
30802
+ if (sides !== 6) {
30803
+ const flickerTicks = 8;
30804
+ const flickerStep = Math.max(60, Math.floor(TUMBLE_MS / flickerTicks));
30805
+ for (let f = 1; f <= flickerTicks; f++) {
30806
+ const t = setTimeout(() => {
30807
+ for (let i = 0; i < clampedCount; i++) {
30808
+ const el = numeralRefs.current[i];
30809
+ if (el)
30810
+ el.textContent = formatDieValue(
30811
+ Math.floor(Math.random() * sides) + 1,
30812
+ sides
30813
+ );
30814
+ }
30815
+ }, f * flickerStep);
30816
+ timersRef.current.push(t);
30817
+ }
30818
+ }
30760
30819
  requestAnimationFrame(() => {
30761
30820
  requestAnimationFrame(() => {
30762
30821
  for (let i = 0; i < clampedCount; i++) {
30763
- const tgt = SHOW_ROT[vals[i]];
30764
- const lx = tgt.x + (Math.random() > 0.5 ? 720 : -720);
30765
- const ly = tgt.y;
30766
- const lz = (Math.random() > 0.5 ? 1 : -1) * 360;
30767
- writeCubeRotation(i, lx, ly, lz, true);
30822
+ const tilt = (Math.random() - 0.5) * 18;
30823
+ if (sides === 6) {
30824
+ const tgt = d6LandingRotation(vals[i]);
30825
+ const lx = tgt.x + (Math.random() > 0.5 ? 720 : -720);
30826
+ const ly = tgt.y;
30827
+ const lz = (Math.random() > 0.5 ? 1 : -1) * 360;
30828
+ writeCubeRotation(i, lx, ly, lz, true, tilt);
30829
+ } else {
30830
+ const lz = (Math.random() > 0.5 ? 1 : -1) * 720 + tilt;
30831
+ writeCubeRotation(i, 0, 0, lz, true);
30832
+ }
30768
30833
  }
30769
30834
  });
30770
30835
  });
30771
30836
  const settleTimer = setTimeout(() => {
30837
+ if (sides !== 6) {
30838
+ for (let i = 0; i < clampedCount; i++) {
30839
+ const el = numeralRefs.current[i];
30840
+ if (el) el.textContent = formatDieValue(vals[i], sides);
30841
+ }
30842
+ }
30772
30843
  setResults(vals);
30773
30844
  setRolling(false);
30774
30845
  rollingRef.current = false;
@@ -30781,6 +30852,7 @@ function DiceRoller({
30781
30852
  timersRef.current.push(settleTimer);
30782
30853
  }, [
30783
30854
  clampedCount,
30855
+ sides,
30784
30856
  defaultPositions,
30785
30857
  effectiveSize,
30786
30858
  half,
@@ -30826,7 +30898,7 @@ function DiceRoller({
30826
30898
  {
30827
30899
  ref: arenaRef,
30828
30900
  role: "img",
30829
- "aria-label": ariaLabel ?? `${clampedCount} dice`,
30901
+ "aria-label": ariaLabel ?? `${clampedCount} ${sides === 6 ? "dice" : `${sides}-sided dice`}`,
30830
30902
  "aria-live": "polite",
30831
30903
  style: {
30832
30904
  position: "relative",
@@ -30889,7 +30961,7 @@ function DiceRoller({
30889
30961
  transformStyle: "preserve-3d",
30890
30962
  willChange: "transform"
30891
30963
  },
30892
- children: [1, 2, 3, 4, 5, 6].map((val, fi) => /* @__PURE__ */ (0, import_jsx_runtime113.jsx)(
30964
+ children: sides === 6 ? [1, 2, 3, 4, 5, 6].map((val) => /* @__PURE__ */ (0, import_jsx_runtime113.jsx)(
30893
30965
  "div",
30894
30966
  {
30895
30967
  style: {
@@ -30899,7 +30971,7 @@ function DiceRoller({
30899
30971
  background: colors.face,
30900
30972
  border: `1px solid ${colors.faceBorder}`,
30901
30973
  borderRadius: faceRadius,
30902
- transform: faceTransforms[fi],
30974
+ transform: faceTransformFor(val),
30903
30975
  transition: "background 0.3s, border-color 0.3s"
30904
30976
  },
30905
30977
  children: /* @__PURE__ */ (0, import_jsx_runtime113.jsx)(
@@ -30913,7 +30985,64 @@ function DiceRoller({
30913
30985
  )
30914
30986
  },
30915
30987
  val
30916
- ))
30988
+ )) : (() => {
30989
+ const shape = POLY_SHAPES[sides];
30990
+ const shown = results?.[d] ?? sides;
30991
+ return /* @__PURE__ */ (0, import_jsx_runtime113.jsxs)(
30992
+ "svg",
30993
+ {
30994
+ viewBox: "0 0 100 100",
30995
+ width: "100%",
30996
+ height: "100%",
30997
+ style: { display: "block", overflow: "visible" },
30998
+ "aria-hidden": "true",
30999
+ children: [
31000
+ /* @__PURE__ */ (0, import_jsx_runtime113.jsx)(
31001
+ "polygon",
31002
+ {
31003
+ points: shape.points,
31004
+ fill: colors.face,
31005
+ stroke: colors.faceBorder,
31006
+ strokeWidth: 2.5,
31007
+ strokeLinejoin: "round",
31008
+ style: { transition: "fill 0.3s, stroke 0.3s" }
31009
+ }
31010
+ ),
31011
+ /* @__PURE__ */ (0, import_jsx_runtime113.jsx)(
31012
+ "polygon",
31013
+ {
31014
+ points: shape.points,
31015
+ fill: "none",
31016
+ stroke: colors.faceBorder,
31017
+ strokeWidth: 1.5,
31018
+ strokeLinejoin: "round",
31019
+ opacity: 0.55,
31020
+ transform: "translate(50 50) scale(0.76) translate(-50 -50)"
31021
+ }
31022
+ ),
31023
+ /* @__PURE__ */ (0, import_jsx_runtime113.jsx)(
31024
+ "text",
31025
+ {
31026
+ ref: getNumeralSetter(d),
31027
+ x: 50,
31028
+ y: shape.textY,
31029
+ textAnchor: "middle",
31030
+ dominantBaseline: "central",
31031
+ fontSize: 100 * shape.fontScale,
31032
+ fontWeight: 800,
31033
+ fill: colors.pip,
31034
+ style: {
31035
+ fontVariantNumeric: "tabular-nums",
31036
+ transition: "fill 0.3s",
31037
+ userSelect: "none"
31038
+ },
31039
+ children: formatDieValue(shown, sides)
31040
+ }
31041
+ )
31042
+ ]
31043
+ }
31044
+ );
31045
+ })()
30917
31046
  }
30918
31047
  )
30919
31048
  ] }, d);
@@ -40298,6 +40427,27 @@ function generateSchedule(teams, roundLabel) {
40298
40427
  }
40299
40428
  return rounds;
40300
40429
  }
40430
+ function hydrateResumeRounds(resume, teams) {
40431
+ if (!resume?.length || teams.length < 2) return [];
40432
+ const expected = teams.length % 2 === 0 ? teams.length - 1 : teams.length;
40433
+ if (resume.length !== expected) return [];
40434
+ const roster = new Set(teams.map((t) => t.toLowerCase()));
40435
+ for (const round of resume) {
40436
+ for (const m of round.matchups) {
40437
+ for (const side of [m.home, m.away]) {
40438
+ if (side !== BYE_TOKEN && !roster.has(side.toLowerCase())) return [];
40439
+ }
40440
+ }
40441
+ }
40442
+ return resume.map((round) => ({
40443
+ label: round.label,
40444
+ matchups: round.matchups.map((m) => ({
40445
+ home: m.home,
40446
+ away: m.away,
40447
+ isBye: m.home === BYE_TOKEN || m.away === BYE_TOKEN
40448
+ }))
40449
+ }));
40450
+ }
40301
40451
  var DEFAULT_LABELS4 = {
40302
40452
  inputPlaceholder: "Team or player name (comma-separated for batch)",
40303
40453
  addButton: "Add",
@@ -40318,6 +40468,7 @@ function RoundRobinScheduler({
40318
40468
  darkMode,
40319
40469
  minTeams = 3,
40320
40470
  defaultTeams = [],
40471
+ resumeRounds,
40321
40472
  onTeamsChange,
40322
40473
  onGenerate,
40323
40474
  labels,
@@ -40331,7 +40482,9 @@ function RoundRobinScheduler({
40331
40482
  const L = { ...DEFAULT_LABELS4, ...labels };
40332
40483
  const [teams, setTeams] = (0, import_react79.useState)(defaultTeams);
40333
40484
  const [input, setInput] = (0, import_react79.useState)("");
40334
- const [rounds, setRounds] = (0, import_react79.useState)([]);
40485
+ const [rounds, setRounds] = (0, import_react79.useState)(
40486
+ () => hydrateResumeRounds(resumeRounds, defaultTeams)
40487
+ );
40335
40488
  const inputRef = (0, import_react79.useRef)(null);
40336
40489
  (0, import_react79.useEffect)(() => {
40337
40490
  onTeamsChange?.(teams);
@@ -40852,6 +41005,8 @@ function Scoreboard({
40852
41005
  maxTeams = 8,
40853
41006
  scoreButtons = [-1, 1, 5, 10],
40854
41007
  defaultTeams,
41008
+ initialRound = 1,
41009
+ initialHistory,
40855
41010
  onTeamsChange,
40856
41011
  onRoundAdvance,
40857
41012
  onNewGame,
@@ -40867,8 +41022,12 @@ function Scoreboard({
40867
41022
  const [teams, setTeams] = (0, import_react80.useState)(
40868
41023
  () => defaultTeams && defaultTeams.length >= minTeams ? defaultTeams.slice(0, maxTeams) : createDefaultTeams(seedCount, L.teamNamePrefix)
40869
41024
  );
40870
- const [round, setRound] = (0, import_react80.useState)(1);
40871
- const [history, setHistory] = (0, import_react80.useState)([]);
41025
+ const [round, setRound] = (0, import_react80.useState)(
41026
+ () => Number.isFinite(initialRound) ? Math.max(1, Math.floor(initialRound)) : 1
41027
+ );
41028
+ const [history, setHistory] = (0, import_react80.useState)(
41029
+ () => initialHistory && initialHistory.length > 0 ? initialHistory.slice() : []
41030
+ );
40872
41031
  const [historyOpen, setHistoryOpen] = (0, import_react80.useState)(false);
40873
41032
  const rootRef = (0, import_react80.useRef)(null);
40874
41033
  const [containerWidth, setContainerWidth] = (0, import_react80.useState)(0);