@lovett/ui 0.0.1

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.
Files changed (102) hide show
  1. package/README.md +28 -0
  2. package/package.json +53 -0
  3. package/src/accordion.tsx +153 -0
  4. package/src/atoms.tsx +197 -0
  5. package/src/badge.tsx +91 -0
  6. package/src/brand-logo-tile.tsx +200 -0
  7. package/src/bulk-action-bar.tsx +104 -0
  8. package/src/button-group.tsx +46 -0
  9. package/src/button.tsx +93 -0
  10. package/src/calculator-shell-v2.tsx +380 -0
  11. package/src/calculator-shell.tsx +310 -0
  12. package/src/card.tsx +369 -0
  13. package/src/checkbox.tsx +114 -0
  14. package/src/chip-nav.tsx +347 -0
  15. package/src/choropleth.tsx +62 -0
  16. package/src/code-block.tsx +235 -0
  17. package/src/completion-ring.tsx +119 -0
  18. package/src/copy-field.tsx +169 -0
  19. package/src/data-grid/column-options-menu.tsx +127 -0
  20. package/src/data-grid/data-grid.tsx +391 -0
  21. package/src/data-grid/drawer-panel.tsx +146 -0
  22. package/src/data-grid/index.ts +29 -0
  23. package/src/data-grid/sortable-parts.tsx +215 -0
  24. package/src/data-grid/table-body.tsx +238 -0
  25. package/src/data-grid/table-elements.tsx +135 -0
  26. package/src/data-grid/table-header.tsx +95 -0
  27. package/src/data-grid/table-overlays.tsx +78 -0
  28. package/src/data-grid/table-parts.tsx +15 -0
  29. package/src/data-grid/table-summary-footer.tsx +101 -0
  30. package/src/data-grid/table-view.tsx +176 -0
  31. package/src/data-grid/types.ts +141 -0
  32. package/src/data-grid/use-grid-columns.ts +311 -0
  33. package/src/data-grid/use-grid-editing.ts +154 -0
  34. package/src/device-frame.tsx +274 -0
  35. package/src/dropdown-button.tsx +221 -0
  36. package/src/dropdown-menu.tsx +450 -0
  37. package/src/empty-state.tsx +85 -0
  38. package/src/file-upload/file-drop-zone.tsx +156 -0
  39. package/src/file-upload/file-preview-grid.tsx +39 -0
  40. package/src/file-upload/file-preview-item.tsx +175 -0
  41. package/src/file-upload/file-thumbnail.tsx +98 -0
  42. package/src/file-upload/file-upload-button.tsx +95 -0
  43. package/src/file-upload/index.ts +13 -0
  44. package/src/file-upload/use-file-upload.ts +371 -0
  45. package/src/filter-bar.tsx +125 -0
  46. package/src/filter-dropdown.tsx +289 -0
  47. package/src/floating-drawer.tsx +420 -0
  48. package/src/folder-card.tsx +188 -0
  49. package/src/folder-tree-picker.tsx +511 -0
  50. package/src/format.ts +117 -0
  51. package/src/frame-stack.tsx +73 -0
  52. package/src/hero-form-card.tsx +471 -0
  53. package/src/index.ts +372 -0
  54. package/src/input.tsx +130 -0
  55. package/src/kbd.tsx +41 -0
  56. package/src/lib/layout.ts +22 -0
  57. package/src/lib/utils.ts +6 -0
  58. package/src/markdown.tsx +214 -0
  59. package/src/menu-button.tsx +142 -0
  60. package/src/meta-previews/CreativeMedia.tsx +119 -0
  61. package/src/meta-previews/InstagramExplorePreview.tsx +340 -0
  62. package/src/meta-previews/InstagramFeedCarousel.tsx +245 -0
  63. package/src/meta-previews/InstagramFeedPreview.tsx +259 -0
  64. package/src/meta-previews/MetaFeedCarousel.tsx +228 -0
  65. package/src/meta-previews/MetaFeedPreview.tsx +294 -0
  66. package/src/meta-previews/MetaInstreamPreview.tsx +135 -0
  67. package/src/meta-previews/MetaMessengerPreview.tsx +795 -0
  68. package/src/meta-previews/MetaReelPreview.tsx +750 -0
  69. package/src/meta-previews/MetaRightColumnPreview.tsx +245 -0
  70. package/src/meta-previews/MetaSearchPreview.tsx +113 -0
  71. package/src/meta-previews/MetaStoryPreview.tsx +1132 -0
  72. package/src/meta-previews/ProfileAvatar.tsx +79 -0
  73. package/src/meta-previews/defaults.ts +27 -0
  74. package/src/meta-previews/index.ts +37 -0
  75. package/src/meta-previews/types.ts +219 -0
  76. package/src/metric-card.tsx +117 -0
  77. package/src/modal.tsx +131 -0
  78. package/src/option-tile.tsx +252 -0
  79. package/src/page-hero.tsx +78 -0
  80. package/src/page-shell.tsx +440 -0
  81. package/src/pagination.tsx +86 -0
  82. package/src/pill-button.tsx +103 -0
  83. package/src/profile-section.tsx +389 -0
  84. package/src/progress-bar.tsx +84 -0
  85. package/src/radio.tsx +172 -0
  86. package/src/range-slider.tsx +161 -0
  87. package/src/resizable.tsx +282 -0
  88. package/src/search-bar.tsx +81 -0
  89. package/src/segmented-pill.tsx +143 -0
  90. package/src/shell.tsx +464 -0
  91. package/src/slider.tsx +107 -0
  92. package/src/sortable-table.tsx +198 -0
  93. package/src/sortable.tsx +137 -0
  94. package/src/stat-row.tsx +132 -0
  95. package/src/stats-grid.tsx +38 -0
  96. package/src/step-loader.tsx +141 -0
  97. package/src/styles.css +562 -0
  98. package/src/tabs.tsx +143 -0
  99. package/src/tag-chip-input.tsx +394 -0
  100. package/src/textarea.tsx +39 -0
  101. package/src/toast.tsx +59 -0
  102. package/src/tokens.css +579 -0
