@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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * SpinWheel SDK v0.1.0-alpha.1
2
+ * SpinWheel SDK v0.1.0-alpha.2
3
3
  * TypeScript declarations
4
4
  */
5
5
 
@@ -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
  */
@@ -26,6 +26,63 @@
26
26
  */
27
27
  function buildCSS(config, hexToRGBA) {
28
28
  const t = config.theme || {};
29
+ const isHexColor = value => typeof value === 'string' && /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(value.trim());
30
+ const parseRgbLike = value => {
31
+ if (typeof value !== 'string') return null;
32
+ 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);
33
+ if (!m) return null;
34
+ const r = Math.min(255, Math.max(0, Number(m[1])));
35
+ const g = Math.min(255, Math.max(0, Number(m[2])));
36
+ const b = Math.min(255, Math.max(0, Number(m[3])));
37
+ if ([r, g, b].some(n => Number.isNaN(n))) return null;
38
+ return {
39
+ r,
40
+ g,
41
+ b
42
+ };
43
+ };
44
+ const parseHex = value => {
45
+ if (!isHexColor(value)) return null;
46
+ const hex = value.trim().slice(1);
47
+ if (hex.length === 3) {
48
+ const r = parseInt(hex[0] + hex[0], 16);
49
+ const g = parseInt(hex[1] + hex[1], 16);
50
+ const b = parseInt(hex[2] + hex[2], 16);
51
+ return {
52
+ r,
53
+ g,
54
+ b
55
+ };
56
+ }
57
+ const r = parseInt(hex.slice(0, 2), 16);
58
+ const g = parseInt(hex.slice(2, 4), 16);
59
+ const b = parseInt(hex.slice(4, 6), 16);
60
+ return {
61
+ r,
62
+ g,
63
+ b
64
+ };
65
+ };
66
+ const toRGBA = (value, alpha) => {
67
+ const rgb = parseHex(value) || parseRgbLike(value);
68
+ if (!rgb) return `rgba(0,0,0,${alpha})`;
69
+ return `rgba(${rgb.r},${rgb.g},${rgb.b},${alpha})`;
70
+ };
71
+ const darkenColor = (value, amount = 0.22) => {
72
+ const rgb = parseHex(value) || parseRgbLike(value);
73
+ if (!rgb) return null;
74
+ const factor = 1 - Math.min(0.9, Math.max(0, amount));
75
+ const r = Math.round(rgb.r * factor);
76
+ const g = Math.round(rgb.g * factor);
77
+ const b = Math.round(rgb.b * factor);
78
+ return `rgb(${r},${g},${b})`;
79
+ };
80
+ const readableTextOn = bg => {
81
+ const rgb = parseHex(bg) || parseRgbLike(bg);
82
+ if (!rgb) return '#1a1a1f';
83
+ const luma = (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b) / 255;
84
+ return luma < 0.55 ? '#ffffff' : '#1a1a1f';
85
+ };
29
86
 
30
87
  // Extract theme colors with fallbacks
31
88
  const gold = t.gold || '#e8c547';
