@zencemarketing/spin-scratch-sdk 0.1.0-alpha.1 → 0.1.0-alpha.2

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/react.cjs.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * SpinWheel SDK v0.1.0-alpha.1
2
+ * SpinWheel SDK v0.1.0-alpha.2
3
3
  * (c) 2026 – MIT License
4
4
  * A dynamic, configurable Spin & Win wheel and Scratch Card SDK for Vanilla JS & React.
5
5
  */
@@ -30,6 +30,63 @@ var React = require('react');
30
30
  */
31
31
  function buildCSS(config, hexToRGBA) {
32
32
  const t = config.theme || {};
33
+ const isHexColor = value => typeof value === 'string' && /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(value.trim());
34
+ const parseRgbLike = value => {
35
+ if (typeof value !== 'string') return null;
36
+ const m = value.trim().match(/^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})(?:\s*,\s*(\d*\.?\d+)\s*)?\)$/i);
37
+ if (!m) return null;
38
+ const r = Math.min(255, Math.max(0, Number(m[1])));
39
+ const g = Math.min(255, Math.max(0, Number(m[2])));
40
+ const b = Math.min(255, Math.max(0, Number(m[3])));
41
+ if ([r, g, b].some(n => Number.isNaN(n))) return null;
42
+ return {
43
+ r,
44
+ g,
45
+ b
46
+ };
47
+ };
48
+ const parseHex = value => {
49
+ if (!isHexColor(value)) return null;
50
+ const hex = value.trim().slice(1);
51
+ if (hex.length === 3) {
52
+ const r = parseInt(hex[0] + hex[0], 16);
53
+ const g = parseInt(hex[1] + hex[1], 16);
54
+ const b = parseInt(hex[2] + hex[2], 16);
55
+ return {
56
+ r,
57
+ g,
58
+ b
59
+ };
60
+ }
61
+ const r = parseInt(hex.slice(0, 2), 16);
62
+ const g = parseInt(hex.slice(2, 4), 16);
63
+ const b = parseInt(hex.slice(4, 6), 16);
64
+ return {
65
+ r,
66
+ g,
67
+ b
68
+ };
69
+ };
70
+ const toRGBA = (value, alpha) => {
71
+ const rgb = parseHex(value) || parseRgbLike(value);
72
+ if (!rgb) return `rgba(0,0,0,${alpha})`;
73
+ return `rgba(${rgb.r},${rgb.g},${rgb.b},${alpha})`;
74
+ };
75
+ const darkenColor = (value, amount = 0.22) => {
76
+ const rgb = parseHex(value) || parseRgbLike(value);
77
+ if (!rgb) return null;
78
+ const factor = 1 - Math.min(0.9, Math.max(0, amount));
79
+ const r = Math.round(rgb.r * factor);
80
+ const g = Math.round(rgb.g * factor);
81
+ const b = Math.round(rgb.b * factor);
82
+ return `rgb(${r},${g},${b})`;
83
+ };
84
+ const readableTextOn = bg => {
85
+ const rgb = parseHex(bg) || parseRgbLike(bg);
86
+ if (!rgb) return '#1a1a1f';
87
+ const luma = (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b) / 255;
88
+ return luma < 0.55 ? '#ffffff' : '#1a1a1f';
89
+ };
33
90
 
34
91
  // Extract theme colors with fallbacks
35
92
  const gold = t.gold || '#e8c547';
