@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,245 @@
1
+ import React from 'react';
2
+ import type { PreviewAdData } from './types';
3
+ import { CreativeMedia } from './CreativeMedia';
4
+
5
+ interface MetaRightColumnPreviewProps {
6
+ adData: PreviewAdData;
7
+ }
8
+
9
+ /**
10
+ * Facebook Desktop Right Column Preview
11
+ * Exact styling extracted from Facebook Creative Hub
12
+ * Reference: creative-spec/references/META_DESIGN_TOKENS.md
13
+ * Source: desktop-right-column.html extraction
14
+ * Container: 375x206px
15
+ */
16
+ export const MetaRightColumnPreview: React.FC<MetaRightColumnPreviewProps> = ({
17
+ adData,
18
+ }) => {
19
+ // Facebook Right Column exact design tokens - extracted from Creative Hub
20
+ const rc = {
21
+ // Container
22
+ containerWidth: 375,
23
+ containerHeight: 206.648,
24
+ containerBg: 'rgb(255, 255, 255)',
25
+ fontFamily: 'system-ui, -apple-system, "system-ui", ".SFNSText-Regular", sans-serif',
26
+ baseColor: 'rgb(28, 30, 33)',
27
+ baseFontSize: 12,
28
+ baseLineHeight: '16.08px',
29
+
30
+ // Inner card
31
+ cardWidth: 343,
32
+ cardHeight: 190.648,
33
+ cardMargin: '0px 8px',
34
+ cardPadding: '8px',
35
+ cardRadius: 6,
36
+
37
+ // Image
38
+ imageWidth: 188.648,
39
+ imageHeight: 188.648,
40
+ imageMinHeight: 1,
41
+ imageMaxHeight: 205,
42
+ imageBorder: '1px solid rgba(0, 0, 0, 0.1)',
43
+ imageRadius: 8,
44
+
45
+ // Content section
46
+ contentWidth: 142.352,
47
+ contentPaddingLeft: 12,
48
+
49
+ // Headline
50
+ headlineSize: 15,
51
+ headlineWeight: 500,
52
+ headlineColor: 'rgb(8, 8, 9)',
53
+ headlineLineHeight: '19.64px',
54
+ headlineHeight: 39.28,
55
+
56
+ // Display Link
57
+ displayLinkSize: 13,
58
+ displayLinkWeight: 400,
59
+ displayLinkColor: 'rgb(101, 104, 108)',
60
+ displayLinkLineHeight: '18.22px',
61
+ };
62
+
63
+ return (
64
+ <div
65
+ className="meta-preview"
66
+ style={{
67
+ display: 'flex',
68
+ alignItems: 'center',
69
+ justifyContent: 'center',
70
+ width: '100%',
71
+ height: '100%',
72
+ fontFamily: rc.fontFamily,
73
+ fontSize: `${rc.baseFontSize}px`,
74
+ color: rc.baseColor,
75
+ lineHeight: rc.baseLineHeight,
76
+ }}
77
+ >
78
+ <div
79
+ style={{
80
+ width: `${rc.containerWidth}px`, maxWidth: '100%',
81
+ height: `${rc.containerHeight}px`,
82
+ backgroundColor: rc.containerBg,
83
+ overflow: 'visible',
84
+ }}
85
+ >
86
+ {/* Inner Card */}
87
+ <div
88
+ style={{
89
+ display: 'flex',
90
+ flexDirection: 'row',
91
+ width: `${rc.cardWidth}px`,
92
+ height: `${rc.cardHeight}px`,
93
+ margin: rc.cardMargin,
94
+ padding: rc.cardPadding,
95
+ borderRadius: `${rc.cardRadius}px`,
96
+ cursor: 'pointer',
97
+ }}
98
+ >
99
+ {/* Content wrapper */}
100
+ <div
101
+ style={{
102
+ display: 'flex',
103
+ flexDirection: 'row',
104
+ alignItems: 'center',
105
+ width: `${rc.cardWidth}px`,
106
+ height: `${rc.cardHeight}px`,
107
+ }}
108
+ >
109
+ {/* Image - 188.648x188.648px with 8px radius */}
110
+ <div
111
+ style={{
112
+ width: `${rc.imageWidth}px`,
113
+ height: `${rc.imageHeight}px`,
114
+ flexShrink: 0,
115
+ }}
116
+ >
117
+ {adData.creativeImage ? (
118
+ <CreativeMedia
119
+ src={adData.creativeImage}
120
+ mediaType={adData.creativeMediaType}
121
+ alt={adData.headline || 'Ad'}
122
+ style={{
123
+ width: `${rc.imageWidth}px`,
124
+ height: `${rc.imageHeight}px`,
125
+ minHeight: `${rc.imageMinHeight}px`,
126
+ maxHeight: `${rc.imageMaxHeight}px`,
127
+ border: rc.imageBorder,
128
+ borderRadius: `${rc.imageRadius}px`,
129
+ objectFit: 'cover',
130
+ display: 'block',
131
+ overflow: 'clip',
132
+ }}
133
+ />
134
+ ) : (
135
+ <div
136
+ style={{
137
+ width: `${rc.imageWidth}px`,
138
+ height: `${rc.imageHeight}px`,
139
+ minHeight: `${rc.imageMinHeight}px`,
140
+ maxHeight: `${rc.imageMaxHeight}px`,
141
+ border: rc.imageBorder,
142
+ borderRadius: `${rc.imageRadius}px`,
143
+ backgroundColor: 'var(--color-surface-100)',
144
+ display: 'flex',
145
+ alignItems: 'center',
146
+ justifyContent: 'center',
147
+ }}
148
+ >
149
+ <div style={{ textAlign: 'center', color: rc.displayLinkColor }}>
150
+ <div
151
+ style={{
152
+ width: '48px',
153
+ height: '48px',
154
+ backgroundColor: 'var(--color-surface-200)',
155
+ borderRadius: '8px',
156
+ margin: '0 auto 8px',
157
+ }}
158
+ />
159
+ <p style={{ fontSize: '12px', margin: 0 }}>1:1</p>
160
+ </div>
161
+ </div>
162
+ )}
163
+ </div>
164
+
165
+ {/* Text Content */}
166
+ <div
167
+ style={{
168
+ width: `${rc.contentWidth}px`,
169
+ paddingLeft: `${rc.contentPaddingLeft}px`,
170
+ }}
171
+ >
172
+ <div
173
+ style={{
174
+ display: 'flex',
175
+ flexDirection: 'column',
176
+ margin: '-5px 0px',
177
+ }}
178
+ >
179
+ {/* Headline - font-size: 15px, font-weight: 500, color: rgb(8, 8, 9) */}
180
+ <div
181
+ style={{
182
+ margin: '5px 0px',
183
+ }}
184
+ >
185
+ <span
186
+ style={{
187
+ display: 'block',
188
+ width: `${rc.contentWidth}px`,
189
+ maxWidth: '100%',
190
+ fontSize: `${rc.headlineSize}px`,
191
+ fontWeight: rc.headlineWeight,
192
+ lineHeight: rc.headlineLineHeight,
193
+ color: rc.headlineColor,
194
+ overflow: 'hidden',
195
+ textOverflow: 'clip',
196
+ }}
197
+ >
198
+ <span
199
+ style={{
200
+ display: '-webkit-box',
201
+ WebkitLineClamp: 2,
202
+ WebkitBoxOrient: 'vertical',
203
+ overflow: 'hidden',
204
+ maxWidth: '100%',
205
+ }}
206
+ >
207
+ {adData.headline}
208
+ </span>
209
+ </span>
210
+ </div>
211
+
212
+ {/* Display Link - font-size: 13px, color: rgb(101, 104, 108) */}
213
+ <div
214
+ style={{
215
+ margin: '5px 0px',
216
+ }}
217
+ >
218
+ <span
219
+ style={{
220
+ display: 'block',
221
+ width: `${rc.contentWidth}px`,
222
+ maxWidth: '100%',
223
+ fontSize: `${rc.displayLinkSize}px`,
224
+ fontWeight: rc.displayLinkWeight,
225
+ lineHeight: rc.displayLinkLineHeight,
226
+ color: rc.displayLinkColor,
227
+ textTransform: 'lowercase',
228
+ overflow: 'hidden',
229
+ textOverflow: 'ellipsis',
230
+ whiteSpace: 'nowrap',
231
+ }}
232
+ >
233
+ {adData.displayLink}
234
+ </span>
235
+ </div>
236
+ </div>
237
+ </div>
238
+ </div>
239
+ </div>
240
+ </div>
241
+ </div>
242
+ );
243
+ };
244
+
245
+ MetaRightColumnPreview.displayName = 'MetaRightColumnPreview';
@@ -0,0 +1,113 @@
1
+ import React from 'react';
2
+ import { MoreHorizontal } from 'lucide-react';
3
+ import type { PreviewAdData } from './types';
4
+ import { ProfileAvatar } from './ProfileAvatar';
5
+
6
+ interface MetaSearchPreviewProps {
7
+ adData: PreviewAdData;
8
+ }
9
+
10
+ export const MetaSearchPreview: React.FC<MetaSearchPreviewProps> = ({
11
+ adData,
12
+ }) => {
13
+ // Truncate text helper
14
+ const truncateText = (text: string, limit: number) => {
15
+ if (!text || text.length <= limit) return text;
16
+ return text.substring(0, limit);
17
+ };
18
+
19
+ return (
20
+ <div className="meta-preview w-full h-full">
21
+ <div
22
+ className="flex items-center justify-center bg-gray-100 rounded-xl w-full h-full"
23
+ style={{
24
+ padding: '24px',
25
+ }}
26
+ >
27
+ <div className="bg-white rounded-lg overflow-hidden w-full max-w-[300px]">
28
+ {/* Image Section - Square 1:1 */}
29
+ <div style={{ width: '100%', position: 'relative', paddingBottom: '100%' }}>
30
+ {adData.creativeImage ? (
31
+ <div
32
+ style={{
33
+ position: 'absolute',
34
+ top: 0,
35
+ left: 0,
36
+ width: '100%',
37
+ height: '100%',
38
+ backgroundImage: `url("${adData.creativeImage}")`,
39
+ backgroundSize: 'cover',
40
+ backgroundPosition: 'center',
41
+ }}
42
+ />
43
+ ) : (
44
+ <div
45
+ className="flex items-center justify-center bg-gray-100"
46
+ style={{
47
+ position: 'absolute',
48
+ top: 0,
49
+ left: 0,
50
+ width: '100%',
51
+ height: '100%',
52
+ }}
53
+ >
54
+ <div className="text-center text-gray-400">
55
+ <div className="w-16 h-16 bg-gray-200 rounded-lg mx-auto mb-2" />
56
+ <p className="text-sm">1:1 Image</p>
57
+ </div>
58
+ </div>
59
+ )}
60
+ </div>
61
+
62
+ {/* Content Section */}
63
+ <div className="p-4">
64
+ {/* Profile Section */}
65
+ <div className="flex items-start gap-2 mb-2">
66
+ {/* Profile Image */}
67
+ <div
68
+ className="flex-shrink-0 rounded-full border overflow-hidden"
69
+ style={{
70
+ width: '20px',
71
+ height: '20px',
72
+ borderColor: 'rgb(235, 237, 240)',
73
+ }}
74
+ >
75
+ <ProfileAvatar src={adData.profileImage} name={adData.brandName} fontSize={10} />
76
+ </div>
77
+
78
+ {/* Brand Name */}
79
+ <div className="flex-1 min-w-0">
80
+ <p className="text-sm font-medium text-gray-900 truncate">
81
+ {truncateText(adData.brandName, 30)}
82
+ </p>
83
+ </div>
84
+
85
+ {/* Ellipsis Button */}
86
+ <button className="flex-shrink-0">
87
+ <MoreHorizontal className="w-5 h-5 text-gray-600" />
88
+ </button>
89
+ </div>
90
+
91
+ {/* Headline */}
92
+ <h3
93
+ className="font-medium mb-2 overflow-hidden"
94
+ style={{
95
+ color: 'var(--color-meta-blue)',
96
+ fontSize: '15px',
97
+ lineHeight: '20px',
98
+ display: '-webkit-box',
99
+ WebkitLineClamp: 2,
100
+ WebkitBoxOrient: 'vertical',
101
+ }}
102
+ >
103
+ {adData.headline}
104
+ </h3>
105
+
106
+ {/* Sponsored Tag */}
107
+ <p className="text-xs text-gray-500">Sponsored</p>
108
+ </div>
109
+ </div>
110
+ </div>
111
+ </div>
112
+ );
113
+ };