@digilogiclabs/saas-factory-ui 1.33.2 → 1.34.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 CHANGED
@@ -4987,9 +4987,9 @@ interface RandomPlayerPickerProps {
4987
4987
  * Visual intensity preset. Default "normal".
4988
4988
  * - "subtle" / "normal": current behavior — shorter spins, standard
4989
4989
  * ease-out curve.
4990
- * - "dramatic": physics-like spin — 1.5× duration, ease-out-quart
4990
+ * - "dramatic": physics-like spin — 2.5× duration, inertia-style
4991
4991
  * curve so motion continues through the full duration with a slow
4992
- * winding-down tail, +4 extra revolutions, plus more
4992
+ * winding-down tail, +12 extra revolutions, plus more
4993
4993
  * particles/glow/shake on reveal.
4994
4994
  */
4995
4995
  intensity?: RandomPlayerIntensity;
@@ -5074,6 +5074,40 @@ interface HeroBannerCta {
5074
5074
  interface HeroBannerSlide {
5075
5075
  /** Background image URL. */
5076
5076
  image?: string;
5077
+ /**
5078
+ * Render this slide's image as a real `<img>` (with `fetchpriority="high"`,
5079
+ * `loading="eager"`, `decoding="async"`) instead of a CSS `background-image`.
5080
+ *
5081
+ * Set this on the first slide to make it a reliable LCP target. CSS
5082
+ * backgrounds are valid LCP candidates but the browser discovers them
5083
+ * later (after CSS parse), and `<link rel="preload">` connects to real
5084
+ * `<img>` elements far more reliably than to `background-image` URLs.
5085
+ *
5086
+ * Only the slide with `priority: true` swaps to `<img>`; all other slides
5087
+ * stay on CSS `background-image` (cheaper to cycle). The `imageFilter`,
5088
+ * `bgColor`, overlays, and accent layers all still apply.
5089
+ */
5090
+ priority?: boolean;
5091
+ /**
5092
+ * Optional alt text for the priority `<img>`. Defaults to empty (decorative)
5093
+ * — appropriate when the slide already carries semantic text via headline /
5094
+ * kicker.
5095
+ */
5096
+ imageAlt?: string;
5097
+ /**
5098
+ * Optional intrinsic width of the priority image (in pixels). When set with
5099
+ * `imageHeight`, emitted as the `<img>` `width` HTML attribute. Lets the
5100
+ * browser compute aspect ratio for the preload connection without waiting
5101
+ * for image headers. Has no layout effect when the parent already sizes
5102
+ * the image via absolute positioning (the default). Ignored unless
5103
+ * `priority` is true.
5104
+ */
5105
+ imageWidth?: number;
5106
+ /**
5107
+ * Optional intrinsic height of the priority image (in pixels). Companion
5108
+ * to `imageWidth`. See `imageWidth` for usage notes.
5109
+ */
5110
+ imageHeight?: number;
5077
5111
  /** CSS gradient string (e.g. `"linear-gradient(135deg,#0f172a,#0f4c75)"`). */
5078
5112
  gradient?: string;
5079
5113
  /** Fallback solid background color. */
package/dist/index.d.ts CHANGED
@@ -4987,9 +4987,9 @@ interface RandomPlayerPickerProps {
4987
4987
  * Visual intensity preset. Default "normal".
4988
4988
  * - "subtle" / "normal": current behavior — shorter spins, standard
4989
4989
  * ease-out curve.
4990
- * - "dramatic": physics-like spin — 1.5× duration, ease-out-quart
4990
+ * - "dramatic": physics-like spin — 2.5× duration, inertia-style
4991
4991
  * curve so motion continues through the full duration with a slow
4992
- * winding-down tail, +4 extra revolutions, plus more
4992
+ * winding-down tail, +12 extra revolutions, plus more
4993
4993
  * particles/glow/shake on reveal.
4994
4994
  */
4995
4995
  intensity?: RandomPlayerIntensity;
@@ -5074,6 +5074,40 @@ interface HeroBannerCta {
5074
5074
  interface HeroBannerSlide {
5075
5075
  /** Background image URL. */
5076
5076
  image?: string;
5077
+ /**
5078
+ * Render this slide's image as a real `<img>` (with `fetchpriority="high"`,
5079
+ * `loading="eager"`, `decoding="async"`) instead of a CSS `background-image`.
5080
+ *
5081
+ * Set this on the first slide to make it a reliable LCP target. CSS
5082
+ * backgrounds are valid LCP candidates but the browser discovers them
5083
+ * later (after CSS parse), and `<link rel="preload">` connects to real
5084
+ * `<img>` elements far more reliably than to `background-image` URLs.
5085
+ *
5086
+ * Only the slide with `priority: true` swaps to `<img>`; all other slides
5087
+ * stay on CSS `background-image` (cheaper to cycle). The `imageFilter`,
5088
+ * `bgColor`, overlays, and accent layers all still apply.
5089
+ */
5090
+ priority?: boolean;
5091
+ /**
5092
+ * Optional alt text for the priority `<img>`. Defaults to empty (decorative)
5093
+ * — appropriate when the slide already carries semantic text via headline /
5094
+ * kicker.
5095
+ */
5096
+ imageAlt?: string;
5097
+ /**
5098
+ * Optional intrinsic width of the priority image (in pixels). When set with
5099
+ * `imageHeight`, emitted as the `<img>` `width` HTML attribute. Lets the
5100
+ * browser compute aspect ratio for the preload connection without waiting
5101
+ * for image headers. Has no layout effect when the parent already sizes
5102
+ * the image via absolute positioning (the default). Ignored unless
5103
+ * `priority` is true.
5104
+ */
5105
+ imageWidth?: number;
5106
+ /**
5107
+ * Optional intrinsic height of the priority image (in pixels). Companion
5108
+ * to `imageWidth`. See `imageWidth` for usage notes.
5109
+ */
5110
+ imageHeight?: number;
5077
5111
  /** CSS gradient string (e.g. `"linear-gradient(135deg,#0f172a,#0f4c75)"`). */
5078
5112
  gradient?: string;
5079
5113
  /** Fallback solid background color. */
package/dist/index.js CHANGED
@@ -10,8 +10,8 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
10
10
  var __esm = (fn, res) => function __init() {
11
11
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
12
12
  };
13
- var __commonJS = (cb, mod) => function __require() {
14
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
13
+ var __commonJS = (cb, mod2) => function __require() {
14
+ return mod2 || (0, cb[__getOwnPropNames(cb)[0]])((mod2 = { exports: {} }).exports, mod2), mod2.exports;
15
15
  };
16
16
  var __export = (target, all) => {
17
17
  for (var name in all)
@@ -25,15 +25,15 @@ var __copyProps = (to, from, except, desc) => {
25
25
  }
26
26
  return to;
27
27
  };
28
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
28
+ var __toESM = (mod2, isNodeMode, target) => (target = mod2 != null ? __create(__getProtoOf(mod2)) : {}, __copyProps(
29
29
  // If the importer is in node compatibility mode or this is not an ESM
30
30
  // file that has been converted to a CommonJS file using a Babel-
31
31
  // compatible transform (i.e. "__esModule" has not been set), then set
32
32
  // "default" to the CommonJS "module.exports" for node compatibility.
33
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
34
- mod
33
+ isNodeMode || !mod2 || !mod2.__esModule ? __defProp(target, "default", { value: mod2, enumerable: true }) : target,
34
+ mod2
35
35
  ));
36
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
36
+ var __toCommonJS = (mod2) => __copyProps(__defProp({}, "__esModule", { value: true }), mod2);
37
37
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
38
38
 
39
39
  // src/config/constants.ts
@@ -33326,8 +33326,8 @@ var WEDGE_PALETTE = [
33326
33326
  "#14b8a6",
33327
33327
  "#f97316"
33328
33328
  ];
33329
- var DEFAULT_EASING = "cubic-bezier(0.12, 0.78, 0.22, 1)";
33330
- var DRAMATIC_EASING = "cubic-bezier(0.22, 1, 0.36, 1)";
33329
+ var DEFAULT_EASING = "cubic-bezier(0.25, 0.46, 0.45, 0.94)";
33330
+ var DRAMATIC_EASING = "cubic-bezier(0.39, 0.575, 0.565, 1)";
33331
33331
  var INTENSITY_CONFIG = {
33332
33332
  subtle: {
33333
33333
  particles: 14,
@@ -33352,9 +33352,9 @@ var INTENSITY_CONFIG = {
33352
33352
  shakePeak: 10,
33353
33353
  glowMult: 1.45,
33354
33354
  pulseStroke: 8,
33355
- durationMult: 1.5,
33355
+ durationMult: 2.5,
33356
33356
  easing: DRAMATIC_EASING,
33357
- revsBoost: 4
33357
+ revsBoost: 12
33358
33358
  }
33359
33359
  };
33360
33360
  function polar(cx, cy, r, a) {
@@ -33380,6 +33380,20 @@ function weightedIndex(pool) {
33380
33380
  }
33381
33381
  return pool.length - 1;
33382
33382
  }
33383
+ function mod(value, divisor) {
33384
+ return (value % divisor + divisor) % divisor;
33385
+ }
33386
+ function wheelIndexAtPointer(rotation, count2) {
33387
+ if (count2 < 1) return -1;
33388
+ const wedgeDeg = 360 / count2;
33389
+ const localDegAtPointer = mod(-rotation, 360);
33390
+ return Math.min(count2 - 1, Math.floor(localDegAtPointer / wedgeDeg));
33391
+ }
33392
+ function reelIndexAtCenter(reelY, centerOffsetPx, rowHeight, count2) {
33393
+ if (count2 < 1) return -1;
33394
+ const centerRow = Math.round((reelY + centerOffsetPx) / rowHeight);
33395
+ return mod(centerRow, count2);
33396
+ }
33383
33397
  function ReelStage({
33384
33398
  pool,
33385
33399
  winner,
@@ -33807,7 +33821,9 @@ function RandomPlayerPicker({
33807
33821
  const winnerIdx = weightedIndex(pool);
33808
33822
  const minRev = minRevolutions + intensityCfg.revsBoost;
33809
33823
  const maxRev = maxRevolutions + intensityCfg.revsBoost;
33810
- const revolutions = minRev + Math.random() * (maxRev - minRev);
33824
+ const minFullRevs = Math.ceil(minRev);
33825
+ const maxFullRevs = Math.max(minFullRevs, Math.floor(maxRev));
33826
+ const revolutions = minFullRevs + Math.floor(Math.random() * (maxFullRevs - minFullRevs + 1));
33811
33827
  const wedgeMidRad = winnerIdx * anglePerWedge + anglePerWedge / 2;
33812
33828
  const wedgeMidDeg = wedgeMidRad * 180 / Math.PI;
33813
33829
  const wheelWobble = (Math.random() - 0.5) * (anglePerWedge * 180 / Math.PI) * 0.6;
@@ -33821,10 +33837,12 @@ function RandomPlayerPicker({
33821
33837
  const currentCenterRow = Math.round(
33822
33838
  (reelY + centerOffsetPx) / reelRowHeight
33823
33839
  );
33824
- const currentCenterMod = (currentCenterRow % n + n) % n;
33840
+ const currentCenterMod = mod(currentCenterRow, n);
33825
33841
  const targetRowOffset = (winnerIdx - currentCenterMod + n) % n;
33826
33842
  const reelRevRows = Math.round(revolutions * n);
33827
33843
  const finalReelY = reelY + (reelRevRows + targetRowOffset) * reelRowHeight;
33844
+ const settledWinnerIdx = mode === "reel" ? reelIndexAtCenter(finalReelY, centerOffsetPx, reelRowHeight, n) : wheelIndexAtPointer(finalRotation, n);
33845
+ const finalWinnerIdx = settledWinnerIdx >= 0 && settledWinnerIdx < pool.length ? settledWinnerIdx : winnerIdx;
33828
33846
  const effectiveDuration = reducedMotion ? 250 : Math.round(spinDuration * intensityCfg.durationMult);
33829
33847
  setSpinning(true);
33830
33848
  setWinner(null);
@@ -33869,7 +33887,7 @@ function RandomPlayerPicker({
33869
33887
  schedule();
33870
33888
  }
33871
33889
  winnerTimer.current = setTimeout(() => {
33872
- const picked = pool[winnerIdx];
33890
+ const picked = pool[finalWinnerIdx];
33873
33891
  if (!picked) {
33874
33892
  setSpinning(false);
33875
33893
  return;
@@ -33916,7 +33934,7 @@ function RandomPlayerPicker({
33916
33934
  }
33917
33935
  if (removeWinners) {
33918
33936
  popTimer.current = setTimeout(() => {
33919
- setPool((p) => p.filter((_, i) => i !== winnerIdx));
33937
+ setPool((p) => p.filter((_, i) => i !== finalWinnerIdx));
33920
33938
  setWinner(null);
33921
33939
  setRotation(0);
33922
33940
  setReelY(0);
@@ -35061,23 +35079,46 @@ function DynamicHeroBanner({
35061
35079
  const filter = IMAGE_FILTERS[slide.imageFilter ?? imageFilter] ?? IMAGE_FILTERS.cinematic;
35062
35080
  const slideOverlayOpacity = slide.overlayOpacity ?? overlayOpacity;
35063
35081
  const slideAlign = slide.textAlign ?? textAlign;
35082
+ const usePriorityImg = slide.priority === true && !!slide.image;
35083
+ const bgDivStyle = usePriorityImg ? {
35084
+ position: "absolute",
35085
+ inset: 0,
35086
+ backgroundColor: slide.bgColor ?? "#0c0e14",
35087
+ transform: isCurrent ? "scale(1)" : "scale(1.06)",
35088
+ transition: reducedMotion ? "none" : `transform ${autoPlayInterval}ms linear`
35089
+ } : {
35090
+ position: "absolute",
35091
+ inset: 0,
35092
+ backgroundImage: slide.image ? `url(${slide.image})` : slide.gradient ?? "none",
35093
+ backgroundColor: slide.bgColor ?? "#0c0e14",
35094
+ backgroundSize: "cover",
35095
+ backgroundPosition: "center",
35096
+ filter,
35097
+ transform: isCurrent ? "scale(1)" : "scale(1.06)",
35098
+ transition: reducedMotion ? "none" : `transform ${autoPlayInterval}ms linear`
35099
+ };
35064
35100
  return /* @__PURE__ */ (0, import_jsx_runtime118.jsxs)("div", { style: bgStyle(i), "aria-hidden": !isCurrent, children: [
35065
- /* @__PURE__ */ (0, import_jsx_runtime118.jsx)(
35066
- "div",
35101
+ /* @__PURE__ */ (0, import_jsx_runtime118.jsx)("div", { style: bgDivStyle, children: usePriorityImg && /* @__PURE__ */ (0, import_jsx_runtime118.jsx)(
35102
+ "img",
35067
35103
  {
35104
+ src: slide.image,
35105
+ alt: slide.imageAlt ?? "",
35106
+ width: slide.imageWidth && slide.imageHeight ? slide.imageWidth : void 0,
35107
+ height: slide.imageWidth && slide.imageHeight ? slide.imageHeight : void 0,
35108
+ fetchPriority: "high",
35109
+ loading: "eager",
35110
+ decoding: "async",
35068
35111
  style: {
35069
35112
  position: "absolute",
35070
35113
  inset: 0,
35071
- backgroundImage: slide.image ? `url(${slide.image})` : slide.gradient ?? "none",
35072
- backgroundColor: slide.bgColor ?? "#0c0e14",
35073
- backgroundSize: "cover",
35074
- backgroundPosition: "center",
35075
- filter,
35076
- transform: isCurrent ? "scale(1)" : "scale(1.06)",
35077
- transition: reducedMotion ? "none" : `transform ${autoPlayInterval}ms linear`
35114
+ width: "100%",
35115
+ height: "100%",
35116
+ objectFit: "cover",
35117
+ objectPosition: "center",
35118
+ filter
35078
35119
  }
35079
35120
  }
35080
- ),
35121
+ ) }),
35081
35122
  /* @__PURE__ */ (0, import_jsx_runtime118.jsx)(
35082
35123
  "div",
35083
35124
  {