@@ -35,11 +92,16 @@ function buildCSS(config, hexToRGBA) {
35
92
  const textMuted = t.textMuted || '#9ca3af';
36
93
 
37
94
  // Configurable colors
38
- const titleColor = config.titleColor || goldLight;
95
+ const hasCustomTitleColor = config.titleColor !== null && config.titleColor !== undefined;
96
+ const titleSpinColor = hasCustomTitleColor ? config.titleColor : goldLight;
97
+ const titleWinColor = hasCustomTitleColor ? config.titleColor : goldDark;
39
98
  const subtitleColor = config.subtitleColor || textMuted;
40
99
  const ringColor = config.ringColor || gold;
41
100
  const pointerColor = config.pointerColor || gold;
42
- const buttonBaseColor = config.buttonColor || gold;
101
+ const hasCustomButtonColor = config.buttonColor !== null && config.buttonColor !== undefined;
102
+ const buttonBaseColor = hasCustomButtonColor ? config.buttonColor : gold;
103
+ const buttonTextColor = hasCustomButtonColor ? readableTextOn(buttonBaseColor) : '#1a1a1f';
104
+ const buttonEdgeColor = hasCustomButtonColor ? darkenColor(buttonBaseColor) || 'rgba(0,0,0,0.25)' : goldDark;
43
105
  const redeemBtnTop = config.winCardRedeemButtonColorTop || '#15803d';
44
106
  const redeemBtnBottom = config.winCardRedeemButtonColorBottom || '#166534';
45
107
  const redeemBtnText = config.winCardRedeemButtonTextColor || '#ffffff';
@@ -53,7 +115,7 @@ function buildCSS(config, hexToRGBA) {
53
115
  // Computed values
54
116
  hexToRGBA(gold, 0.4);
55
117
  const ringGlow = hexToRGBA(ringColor, 0.4);
56
- const buttonGlow = hexToRGBA(buttonBaseColor, 0.4);
118
+ const buttonGlow = toRGBA(buttonBaseColor, 0.4);
57
119
 
58
120
  // Background styles
59
121
  const bgStyle = config.backgroundColor ? `background-color:${config.backgroundColor};` : '';
@@ -64,9 +126,10 @@ function buildCSS(config, hexToRGBA) {
64
126
  const ringAnimationCSS = ringAnimation ? `animation:sw-ring-pulse 3s ease-in-out infinite;` : '';
65
127
 
66
128
  // Button shadow and animation
67
- 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);`;
68
- 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)}`;
69
- 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)}`;
129
+ 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);`;
130
+ 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)}`;
131
+ 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)}`;
132
+ const buttonBackground = hasCustomButtonColor ? `background:${buttonBaseColor};` : `background:linear-gradient(180deg,${goldLight},${goldDark});`;
70
133
  return `
71
134
  /* === SpinWheel SDK v2.1 === */
72
135
  @import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700;800&family=Outfit:wght@300;500;600;700&display=swap');
@@ -87,10 +150,10 @@ function buildCSS(config, hexToRGBA) {
87
150
  font-size:clamp(2rem,6vw,3.5rem); font-weight:800; letter-spacing:.02em;
88
151
  }
89
152
  .sw-title .sw-spin-text {
90
- color:${titleColor};
91
- text-shadow:0 0 30px ${hexToRGBA(titleColor, 0.4)}, 0 0 60px ${hexToRGBA(titleColor, 0.2)};
153
+ color:${titleSpinColor};
154
+ text-shadow:0 0 30px ${toRGBA(titleSpinColor, 0.4)}, 0 0 60px ${toRGBA(titleSpinColor, 0.2)};
92
155
  }
93
- .sw-title .sw-win-text { color:${goldDark}; }
156
+ .sw-title .sw-win-text { color:${titleWinColor}; }
94
157
  .sw-subtitle { margin-top:.5rem; color:${subtitleColor}; font-size:1rem; font-weight:300; }
95
158
 
96
159
  /* ── wheel ── */
@@ -172,7 +235,7 @@ ${ringAnimation ? `
172
235
  .sw-spin-btn {
173
236
  font-family:'Outfit',sans-serif; font-size:1.25rem; font-weight:700;
174
237
  letter-spacing:.15em; padding:1rem 3rem; border:none; border-radius:12px;
175
- background:linear-gradient(180deg,${goldLight},${goldDark}); color:#1a1a1f;
238
+ ${buttonBackground} color:${buttonTextColor};
176
239
  cursor:pointer;
177
240
  ${buttonBoxShadow}
178
241
  transition:transform .15s ease, box-shadow .2s ease, filter .2s ease;
@@ -1424,7 +1487,7 @@ class SpinWheel {
1424
1487
  static Instance = SpinWheelInstance;
1425
1488
 
1426
1489
  /** Current SDK version – kept in sync with package.json via build banner */
1427
- static VERSION = '0.1.0-alpha.1';
1490
+ static VERSION = '0.1.0-alpha.2';
1428
1491
 
1429
1492
  /** Export defaults for reference */
1430
1493
  static DEFAULTS = DEFAULTS$1;
@@ -1659,7 +1722,8 @@ function buildScratchCSS(config, hexToRGBA) {
1659
1722
 
1660
1723
  // Background configs
1661
1724
  const cardBg = config.cardBackground || `linear-gradient(180deg, #faf8fc 0%, #f0ebf5 100%)`;
1662
- const scratchZoneBg = config.scratchZoneBackground || `linear-gradient(145deg, ${purpleDark} 0%, ${purpleMid} 50%, #5a3a7a 100%)`;
1725
+ const hasCustomScratchZoneBg = config.scratchZoneBackground !== null && config.scratchZoneBackground !== undefined;
1726
+ const scratchZoneBg = hasCustomScratchZoneBg ? config.scratchZoneBackground : `linear-gradient(145deg, ${purpleDark} 0%, ${purpleMid} 50%, #5a3a7a 100%)`;
1663
1727
  const modalBtnBg = config.modalButtonColor || `linear-gradient(145deg, ${purpleMid} 0%, ${purpleDark} 100%)`;
1664
1728
 
1665
1729
  // Computed values
@@ -1769,9 +1833,7 @@ function buildScratchCSS(config, hexToRGBA) {
1769
1833
  align-items:center;
1770
1834
  justify-content:center;
1771
1835
  padding:20px;
1772
- background:
1773
- radial-gradient(circle at 30% 30%, rgba(255,255,255,0.08) 0%, transparent 45%),
1774
- linear-gradient(145deg, ${purpleDark} 0%, ${purpleMid} 100%);
1836
+ 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%)`};
1775
1837
  }
1776
1838
 
1777
1839
  .sc-prize-content::before {
@@ -3396,7 +3458,7 @@ const ScratchCard = {
3396
3458
  /** The underlying class – useful for `instanceof` checks or subclassing. */
3397
3459
  Instance: ScratchCardInstance,
3398
3460
  /** Current SDK version */
3399
- VERSION: '0.1.0-alpha.1',
3461
+ VERSION: '0.1.0-alpha.2',
3400
3462
  /** Export defaults for reference */
3401
3463
  DEFAULTS: DEFAULTS
3402
3464
  };