@wix/ditto-codegen-public 1.0.181 → 1.0.183
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/examples-apps/custom-element/countdown-widget/components/ColorPickerField.tsx +27 -0
- package/dist/examples-apps/custom-element/countdown-widget/components/FontPickerField.tsx +34 -0
- package/dist/examples-apps/custom-element/countdown-widget/components/Separator.tsx +10 -0
- package/dist/examples-apps/custom-element/countdown-widget/components/TimeBlock.tsx +23 -0
- package/dist/examples-apps/custom-element/countdown-widget/extensions.ts +18 -0
- package/dist/examples-apps/custom-element/countdown-widget/panel.tsx +146 -0
- package/dist/examples-apps/custom-element/countdown-widget/styles.ts +73 -0
- package/dist/examples-apps/custom-element/countdown-widget/utils.ts +46 -0
- package/dist/examples-apps/custom-element/countdown-widget/widget.tsx +97 -0
- package/dist/out.js +124 -19
- package/dist/wix-cli-templates/src/site/widgets/custom-elements/my-widget/panel.tsx +53 -0
- package/dist/wix-cli-templates/src/site/widgets/custom-elements/my-widget/widget.tsx +33 -4
- package/package.json +2 -2
- package/dist/examples-apps/custom-element/src/widgets/custom-elements/countdown-timer/widget.tsx +0 -493
package/dist/examples-apps/custom-element/src/widgets/custom-elements/countdown-timer/widget.tsx
DELETED
|
@@ -1,493 +0,0 @@
|
|
|
1
|
-
import { items } from '@wix/data';
|
|
2
|
-
import { window as wixWindow } from '@wix/site-window';
|
|
3
|
-
|
|
4
|
-
interface CountdownConfig {
|
|
5
|
-
_id: string;
|
|
6
|
-
targetDate: Date;
|
|
7
|
-
title?: string;
|
|
8
|
-
isActive: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface TimeRemaining {
|
|
12
|
-
days: number;
|
|
13
|
-
hours: number;
|
|
14
|
-
minutes: number;
|
|
15
|
-
seconds: number;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export default class extends HTMLElement {
|
|
19
|
-
private intervalId: number | null = null;
|
|
20
|
-
private config: CountdownConfig | null = null;
|
|
21
|
-
private isEditorMode = false;
|
|
22
|
-
|
|
23
|
-
public async connectedCallback(): Promise<void> {
|
|
24
|
-
await this.checkEnvironment();
|
|
25
|
-
if (this.isEditorMode) {
|
|
26
|
-
this.renderEditorPlaceholder();
|
|
27
|
-
} else {
|
|
28
|
-
await this.loadConfig();
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
public disconnectedCallback(): void {
|
|
33
|
-
if (this.intervalId !== null) {
|
|
34
|
-
clearInterval(this.intervalId);
|
|
35
|
-
this.intervalId = null;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
private async checkEnvironment(): Promise<void> {
|
|
40
|
-
try {
|
|
41
|
-
const currentViewMode = await wixWindow.viewMode();
|
|
42
|
-
this.isEditorMode = currentViewMode === 'Editor';
|
|
43
|
-
} catch (error) {
|
|
44
|
-
console.error('Failed to check view mode:', error);
|
|
45
|
-
this.isEditorMode = false;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
private async loadConfig(): Promise<void> {
|
|
50
|
-
try {
|
|
51
|
-
const result = await items.query('countdown-config')
|
|
52
|
-
.eq('isActive', true)
|
|
53
|
-
.limit(1)
|
|
54
|
-
.find();
|
|
55
|
-
|
|
56
|
-
if (result.items.length > 0) {
|
|
57
|
-
const item = result.items[0];
|
|
58
|
-
this.config = {
|
|
59
|
-
_id: item._id as string,
|
|
60
|
-
targetDate: new Date(item.targetDate as string),
|
|
61
|
-
title: item.title as string | undefined,
|
|
62
|
-
isActive: item.isActive as boolean
|
|
63
|
-
};
|
|
64
|
-
this.render();
|
|
65
|
-
this.startCountdown();
|
|
66
|
-
} else {
|
|
67
|
-
this.renderNoConfig();
|
|
68
|
-
}
|
|
69
|
-
} catch (error) {
|
|
70
|
-
console.error('Failed to load countdown config:', error);
|
|
71
|
-
this.renderError();
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
private calculateTimeRemaining(targetDate: Date): TimeRemaining | null {
|
|
76
|
-
const now = new Date().getTime();
|
|
77
|
-
const target = targetDate.getTime();
|
|
78
|
-
const difference = target - now;
|
|
79
|
-
|
|
80
|
-
if (difference <= 0) {
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const days = Math.floor(difference / (1000 * 60 * 60 * 24));
|
|
85
|
-
const hours = Math.floor((difference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
|
86
|
-
const minutes = Math.floor((difference % (1000 * 60 * 60)) / (1000 * 60));
|
|
87
|
-
const seconds = Math.floor((difference % (1000 * 60)) / 1000);
|
|
88
|
-
|
|
89
|
-
return { days, hours, minutes, seconds };
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
private startCountdown(): void {
|
|
93
|
-
if (!this.config || !this.config.isActive) {
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
this.intervalId = window.setInterval(() => {
|
|
98
|
-
this.updateCountdown();
|
|
99
|
-
}, 1000);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
private updateCountdown(): void {
|
|
103
|
-
if (!this.config) {
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const timeRemaining = this.calculateTimeRemaining(this.config.targetDate);
|
|
108
|
-
|
|
109
|
-
if (timeRemaining === null) {
|
|
110
|
-
this.renderExpired();
|
|
111
|
-
if (this.intervalId !== null) {
|
|
112
|
-
clearInterval(this.intervalId);
|
|
113
|
-
this.intervalId = null;
|
|
114
|
-
}
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
this.renderCountdown(timeRemaining);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
private render(): void {
|
|
122
|
-
if (!this.config) {
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const timeRemaining = this.calculateTimeRemaining(this.config.targetDate);
|
|
127
|
-
|
|
128
|
-
if (timeRemaining === null) {
|
|
129
|
-
this.renderExpired();
|
|
130
|
-
} else {
|
|
131
|
-
this.renderCountdown(timeRemaining);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
private getStyles(): string {
|
|
136
|
-
return `
|
|
137
|
-
<style>
|
|
138
|
-
/* Editor Placeholder Styles */
|
|
139
|
-
.editor-container {
|
|
140
|
-
font-family: 'Courier New', monospace;
|
|
141
|
-
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
142
|
-
color: #00ff88;
|
|
143
|
-
padding: 48px 24px;
|
|
144
|
-
border-radius: 16px;
|
|
145
|
-
text-align: center;
|
|
146
|
-
box-shadow: 0 8px 32px rgba(0, 255, 136, 0.2);
|
|
147
|
-
max-width: 600px;
|
|
148
|
-
margin: 0 auto;
|
|
149
|
-
border: 2px solid #00ff88;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
.editor-icon {
|
|
153
|
-
font-size: 64px;
|
|
154
|
-
margin-bottom: 16px;
|
|
155
|
-
animation: pulse 2s ease-in-out infinite;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
.editor-title {
|
|
159
|
-
font-size: 28px;
|
|
160
|
-
font-weight: 700;
|
|
161
|
-
margin: 0 0 12px 0;
|
|
162
|
-
text-shadow: 0 0 10px rgba(0, 255, 136, 0.5);
|
|
163
|
-
letter-spacing: 2px;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
.editor-message {
|
|
167
|
-
font-size: 16px;
|
|
168
|
-
opacity: 0.85;
|
|
169
|
-
margin: 0;
|
|
170
|
-
line-height: 1.6;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/* Countdown Timer Styles */
|
|
174
|
-
.timer-container {
|
|
175
|
-
font-family: 'Orbitron', 'Rajdhani', 'Exo 2', monospace, sans-serif;
|
|
176
|
-
background: linear-gradient(135deg, #0f2027 0%, #203a43 50%, #2c5364 100%);
|
|
177
|
-
color: #00d9ff;
|
|
178
|
-
padding: 40px 24px;
|
|
179
|
-
border-radius: 20px;
|
|
180
|
-
text-align: center;
|
|
181
|
-
box-shadow: 0 20px 60px rgba(0, 217, 255, 0.3), inset 0 0 40px rgba(0, 217, 255, 0.1);
|
|
182
|
-
max-width: 700px;
|
|
183
|
-
margin: 0 auto;
|
|
184
|
-
position: relative;
|
|
185
|
-
overflow: hidden;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
.timer-glow {
|
|
189
|
-
content: '';
|
|
190
|
-
position: absolute;
|
|
191
|
-
top: -50%;
|
|
192
|
-
left: -50%;
|
|
193
|
-
width: 200%;
|
|
194
|
-
height: 200%;
|
|
195
|
-
background: radial-gradient(circle, rgba(0, 217, 255, 0.1) 0%, transparent 70%);
|
|
196
|
-
animation: rotate 10s linear infinite;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
.timer-title {
|
|
200
|
-
font-size: 32px;
|
|
201
|
-
font-weight: 900;
|
|
202
|
-
margin: 0 0 12px 0;
|
|
203
|
-
text-shadow: 0 0 20px rgba(0, 217, 255, 0.8), 0 0 40px rgba(0, 217, 255, 0.4);
|
|
204
|
-
letter-spacing: 3px;
|
|
205
|
-
text-transform: uppercase;
|
|
206
|
-
position: relative;
|
|
207
|
-
z-index: 1;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
.timer-grid {
|
|
211
|
-
display: grid;
|
|
212
|
-
grid-template-columns: repeat(4, 1fr);
|
|
213
|
-
gap: 20px;
|
|
214
|
-
margin: 32px 0;
|
|
215
|
-
position: relative;
|
|
216
|
-
z-index: 1;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
.time-unit {
|
|
220
|
-
background: rgba(0, 217, 255, 0.15);
|
|
221
|
-
border-radius: 12px;
|
|
222
|
-
padding: 24px 12px;
|
|
223
|
-
backdrop-filter: blur(10px);
|
|
224
|
-
border: 2px solid rgba(0, 217, 255, 0.3);
|
|
225
|
-
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3), inset 0 0 20px rgba(0, 217, 255, 0.1);
|
|
226
|
-
transition: all 0.3s ease;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
.time-unit:hover {
|
|
230
|
-
transform: translateY(-4px);
|
|
231
|
-
border-color: rgba(0, 217, 255, 0.6);
|
|
232
|
-
box-shadow: 0 12px 40px rgba(0, 217, 255, 0.4), inset 0 0 30px rgba(0, 217, 255, 0.2);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
.time-number {
|
|
236
|
-
font-size: 48px;
|
|
237
|
-
font-weight: 900;
|
|
238
|
-
margin: 0;
|
|
239
|
-
text-shadow: 0 0 15px rgba(0, 217, 255, 0.8), 0 0 30px rgba(0, 217, 255, 0.4);
|
|
240
|
-
font-family: 'Orbitron', monospace;
|
|
241
|
-
letter-spacing: 2px;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
.time-label {
|
|
245
|
-
font-size: 13px;
|
|
246
|
-
text-transform: uppercase;
|
|
247
|
-
letter-spacing: 2px;
|
|
248
|
-
margin: 8px 0 0 0;
|
|
249
|
-
opacity: 0.9;
|
|
250
|
-
font-weight: 700;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/* Expired State Styles */
|
|
254
|
-
.expired-container {
|
|
255
|
-
font-family: 'Bebas Neue', 'Anton', 'Oswald', sans-serif;
|
|
256
|
-
background: linear-gradient(135deg, #2d1b00 0%, #5c3a1f 50%, #8b4513 100%);
|
|
257
|
-
color: #ffa500;
|
|
258
|
-
padding: 48px 24px;
|
|
259
|
-
border-radius: 20px;
|
|
260
|
-
text-align: center;
|
|
261
|
-
box-shadow: 0 20px 60px rgba(255, 165, 0, 0.4), inset 0 0 40px rgba(255, 165, 0, 0.1);
|
|
262
|
-
max-width: 600px;
|
|
263
|
-
margin: 0 auto;
|
|
264
|
-
border: 3px solid #ffa500;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
.expired-icon {
|
|
268
|
-
font-size: 80px;
|
|
269
|
-
margin-bottom: 16px;
|
|
270
|
-
animation: bounce 1s ease-in-out infinite;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
.expired-title {
|
|
274
|
-
font-size: 48px;
|
|
275
|
-
font-weight: 900;
|
|
276
|
-
margin: 0 0 16px 0;
|
|
277
|
-
text-shadow: 0 0 20px rgba(255, 165, 0, 0.8), 0 4px 8px rgba(0, 0, 0, 0.5);
|
|
278
|
-
letter-spacing: 4px;
|
|
279
|
-
text-transform: uppercase;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
.expired-message {
|
|
283
|
-
font-size: 20px;
|
|
284
|
-
margin: 0;
|
|
285
|
-
opacity: 0.95;
|
|
286
|
-
font-weight: 600;
|
|
287
|
-
letter-spacing: 1px;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/* No Config State Styles */
|
|
291
|
-
.no-config-container {
|
|
292
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
293
|
-
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
|
294
|
-
color: #2c3e50;
|
|
295
|
-
padding: 40px 24px;
|
|
296
|
-
border-radius: 16px;
|
|
297
|
-
text-align: center;
|
|
298
|
-
border: 2px dashed #95a5a6;
|
|
299
|
-
max-width: 500px;
|
|
300
|
-
margin: 0 auto;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
.no-config-icon {
|
|
304
|
-
font-size: 64px;
|
|
305
|
-
margin-bottom: 16px;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
.no-config-title {
|
|
309
|
-
font-size: 24px;
|
|
310
|
-
font-weight: 700;
|
|
311
|
-
margin: 0 0 12px 0;
|
|
312
|
-
color: #34495e;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
.no-config-message {
|
|
316
|
-
font-size: 16px;
|
|
317
|
-
margin: 0;
|
|
318
|
-
line-height: 1.6;
|
|
319
|
-
color: #7f8c8d;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
/* Error State Styles */
|
|
323
|
-
.error-container {
|
|
324
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
325
|
-
background: #fff5f5;
|
|
326
|
-
color: #c53030;
|
|
327
|
-
padding: 32px 24px;
|
|
328
|
-
border-radius: 12px;
|
|
329
|
-
text-align: center;
|
|
330
|
-
border: 2px solid #fc8181;
|
|
331
|
-
max-width: 500px;
|
|
332
|
-
margin: 0 auto;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
.error-icon {
|
|
336
|
-
font-size: 48px;
|
|
337
|
-
margin-bottom: 12px;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
.error-message {
|
|
341
|
-
margin: 0;
|
|
342
|
-
font-size: 16px;
|
|
343
|
-
font-weight: 600;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
/* Animations */
|
|
347
|
-
@keyframes pulse {
|
|
348
|
-
0%, 100% {
|
|
349
|
-
opacity: 1;
|
|
350
|
-
transform: scale(1);
|
|
351
|
-
}
|
|
352
|
-
50% {
|
|
353
|
-
opacity: 0.6;
|
|
354
|
-
transform: scale(1.1);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
@keyframes rotate {
|
|
359
|
-
from {
|
|
360
|
-
transform: rotate(0deg);
|
|
361
|
-
}
|
|
362
|
-
to {
|
|
363
|
-
transform: rotate(360deg);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
@keyframes bounce {
|
|
368
|
-
0%, 100% {
|
|
369
|
-
transform: translateY(0);
|
|
370
|
-
}
|
|
371
|
-
50% {
|
|
372
|
-
transform: translateY(-20px);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
/* Responsive Styles */
|
|
377
|
-
@media (max-width: 600px) {
|
|
378
|
-
.timer-grid {
|
|
379
|
-
grid-template-columns: repeat(2, 1fr);
|
|
380
|
-
gap: 16px;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
.time-number {
|
|
384
|
-
font-size: 36px;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
.timer-container {
|
|
388
|
-
padding: 24px 16px;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
.timer-title {
|
|
392
|
-
font-size: 24px;
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
</style>
|
|
396
|
-
`;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
private renderEditorPlaceholder(): void {
|
|
400
|
-
this.innerHTML = `
|
|
401
|
-
${this.getStyles()}
|
|
402
|
-
<div class="editor-container" data-testid="countdown-timer-editor">
|
|
403
|
-
<div class="editor-icon">⏱️</div>
|
|
404
|
-
<h2 class="editor-title">COUNTDOWN TIMER</h2>
|
|
405
|
-
<p class="editor-message">
|
|
406
|
-
This widget will display a live countdown on your published site.<br>
|
|
407
|
-
Configure the target date in your dashboard to get started.
|
|
408
|
-
</p>
|
|
409
|
-
</div>
|
|
410
|
-
`;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
private renderCountdown(timeRemaining: TimeRemaining): void {
|
|
414
|
-
if (!this.config) {
|
|
415
|
-
return;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
const timeUnits = [
|
|
419
|
-
{ value: timeRemaining.days, label: 'Days' },
|
|
420
|
-
{ value: timeRemaining.hours, label: 'Hours' },
|
|
421
|
-
{ value: timeRemaining.minutes, label: 'Minutes' },
|
|
422
|
-
{ value: timeRemaining.seconds, label: 'Seconds' }
|
|
423
|
-
];
|
|
424
|
-
|
|
425
|
-
const timeUnitsHtml = timeUnits
|
|
426
|
-
.map(unit => `
|
|
427
|
-
<div class="time-unit">
|
|
428
|
-
<div class="time-number">${unit.value.toString().padStart(2, '0')}</div>
|
|
429
|
-
<div class="time-label">${unit.label}</div>
|
|
430
|
-
</div>
|
|
431
|
-
`)
|
|
432
|
-
.join('');
|
|
433
|
-
|
|
434
|
-
const titleHtml = this.config.title
|
|
435
|
-
? `<h2 class="timer-title">${this.escapeHtml(this.config.title)}</h2>`
|
|
436
|
-
: '';
|
|
437
|
-
|
|
438
|
-
this.innerHTML = `
|
|
439
|
-
${this.getStyles()}
|
|
440
|
-
<div class="timer-container" data-testid="countdown-timer">
|
|
441
|
-
<div class="timer-glow"></div>
|
|
442
|
-
${titleHtml}
|
|
443
|
-
<div class="timer-grid">
|
|
444
|
-
${timeUnitsHtml}
|
|
445
|
-
</div>
|
|
446
|
-
</div>
|
|
447
|
-
`;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
private renderExpired(): void {
|
|
451
|
-
const message = this.config && this.config.title
|
|
452
|
-
? `${this.escapeHtml(this.config.title)} has ended.`
|
|
453
|
-
: 'The countdown has ended.';
|
|
454
|
-
|
|
455
|
-
this.innerHTML = `
|
|
456
|
-
${this.getStyles()}
|
|
457
|
-
<div class="expired-container" data-testid="countdown-timer-expired">
|
|
458
|
-
<div class="expired-icon">🎉</div>
|
|
459
|
-
<h2 class="expired-title">Time's Up!</h2>
|
|
460
|
-
<p class="expired-message">${message}</p>
|
|
461
|
-
</div>
|
|
462
|
-
`;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
private renderNoConfig(): void {
|
|
466
|
-
this.innerHTML = `
|
|
467
|
-
${this.getStyles()}
|
|
468
|
-
<div class="no-config-container" data-testid="countdown-timer-no-config">
|
|
469
|
-
<div class="no-config-icon">⚙️</div>
|
|
470
|
-
<h2 class="no-config-title">No Active Countdown</h2>
|
|
471
|
-
<p class="no-config-message">
|
|
472
|
-
Please configure a countdown timer in your dashboard to display it here.
|
|
473
|
-
</p>
|
|
474
|
-
</div>
|
|
475
|
-
`;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
private renderError(): void {
|
|
479
|
-
this.innerHTML = `
|
|
480
|
-
${this.getStyles()}
|
|
481
|
-
<div class="error-container" data-testid="countdown-timer-error">
|
|
482
|
-
<div class="error-icon">⚠️</div>
|
|
483
|
-
<p class="error-message">Unable to load countdown timer</p>
|
|
484
|
-
</div>
|
|
485
|
-
`;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
private escapeHtml(text: string): string {
|
|
489
|
-
const div = document.createElement('div');
|
|
490
|
-
div.textContent = text;
|
|
491
|
-
return div.innerHTML;
|
|
492
|
-
}
|
|
493
|
-
}
|