@@ -39,11 +96,16 @@ function buildCSS(config, hexToRGBA) {
39
96
  const textMuted = t.textMuted || '#9ca3af';
40
97
 
41
98
  // Configurable colors
42
- const titleColor = config.titleColor || goldLight;
99
+ const hasCustomTitleColor = config.titleColor !== null && config.titleColor !== undefined;
100
+ const titleSpinColor = hasCustomTitleColor ? config.titleColor : goldLight;
101
+ const titleWinColor = hasCustomTitleColor ? config.titleColor : goldDark;
43
102
  const subtitleColor = config.subtitleColor || textMuted;
44
103
  const ringColor = config.ringColor || gold;
45
104
  const pointerColor = config.pointerColor || gold;
46
- const buttonBaseColor = config.buttonColor || gold;
105
+ const hasCustomButtonColor = config.buttonColor !== null && config.buttonColor !== undefined;
106
+ const buttonBaseColor = hasCustomButtonColor ? config.buttonColor : gold;
107
+ const buttonTextColor = hasCustomButtonColor ? readableTextOn(buttonBaseColor) : '#1a1a1f';
108
+ const buttonEdgeColor = hasCustomButtonColor ? darkenColor(buttonBaseColor) || 'rgba(0,0,0,0.25)' : goldDark;
47
109
  const redeemBtnTop = config.winCardRedeemButtonColorTop || '#15803d';
48
110
  const redeemBtnBottom = config.winCardRedeemButtonColorBottom || '#166534';
49
111
  const redeemBtnText = config.winCardRedeemButtonTextColor || '#ffffff';
@@ -57,7 +119,7 @@ function buildCSS(config, hexToRGBA) {
57
119
  // Computed values
58
120
  hexToRGBA(gold, 0.4);
59
121
  const ringGlow = hexToRGBA(ringColor, 0.4);
60
- const buttonGlow = hexToRGBA(buttonBaseColor, 0.4);
122
+ const buttonGlow = toRGBA(buttonBaseColor, 0.4);
61
123
 
62
124
  // Background styles
63
125
  const bgStyle = config.backgroundColor ? `background-color:${config.backgroundColor};` : '';
@@ -68,9 +130,10 @@ function buildCSS(config, hexToRGBA) {
68
130
  const ringAnimationCSS = ringAnimation ? `animation:sw-ring-pulse 3s ease-in-out infinite;` : '';
69
131
 
70
132
  // Button shadow and animation
71
- const buttonBoxShadow = buttonShadow ? `box-shadow:0 4px 0 ${goldDark}, 0 6px 20px ${buttonGlow}, inset 0 1px 0 rgba(255,255,255,.3);` : `box-shadow:0 4px 0 ${goldDark}, inset 0 1px 0 rgba(255,255,255,.3);`;
72
- const buttonHoverStyle = buttonAnimation ? `.sw-spin-btn:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 6px 0 ${goldDark},0 8px 28px ${buttonGlow},inset 0 1px 0 rgba(255,255,255,.3)}` : `.sw-spin-btn:hover:not(:disabled){filter:brightness(1.05)}`;
73
- const buttonActiveStyle = buttonAnimation ? `.sw-spin-btn:active:not(:disabled){transform:translateY(2px);box-shadow:0 2px 0 ${goldDark},0 4px 15px ${buttonGlow},inset 0 1px 0 rgba(255,255,255,.2)}` : `.sw-spin-btn:active:not(:disabled){transform:translateY(1px)}`;
133
+ const buttonBoxShadow = buttonShadow ? `box-shadow:0 4px 0 ${buttonEdgeColor}, 0 6px 20px ${buttonGlow}, inset 0 1px 0 rgba(255,255,255,.3);` : `box-shadow:inset 0 1px 0 rgba(255,255,255,.25);`;
134
+ const buttonHoverStyle = buttonAnimation ? buttonShadow ? `.sw-spin-btn:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 6px 0 ${buttonEdgeColor},0 8px 28px ${buttonGlow},inset 0 1px 0 rgba(255,255,255,.3)}` : `.sw-spin-btn:hover:not(:disabled){transform:translateY(-2px);filter:brightness(1.05)}` : `.sw-spin-btn:hover:not(:disabled){filter:brightness(1.05)}`;
135
+ const buttonActiveStyle = buttonAnimation ? buttonShadow ? `.sw-spin-btn:active:not(:disabled){transform:translateY(2px);box-shadow:0 2px 0 ${buttonEdgeColor},0 4px 15px ${buttonGlow},inset 0 1px 0 rgba(255,255,255,.2)}` : `.sw-spin-btn:active:not(:disabled){transform:translateY(1px);filter:brightness(0.98)}` : `.sw-spin-btn:active:not(:disabled){transform:translateY(1px)}`;
136
+ const buttonBackground = hasCustomButtonColor ? `background:${buttonBaseColor};` : `background:linear-gradient(180deg,${goldLight},${goldDark});`;
74
137
  return `
75
138
  /* === SpinWheel SDK v2.1 === */
76
139
  @import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700;800&family=Outfit:wght@300;500;600;700&display=swap');
@@ -91,10 +154,10 @@ function buildCSS(config, hexToRGBA) {
91
154
  font-size:clamp(2rem,6vw,3.5rem); font-weight:800; letter-spacing:.02em;
92
155
  }
93
156
  .sw-title .sw-spin-text {
94
- color:${titleColor};
95
- text-shadow:0 0 30px ${hexToRGBA(titleColor, 0.4)}, 0 0 60px ${hexToRGBA(titleColor, 0.2)};
157
+ color:${titleSpinColor};
158
+ text-shadow:0 0 30px ${toRGBA(titleSpinColor, 0.4)}, 0 0 60px ${toRGBA(titleSpinColor, 0.2)};
96
159
  }
97
- .sw-title .sw-win-text { color:${goldDark}; }
160
+ .sw-title .sw-win-text { color:${titleWinColor}; }
98
161
  .sw-subtitle { margin-top:.5rem; color:${subtitleColor}; font-size:1rem; font-weight:300; }
99
162
 
100
163
  /* ── wheel ── */
@@ -176,7 +239,7 @@ ${ringAnimation ? `
176
239
  .sw-spin-btn {
177
240
  font-family:'Outfit',sans-serif; font-size:1.25rem; font-weight:700;
178
241
  letter-spacing:.15em; padding:1rem 3rem; border:none; border-radius:12px;
179
- background:linear-gradient(180deg,${goldLight},${goldDark}); color:#1a1a1f;
242
+ ${buttonBackground} color:${buttonTextColor};
180
243
  cursor:pointer;
181
244
  ${buttonBoxShadow}
182
245
  transition:transform .15s ease, box-shadow .2s ease, filter .2s ease;
@@ -1428,7 +1491,7 @@ class SpinWheel {
1428
1491
  static Instance = SpinWheelInstance;
1429
1492
 
1430
1493
  /** Current SDK version – kept in sync with package.json via build banner */
1431
- static VERSION = '0.1.0-alpha.1';
1494
+ static VERSION = '0.1.0-alpha.2';
1432
1495
 
1433
1496
  /** Export defaults for reference */
1434
1497
  static DEFAULTS = DEFAULTS$1;
@@ -1657,7 +1720,8 @@ function buildScratchCSS(config, hexToRGBA) {
1657
1720
 
1658
1721
  // Background configs
1659
1722
  const cardBg = config.cardBackground || `linear-gradient(180deg, #faf8fc 0%, #f0ebf5 100%)`;
1660
- const scratchZoneBg = config.scratchZoneBackground || `linear-gradient(145deg, ${purpleDark} 0%, ${purpleMid} 50%, #5a3a7a 100%)`;
1723
+ const hasCustomScratchZoneBg = config.scratchZoneBackground !== null && config.scratchZoneBackground !== undefined;
1724
+ const scratchZoneBg = hasCustomScratchZoneBg ? config.scratchZoneBackground : `linear-gradient(145deg, ${purpleDark} 0%, ${purpleMid} 50%, #5a3a7a 100%)`;
1661
1725
  const modalBtnBg = config.modalButtonColor || `linear-gradient(145deg, ${purpleMid} 0%, ${purpleDark} 100%)`;
1662
1726
 
1663
1727
  // Computed values
@@ -1767,9 +1831,7 @@ function buildScratchCSS(config, hexToRGBA) {
1767
1831
  align-items:center;
1768
1832
  justify-content:center;
1769
1833
  padding:20px;
1770
- background:
1771
- radial-gradient(circle at 30% 30%, rgba(255,255,255,0.08) 0%, transparent 45%),
1772
- linear-gradient(145deg, ${purpleDark} 0%, ${purpleMid} 100%);
1834
+ background:${hasCustomScratchZoneBg ? scratchZoneBg : `radial-gradient(circle at 30% 30%, rgba(255,255,255,0.08) 0%, transparent 45%), linear-gradient(145deg, ${purpleDark} 0%, ${purpleMid} 100%)`};
1773
1835
  }
1774
1836
 
1775
1837
  .sc-prize-content::before {
@@ -3394,7 +3456,7 @@ const ScratchCard = {
3394
3456
  /** The underlying class – useful for `instanceof` checks or subclassing. */
3395
3457
  Instance: ScratchCardInstance,
3396
3458
  /** Current SDK version */
3397
- VERSION: '0.1.0-alpha.1',
3459
+ VERSION: '0.1.0-alpha.2',
3398
3460
  /** Export defaults for reference */
3399
3461
  DEFAULTS: DEFAULTS
3400
3462
  };