@@ -0,0 +1,750 @@
1
+ import React from 'react';
2
+ import type { PreviewAdData, PreviewPlatform } from './types';
3
+ import { CreativeMedia } from './CreativeMedia';
4
+ import { ProfileAvatar } from './ProfileAvatar';
5
+
6
+ interface MetaReelPreviewProps {
7
+ platform: PreviewPlatform;
8
+ adData: PreviewAdData;
9
+ dominantColor?: string | undefined;
10
+ accentColor?: string | undefined;
11
+ }
12
+
13
+ /**
14
+ * Facebook/Instagram Reels Preview
15
+ * Exact styling extracted from Facebook Creative Hub
16
+ * Reference: creative-spec/references/META_DESIGN_TOKENS.md
17
+ * Source: Facebook-Reels-singleimage-1080x1080.html extraction
18
+ * Container: 320x567px (9:16 ratio)
19
+ */
20
+ export const MetaReelPreview: React.FC<MetaReelPreviewProps> = ({
21
+ platform,
22
+ adData,
23
+ dominantColor = 'rgb(40, 40, 40)',
24
+ }) => {
25
+
26
+ // Instagram animation states
27
+ const [instagramFrame, setInstagramFrame] = React.useState<'transparent' | 'solid' | 'expanded'>('transparent');
28
+
29
+ // Facebook Reels exact design tokens - extracted from Creative Hub
30
+ const reels = {
31
+ // Container - 320x567px (9:16 ratio)
32
+ containerWidth: 320,
33
+ containerHeight: 567,
34
+ // FB Reels = solid black, IG Reels = dominant color gradient
35
+ containerBg: platform === 'instagram'
36
+ ? `linear-gradient(to bottom, ${dominantColor}, rgb(0, 0, 0))`
37
+ : 'linear-gradient(rgb(0, 0, 0), rgb(0, 0, 0))',
38
+ containerBorder: '1px solid rgba(0, 0, 0, 0.3)',
39
+ fontFamily: 'system-ui, -apple-system, "system-ui", ".SFNSText-Regular", sans-serif',
40
+
41
+ // Image centered in black container
42
+ imageWidth: 318,
43
+ imageHeight: 318,
44
+ imageMarginTop: 123.5, // Centers in 565px height
45
+
46
+ // Sponsored pill (top left)
47
+ sponsoredPillTop: 10,
48
+ sponsoredPillLeft: 10,
49
+ sponsoredPillPadding: '4px 10px',
50
+ sponsoredPillBg: 'rgba(24, 25, 26, 0.6)',
51
+ sponsoredPillRadius: '50px',
52
+ sponsoredPillSize: 12,
53
+ sponsoredPillColor: 'rgb(255, 255, 255)',
54
+
55
+ // Profile section (bottom overlay)
56
+ profileTop: 422.922,
57
+ profileLeft: 16,
58
+ profileWidth: 250,
59
+
60
+ // Profile image
61
+ profileImageSize: 30,
62
+ profileImageRadius: '50px',
63
+
64
+ // Page name
65
+ pageNameSize: 14,
66
+ pageNameWeight: 400,
67
+ pageNameColor: 'rgb(255, 255, 255)',
68
+ pageNameLineHeight: '19px',
69
+
70
+ // Primary text
71
+ primaryTextSize: 14,
72
+ primaryTextWeight: 400,
73
+ primaryTextColor: 'rgb(252, 252, 252)',
74
+ primaryTextLineHeight: '16px',
75
+
76
+ // CTA button
77
+ ctaButtonWidth: 250,
78
+ ctaButtonHeight: 34,
79
+ ctaButtonMarginTop: 12,
80
+ ctaButtonRadius: 5,
81
+ ctaButtonTextSize: 14,
82
+ ctaButtonTextWeight: 700,
83
+ ctaButtonTextColor: 'rgba(0, 0, 0, 0.85)',
84
+ ctaButtonLineHeight: '20px',
85
+ // CTA button — white bg with dark text per 2026 extraction
86
+ ctaButtonBg: 'rgba(228, 230, 235, 0.95)',
87
+
88
+ // Action icons (right side)
89
+ actionIconsTop: 483,
90
+ actionIconsRight: 10,
91
+ actionIconsWidth: 36,
92
+ actionIconSize: 24,
93
+ actionIconMarginLeft: 6,
94
+ actionIconBg: 'rgba(228, 230, 235, 0)',
95
+ actionIconRadius: 15,
96
+ };
97
+
98
+ // Truncate text to 30 characters for collapsed view
99
+ const truncateText = (text: string, limit: number = 30) => {
100
+ if (!text || text.length <= limit) return text;
101
+ return text.substring(0, limit);
102
+ };
103
+
104
+ // Instagram Reels animation cycle
105
+ React.useEffect(() => {
106
+ if (platform !== 'instagram') return;
107
+
108
+ const cycle = () => {
109
+ // Transparent button (0-5s)
110
+ setInstagramFrame('transparent');
111
+
112
+ setTimeout(() => {
113
+ // Solid white button (5-10s)
114
+ setInstagramFrame('solid');
115
+ }, 5000);
116
+
117
+ setTimeout(() => {
118
+ // Expanded CTA (10-30s)
119
+ setInstagramFrame('expanded');
120
+ }, 10000);
121
+ };
122
+
123
+ // Start cycle immediately
124
+ cycle();
125
+
126
+ // Repeat every 30 seconds
127
+ const interval = setInterval(cycle, 30000);
128
+
129
+ return () => clearInterval(interval);
130
+ }, [platform]);
131
+
132
+ return (
133
+ <div
134
+ className="meta-preview"
135
+ style={{
136
+ width: '100%',
137
+ height: '100%',
138
+ display: 'flex',
139
+ alignItems: 'center',
140
+ justifyContent: 'center',
141
+ fontFamily: reels.fontFamily,
142
+ }}
143
+ >
144
+ <div
145
+ style={{
146
+ width: `${reels.containerWidth}px`, maxWidth: '100%',
147
+ height: `${reels.containerHeight}px`,
148
+ backgroundImage: reels.containerBg,
149
+ border: reels.containerBorder,
150
+ borderRadius: '8px',
151
+ overflow: 'hidden',
152
+ position: 'relative',
153
+ }}
154
+ >
155
+ {/* Creative Image - Centered vertically */}
156
+ {adData.creativeImage ? (
157
+ <CreativeMedia
158
+ src={adData.creativeImage}
159
+ mediaType={adData.creativeMediaType}
160
+ alt="Reel ad"
161
+ autoPlay
162
+ showPlayOverlay={false}
163
+ style={{
164
+ position: 'absolute',
165
+ width: `${reels.imageWidth}px`,
166
+ height: `${reels.imageHeight}px`,
167
+ top: `${reels.imageMarginTop}px`,
168
+ left: '1px',
169
+ objectFit: 'cover',
170
+ }}
171
+ />
172
+ ) : (
173
+ <div
174
+ style={{
175
+ position: 'absolute',
176
+ width: `${reels.imageWidth}px`,
177
+ height: `${reels.imageHeight}px`,
178
+ top: `${reels.imageMarginTop}px`,
179
+ left: '1px',
180
+ backgroundColor: '#333',
181
+ display: 'flex',
182
+ alignItems: 'center',
183
+ justifyContent: 'center',
184
+ }}
185
+ >
186
+ <div style={{ textAlign: 'center', color: 'rgba(255, 255, 255, 0.6)' }}>
187
+ <div
188
+ style={{
189
+ width: '64px',
190
+ height: '64px',
191
+ borderRadius: '8px',
192
+ backgroundColor: 'rgba(255, 255, 255, 0.2)',
193
+ margin: '0 auto 8px',
194
+ }}
195
+ />
196
+ <p style={{ fontSize: '14px', margin: 0 }}>Reel Preview</p>
197
+ <p style={{ fontSize: '12px', margin: '4px 0 0' }}>9:16 Format</p>
198
+ </div>
199
+ </div>
200
+ )}
201
+
202
+ {/* Top gradient overlay */}
203
+ <div
204
+ style={{
205
+ position: 'absolute',
206
+ top: 0,
207
+ left: 0,
208
+ width: `${reels.imageWidth}px`,
209
+ height: '98px',
210
+ background: 'linear-gradient(180deg, rgba(0,0,0,0.4) 0%, transparent 100%)',
211
+ pointerEvents: 'none',
212
+ }}
213
+ />
214
+
215
+ {/* Bottom gradient overlay */}
216
+ <div
217
+ style={{
218
+ position: 'absolute',
219
+ bottom: 0,
220
+ left: 0,
221
+ width: `${reels.imageWidth}px`,
222
+ height: '271px',
223
+ background: 'linear-gradient(0deg, rgba(0,0,0,0.6) 0%, transparent 100%)',
224
+ pointerEvents: 'none',
225
+ }}
226
+ />
227
+
228
+ {/* Sponsored Pill - Top left */}
229
+ <div
230
+ style={{
231
+ position: 'absolute',
232
+ top: `${reels.sponsoredPillTop}px`,
233
+ left: `${reels.sponsoredPillLeft}px`,
234
+ padding: reels.sponsoredPillPadding,
235
+ backgroundColor: reels.sponsoredPillBg,
236
+ borderRadius: reels.sponsoredPillRadius,
237
+ }}
238
+ >
239
+ <span
240
+ style={{
241
+ fontSize: `${reels.sponsoredPillSize}px`,
242
+ fontWeight: 400,
243
+ color: reels.sponsoredPillColor,
244
+ }}
245
+ >
246
+ Sponsored
247
+ </span>
248
+ </div>
249
+
250
+ {/* Play Icon Overlay - Center */}
251
+ {adData.creativeImage && (
252
+ <div
253
+ style={{
254
+ position: 'absolute',
255
+ top: '50%',
256
+ left: '50%',
257
+ transform: 'translate(-50%, -50%)',
258
+ width: '64px',
259
+ height: '64px',
260
+ backgroundColor: 'rgba(0, 0, 0, 0.4)',
261
+ borderRadius: '50%',
262
+ display: 'flex',
263
+ alignItems: 'center',
264
+ justifyContent: 'center',
265
+ }}
266
+ >
267
+ <svg width="32" height="32" viewBox="0 0 24 24" fill="white">
268
+ <polygon points="5 3 19 12 5 21 5 3" />
269
+ </svg>
270
+ </div>
271
+ )}
272
+
273
+ {platform === 'facebook' ? (
274
+ /* Facebook Reels Layout — anchored to bottom */
275
+ <>
276
+ {/* Bottom content area */}
277
+ <div
278
+ style={{
279
+ position: 'absolute',
280
+ bottom: '12px',
281
+ left: `${reels.profileLeft}px`,
282
+ right: '50px',
283
+ zIndex: 10,
284
+ }}
285
+ >
286
+ {/* Profile Image + Page Name */}
287
+ <div
288
+ style={{
289
+ display: 'flex',
290
+ alignItems: 'center',
291
+ gap: '8px',
292
+ marginBottom: '6px',
293
+ }}
294
+ >
295
+ <div
296
+ style={{
297
+ width: `${reels.profileImageSize}px`,
298
+ height: `${reels.profileImageSize}px`,
299
+ borderRadius: reels.profileImageRadius,
300
+ overflow: 'hidden',
301
+ flexShrink: 0,
302
+ }}
303
+ >
304
+ <ProfileAvatar src={adData.profileImage} name={adData.brandName} fontSize={12} />
305
+ </div>
306
+ <span
307
+ style={{
308
+ fontSize: `${reels.pageNameSize}px`,
309
+ fontWeight: reels.pageNameWeight,
310
+ lineHeight: reels.pageNameLineHeight,
311
+ color: reels.pageNameColor,
312
+ overflow: 'hidden',
313
+ textOverflow: 'ellipsis',
314
+ whiteSpace: 'nowrap',
315
+ }}
316
+ >
317
+ {adData.brandName}
318
+ </span>
319
+ </div>
320
+
321
+ {/* Primary Text */}
322
+ <div style={{ marginBottom: '6px' }}>
323
+ <span
324
+ style={{
325
+ fontSize: `${reels.primaryTextSize}px`,
326
+ fontWeight: reels.primaryTextWeight,
327
+ lineHeight: reels.primaryTextLineHeight,
328
+ color: reels.primaryTextColor,
329
+ }}
330
+ >
331
+ {truncateText(adData.primaryText || adData.headline, 30)}
332
+ {(adData.primaryText || adData.headline || '').length > 30 && (
333
+ <span style={{ color: 'rgba(255,255,255,0.7)' }}>...more</span>
334
+ )}
335
+ </span>
336
+ </div>
337
+
338
+ {/* CTA Button */}
339
+ <div
340
+ style={{
341
+ display: 'flex',
342
+ alignItems: 'center',
343
+ justifyContent: 'center',
344
+ width: `${reels.ctaButtonWidth}px`,
345
+ height: `${reels.ctaButtonHeight}px`,
346
+ backgroundColor: reels.ctaButtonBg,
347
+ borderRadius: `${reels.ctaButtonRadius}px`,
348
+ cursor: 'pointer',
349
+ }}
350
+ >
351
+ <span
352
+ style={{
353
+ fontSize: `${reels.ctaButtonTextSize}px`,
354
+ fontWeight: reels.ctaButtonTextWeight,
355
+ lineHeight: reels.ctaButtonLineHeight,
356
+ color: reels.ctaButtonTextColor,
357
+ }}
358
+ >
359
+ {adData.callToAction || 'Learn more'}
360
+ </span>
361
+ </div>
362
+ </div>
363
+
364
+ {/* Action Icons - Right side, anchored to bottom */}
365
+ <div
366
+ style={{
367
+ position: 'absolute',
368
+ bottom: '12px',
369
+ right: '10px',
370
+ width: `${reels.actionIconsWidth}px`,
371
+ display: 'flex',
372
+ flexDirection: 'column',
373
+ alignItems: 'center',
374
+ gap: '14px',
375
+ zIndex: 10,
376
+ }}
377
+ >
378
+ {/* Like */}
379
+ <button
380
+ style={{
381
+ width: `${reels.actionIconSize}px`,
382
+ height: `${reels.actionIconSize}px`,
383
+ marginLeft: `${reels.actionIconMarginLeft}px`,
384
+ backgroundColor: reels.actionIconBg,
385
+ borderRadius: `${reels.actionIconRadius}px`,
386
+ border: 'none',
387
+ cursor: 'pointer',
388
+ display: 'flex',
389
+ alignItems: 'center',
390
+ justifyContent: 'center',
391
+ }}
392
+ >
393
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2">
394
+ <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/>
395
+ </svg>
396
+ </button>
397
+
398
+ {/* Comment */}
399
+ <button
400
+ style={{
401
+ width: `${reels.actionIconSize}px`,
402
+ height: `${reels.actionIconSize}px`,
403
+ marginLeft: `${reels.actionIconMarginLeft}px`,
404
+ backgroundColor: reels.actionIconBg,
405
+ borderRadius: `${reels.actionIconRadius}px`,
406
+ border: 'none',
407
+ cursor: 'pointer',
408
+ display: 'flex',
409
+ alignItems: 'center',
410
+ justifyContent: 'center',
411
+ }}
412
+ >
413
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2">
414
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
415
+ </svg>
416
+ </button>
417
+
418
+ {/* Share */}
419
+ <button
420
+ style={{
421
+ width: `${reels.actionIconSize}px`,
422
+ height: `${reels.actionIconSize}px`,
423
+ marginLeft: `${reels.actionIconMarginLeft}px`,
424
+ backgroundColor: reels.actionIconBg,
425
+ borderRadius: `${reels.actionIconRadius}px`,
426
+ border: 'none',
427
+ cursor: 'pointer',
428
+ display: 'flex',
429
+ alignItems: 'center',
430
+ justifyContent: 'center',
431
+ }}
432
+ >
433
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2">
434
+ <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/>
435
+ <polyline points="16 6 12 2 8 6"/>
436
+ <line x1="12" y1="2" x2="12" y2="15"/>
437
+ </svg>
438
+ </button>
439
+ </div>
440
+ </>
441
+ ) : (
442
+ /* Instagram Reels Layout - Animated */
443
+ <>
444
+ {/* Profile Section - Bottom overlay */}
445
+ <div
446
+ style={{
447
+ position: 'absolute',
448
+ bottom: '12px',
449
+ left: '16px',
450
+ right: '50px',
451
+ zIndex: 10,
452
+ }}
453
+ >
454
+ {/* Profile Image + Page Name + Follow Button */}
455
+ <div
456
+ style={{
457
+ display: 'flex',
458
+ alignItems: 'center',
459
+ gap: '8px',
460
+ marginBottom: '12px',
461
+ }}
462
+ >
463
+ {/* Profile Image */}
464
+ <div
465
+ style={{
466
+ width: `${reels.profileImageSize}px`,
467
+ height: `${reels.profileImageSize}px`,
468
+ borderRadius: reels.profileImageRadius,
469
+ overflow: 'hidden',
470
+ flexShrink: 0,
471
+ border: '2px solid rgba(255, 255, 255, 0.2)',
472
+ }}
473
+ >
474
+ <ProfileAvatar
475
+ src={adData.profileImage}
476
+ name={adData.brandName}
477
+ fontSize={10}
478
+ background="linear-gradient(45deg, #f09433 0%, #e6683c 25%, #dc2743 50%, #cc2366 75%, #bc1888 100%)"
479
+ />
480
+ </div>
481
+
482
+ {/* Page Name */}
483
+ <span
484
+ style={{
485
+ fontSize: `${reels.pageNameSize}px`,
486
+ fontWeight: 600,
487
+ color: reels.pageNameColor,
488
+ flex: 1,
489
+ overflow: 'hidden',
490
+ textOverflow: 'ellipsis',
491
+ whiteSpace: 'nowrap',
492
+ }}
493
+ >
494
+ {adData.brandName}
495
+ </span>
496
+
497
+ {/* Follow Button */}
498
+ <button
499
+ style={{
500
+ backgroundColor: 'rgb(0, 149, 246)',
501
+ color: 'white',
502
+ fontSize: '12px',
503
+ fontWeight: 600,
504
+ padding: '6px 12px',
505
+ borderRadius: '8px',
506
+ border: 'none',
507
+ cursor: 'pointer',
508
+ flexShrink: 0,
509
+ }}
510
+ >
511
+ Follow
512
+ </button>
513
+ </div>
514
+
515
+ {/* Animated CTA Section */}
516
+ {instagramFrame === 'expanded' ? (
517
+ /* Frame 2: Expanded CTA Container */
518
+ <div
519
+ style={{
520
+ backgroundColor: 'rgba(38, 38, 38, 0.9)',
521
+ backdropFilter: 'blur(8px)',
522
+ borderRadius: '16px',
523
+ padding: '16px',
524
+ marginBottom: '8px',
525
+ }}
526
+ >
527
+ {/* Thumbnail + Headline */}
528
+ <div
529
+ style={{
530
+ display: 'flex',
531
+ gap: '12px',
532
+ alignItems: 'center',
533
+ marginBottom: '12px',
534
+ }}
535
+ >
536
+ {/* 1:1 Thumbnail */}
537
+ <div
538
+ style={{
539
+ width: '64px',
540
+ height: '64px',
541
+ borderRadius: '8px',
542
+ overflow: 'hidden',
543
+ flexShrink: 0,
544
+ backgroundColor: '#555',
545
+ }}
546
+ >
547
+ {adData.creativeImage ? (
548
+ <CreativeMedia
549
+ src={adData.creativeImage}
550
+ mediaType={adData.creativeMediaType}
551
+ alt="Ad preview"
552
+ autoPlay
553
+ showPlayOverlay={false}
554
+ style={{
555
+ width: '100%',
556
+ height: '100%',
557
+ objectFit: 'cover',
558
+ }}
559
+ />
560
+ ) : (
561
+ <div
562
+ style={{
563
+ width: '100%',
564
+ height: '100%',
565
+ display: 'flex',
566
+ alignItems: 'center',
567
+ justifyContent: 'center',
568
+ }}
569
+ >
570
+ <div style={{ width: '48px', height: '48px', backgroundColor: '#666', borderRadius: '4px' }} />
571
+ </div>
572
+ )}
573
+ </div>
574
+
575
+ {/* Headline */}
576
+ <div style={{ flex: 1, minWidth: 0 }}>
577
+ <span
578
+ style={{
579
+ fontSize: '11px',
580
+ fontWeight: 500,
581
+ color: 'white',
582
+ lineHeight: '14px',
583
+ }}
584
+ >
585
+ {adData.headline}
586
+ </span>
587
+ </div>
588
+ </div>
589
+
590
+ {/* CTA Button */}
591
+ <div
592
+ style={{
593
+ display: 'flex',
594
+ alignItems: 'center',
595
+ justifyContent: 'center',
596
+ width: '100%',
597
+ height: '40px',
598
+ backgroundColor: 'white',
599
+ borderRadius: '8px',
600
+ cursor: 'pointer',
601
+ }}
602
+ >
603
+ <span
604
+ style={{
605
+ fontSize: '14px',
606
+ fontWeight: 600,
607
+ color: 'black',
608
+ }}
609
+ >
610
+ {adData.callToAction || 'Learn more'}
611
+ </span>
612
+ </div>
613
+ </div>
614
+ ) : (
615
+ /* Frame 1: Simple CTA (transparent or solid) */
616
+ <div
617
+ style={{
618
+ display: 'flex',
619
+ alignItems: 'center',
620
+ justifyContent: 'center',
621
+ width: '100%',
622
+ height: '36px',
623
+ backgroundColor: instagramFrame === 'transparent' ? 'transparent' : 'white',
624
+ border: instagramFrame === 'transparent' ? '2px solid white' : 'none',
625
+ borderRadius: '8px',
626
+ marginBottom: '8px',
627
+ cursor: 'pointer',
628
+ transition: 'all 0.5s ease',
629
+ }}
630
+ >
631
+ <span
632
+ style={{
633
+ fontSize: '14px',
634
+ fontWeight: 600,
635
+ color: instagramFrame === 'transparent' ? 'white' : 'black',
636
+ }}
637
+ >
638
+ {adData.callToAction || 'Learn more'}
639
+ </span>
640
+ </div>
641
+ )}
642
+
643
+ {/* Primary Text */}
644
+ <span
645
+ style={{
646
+ display: 'block',
647
+ fontSize: `${reels.primaryTextSize}px`,
648
+ fontWeight: reels.primaryTextWeight,
649
+ lineHeight: reels.primaryTextLineHeight,
650
+ color: reels.primaryTextColor,
651
+ marginBottom: '4px',
652
+ }}
653
+ >
654
+ {truncateText(adData.primaryText || adData.headline, 25)}
655
+ {(adData.primaryText || adData.headline || '').length > 25 && (
656
+ <span style={{ color: 'rgba(255,255,255,0.7)' }}>...more</span>
657
+ )}
658
+ </span>
659
+
660
+ {/* Ad Tag */}
661
+ <span
662
+ style={{
663
+ fontSize: '12px',
664
+ color: 'rgba(255, 255, 255, 0.7)',
665
+ }}
666
+ >
667
+ Ad
668
+ </span>
669
+ </div>
670
+
671
+ {/* Right Side Actions - Instagram style, anchored to bottom */}
672
+ <div
673
+ style={{
674
+ position: 'absolute',
675
+ bottom: '12px',
676
+ right: '12px',
677
+ display: 'flex',
678
+ flexDirection: 'column',
679
+ alignItems: 'center',
680
+ gap: '14px',
681
+ zIndex: 10,
682
+ }}
683
+ >
684
+ {/* Like */}
685
+ <button
686
+ style={{
687
+ background: 'none',
688
+ border: 'none',
689
+ cursor: 'pointer',
690
+ padding: 0,
691
+ }}
692
+ >
693
+ <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2">
694
+ <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/>
695
+ </svg>
696
+ </button>
697
+
698
+ {/* Comment */}
699
+ <button
700
+ style={{
701
+ background: 'none',
702
+ border: 'none',
703
+ cursor: 'pointer',
704
+ padding: 0,
705
+ }}
706
+ >
707
+ <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2">
708
+ <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/>
709
+ </svg>
710
+ </button>
711
+
712
+ {/* Send */}
713
+ <button
714
+ style={{
715
+ background: 'none',
716
+ border: 'none',
717
+ cursor: 'pointer',
718
+ padding: 0,
719
+ }}
720
+ >
721
+ <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2">
722
+ <line x1="22" y1="2" x2="11" y2="13"/>
723
+ <polygon points="22 2 15 22 11 13 2 9 22 2"/>
724
+ </svg>
725
+ </button>
726
+
727
+ {/* More */}
728
+ <button
729
+ style={{
730
+ background: 'none',
731
+ border: 'none',
732
+ cursor: 'pointer',
733
+ padding: 0,
734
+ }}
735
+ >
736
+ <svg width="28" height="28" viewBox="0 0 24 24" fill="white">
737
+ <circle cx="6" cy="12" r="2" />
738
+ <circle cx="12" cy="12" r="2" />
739
+ <circle cx="18" cy="12" r="2" />
740
+ </svg>
741
+ </button>
742
+ </div>
743
+ </>
744
+ )}
745
+ </div>
746
+ </div>
747
+ );
748
+ };
749
+
750
+ MetaReelPreview.displayName = 'MetaReelPreview';