PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.7
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.7
2.0.13 2.0.12 2.0.11 2.0.10 2.0.9 trunk 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8
blockenberg / blocks / social-proof-bar / index.js

index.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.7, at blocks/social-proof-bar/index.js

618 lines 33.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 ( function () {
2 var el = wp.element.createElement;
3 var Fragment = wp.element.Fragment;
4 var registerBlockType = wp.blocks.registerBlockType;
5 var __ = wp.i18n.__;
6 var InspectorControls = wp.blockEditor.InspectorControls;
7 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
8 var MediaUpload = wp.blockEditor.MediaUpload;
9 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
10 var useBlockProps = wp.blockEditor.useBlockProps;
11 var PanelBody = wp.components.PanelBody;
12 var RangeControl = wp.components.RangeControl;
13 var SelectControl = wp.components.SelectControl;
14 var TextControl = wp.components.TextControl;
15 var ToggleControl = wp.components.ToggleControl;
16 var Button = wp.components.Button;
17 var _tc; function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
18 var _tv; function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
19
20 function buildWrapStyle(a) {
21 var s = {
22 paddingTop: a.paddingTop + 'px',
23 paddingBottom: a.paddingBottom + 'px'
24 };
25 if (a.bgColor) s.background = a.bgColor;
26 if (a.borderTop) s.borderTop = '1px solid ' + (a.dividerColor || '#e5e7eb');
27 if (a.borderBottom) s.borderBottom = '1px solid ' + (a.dividerColor || '#e5e7eb');
28 var _tvFn = getTypoCssVars();
29 if (_tvFn) {
30 Object.assign(s, _tvFn(a.ratingTypo || {}, '--bkspb-rt-'));
31 Object.assign(s, _tvFn(a.taglineTypo || {}, '--bkspb-tg-'));
32 }
33 return s;
34 }
35
36 function uid() { return 'id-' + Math.random().toString(36).slice(2, 8); }
37
38 // Star rating SVG row
39 function StarRating(props) {
40 var val = props.value || 0;
41 var color = props.color || '#f59e0b';
42 var size = props.size || 16;
43 var stars = [];
44 for (var i = 1; i <= 5; i++) {
45 var fill = i <= Math.floor(val) ? color : (i <= val + 0.5 ? 'url(#bksp-half-' + props.uid + ')' : '#e5e7eb');
46 stars.push(el('svg', {
47 key: i, width: size, height: size, viewBox: '0 0 24 24',
48 style: { fill: fill, flexShrink: 0 }
49 }, el('path', { d: 'M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z' })));
50 }
51 return el('div', { style: { display: 'flex', alignItems: 'center', gap: '2px' } }, stars);
52 }
53
54 // Avatar circle (initials fallback)
55 function Avatar(props) {
56 var av = props.avatar;
57 var size = props.size || 40;
58 var border = props.border || '#ffffff';
59 var fallbackBg = props.fallbackBg || '#6c3fb5';
60
61 var style = {
62 width: size + 'px', height: size + 'px', borderRadius: '50%',
63 border: '2px solid ' + border,
64 overflow: 'hidden', flexShrink: 0,
65 background: av.imageUrl ? 'transparent' : fallbackBg,
66 display: 'flex', alignItems: 'center', justifyContent: 'center',
67 color: '#fff', fontSize: Math.round(size * 0.35) + 'px', fontWeight: 700
68 };
69
70 if (av.imageUrl) {
71 return el('div', { style: style },
72 el('img', { src: av.imageUrl, alt: av.name || '', style: { width: '100%', height: '100%', objectFit: 'cover' } })
73 );
74 }
75 return el('div', { style: style }, (av.initials || '?'));
76 }
77
78 // Logo placeholder
79 function LogoItem(props) {
80 var logo = props.logo;
81 var height = props.height || 28;
82 var opacity = (props.opacity || 50) / 100;
83 var grayscale = props.grayscale;
84
85 var imgStyle = {
86 height: height + 'px', width: 'auto', display: 'block',
87 opacity: opacity,
88 filter: grayscale ? 'grayscale(100%)' : 'none'
89 };
90
91 if (logo.imageUrl) {
92 return el('img', { src: logo.imageUrl, alt: logo.name || '', style: imgStyle });
93 }
94 // Placeholder
95 return el('div', {
96 style: {
97 height: height + 'px', padding: '0 12px',
98 background: '#e5e7eb', borderRadius: '6px',
99 display: 'flex', alignItems: 'center', justifyContent: 'center',
100 fontSize: '12px', color: '#9ca3af', fontWeight: 600, opacity: opacity,
101 whiteSpace: 'nowrap'
102 }
103 }, logo.name || 'Logo');
104 }
105
106 // Main preview component
107 function ProofBarPreview(props) {
108 var a = props.attrs;
109 var isCenter = a.layout !== 'left' && a.layout !== 'spread';
110 var isSpread = a.layout === 'spread';
111
112 var wrapStyle = {
113 display: 'flex',
114 flexDirection: isSpread ? 'row' : 'column',
115 alignItems: isSpread ? 'center' : 'center',
116 justifyContent: isSpread ? 'space-between' : 'center',
117 flexWrap: 'wrap',
118 gap: a.gap + 'px',
119 textAlign: isCenter ? 'center' : 'left'
120 };
121
122 // Avatar bar + rating + tagline (left cluster)
123 var socialPart = el('div', {
124 style: {
125 display: 'flex',
126 flexDirection: isSpread ? 'column' : 'row',
127 alignItems: 'center',
128 gap: '12px',
129 flexWrap: 'wrap',
130 justifyContent: isCenter ? 'center' : 'flex-start'
131 }
132 },
133 a.showAvatars && (a.avatars || []).length > 0 && el('div', {
134 style: {
135 display: 'flex',
136 flexDirection: 'row',
137 paddingLeft: a.avatarSize / 3 + 'px'
138 }
139 },
140 (a.avatars || []).map(function (av, i) {
141 return el('div', {
142 key: av.id,
143 style: { marginLeft: i === 0 ? 0 : ('-' + a.avatarOverlap + 'px') }
144 }, el(Avatar, { avatar: av, size: a.avatarSize, border: a.avatarBorderColor, fallbackBg: a.avatarFallbackBg }));
145 })
146 ),
147 a.showRating && el('div', {
148 style: {
149 display: 'flex',
150 flexDirection: isSpread ? 'column' : 'column',
151 alignItems: isCenter ? 'center' : 'flex-start',
152 gap: '2px'
153 }
154 },
155 el('div', { style: { display: 'flex', alignItems: 'center', gap: '6px' } },
156 el(StarRating, { value: a.ratingValue, color: a.starColor, size: a.ratingNumSize, uid: 'ed' }),
157 el('span', { className: 'bksp-rating-val', style: { color: a.textColor } }, a.ratingValue)
158 ),
159 el('span', { className: 'bksp-review-text', style: { color: a.textColor, opacity: 0.7 } },
160 a.reviewCount + ' ' + a.ratingLabel
161 )
162 )
163 );
164
165 // Tagline
166 var taglinePart = a.showTagline && el('div', {
167 className: 'bksp-tagline-text', style: { color: a.textColor, opacity: 0.8 }
168 }, a.tagline);
169
170 // Divider
171 var dividerPart = a.dividerStyle !== 'none' && el('div', {
172 style: {
173 [isSpread ? 'width' : 'height']: isSpread ? '1px' : 'auto',
174 [isSpread ? 'height' : 'width']: isSpread ? '40px' : '1px',
175 background: a.dividerColor || '#e5e7eb',
176 flexShrink: 0
177 }
178 });
179
180 // Logo strip
181 var logosPart = a.showLogos && el('div', {
182 style: {
183 display: 'flex',
184 flexDirection: isSpread ? 'column' : 'row',
185 alignItems: 'center',
186 gap: a.logoGap + 'px',
187 flexWrap: 'wrap',
188 justifyContent: isCenter ? 'center' : 'flex-start'
189 }
190 },
191 a.showLogosLabel && el('span', {
192 className: 'bksp-logos-text', style: { color: a.textColor, opacity: 0.5, whiteSpace: 'nowrap' }
193 }, a.logosLabel),
194 (a.logos || []).map(function (logo) {
195 return el('div', { key: logo.id },
196 el(LogoItem, { logo: logo, height: a.logoHeight, opacity: a.logoOpacity, grayscale: a.logoGrayscale })
197 );
198 })
199 );
200
201 if (isSpread) {
202 return el('div', { style: wrapStyle }, socialPart, taglinePart, dividerPart, logosPart);
203 }
204
205 return el('div', { style: wrapStyle }, socialPart, taglinePart, a.showLogos && dividerPart, logosPart);
206 }
207
208 // Avatar editor item
209 function AvatarEditor(props) {
210 var av = props.av;
211 var idx = props.idx;
212 var onUpdate = props.onUpdate;
213 var onRemove = props.onRemove;
214
215 return el('div', { style: { border: '1px solid #e5e7eb', borderRadius: '6px', padding: '10px', marginBottom: '8px' } },
216 el('div', { style: { display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '8px' } },
217 el('div', {
218 style: {
219 width: '32px', height: '32px', borderRadius: '50%',
220 background: av.imageUrl ? 'transparent' : '#6c3fb5',
221 overflow: 'hidden', flexShrink: 0,
222 display: 'flex', alignItems: 'center', justifyContent: 'center',
223 fontSize: '11px', color: '#fff', fontWeight: 700
224 }
225 }, av.imageUrl ? el('img', { src: av.imageUrl, style: { width: '100%', height: '100%', objectFit: 'cover' } }) : (av.initials || '?')),
226 el('span', { style: { fontSize: '13px', fontWeight: 600, flexGrow: 1 } }, av.name || ('Avatar ' + (idx + 1)))
227 ),
228 el(MediaUploadCheck, null,
229 el(MediaUpload, {
230 onSelect: function (media) { onUpdate(idx, 'imageUrl', media.url); },
231 allowedTypes: ['image'],
232 render: function (rp) {
233 return el(Button, { variant: 'secondary', isSmall: true, onClick: rp.open },
234 av.imageUrl ? __('Change photo', 'blockenberg') : __('Upload photo', 'blockenberg'));
235 }
236 })
237 ),
238 el(TextControl, { label: __('Name', 'blockenberg'), value: av.name, onChange: function (v) { onUpdate(idx, 'name', v); } }),
239 el(TextControl, { label: __('Initials (fallback)', 'blockenberg'), value: av.initials, onChange: function (v) { onUpdate(idx, 'initials', v); } }),
240 el(Button, {
241 variant: 'secondary', isDestructive: true, isSmall: true,
242 onClick: function () { onRemove(idx); }
243 }, __('Remove', 'blockenberg'))
244 );
245 }
246
247 registerBlockType('blockenberg/social-proof-bar', {
248
249 edit: function (props) {
250 var attrs = props.attributes;
251 var setAttr = props.setAttributes;
252 var avatars = attrs.avatars || [];
253 var logos = attrs.logos || [];
254
255 function updateAvatar(i, key, val) {
256 var next = avatars.map(function (av, j) {
257 if (j !== i) return av;
258 var u = Object.assign({}, av); u[key] = val; return u;
259 });
260 setAttr({ avatars: next });
261 }
262
263 function removeAvatar(i) { setAttr({ avatars: avatars.filter(function (_, j) { return j !== i; }) }); }
264
265 function addAvatar() {
266 setAttr({ avatars: avatars.concat([{ id: uid(), imageUrl: '', name: '', initials: '' }]) });
267 }
268
269 function updateLogo(i, key, val) {
270 var next = logos.map(function (l, j) {
271 if (j !== i) return l;
272 var u = Object.assign({}, l); u[key] = val; return u;
273 });
274 setAttr({ logos: next });
275 }
276
277 function removeLogo(i) { setAttr({ logos: logos.filter(function (_, j) { return j !== i; }) }); }
278
279 function addLogo() {
280 setAttr({ logos: logos.concat([{ id: uid(), imageUrl: '', name: 'Partner', url: '' }]) });
281 }
282
283 var wrapStyle = buildWrapStyle(attrs);
284
285 var blockProps = useBlockProps({ className: 'bksp-wrap', style: wrapStyle });
286
287 return el(Fragment, null,
288 el(InspectorControls, null,
289
290 el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: true },
291 el(SelectControl, {
292 label: __('Layout', 'blockenberg'),
293 value: attrs.layout,
294 options: [
295 { label: 'Centered (stacked)', value: 'centered' },
296 { label: 'Left aligned', value: 'left' },
297 { label: 'Spread (side by side)', value: 'spread' }
298 ],
299 onChange: function (v) { setAttr({ layout: v }); }
300 }),
301 el(RangeControl, {
302 label: __('Gap (px)', 'blockenberg'),
303 value: attrs.gap, min: 8, max: 80,
304 onChange: function (v) { setAttr({ gap: v }); }
305 }),
306 el(SelectControl, {
307 label: __('Divider style', 'blockenberg'),
308 value: attrs.dividerStyle,
309 options: [
310 { label: 'Line', value: 'line' },
311 { label: 'Dot', value: 'dot' },
312 { label: 'None', value: 'none' }
313 ],
314 onChange: function (v) { setAttr({ dividerStyle: v }); }
315 }),
316 el(ToggleControl, {
317 label: __('Border top', 'blockenberg'),
318 checked: attrs.borderTop,
319 onChange: function (v) { setAttr({ borderTop: v }); },
320 __nextHasNoMarginBottom: true
321 }),
322 el(ToggleControl, {
323 label: __('Border bottom', 'blockenberg'),
324 checked: attrs.borderBottom,
325 onChange: function (v) { setAttr({ borderBottom: v }); },
326 __nextHasNoMarginBottom: true
327 }),
328 el(RangeControl, {
329 label: __('Padding Top (px)', 'blockenberg'),
330 value: attrs.paddingTop, min: 0, max: 200,
331 onChange: function (v) { setAttr({ paddingTop: v }); }
332 }),
333 el(RangeControl, {
334 label: __('Padding Bottom (px)', 'blockenberg'),
335 value: attrs.paddingBottom, min: 0, max: 200,
336 onChange: function (v) { setAttr({ paddingBottom: v }); }
337 })
338 ),
339
340 el(PanelBody, { title: __('Avatars', 'blockenberg'), initialOpen: false },
341 el(ToggleControl, {
342 label: __('Show avatars', 'blockenberg'),
343 checked: attrs.showAvatars,
344 onChange: function (v) { setAttr({ showAvatars: v }); },
345 __nextHasNoMarginBottom: true
346 }),
347 attrs.showAvatars && el(Fragment, null,
348 el(RangeControl, {
349 label: __('Avatar size (px)', 'blockenberg'),
350 value: attrs.avatarSize, min: 24, max: 80,
351 onChange: function (v) { setAttr({ avatarSize: v }); }
352 }),
353 el(RangeControl, {
354 label: __('Overlap (px)', 'blockenberg'),
355 value: attrs.avatarOverlap, min: 0, max: 30,
356 onChange: function (v) { setAttr({ avatarOverlap: v }); }
357 }),
358 avatars.map(function (av, i) {
359 return el(AvatarEditor, { key: av.id, av: av, idx: i, onUpdate: updateAvatar, onRemove: removeAvatar });
360 }),
361 el(Button, {
362 variant: 'secondary', isSmall: true,
363 style: { width: '100%', justifyContent: 'center', marginTop: '6px' },
364 onClick: addAvatar
365 }, __('+ Add avatar', 'blockenberg'))
366 )
367 ),
368
369 el(PanelBody, { title: __('Rating', 'blockenberg'), initialOpen: false },
370 el(ToggleControl, {
371 label: __('Show rating', 'blockenberg'),
372 checked: attrs.showRating,
373 onChange: function (v) { setAttr({ showRating: v }); },
374 __nextHasNoMarginBottom: true
375 }),
376 attrs.showRating && el(Fragment, null,
377 el(RangeControl, {
378 label: __('Rating value', 'blockenberg'),
379 value: attrs.ratingValue, min: 1, max: 5, step: 0.1,
380 onChange: function (v) { setAttr({ ratingValue: v }); }
381 }),
382 el(TextControl, {
383 label: __('Review count', 'blockenberg'),
384 value: attrs.reviewCount,
385 onChange: function (v) { setAttr({ reviewCount: v }); }
386 }),
387 el(TextControl, {
388 label: __('Rating label', 'blockenberg'),
389 value: attrs.ratingLabel,
390 onChange: function (v) { setAttr({ ratingLabel: v }); }
391 })
392 )
393 ),
394
395 el(PanelBody, { title: __('Tagline', 'blockenberg'), initialOpen: false },
396 el(ToggleControl, {
397 label: __('Show tagline', 'blockenberg'),
398 checked: attrs.showTagline,
399 onChange: function (v) { setAttr({ showTagline: v }); },
400 __nextHasNoMarginBottom: true
401 }),
402 attrs.showTagline && el(TextControl, {
403 label: __('Tagline text', 'blockenberg'),
404 value: attrs.tagline,
405 onChange: function (v) { setAttr({ tagline: v }); }
406 })
407 ),
408
409 el(PanelBody, { title: __('Logos', 'blockenberg'), initialOpen: false },
410 el(ToggleControl, {
411 label: __('Show logos', 'blockenberg'),
412 checked: attrs.showLogos,
413 onChange: function (v) { setAttr({ showLogos: v }); },
414 __nextHasNoMarginBottom: true
415 }),
416 attrs.showLogos && el(Fragment, null,
417 el(ToggleControl, {
418 label: __('Show "As seen in" label', 'blockenberg'),
419 checked: attrs.showLogosLabel,
420 onChange: function (v) { setAttr({ showLogosLabel: v }); },
421 __nextHasNoMarginBottom: true
422 }),
423 attrs.showLogosLabel && el(TextControl, {
424 label: __('Label text', 'blockenberg'),
425 value: attrs.logosLabel,
426 onChange: function (v) { setAttr({ logosLabel: v }); }
427 }),
428 el(RangeControl, {
429 label: __('Logo height (px)', 'blockenberg'),
430 value: attrs.logoHeight, min: 16, max: 80,
431 onChange: function (v) { setAttr({ logoHeight: v }); }
432 }),
433 el(RangeControl, {
434 label: __('Logo opacity (%)', 'blockenberg'),
435 value: attrs.logoOpacity, min: 10, max: 100,
436 onChange: function (v) { setAttr({ logoOpacity: v }); }
437 }),
438 el(ToggleControl, {
439 label: __('Grayscale logos', 'blockenberg'),
440 checked: attrs.logoGrayscale,
441 onChange: function (v) { setAttr({ logoGrayscale: v }); },
442 __nextHasNoMarginBottom: true
443 }),
444 el(RangeControl, {
445 label: __('Logo gap (px)', 'blockenberg'),
446 value: attrs.logoGap, min: 8, max: 80,
447 onChange: function (v) { setAttr({ logoGap: v }); }
448 }),
449 logos.map(function (logo, i) {
450 return el('div', {
451 key: logo.id,
452 style: { border: '1px solid #e5e7eb', borderRadius: '6px', padding: '10px', marginBottom: '8px' }
453 },
454 el(TextControl, { label: __('Name', 'blockenberg'), value: logo.name, onChange: function (v) { updateLogo(i, 'name', v); } }),
455 el(MediaUploadCheck, null,
456 el(MediaUpload, {
457 onSelect: function (media) { updateLogo(i, 'imageUrl', media.url); },
458 allowedTypes: ['image'],
459 render: function (rp) {
460 return el(Button, { variant: 'secondary', isSmall: true, onClick: rp.open },
461 logo.imageUrl ? __('Change logo', 'blockenberg') : __('Upload logo', 'blockenberg'));
462 }
463 })
464 ),
465 el(TextControl, { label: __('Link URL', 'blockenberg'), value: logo.url, type: 'url', onChange: function (v) { updateLogo(i, 'url', v); } }),
466 el(Button, {
467 variant: 'secondary', isDestructive: true, isSmall: true, style: { marginTop: '6px' },
468 onClick: function () { removeLogo(i); }
469 }, __('Remove', 'blockenberg'))
470 );
471 }),
472 el(Button, {
473 variant: 'secondary', isSmall: true,
474 style: { width: '100%', justifyContent: 'center', marginTop: '6px' },
475 onClick: addLogo
476 }, __('+ Add logo', 'blockenberg'))
477 )
478 ),
479
480 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
481 getTypoControl() && el(getTypoControl(), {
482 label: __('Rating Number', 'blockenberg'),
483 value: attrs.ratingTypo || {},
484 onChange: function (v) { setAttr({ ratingTypo: v }); }
485 }),
486 getTypoControl() && el(getTypoControl(), {
487 label: __('Tagline / Labels', 'blockenberg'),
488 value: attrs.taglineTypo || {},
489 onChange: function (v) { setAttr({ taglineTypo: v }); }
490 })
491 ),
492
493 el(PanelColorSettings, {
494 title: __('Colors', 'blockenberg'),
495 initialOpen: false,
496 colorSettings: [
497 { label: __('Text Color', 'blockenberg'), value: attrs.textColor, onChange: function (v) { setAttr({ textColor: v || '#374151' }); } },
498 { label: __('Star Color', 'blockenberg'), value: attrs.starColor, onChange: function (v) { setAttr({ starColor: v || '#f59e0b' }); } },
499 { label: __('Avatar Border', 'blockenberg'), value: attrs.avatarBorderColor, onChange: function (v) { setAttr({ avatarBorderColor: v || '#ffffff' }); } },
500 { label: __('Avatar Fallback BG', 'blockenberg'), value: attrs.avatarFallbackBg, onChange: function (v) { setAttr({ avatarFallbackBg: v || '#6c3fb5' }); } },
501 { label: __('Divider / Border', 'blockenberg'), value: attrs.dividerColor, onChange: function (v) { setAttr({ dividerColor: v || '#e5e7eb' }); } },
502 { label: __('Background', 'blockenberg'), value: attrs.bgColor, onChange: function (v) { setAttr({ bgColor: v || '' }); } }
503 ]
504 })
505 ),
506
507 el('div', blockProps,
508 el(ProofBarPreview, { attrs: attrs })
509 )
510 );
511 },
512
513 save: function (props) {
514 var a = props.attributes;
515 var hasTypo = a.ratingTypo !== undefined;
516
517 var wrapStyle = buildWrapStyle(a);
518
519 var isSpread = a.layout === 'spread';
520 var isCenter = a.layout !== 'left' && a.layout !== 'spread';
521
522 var blockProps = wp.blockEditor.useBlockProps.save({
523 className: 'bksp-wrap bksp-layout--' + a.layout,
524 style: wrapStyle
525 });
526
527 // Stars
528 var starNodes = [];
529 for (var si = 1; si <= 5; si++) {
530 var filled = si <= a.ratingValue;
531 starNodes.push(el('svg', {
532 key: si, width: a.ratingNumSize, height: a.ratingNumSize, viewBox: '0 0 24 24',
533 style: { fill: filled ? (a.starColor || '#f59e0b') : '#e5e7eb', flexShrink: 0 }
534 }, el('path', { d: 'M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z' })));
535 }
536
537 var avatarNodes = (a.avatars || []).map(function (av, i) {
538 var avStyle = {
539 width: a.avatarSize + 'px', height: a.avatarSize + 'px', borderRadius: '50%',
540 border: '2px solid ' + (a.avatarBorderColor || '#ffffff'),
541 overflow: 'hidden', flexShrink: 0,
542 background: av.imageUrl ? 'transparent' : (a.avatarFallbackBg || '#6c3fb5'),
543 display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
544 color: '#fff', fontSize: Math.round(a.avatarSize * 0.35) + 'px', fontWeight: 700,
545 marginLeft: i === 0 ? 0 : ('-' + a.avatarOverlap + 'px'),
546 position: 'relative', zIndex: 10 - i
547 };
548 return el('div', { key: av.id, className: 'bksp-avatar', style: avStyle },
549 av.imageUrl
550 ? el('img', { src: av.imageUrl, alt: av.name || '', style: { width: '100%', height: '100%', objectFit: 'cover' } })
551 : el('span', null, av.initials || '?')
552 );
553 });
554
555 var logoNodes = (a.logos || []).map(function (logo) {
556 var imgStyle = {
557 height: a.logoHeight + 'px', width: 'auto',
558 opacity: (a.logoOpacity || 50) / 100,
559 filter: a.logoGrayscale ? 'grayscale(100%)' : 'none',
560 display: 'block'
561 };
562 var content = logo.imageUrl
563 ? el('img', { src: logo.imageUrl, alt: logo.name || '', style: imgStyle })
564 : el('div', { style: Object.assign({}, imgStyle, { padding: '0 12px', background: '#e5e7eb', borderRadius: '6px', display: 'flex', alignItems: 'center', fontSize: '12px', color: '#9ca3af', fontWeight: 600, whiteSpace: 'nowrap' }) }, logo.name);
565 return logo.url
566 ? el('a', { key: logo.id, href: logo.url, rel: 'noopener noreferrer', target: '_blank', className: 'bksp-logo' }, content)
567 : el('div', { key: logo.id, className: 'bksp-logo' }, content);
568 });
569
570 var innerFlex = { display: 'flex', flexWrap: 'wrap', gap: a.gap + 'px', alignItems: 'center', justifyContent: isCenter ? 'center' : (isSpread ? 'space-between' : 'flex-start') };
571
572 return el('div', blockProps,
573 el('div', { className: 'bksp-inner', style: innerFlex },
574
575 // Social cluster (avatars + rating)
576 el('div', { className: 'bksp-social', style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: '12px', justifyContent: isCenter ? 'center' : 'flex-start' } },
577 a.showAvatars && avatarNodes.length > 0 && el('div', { className: 'bksp-avatars', style: { display: 'flex', alignItems: 'center' } }, avatarNodes),
578 a.showRating && el('div', { className: 'bksp-rating' },
579 el('div', { style: { display: 'flex', alignItems: 'center', gap: '4px' } },
580 el('div', { style: { display: 'flex', gap: '2px' } }, starNodes),
581 hasTypo
582 ? el('span', { className: 'bksp-rating-val', style: { color: a.textColor } }, a.ratingValue)
583 : el('span', { style: { fontSize: a.ratingNumSize + 'px', fontWeight: 700, color: a.textColor } }, a.ratingValue)
584 ),
585 hasTypo
586 ? el('div', { className: 'bksp-review-text', style: { color: a.textColor, opacity: 0.7 } }, a.reviewCount + ' ' + a.ratingLabel)
587 : el('div', { style: { fontSize: a.taglineSize + 'px', color: a.textColor, opacity: 0.7 } }, a.reviewCount + ' ' + a.ratingLabel)
588 )
589 ),
590
591 a.showTagline && (hasTypo
592 ? el('p', { className: 'bksp-tagline', style: { color: a.textColor, margin: 0 } }, a.tagline)
593 : el('p', { className: 'bksp-tagline', style: { fontSize: a.taglineSize + 'px', color: a.textColor, margin: 0, fontStyle: 'italic' } }, a.tagline)
594 ),
595
596 a.showLogos && a.dividerStyle !== 'none' && el('div', { className: 'bksp-divider', 'aria-hidden': 'true' }),
597
598 a.showLogos && el('div', { className: 'bksp-logos', style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: a.logoGap + 'px', justifyContent: isCenter ? 'center' : 'flex-start' } },
599 a.showLogosLabel && (hasTypo
600 ? el('span', { className: 'bksp-logos-text', style: { color: a.textColor, opacity: 0.5 } }, a.logosLabel)
601 : el('span', { style: { fontSize: a.taglineSize + 'px', color: a.textColor, opacity: 0.5, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.08em' } }, a.logosLabel)
602 ),
603 logoNodes
604 )
605 )
606 );
607 },
608
609 deprecated: [{
610 attributes: {"layout":{"type":"string","default":"centered"},"showAvatars":{"type":"boolean","default":true},"avatars":{"type":"array","default":[{"id":"a1","imageUrl":"","name":"User 1","initials":"JD"},{"id":"a2","imageUrl":"","name":"User 2","initials":"AM"},{"id":"a3","imageUrl":"","name":"User 3","initials":"SK"},{"id":"a4","imageUrl":"","name":"User 4","initials":"RL"}]},"avatarSize":{"type":"number","default":40},"avatarOverlap":{"type":"number","default":10},"avatarBorderColor":{"type":"string","default":"#ffffff"},"avatarFallbackBg":{"type":"string","default":"#6c3fb5"},"showRating":{"type":"boolean","default":true},"ratingValue":{"type":"number","default":4.9},"reviewCount":{"type":"string","default":"2,400+"},"ratingLabel":{"type":"string","default":"reviews"},"starColor":{"type":"string","default":"#f59e0b"},"showTagline":{"type":"boolean","default":true},"tagline":{"type":"string","default":"Trusted by 10,000+ marketers worldwide"},"dividerStyle":{"type":"string","default":"line"},"showLogos":{"type":"boolean","default":true},"logosLabel":{"type":"string","default":"As seen in"},"showLogosLabel":{"type":"boolean","default":true},"logos":{"type":"array","default":[{"id":"l1","imageUrl":"","name":"Forbes","url":""},{"id":"l2","imageUrl":"","name":"TechCrunch","url":""},{"id":"l3","imageUrl":"","name":"Wired","url":""},{"id":"l4","imageUrl":"","name":"Product Hunt","url":""}]},"logoHeight":{"type":"number","default":28},"logoOpacity":{"type":"number","default":50},"logoGrayscale":{"type":"boolean","default":true},"logoGap":{"type":"number","default":40},"gap":{"type":"number","default":32},"borderRadius":{"type":"number","default":0},"borderTop":{"type":"boolean","default":false},"borderBottom":{"type":"boolean","default":false},"taglineSize":{"type":"number","default":14},"ratingNumSize":{"type":"number","default":16},"bgColor":{"type":"string","default":""},"textColor":{"type":"string","default":"#374151"},"dividerColor":{"type":"string","default":"#e5e7eb"},"paddingTop":{"type":"number","default":32},"paddingBottom":{"type":"number","default":32},"ratingNumFontWeight":{"type":"number","default":700},"taglineFontWeight":{"type":"number","default":600}},
611 save: function (props) {
612 /* delegates to current save — hasTypo will be false for old attrs */
613 return wp.blocks.getBlockType('blockenberg/social-proof-bar').save(props);
614 }
615 }]
616 });
617 }() );
618