@pcampus/reward-widget 0.1.0 → 0.2.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/README.md +27 -4
- package/dist/index.d.ts +5 -1
- package/dist/index.js +1 -1
- package/dist/theme.d.ts +5 -0
- package/dist/theme.js +24 -0
- package/dist/widgets/chrome.d.ts +13 -0
- package/dist/widgets/chrome.js +25 -0
- package/dist/widgets/coupon-success.d.ts +13 -0
- package/dist/widgets/coupon-success.js +256 -0
- package/dist/widgets/index.d.ts +1 -0
- package/dist/widgets/index.js +5 -4
- package/dist/widgets/mystery-box.js +310 -16
- package/dist/widgets/pick-card.d.ts +3 -0
- package/dist/widgets/pick-card.js +195 -33
- package/dist/widgets/quiz.d.ts +13 -3
- package/dist/widgets/quiz.js +396 -47
- package/dist/widgets/scratch.d.ts +3 -0
- package/dist/widgets/scratch.js +347 -47
- package/dist/widgets/spin.d.ts +12 -1
- package/dist/widgets/spin.js +278 -53
- package/dist/widgets/types.d.ts +19 -0
- package/package.json +12 -3
|
@@ -1,32 +1,326 @@
|
|
|
1
|
+
import { DEFAULT_WIDGET_SURFACE, mixPrimary, resolveWidgetChrome } from "./chrome.js";
|
|
2
|
+
import { showCouponSuccess } from "./coupon-success.js";
|
|
3
|
+
import { textEscape } from "./types.js";
|
|
1
4
|
export function renderMysteryBoxMarkup(rewards = [], theme) {
|
|
2
|
-
const
|
|
5
|
+
const topReward = rewards[0]?.name || "ของรางวัลพิเศษ";
|
|
6
|
+
const { primary, surface, border } = resolveWidgetChrome(theme);
|
|
7
|
+
const boxColor = theme?.hubBackgroundColor?.trim() || DEFAULT_WIDGET_SURFACE;
|
|
8
|
+
const lidColor = boxColor;
|
|
9
|
+
const ribbonColor = primary;
|
|
10
|
+
const stripeColor = theme?.textColor?.trim() || primary;
|
|
11
|
+
const logoFill = theme?.promptColor?.trim() || "#181817";
|
|
3
12
|
return `
|
|
4
|
-
<
|
|
5
|
-
|
|
13
|
+
<style>
|
|
14
|
+
.pcampus-mystery-box-wrapper {
|
|
15
|
+
perspective: 1200px;
|
|
16
|
+
transform-style: preserve-3d;
|
|
17
|
+
width: 100%;
|
|
18
|
+
max-width: 22rem;
|
|
19
|
+
height: clamp(16rem, 50vw, 22rem);
|
|
20
|
+
display: flex;
|
|
21
|
+
justify-content: center;
|
|
22
|
+
align-items: flex-end;
|
|
23
|
+
padding: 3.25rem 0 1.75rem;
|
|
24
|
+
box-sizing: border-box;
|
|
25
|
+
position: relative;
|
|
26
|
+
user-select: none;
|
|
27
|
+
overflow: visible;
|
|
28
|
+
}
|
|
29
|
+
.pcampus-gift-box {
|
|
30
|
+
position: relative;
|
|
31
|
+
width: 150px;
|
|
32
|
+
height: 150px;
|
|
33
|
+
transform-style: preserve-3d;
|
|
34
|
+
transform: rotateX(-18deg) rotateY(22deg);
|
|
35
|
+
cursor: pointer;
|
|
36
|
+
}
|
|
37
|
+
.pcampus-gift-box:hover:not(.opened):not(.wiggling) {
|
|
38
|
+
transform: rotateX(-18deg) rotateY(22deg) scale(1.04);
|
|
39
|
+
}
|
|
40
|
+
.pcampus-gift-box.opened {
|
|
41
|
+
transform: rotateX(-18deg) rotateY(22deg);
|
|
42
|
+
cursor: default;
|
|
43
|
+
}
|
|
44
|
+
@keyframes pcampus-box-jiggle {
|
|
45
|
+
0%, 100% { transform: rotateX(-18deg) rotateY(22deg) rotateZ(0deg); }
|
|
46
|
+
16% { transform: rotateX(-18deg) rotateY(22deg) rotateZ(-8deg); }
|
|
47
|
+
32% { transform: rotateX(-18deg) rotateY(22deg) rotateZ(8deg); }
|
|
48
|
+
48% { transform: rotateX(-18deg) rotateY(22deg) rotateZ(-6deg); }
|
|
49
|
+
64% { transform: rotateX(-18deg) rotateY(22deg) rotateZ(5deg); }
|
|
50
|
+
80% { transform: rotateX(-18deg) rotateY(22deg) rotateZ(-3deg); }
|
|
51
|
+
}
|
|
52
|
+
.pcampus-gift-box.wiggling {
|
|
53
|
+
animation: pcampus-box-jiggle 0.48s ease-in-out;
|
|
54
|
+
}
|
|
55
|
+
.pcampus-gift-box-body {
|
|
56
|
+
position: absolute;
|
|
57
|
+
width: 150px;
|
|
58
|
+
height: 150px;
|
|
59
|
+
transform-style: preserve-3d;
|
|
60
|
+
}
|
|
61
|
+
.pcampus-body-face {
|
|
62
|
+
position: absolute;
|
|
63
|
+
top: 0;
|
|
64
|
+
left: 0;
|
|
65
|
+
width: 150px;
|
|
66
|
+
height: 150px;
|
|
67
|
+
background-color: ${boxColor};
|
|
68
|
+
border: 1.5px solid ${border};
|
|
69
|
+
box-sizing: border-box;
|
|
70
|
+
}
|
|
71
|
+
.pcampus-body-face::after {
|
|
72
|
+
content: '';
|
|
73
|
+
position: absolute;
|
|
74
|
+
background-color: ${stripeColor};
|
|
75
|
+
box-shadow: 0 0 8px ${mixPrimary(stripeColor, 30)};
|
|
76
|
+
}
|
|
77
|
+
.pcampus-body-front::after, .pcampus-body-back::after,
|
|
78
|
+
.pcampus-body-left::after, .pcampus-body-right::after {
|
|
79
|
+
width: 30px;
|
|
80
|
+
height: 100%;
|
|
81
|
+
left: 60px;
|
|
82
|
+
top: 0;
|
|
83
|
+
}
|
|
84
|
+
.pcampus-body-front { transform: rotateY(0deg) translateZ(75px); }
|
|
85
|
+
.pcampus-body-back { transform: rotateY(180deg) translateZ(75px); }
|
|
86
|
+
.pcampus-body-left { transform: rotateY(-90deg) translateZ(75px); }
|
|
87
|
+
.pcampus-body-right { transform: rotateY(90deg) translateZ(75px); }
|
|
88
|
+
.pcampus-body-bottom { transform: rotateX(-90deg) translateZ(75px); background-color: #141413; }
|
|
6
89
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
90
|
+
.pcampus-box-front-seal {
|
|
91
|
+
position: absolute;
|
|
92
|
+
top: 50%;
|
|
93
|
+
left: 50%;
|
|
94
|
+
transform: translate(-50%, -50%);
|
|
95
|
+
width: 64px;
|
|
96
|
+
height: 64px;
|
|
97
|
+
border-radius: 10px;
|
|
98
|
+
background: ${logoFill};
|
|
99
|
+
border: 1.5px solid ${ribbonColor};
|
|
100
|
+
display: flex;
|
|
101
|
+
align-items: center;
|
|
102
|
+
justify-content: center;
|
|
103
|
+
z-index: 5;
|
|
104
|
+
overflow: hidden;
|
|
105
|
+
box-shadow: 0 4px 10px rgba(0,0,0,0.5);
|
|
106
|
+
padding: 4px;
|
|
107
|
+
box-sizing: border-box;
|
|
108
|
+
}
|
|
12
109
|
|
|
13
|
-
|
|
14
|
-
|
|
110
|
+
.pcampus-gift-box-lid {
|
|
111
|
+
position: absolute;
|
|
112
|
+
width: 160px;
|
|
113
|
+
height: 38px;
|
|
114
|
+
top: -14px;
|
|
115
|
+
left: -5px;
|
|
116
|
+
transform-style: preserve-3d;
|
|
117
|
+
transform-origin: 50% 100% -80px;
|
|
118
|
+
transform: rotateX(0deg);
|
|
119
|
+
transition: transform 1.55s cubic-bezier(0.22, 1, 0.36, 1);
|
|
120
|
+
z-index: 10;
|
|
121
|
+
}
|
|
122
|
+
.pcampus-gift-box.opened .pcampus-gift-box-lid {
|
|
123
|
+
transform: rotateX(118deg);
|
|
124
|
+
}
|
|
125
|
+
.pcampus-lid-face {
|
|
126
|
+
position: absolute;
|
|
127
|
+
top: 0;
|
|
128
|
+
left: 0;
|
|
129
|
+
background-color: ${lidColor};
|
|
130
|
+
box-sizing: border-box;
|
|
131
|
+
}
|
|
132
|
+
.pcampus-lid-front,
|
|
133
|
+
.pcampus-lid-back,
|
|
134
|
+
.pcampus-lid-left,
|
|
135
|
+
.pcampus-lid-right {
|
|
136
|
+
border: 1.5px solid ${border};
|
|
137
|
+
}
|
|
138
|
+
.pcampus-lid-top {
|
|
139
|
+
width: 160px;
|
|
140
|
+
height: 160px;
|
|
141
|
+
transform: rotateX(90deg) translateZ(80px);
|
|
142
|
+
display: flex;
|
|
143
|
+
align-items: center;
|
|
144
|
+
justify-content: center;
|
|
145
|
+
position: relative;
|
|
146
|
+
transform-style: preserve-3d;
|
|
147
|
+
overflow: visible;
|
|
148
|
+
border: none;
|
|
149
|
+
}
|
|
150
|
+
.pcampus-lid-top::before {
|
|
151
|
+
content: '';
|
|
152
|
+
position: absolute;
|
|
153
|
+
width: 30px;
|
|
154
|
+
height: 100%;
|
|
155
|
+
left: 65px;
|
|
156
|
+
top: 0;
|
|
157
|
+
background-color: ${stripeColor};
|
|
158
|
+
}
|
|
159
|
+
.pcampus-lid-top::after {
|
|
160
|
+
content: '';
|
|
161
|
+
position: absolute;
|
|
162
|
+
width: 100%;
|
|
163
|
+
height: 30px;
|
|
164
|
+
left: 0;
|
|
165
|
+
top: 65px;
|
|
166
|
+
background-color: ${stripeColor};
|
|
167
|
+
}
|
|
168
|
+
.pcampus-lid-front { width: 160px; height: 38px; transform: rotateY(0deg) translateZ(80px); }
|
|
169
|
+
.pcampus-lid-front::after { content: ''; position: absolute; width: 30px; height: 100%; left: 65px; background-color: ${stripeColor}; }
|
|
170
|
+
.pcampus-lid-back { width: 160px; height: 38px; transform: rotateY(180deg) translateZ(80px); }
|
|
171
|
+
.pcampus-lid-back::after { content: ''; position: absolute; width: 30px; height: 100%; left: 65px; background-color: ${stripeColor}; }
|
|
172
|
+
.pcampus-lid-left { width: 160px; height: 38px; transform: rotateY(-90deg) translateZ(80px); }
|
|
173
|
+
.pcampus-lid-left::after { content: ''; position: absolute; width: 30px; height: 100%; left: 65px; background-color: ${stripeColor}; }
|
|
174
|
+
.pcampus-lid-right { width: 160px; height: 38px; transform: rotateY(90deg) translateZ(80px); }
|
|
175
|
+
.pcampus-lid-right::after { content: ''; position: absolute; width: 30px; height: 100%; left: 65px; background-color: ${stripeColor}; }
|
|
176
|
+
|
|
177
|
+
.pcampus-box-glow {
|
|
178
|
+
position: absolute;
|
|
179
|
+
top: 50%;
|
|
180
|
+
left: 50%;
|
|
181
|
+
transform: translate(-50%, -50%) scale(0.1);
|
|
182
|
+
width: 14rem;
|
|
183
|
+
height: 14rem;
|
|
184
|
+
background: radial-gradient(circle, ${mixPrimary(primary, 50)} 0%, ${mixPrimary(primary, 0)} 70%);
|
|
185
|
+
z-index: 1;
|
|
186
|
+
pointer-events: none;
|
|
187
|
+
opacity: 0;
|
|
188
|
+
transition: transform 0.8s ease-out, opacity 0.8s;
|
|
189
|
+
}
|
|
190
|
+
.pcampus-mystery-box-wrapper.opened .pcampus-box-glow {
|
|
191
|
+
transform: translate(-50%, -50%) scale(1.5);
|
|
192
|
+
opacity: 1;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.pcampus-prize-popup {
|
|
196
|
+
position: absolute;
|
|
197
|
+
left: 50%;
|
|
198
|
+
bottom: 11.5rem;
|
|
199
|
+
transform: translateX(-50%) translateY(0.5rem) scale(0.85);
|
|
200
|
+
opacity: 0;
|
|
201
|
+
z-index: 20;
|
|
202
|
+
transition: transform 0.55s cubic-bezier(0.22, 1, 0.36, 1) 0.7s, opacity 0.55s ease 0.7s;
|
|
203
|
+
pointer-events: none;
|
|
204
|
+
background: rgba(28, 28, 27, 0.95);
|
|
205
|
+
border: 2px solid ${ribbonColor};
|
|
206
|
+
padding: 0.85rem 1.4rem;
|
|
207
|
+
border-radius: 16px;
|
|
208
|
+
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.5);
|
|
209
|
+
white-space: nowrap;
|
|
210
|
+
text-align: center;
|
|
211
|
+
display: flex;
|
|
212
|
+
flex-direction: column;
|
|
213
|
+
align-items: center;
|
|
214
|
+
}
|
|
215
|
+
.pcampus-mystery-box-wrapper.opened .pcampus-prize-popup {
|
|
216
|
+
transform: translateX(-50%) translateY(0) scale(1);
|
|
217
|
+
opacity: 1;
|
|
218
|
+
}
|
|
219
|
+
</style>
|
|
220
|
+
|
|
221
|
+
<div class="pcampus-mystery-box-wrap" style="position:relative;display:flex;flex-direction:column;align-items:center;width:100%;box-sizing:border-box;background:${surface};">
|
|
222
|
+
|
|
223
|
+
<div class="pcampus-mystery-box-wrapper">
|
|
224
|
+
<div class="pcampus-mystery-box pcampus-gift-box">
|
|
225
|
+
|
|
226
|
+
<div class="pcampus-gift-box-body">
|
|
227
|
+
<div class="pcampus-body-face pcampus-body-front">
|
|
228
|
+
<div class="pcampus-box-front-seal">
|
|
229
|
+
${theme?.brandLogoUrl ? `
|
|
230
|
+
<img src="${textEscape(theme.brandLogoUrl)}" alt="${textEscape(theme?.brandName || 'Brand')}" style="max-width:92%;max-height:92%;object-fit:contain;" />
|
|
231
|
+
` : `
|
|
232
|
+
<i class="fa-solid fa-lock" style="font-size:0.75rem;color:${ribbonColor};"></i>
|
|
233
|
+
`}
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
<div class="pcampus-body-face pcampus-body-back"></div>
|
|
237
|
+
<div class="pcampus-body-face pcampus-body-left"></div>
|
|
238
|
+
<div class="pcampus-body-face pcampus-body-right"></div>
|
|
239
|
+
<div class="pcampus-body-face pcampus-body-bottom"></div>
|
|
240
|
+
</div>
|
|
241
|
+
|
|
242
|
+
<div class="pcampus-gift-box-lid">
|
|
243
|
+
<div class="pcampus-lid-face pcampus-lid-top"></div>
|
|
244
|
+
<div class="pcampus-lid-face pcampus-lid-front"></div>
|
|
245
|
+
<div class="pcampus-lid-face pcampus-lid-back"></div>
|
|
246
|
+
<div class="pcampus-lid-face pcampus-lid-left"></div>
|
|
247
|
+
<div class="pcampus-lid-face pcampus-lid-right"></div>
|
|
248
|
+
</div>
|
|
249
|
+
|
|
250
|
+
</div>
|
|
251
|
+
|
|
252
|
+
<div class="pcampus-prize-popup">
|
|
253
|
+
<span style="font-size:0.7rem;font-weight:700;color:${primary};text-transform:uppercase;letter-spacing:0.1em;margin-bottom:0.25rem;">
|
|
254
|
+
${textEscape(theme?.brandName ? `${theme.brandName} REWARD` : "CONGRATULATIONS")}
|
|
255
|
+
</span>
|
|
256
|
+
<span style="font-size:1.15rem;font-weight:900;color:#ffffff;">${textEscape(topReward)}</span>
|
|
257
|
+
</div>
|
|
258
|
+
<div class="pcampus-box-glow"></div>
|
|
15
259
|
</div>
|
|
260
|
+
|
|
261
|
+
<div class="pcampus-mystery-outcome" style="margin-top:1rem;font-size:0.92rem;font-weight:600;color:#f0efed;text-align:center;padding:0.45rem 1.25rem;border-radius:9999px;border:1px solid rgba(240, 239, 237, 0.08);background:rgba(240, 239, 237, 0.03);">
|
|
262
|
+
แตะที่กล่องของขวัญ MYSTERY CHEST เพื่อเปิดดูรางวัล
|
|
263
|
+
</div>
|
|
264
|
+
|
|
16
265
|
</div>
|
|
17
266
|
`;
|
|
18
267
|
}
|
|
268
|
+
function ensureFontAwesomeLoaded() {
|
|
269
|
+
if (typeof document === "undefined")
|
|
270
|
+
return;
|
|
271
|
+
const id = "pcampus-fa-cdn";
|
|
272
|
+
if (!document.getElementById(id)) {
|
|
273
|
+
const link = document.createElement("link");
|
|
274
|
+
link.id = id;
|
|
275
|
+
link.rel = "stylesheet";
|
|
276
|
+
link.href = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css";
|
|
277
|
+
document.head.appendChild(link);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
19
280
|
export function mountMysteryBox(target, rewards = [], theme, onAction) {
|
|
281
|
+
ensureFontAwesomeLoaded();
|
|
20
282
|
target.innerHTML = renderMysteryBoxMarkup(rewards, theme);
|
|
21
|
-
const
|
|
283
|
+
const wrapper = target.querySelector(".pcampus-mystery-box-wrapper");
|
|
284
|
+
const box = target.querySelector(".pcampus-gift-box");
|
|
285
|
+
const outcome = target.querySelector(".pcampus-mystery-outcome");
|
|
286
|
+
let isOpened = false;
|
|
287
|
+
const rewardItem = rewards[0] || { name: "ของขวัญพิเศษ" };
|
|
288
|
+
const { primary } = resolveWidgetChrome(theme);
|
|
22
289
|
if (box) {
|
|
23
|
-
|
|
24
|
-
|
|
290
|
+
let lidStarted = false;
|
|
291
|
+
const openLid = () => {
|
|
292
|
+
if (lidStarted)
|
|
293
|
+
return;
|
|
294
|
+
lidStarted = true;
|
|
295
|
+
box.classList.remove("wiggling");
|
|
296
|
+
box.classList.add("opened");
|
|
297
|
+
wrapper?.classList.add("opened");
|
|
298
|
+
if (outcome) {
|
|
299
|
+
outcome.innerHTML = `ยินดีด้วย! คุณได้รับ: <strong style="color:${primary};">${textEscape(rewardItem.name)}</strong>`;
|
|
300
|
+
}
|
|
301
|
+
showCouponSuccess({
|
|
302
|
+
rewardName: rewardItem.name,
|
|
303
|
+
couponCode: rewardItem.couponCode,
|
|
304
|
+
});
|
|
25
305
|
setTimeout(() => {
|
|
26
|
-
box.style.transform = "scale(1)";
|
|
27
306
|
if (onAction)
|
|
28
|
-
onAction(
|
|
29
|
-
},
|
|
307
|
+
onAction(rewardItem);
|
|
308
|
+
}, 1600);
|
|
309
|
+
};
|
|
310
|
+
box.addEventListener("click", () => {
|
|
311
|
+
if (isOpened)
|
|
312
|
+
return;
|
|
313
|
+
isOpened = true;
|
|
314
|
+
box.classList.add("wiggling");
|
|
315
|
+
const fallback = window.setTimeout(openLid, 520);
|
|
316
|
+
const onJiggleEnd = (event) => {
|
|
317
|
+
if (event.animationName !== "pcampus-box-jiggle")
|
|
318
|
+
return;
|
|
319
|
+
window.clearTimeout(fallback);
|
|
320
|
+
box.removeEventListener("animationend", onJiggleEnd);
|
|
321
|
+
openLid();
|
|
322
|
+
};
|
|
323
|
+
box.addEventListener("animationend", onJiggleEnd);
|
|
30
324
|
});
|
|
31
325
|
}
|
|
32
326
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { RewardWidgetTheme } from "../index.js";
|
|
2
2
|
import { type WidgetRewardItem } from "./types.js";
|
|
3
|
+
export declare const PICK_CARD_VISIBLE_MAX = 6;
|
|
4
|
+
export declare function pickCardVisibleItems(rewards?: WidgetRewardItem[]): WidgetRewardItem[];
|
|
5
|
+
export declare function pickWeightedReward(rewards: WidgetRewardItem[], random?: () => number): WidgetRewardItem;
|
|
3
6
|
export declare function renderPickCardMarkup(rewards?: WidgetRewardItem[], theme?: RewardWidgetTheme): string;
|
|
4
7
|
export declare function mountPickCard(target: Element, rewards?: WidgetRewardItem[], theme?: RewardWidgetTheme, onAction?: (reward?: WidgetRewardItem) => void): void;
|
|
@@ -1,51 +1,213 @@
|
|
|
1
|
+
import { DEFAULT_WIDGET_SURFACE, resolveWidgetChrome } from "./chrome.js";
|
|
2
|
+
import { showCouponSuccess } from "./coupon-success.js";
|
|
3
|
+
import { textEscape } from "./types.js";
|
|
4
|
+
export const PICK_CARD_VISIBLE_MAX = 6;
|
|
5
|
+
const PLACEHOLDER_CARDS = [
|
|
6
|
+
{ name: "ส่วนลด 100 บาท" },
|
|
7
|
+
{ name: "รับโบนัส 500 คะแนน" },
|
|
8
|
+
{ name: "คูปองส่งฟรี VIP" },
|
|
9
|
+
];
|
|
10
|
+
export function pickCardVisibleItems(rewards = []) {
|
|
11
|
+
const source = rewards.length > 0 ? rewards : PLACEHOLDER_CARDS;
|
|
12
|
+
return source.slice(0, PICK_CARD_VISIBLE_MAX);
|
|
13
|
+
}
|
|
14
|
+
export function pickWeightedReward(rewards, random = Math.random) {
|
|
15
|
+
if (rewards.length === 0) {
|
|
16
|
+
return { name: "ส่วนลดพิเศษ" };
|
|
17
|
+
}
|
|
18
|
+
const weights = rewards.map((reward) => {
|
|
19
|
+
const weight = Number(reward.weight);
|
|
20
|
+
return Number.isFinite(weight) && weight > 0 ? weight : 0;
|
|
21
|
+
});
|
|
22
|
+
const total = weights.reduce((sum, weight) => sum + weight, 0);
|
|
23
|
+
if (total <= 0) {
|
|
24
|
+
const index = Math.min(rewards.length - 1, Math.floor(random() * rewards.length));
|
|
25
|
+
return rewards[index];
|
|
26
|
+
}
|
|
27
|
+
let ticket = random() * total;
|
|
28
|
+
for (let index = 0; index < rewards.length; index += 1) {
|
|
29
|
+
ticket -= weights[index];
|
|
30
|
+
if (ticket <= 0) {
|
|
31
|
+
return rewards[index];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return rewards[rewards.length - 1];
|
|
35
|
+
}
|
|
1
36
|
export function renderPickCardMarkup(rewards = [], theme) {
|
|
2
|
-
const primary = theme
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
rewards[2] || { name: "3rd Prize" },
|
|
9
|
-
];
|
|
37
|
+
const { primary, surface, border } = resolveWidgetChrome(theme);
|
|
38
|
+
const cardFill = theme?.hubBackgroundColor?.trim() || DEFAULT_WIDGET_SURFACE;
|
|
39
|
+
const ink = theme?.textColor?.trim() || "#f0efed";
|
|
40
|
+
const prompt = theme?.promptColor?.trim() || "#f0efed";
|
|
41
|
+
const items = pickCardVisibleItems(rewards);
|
|
42
|
+
const weightedPool = rewards.length > PICK_CARD_VISIBLE_MAX;
|
|
10
43
|
const cardsHtml = items
|
|
11
44
|
.map((item, idx) => `
|
|
12
|
-
<div class="pcampus-pick-card" data-card-index="${idx}" style="
|
|
13
|
-
<div
|
|
14
|
-
|
|
15
|
-
|
|
45
|
+
<div class="pcampus-pick-card pcampus-card-item" data-card-index="${idx}" style="--i:${idx};--n:${items.length}">
|
|
46
|
+
<div class="pcampus-card-face pcampus-card-front">
|
|
47
|
+
${theme?.brandLogoUrl
|
|
48
|
+
? `<img src="${textEscape(theme.brandLogoUrl)}" alt="" class="pcampus-card-mark" />`
|
|
49
|
+
: `<span class="pcampus-card-mark pcampus-card-mark-dot" aria-hidden="true"></span>`}
|
|
16
50
|
</div>
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
<svg style="width:24px;height:24px;color:#fde68a;" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
20
|
-
<rect x="5" y="3" width="14" height="18" rx="2" stroke-width="1.5" fill="rgba(255,255,255,0.15)"/>
|
|
21
|
-
<path d="M12 8l1.5 2.5L16 12l-2.5 1.5L12 16l-1.5-2.5L8 12l2.5-1.5z" fill="currentColor"/>
|
|
22
|
-
</svg>
|
|
23
|
-
<span style="font-size:9px;font-weight:900;letter-spacing:0.05em;color:#ffffff;text-transform:uppercase;">PICK</span>
|
|
24
|
-
</div>
|
|
25
|
-
|
|
26
|
-
<div style="display:flex;width:100%;justify-content:space-between;color:#fde68a;">
|
|
27
|
-
<svg style="width:8px;height:8px;fill:currentColor;" viewBox="0 0 24 24"><path d="M12 2l2.5 7.5L22 12l-7.5 2.5L12 22l-2.5-7.5L2 12l7.5-2.5z"/></svg>
|
|
28
|
-
<svg style="width:8px;height:8px;fill:currentColor;" viewBox="0 0 24 24"><path d="M12 2l2.5 7.5L22 12l-7.5 2.5L12 22l-2.5-7.5L2 12l7.5-2.5z"/></svg>
|
|
51
|
+
<div class="pcampus-card-face pcampus-card-back">
|
|
52
|
+
<div class="pcampus-card-back-prize">${weightedPool ? "" : textEscape(item.name)}</div>
|
|
29
53
|
</div>
|
|
30
54
|
</div>
|
|
31
55
|
`)
|
|
32
56
|
.join("");
|
|
33
57
|
return `
|
|
34
|
-
<
|
|
35
|
-
|
|
58
|
+
<style>
|
|
59
|
+
.pcampus-cards-container {
|
|
60
|
+
display: flex;
|
|
61
|
+
flex-wrap: wrap;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
align-items: center;
|
|
64
|
+
gap: clamp(0.5rem, 2.2vw, 1.1rem);
|
|
65
|
+
width: 100%;
|
|
66
|
+
max-width: 42rem;
|
|
67
|
+
perspective: 1200px;
|
|
68
|
+
padding: 1.5rem 0.25rem;
|
|
69
|
+
box-sizing: border-box;
|
|
70
|
+
}
|
|
71
|
+
.pcampus-card-item {
|
|
72
|
+
width: min(24vw, 7.6rem);
|
|
73
|
+
height: min(37vw, 11.6rem);
|
|
74
|
+
position: relative;
|
|
75
|
+
transform-style: preserve-3d;
|
|
76
|
+
transition: transform 0.24s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.24s ease, opacity 0.3s ease;
|
|
77
|
+
cursor: pointer;
|
|
78
|
+
user-select: none;
|
|
79
|
+
touch-action: manipulation;
|
|
80
|
+
}
|
|
81
|
+
.pcampus-card-item:hover:not(.flipped):not(.dimmed) {
|
|
82
|
+
transform: translateY(-10px);
|
|
83
|
+
}
|
|
84
|
+
.pcampus-card-item.flipped {
|
|
85
|
+
transform: rotateY(180deg) scale(1.04) !important;
|
|
86
|
+
transition: transform 0.65s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
87
|
+
cursor: default;
|
|
88
|
+
}
|
|
89
|
+
.pcampus-card-item.dimmed {
|
|
90
|
+
opacity: 0.35;
|
|
91
|
+
transform: scale(0.92);
|
|
92
|
+
cursor: default;
|
|
93
|
+
pointer-events: none;
|
|
94
|
+
}
|
|
95
|
+
.pcampus-cards-container.is-stacked {
|
|
96
|
+
display: grid;
|
|
97
|
+
place-items: center;
|
|
98
|
+
height: min(46vw, 14.5rem);
|
|
99
|
+
max-width: 26rem;
|
|
100
|
+
}
|
|
101
|
+
.pcampus-cards-container.is-stacked .pcampus-card-item {
|
|
102
|
+
grid-area: 1 / 1;
|
|
103
|
+
--mid: calc((var(--n) - 1) / 2);
|
|
104
|
+
--dx: calc((var(--i) - var(--mid)) * min(1.45rem, 12rem / var(--n)));
|
|
105
|
+
--rot: calc((var(--i) - var(--mid)) * 3.4deg);
|
|
106
|
+
transform: translateX(var(--dx)) rotate(var(--rot));
|
|
107
|
+
z-index: calc(var(--i) + 1);
|
|
108
|
+
}
|
|
109
|
+
.pcampus-cards-container.is-stacked .pcampus-card-item:hover:not(.flipped):not(.dimmed) {
|
|
110
|
+
transform: translateX(var(--dx)) translateY(-16px) rotate(var(--rot));
|
|
111
|
+
}
|
|
112
|
+
.pcampus-cards-container.is-stacked .pcampus-card-item.flipped {
|
|
113
|
+
transform: translateX(var(--dx)) rotateY(180deg) scale(1.08) !important;
|
|
114
|
+
z-index: 50;
|
|
115
|
+
}
|
|
116
|
+
.pcampus-cards-container.is-stacked .pcampus-card-item.dimmed {
|
|
117
|
+
opacity: 0.28;
|
|
118
|
+
transform: translateX(var(--dx)) rotate(var(--rot)) scale(0.9);
|
|
119
|
+
}
|
|
120
|
+
.pcampus-card-face {
|
|
121
|
+
position: absolute;
|
|
122
|
+
inset: 0;
|
|
123
|
+
width: 100%;
|
|
124
|
+
height: 100%;
|
|
125
|
+
backface-visibility: hidden;
|
|
126
|
+
border-radius: clamp(12px, 2.5vw, 16px);
|
|
127
|
+
display: flex;
|
|
128
|
+
align-items: center;
|
|
129
|
+
justify-content: center;
|
|
130
|
+
padding: 0.55rem;
|
|
131
|
+
box-sizing: border-box;
|
|
132
|
+
text-align: center;
|
|
133
|
+
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.4);
|
|
134
|
+
overflow: hidden;
|
|
135
|
+
}
|
|
136
|
+
.pcampus-card-front {
|
|
137
|
+
background: ${cardFill};
|
|
138
|
+
border: 2px solid ${border};
|
|
139
|
+
}
|
|
140
|
+
.pcampus-card-mark {
|
|
141
|
+
width: 78%;
|
|
142
|
+
height: 78%;
|
|
143
|
+
object-fit: contain;
|
|
144
|
+
}
|
|
145
|
+
.pcampus-card-mark-dot {
|
|
146
|
+
display: block;
|
|
147
|
+
width: 0.7rem;
|
|
148
|
+
height: 0.7rem;
|
|
149
|
+
border-radius: 50%;
|
|
150
|
+
background: ${primary};
|
|
151
|
+
opacity: 0.85;
|
|
152
|
+
}
|
|
153
|
+
.pcampus-card-back {
|
|
154
|
+
background: ${cardFill};
|
|
155
|
+
border: 2px solid ${border};
|
|
156
|
+
transform: rotateY(180deg);
|
|
157
|
+
}
|
|
158
|
+
.pcampus-card-back-prize {
|
|
159
|
+
font-weight: 700;
|
|
160
|
+
font-size: clamp(0.85rem, 2.4vw, 1.05rem);
|
|
161
|
+
color: ${ink};
|
|
162
|
+
line-height: 1.3;
|
|
163
|
+
}
|
|
164
|
+
</style>
|
|
165
|
+
|
|
166
|
+
<div class="pcampus-pick-card-wrap" style="position:relative;display:flex;flex-direction:column;align-items:center;width:100%;box-sizing:border-box;background:${surface};">
|
|
167
|
+
<div class="pcampus-cards-container${items.length >= 5 ? " is-stacked" : ""}" data-card-count="${items.length}">
|
|
168
|
+
${cardsHtml}
|
|
169
|
+
</div>
|
|
170
|
+
<div class="pcampus-pick-card-outcome" style="margin-top:1.25rem;font-size:0.92rem;font-weight:600;color:${prompt};text-align:center;">
|
|
171
|
+
เลือกการ์ด 1 ใบเพื่อเปิดดูรางวัล
|
|
172
|
+
</div>
|
|
36
173
|
</div>
|
|
37
174
|
`;
|
|
38
175
|
}
|
|
39
176
|
export function mountPickCard(target, rewards = [], theme, onAction) {
|
|
40
177
|
target.innerHTML = renderPickCardMarkup(rewards, theme);
|
|
41
|
-
const
|
|
42
|
-
|
|
178
|
+
const cardElements = target.querySelectorAll(".pcampus-card-item");
|
|
179
|
+
const outcome = target.querySelector(".pcampus-pick-card-outcome");
|
|
180
|
+
let isPicked = false;
|
|
181
|
+
cardElements.forEach((card, idx) => {
|
|
43
182
|
card.addEventListener("click", () => {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
183
|
+
if (isPicked)
|
|
184
|
+
return;
|
|
185
|
+
isPicked = true;
|
|
186
|
+
const pool = rewards.length > 0 ? rewards : PLACEHOLDER_CARDS;
|
|
187
|
+
const chosenReward = pool.length > PICK_CARD_VISIBLE_MAX
|
|
188
|
+
? pickWeightedReward(pool)
|
|
189
|
+
: pool[idx] || pool[0] || { name: "ส่วนลดพิเศษ" };
|
|
190
|
+
card.classList.add("flipped");
|
|
191
|
+
const prizeLabel = card.querySelector(".pcampus-card-back-prize");
|
|
192
|
+
if (prizeLabel) {
|
|
193
|
+
prizeLabel.textContent = chosenReward.name;
|
|
194
|
+
}
|
|
195
|
+
cardElements.forEach((other, otherIdx) => {
|
|
196
|
+
if (otherIdx !== idx) {
|
|
197
|
+
other.classList.add("dimmed");
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
if (outcome) {
|
|
201
|
+
outcome.innerHTML = `ยินดีด้วย! คุณได้รับ: <strong>${textEscape(chosenReward.name)}</strong>`;
|
|
202
|
+
}
|
|
203
|
+
showCouponSuccess({
|
|
204
|
+
rewardName: chosenReward.name,
|
|
205
|
+
couponCode: chosenReward.couponCode,
|
|
206
|
+
});
|
|
207
|
+
setTimeout(() => {
|
|
208
|
+
if (onAction)
|
|
209
|
+
onAction(chosenReward);
|
|
210
|
+
}, 500);
|
|
49
211
|
});
|
|
50
212
|
});
|
|
51
213
|
}
|
package/dist/widgets/quiz.d.ts
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import type { RewardWidgetTheme } from "../index.js";
|
|
2
|
-
import { type WidgetRewardItem } from "./types.js";
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { type QuizAnswerIndex, type QuizContent, type WidgetRewardItem } from "./types.js";
|
|
3
|
+
type ResolvedQuiz = {
|
|
4
|
+
heading: string;
|
|
5
|
+
items: Array<{
|
|
6
|
+
question: string;
|
|
7
|
+
options: string[];
|
|
8
|
+
}>;
|
|
9
|
+
};
|
|
10
|
+
export declare function resolveQuizContent(content?: QuizContent, brandName?: string): ResolvedQuiz;
|
|
11
|
+
export declare function renderQuizMarkup(rewards?: WidgetRewardItem[], theme?: RewardWidgetTheme, quiz?: QuizContent): string;
|
|
12
|
+
export declare function collectQuizAnswer(answers: QuizAnswerIndex[], questionIndex: number, optionIndex: number): QuizAnswerIndex[];
|
|
13
|
+
export declare function mountQuiz(target: Element, rewards?: WidgetRewardItem[], theme?: RewardWidgetTheme, onAction?: (reward?: WidgetRewardItem) => void, quiz?: QuizContent, onQuizComplete?: (answers: QuizAnswerIndex[]) => void): void;
|
|
14
|
+
export {};
|