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 / review-showcase / index.js

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

501 lines 29.1 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 __ = wp.i18n.__;
5 var useState = wp.element.useState;
6 var registerBlockType = wp.blocks.registerBlockType;
7 var InspectorControls = wp.blockEditor.InspectorControls;
8 var useBlockProps = wp.blockEditor.useBlockProps;
9 var PanelBody = wp.components.PanelBody;
10 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
11 var ToggleControl = wp.components.ToggleControl;
12 var RangeControl = wp.components.RangeControl;
13 var SelectControl = wp.components.SelectControl;
14 var TextControl = wp.components.TextControl;
15 var Button = wp.components.Button;
16
17 var _tc, _tv;
18 function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
19 function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
20
21 // Platform display config (logo SVG inline or text badge)
22 var PLATFORM_CONFIG = {
23 google: { label: 'Google', color: '#ea4335', badge: 'G' },
24 g2: { label: 'G2', color: '#ff492c', badge: 'G2' },
25 trustpilot: { label: 'Trustpilot', color: '#00b67a', badge: 'TP' },
26 capterra: { label: 'Capterra', color: '#ff9d28', badge: 'Ca' },
27 producthunt:{ label: 'Product Hunt',color: '#da552f', badge: 'PH' },
28 appstore: { label: 'App Store', color: '#007aff', badge: 'AS' }
29 };
30
31 var layoutOptions = [
32 { label: __('Platforms Top + Featured Quote', 'blockenberg'), value: 'platforms-top' },
33 { label: __('Split (Stats Left, Quote Right)', 'blockenberg'), value: 'split' },
34 { label: __('Compact (Platforms Only)', 'blockenberg'), value: 'compact' }
35 ];
36
37 var styleOptions = [
38 { label: __('Clean', 'blockenberg'), value: 'clean' },
39 { label: __('Card', 'blockenberg'), value: 'card' },
40 { label: __('Dark', 'blockenberg'), value: 'dark' },
41 { label: __('Gradient', 'blockenberg'), value: 'gradient' }
42 ];
43
44 var platformLogoOptions = Object.keys(PLATFORM_CONFIG).map(function (k) {
45 return { label: PLATFORM_CONFIG[k].label, value: k };
46 });
47
48 // Render star rating (0–5, supports half)
49 function renderStars(rating, starColor, starSize) {
50 var stars = [];
51 for (var i = 1; i <= 5; i++) {
52 var fill = i <= Math.floor(rating) ? starColor : (i - 0.5 <= rating ? 'url(#half-' + i + ')' : '#e2e8f0');
53 stars.push(el('span', {
54 key: i,
55 className: 'bkbg-rs-star',
56 style: { color: i <= rating ? starColor : '#e2e8f0', fontSize: starSize + 'px' }
57 }, i <= Math.floor(rating) ? '�
58 ' : (i - 0.5 <= rating ? '�
59 ' : '')));
60 }
61 return el('div', { className: 'bkbg-rs-stars' }, stars);
62 }
63
64 // Platform badge element
65 function renderPlatformBadge(platform, cfg) {
66 return el('div', {
67 className: 'bkbg-rs-platform-badge',
68 key: platform,
69 style: { '--bkbg-rs-plat-c': cfg.color }
70 },
71 el('div', { className: 'bkbg-rs-plat-logo', style: { background: cfg.color } }, cfg.badge)
72 );
73 }
74
75 registerBlockType('blockenberg/review-showcase', {
76 title: __('Review Showcase', 'blockenberg'),
77 icon: 'star-filled',
78 category: 'bkbg-marketing',
79 description: __('Showcase aggregate ratings from review platforms with a featured testimonial.', 'blockenberg'),
80
81 edit: function (props) {
82 var a = props.attributes;
83 var setAttributes = props.setAttributes;
84
85 function wrapStyle(a) {
86 var _tv = getTypoCssVars();
87 var s = {
88 '--bkbg-rs-star-c': a.starColor,
89 '--bkbg-rs-heading-c': a.headingColor,
90 '--bkbg-rs-rating-c': a.ratingColor,
91 '--bkbg-rs-quote-c': a.quoteColor,
92 '--bkbg-rs-card-bg': a.cardBg,
93 '--bkbg-rs-card-border': a.cardBorder,
94 '--bkbg-rs-accent': a.accentColor,
95 '--bkbg-rs-rating-sz': a.ratingSize + 'px',
96 '--bkbg-rs-star-sz': a.starSize + 'px',
97 '--bkbg-rs-pt': a.paddingTop + 'px',
98 '--bkbg-rs-pb': a.paddingBottom + 'px'
99 };
100 Object.assign(s, _tv(a.headingTypo, '--bkrsc-ht-'));
101 Object.assign(s, _tv(a.quoteTypo, '--bkrsc-qt-'));
102 return s;
103 }
104
105 function updatePlatform(index, key, value) {
106 var newPlatforms = a.platforms.slice();
107 newPlatforms[index] = Object.assign({}, newPlatforms[index]);
108 newPlatforms[index][key] = value;
109 setAttributes({ platforms: newPlatforms });
110 }
111
112 var inspector = el(InspectorControls, {},
113 el(PanelBody, { title: __('Heading', 'blockenberg'), initialOpen: true },
114 el(ToggleControl, {
115 label: __('Show Heading', 'blockenberg'),
116 checked: a.showHeading,
117 __nextHasNoMarginBottom: true,
118 onChange: function (v) { setAttributes({ showHeading: v }); }
119 }),
120 a.showHeading && el(TextControl, {
121 label: __('Heading', 'blockenberg'),
122 value: a.heading,
123 onChange: function (v) { setAttributes({ heading: v }); }
124 }),
125 el(ToggleControl, {
126 label: __('Show Overall Rating', 'blockenberg'),
127 checked: a.showOverall,
128 __nextHasNoMarginBottom: true,
129 onChange: function (v) { setAttributes({ showOverall: v }); }
130 }),
131 a.showOverall && el(RangeControl, {
132 label: __('Overall Rating', 'blockenberg'),
133 value: a.overallRating,
134 onChange: function (v) { setAttributes({ overallRating: v }); },
135 min: 1, max: 5, step: 0.1
136 }),
137 a.showOverall && el(TextControl, {
138 label: __('Review Count Label', 'blockenberg'),
139 value: a.reviewCount,
140 onChange: function (v) { setAttributes({ reviewCount: v }); }
141 })
142 ),
143
144 el(PanelBody, { title: __('Platforms', 'blockenberg'), initialOpen: false },
145 a.platforms.map(function (plat, index) {
146 return el('div', { key: plat.id, className: 'bkbg-rs-plat-ctrl' },
147 el('div', { className: 'bkbg-rs-plat-ctrl-head' },
148 el('strong', {}, plat.name),
149 el(ToggleControl, {
150 checked: plat.active,
151 __nextHasNoMarginBottom: true,
152 onChange: function (v) { updatePlatform(index, 'active', v); }
153 })
154 ),
155 plat.active && el(Fragment, {},
156 el(TextControl, {
157 label: __('Name', 'blockenberg'),
158 value: plat.name,
159 onChange: function (v) { updatePlatform(index, 'name', v); }
160 }),
161 el(SelectControl, {
162 label: __('Logo', 'blockenberg'),
163 value: plat.logo,
164 options: platformLogoOptions,
165 onChange: function (v) { updatePlatform(index, 'logo', v); }
166 }),
167 el(RangeControl, {
168 label: __('Rating', 'blockenberg'),
169 value: plat.rating,
170 onChange: function (v) { updatePlatform(index, 'rating', v); },
171 min: 1, max: 5, step: 0.1
172 }),
173 el(TextControl, {
174 label: __('Review Count', 'blockenberg'),
175 value: plat.count,
176 onChange: function (v) { updatePlatform(index, 'count', v); }
177 })
178 )
179 );
180 })
181 ),
182
183 el(PanelBody, { title: __('Featured Testimonial', 'blockenberg'), initialOpen: false },
184 el(ToggleControl, {
185 label: __('Show Featured Quote', 'blockenberg'),
186 checked: a.showFeatured,
187 __nextHasNoMarginBottom: true,
188 onChange: function (v) { setAttributes({ showFeatured: v }); }
189 }),
190 a.showFeatured && el(Fragment, {},
191 el(TextControl, {
192 label: __('Quote', 'blockenberg'),
193 value: a.featuredQuote,
194 onChange: function (v) { setAttributes({ featuredQuote: v }); }
195 }),
196 el(TextControl, {
197 label: __('Reviewer Name', 'blockenberg'),
198 value: a.featuredName,
199 onChange: function (v) { setAttributes({ featuredName: v }); }
200 }),
201 el(TextControl, {
202 label: __('Reviewer Role', 'blockenberg'),
203 value: a.featuredRole,
204 onChange: function (v) { setAttributes({ featuredRole: v }); }
205 }),
206 el(SelectControl, {
207 label: __('Platform Source', 'blockenberg'),
208 value: a.featuredPlatform,
209 options: platformLogoOptions,
210 onChange: function (v) { setAttributes({ featuredPlatform: v }); }
211 })
212 )
213 ),
214
215 el(PanelBody, { title: __('Style & Layout', 'blockenberg'), initialOpen: false },
216 el(SelectControl, {
217 label: __('Layout', 'blockenberg'),
218 value: a.layout,
219 options: layoutOptions,
220 onChange: function (v) { setAttributes({ layout: v }); }
221 }),
222 el(SelectControl, {
223 label: __('Style', 'blockenberg'),
224 value: a.style,
225 options: styleOptions,
226 onChange: function (v) { setAttributes({ style: v }); }
227 }),
228 el(RangeControl, {
229 label: __('Padding Top (px)', 'blockenberg'),
230 value: a.paddingTop,
231 onChange: function (v) { setAttributes({ paddingTop: v }); },
232 min: 0, max: 120
233 }),
234 el(RangeControl, {
235 label: __('Padding Bottom (px)', 'blockenberg'),
236 value: a.paddingBottom,
237 onChange: function (v) { setAttributes({ paddingBottom: v }); },
238 min: 0, max: 120
239 })
240 ),
241
242 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
243 (function () { var TC = getTypoControl(); return TC ? [
244 el(TC, { key: 'ht', label: __('Heading'), value: a.headingTypo, onChange: function (v) { setAttributes({ headingTypo: v }); } }),
245 el(TC, { key: 'qt', label: __('Quote'), value: a.quoteTypo, onChange: function (v) { setAttributes({ quoteTypo: v }); } }),
246 ] : null; })(),
247 el(RangeControl, { label: __('Rating Number Size (px)', 'blockenberg'), value: a.ratingSize, onChange: function (v) { setAttributes({ ratingSize: v }); }, min: 24, max: 96, __nextHasNoMarginBottom: true }),
248 el(RangeControl, { label: __('Star Size (px)', 'blockenberg'), value: a.starSize, onChange: function (v) { setAttributes({ starSize: v }); }, min: 14, max: 36, __nextHasNoMarginBottom: true })
249 ),
250
251 el(PanelColorSettings, {
252 title: __('Colors', 'blockenberg'),
253 initialOpen: false,
254 colorSettings: [
255 { value: a.starColor, onChange: function (v) { setAttributes({ starColor: v || '#f59e0b' }); }, label: __('Star Color', 'blockenberg') },
256 { value: a.headingColor, onChange: function (v) { setAttributes({ headingColor: v || '#0f172a' }); }, label: __('Heading Color', 'blockenberg') },
257 { value: a.ratingColor, onChange: function (v) { setAttributes({ ratingColor: v || '#0f172a' }); }, label: __('Rating Number Color', 'blockenberg') },
258 { value: a.quoteColor, onChange: function (v) { setAttributes({ quoteColor: v || '#475569' }); }, label: __('Quote Color', 'blockenberg') },
259 { value: a.cardBg, onChange: function (v) { setAttributes({ cardBg: v || '#ffffff' }); }, label: __('Card Background', 'blockenberg') },
260 { value: a.cardBorder, onChange: function (v) { setAttributes({ cardBorder: v || '#e2e8f0' }); }, label: __('Card Border', 'blockenberg') },
261 { value: a.accentColor, onChange: function (v) { setAttributes({ accentColor: v || '#6c3fb5' }); }, label: __('Accent Color', 'blockenberg') }
262 ]
263 })
264 );
265
266 var activePlatforms = a.platforms.filter(function (p) { return p.active; });
267 var wrapClass = 'bkbg-rs-wrap bkbg-rs-layout--' + a.layout + ' bkbg-rs-style--' + a.style;
268 var blockProps = useBlockProps({ className: wrapClass, style: wrapStyle(a) });
269
270 // Overall rating display
271 var overallEl = a.showOverall ? el('div', { className: 'bkbg-rs-overall' },
272 el('div', { className: 'bkbg-rs-rating-num' }, a.overallRating.toFixed(1)),
273 el('div', { className: 'bkbg-rs-overall-right' },
274 renderStars(a.overallRating, a.starColor, a.starSize),
275 el('div', { className: 'bkbg-rs-count' }, a.reviewCount + ' ' + __('reviews', 'blockenberg'))
276 )
277 ) : null;
278
279 // Platforms row
280 var platformsEl = el('div', { className: 'bkbg-rs-platforms' },
281 activePlatforms.map(function (plat) {
282 var cfg = PLATFORM_CONFIG[plat.logo] || { label: plat.name, color: '#666', badge: plat.name.slice(0, 2) };
283 return el('div', { key: plat.id, className: 'bkbg-rs-plat-card' },
284 el('div', { className: 'bkbg-rs-plat-top' },
285 el('div', { className: 'bkbg-rs-plat-logo', style: { background: cfg.color } }, cfg.badge),
286 el('div', { className: 'bkbg-rs-plat-name' }, plat.name)
287 ),
288 renderStars(plat.rating, a.starColor, 14),
289 el('div', { className: 'bkbg-rs-plat-meta' },
290 el('strong', {}, plat.rating.toFixed(1)),
291 el('span', {}, ' / 5 · ' + plat.count + ' ' + __('reviews', 'blockenberg'))
292 )
293 );
294 })
295 );
296
297 // Featured quote
298 var featuredEl = a.showFeatured ? el('div', { className: 'bkbg-rs-featured' },
299 el('div', { className: 'bkbg-rs-featured-inner' },
300 el('div', { className: 'bkbg-rs-feat-stars' }, renderStars(5, a.starColor, a.starSize)),
301 el('p', { className: 'bkbg-rs-feat-quote' }, '\u201C' + a.featuredQuote + '\u201D'),
302 el('div', { className: 'bkbg-rs-feat-attr' },
303 el('div', { className: 'bkbg-rs-feat-avatar' }, a.featuredName.charAt(0)),
304 el('div', { className: 'bkbg-rs-feat-meta' },
305 el('div', { className: 'bkbg-rs-feat-name' }, a.featuredName),
306 el('div', { className: 'bkbg-rs-feat-role' }, a.featuredRole)
307 ),
308 (function () {
309 var cfg = PLATFORM_CONFIG[a.featuredPlatform];
310 return cfg ? el('div', { className: 'bkbg-rs-plat-logo bkbg-rs-feat-plat', style: { background: cfg.color } }, cfg.badge) : null;
311 })()
312 )
313 )
314 ) : null;
315
316 return el(Fragment, {},
317 inspector,
318 el('div', blockProps,
319 a.showHeading && el('h2', { className: 'bkbg-rs-heading' }, a.heading),
320 overallEl,
321 platformsEl,
322 featuredEl
323 )
324 );
325 },
326
327 save: function (props) {
328 var a = props.attributes;
329
330 function wrapStyle(a) {
331 var _tv = getTypoCssVars();
332 var s = {
333 '--bkbg-rs-star-c': a.starColor,
334 '--bkbg-rs-heading-c': a.headingColor,
335 '--bkbg-rs-rating-c': a.ratingColor,
336 '--bkbg-rs-quote-c': a.quoteColor,
337 '--bkbg-rs-card-bg': a.cardBg,
338 '--bkbg-rs-card-border': a.cardBorder,
339 '--bkbg-rs-accent': a.accentColor,
340 '--bkbg-rs-rating-sz': a.ratingSize + 'px',
341 '--bkbg-rs-star-sz': a.starSize + 'px',
342 '--bkbg-rs-pt': a.paddingTop + 'px',
343 '--bkbg-rs-pb': a.paddingBottom + 'px'
344 };
345 Object.assign(s, _tv(a.headingTypo, '--bkrsc-ht-'));
346 Object.assign(s, _tv(a.quoteTypo, '--bkrsc-qt-'));
347 return s;
348 }
349
350 function renderStarsSave(rating, starColor, starSize) {
351 var stars = [];
352 for (var i = 1; i <= 5; i++) {
353 stars.push(el('span', {
354 key: i,
355 className: 'bkbg-rs-star',
356 style: { color: i <= Math.round(rating) ? starColor : '#e2e8f0', fontSize: starSize + 'px' }
357 }, i <= Math.round(rating) ? '�
358 ' : ''));
359 }
360 return el('div', { className: 'bkbg-rs-stars' }, stars);
361 }
362
363 var activePlatforms = a.platforms.filter(function (p) { return p.active; });
364 var wrapClass = 'bkbg-rs-wrap bkbg-rs-layout--' + a.layout + ' bkbg-rs-style--' + a.style;
365 var blockProps = wp.blockEditor.useBlockProps.save({ className: wrapClass, style: wrapStyle(a) });
366
367 return el('div', blockProps,
368 a.showHeading && el('h2', { className: 'bkbg-rs-heading' }, a.heading),
369 a.showOverall && el('div', { className: 'bkbg-rs-overall' },
370 el('div', { className: 'bkbg-rs-rating-num' }, a.overallRating.toFixed(1)),
371 el('div', { className: 'bkbg-rs-overall-right' },
372 renderStarsSave(a.overallRating, a.starColor, a.starSize),
373 el('div', { className: 'bkbg-rs-count' }, a.reviewCount + ' reviews')
374 )
375 ),
376 el('div', { className: 'bkbg-rs-platforms' },
377 activePlatforms.map(function (plat) {
378 var cfg = PLATFORM_CONFIG[plat.logo] || { label: plat.name, color: '#666', badge: plat.name.slice(0, 2) };
379 return el('div', { key: plat.id, className: 'bkbg-rs-plat-card' },
380 el('div', { className: 'bkbg-rs-plat-top' },
381 el('div', { className: 'bkbg-rs-plat-logo', style: { background: cfg.color } }, cfg.badge),
382 el('div', { className: 'bkbg-rs-plat-name' }, plat.name)
383 ),
384 renderStarsSave(plat.rating, a.starColor, 14),
385 el('div', { className: 'bkbg-rs-plat-meta' },
386 el('strong', {}, plat.rating.toFixed(1)),
387 el('span', {}, ' / 5 · ' + plat.count + ' reviews')
388 )
389 );
390 })
391 ),
392 a.showFeatured && el('div', { className: 'bkbg-rs-featured' },
393 el('div', { className: 'bkbg-rs-featured-inner' },
394 el('div', { className: 'bkbg-rs-feat-stars' }, renderStarsSave(5, a.starColor, a.starSize)),
395 el('p', { className: 'bkbg-rs-feat-quote' }, '\u201C' + a.featuredQuote + '\u201D'),
396 el('div', { className: 'bkbg-rs-feat-attr' },
397 el('div', { className: 'bkbg-rs-feat-avatar' }, a.featuredName.charAt(0)),
398 el('div', { className: 'bkbg-rs-feat-meta' },
399 el('div', { className: 'bkbg-rs-feat-name' }, a.featuredName),
400 el('div', { className: 'bkbg-rs-feat-role' }, a.featuredRole)
401 ),
402 (function () {
403 var cfg = PLATFORM_CONFIG[a.featuredPlatform];
404 return cfg ? el('div', { className: 'bkbg-rs-plat-logo bkbg-rs-feat-plat', style: { background: cfg.color } }, cfg.badge) : null;
405 })()
406 )
407 )
408 )
409 );
410 },
411 deprecated: [{
412 attributes: {"heading":{"type":"string","default":"Loved by thousands of teams"},"showHeading":{"type":"boolean","default":true},"overallRating":{"type":"number","default":4.9},"reviewCount":{"type":"string","default":"2,400+"},"showOverall":{"type":"boolean","default":true},"platforms":{"type":"array","default":[{"id":"p1","name":"Google","logo":"google","rating":4.9,"count":"1.2k","active":true},{"id":"p2","name":"G2","logo":"g2","rating":4.8,"count":"850","active":true},{"id":"p3","name":"Trustpilot","logo":"trustpilot","rating":4.9,"count":"400","active":true},{"id":"p4","name":"Capterra","logo":"capterra","rating":4.8,"count":"320","active":false}]},"showFeatured":{"type":"boolean","default":true},"featuredQuote":{"type":"string","default":"This product completely transformed how our team works. The setup was effortless and the results were immediate."},"featuredName":{"type":"string","default":"Sarah M."},"featuredRole":{"type":"string","default":"Product Manager at Acme Corp"},"featuredPlatform":{"type":"string","default":"google"},"layout":{"type":"string","default":"platforms-top"},"style":{"type":"string","default":"clean"},"starSize":{"type":"number","default":20},"ratingSize":{"type":"number","default":48},"headingSize":{"type":"number","default":28},"headingFontWeight":{"type":"number","default":800},"headingLineHeight":{"type":"number","default":1.2},"quoteSize":{"type":"number","default":16},"quoteFontWeight":{"type":"number","default":400},"quoteLineHeight":{"type":"number","default":1.6},"paddingTop":{"type":"number","default":0},"paddingBottom":{"type":"number","default":0},"starColor":{"type":"string","default":"#f59e0b"},"headingColor":{"type":"string","default":"#0f172a"},"ratingColor":{"type":"string","default":"#0f172a"},"quoteColor":{"type":"string","default":"#475569"},"cardBg":{"type":"string","default":"#ffffff"},"cardBorder":{"type":"string","default":"#e2e8f0"},"accentColor":{"type":"string","default":"#6c3fb5"},"headingTypo":{"type":"object","default":{}},"quoteTypo":{"type":"object","default":{}}},
413 save: function (props) {
414 var a = props.attributes;
415
416 function wrapStyle(a) {
417 return {
418 '--bkbg-rs-star-c': a.starColor,
419 '--bkbg-rs-heading-c': a.headingColor,
420 '--bkbg-rs-rating-c': a.ratingColor,
421 '--bkbg-rs-quote-c': a.quoteColor,
422 '--bkbg-rs-card-bg': a.cardBg,
423 '--bkbg-rs-card-border': a.cardBorder,
424 '--bkbg-rs-accent': a.accentColor,
425 '--bkbg-rs-heading-sz': a.headingSize + 'px',
426 '--bkbg-rs-heading-fw': a.headingFontWeight,
427 '--bkbg-rs-heading-lh': a.headingLineHeight,
428 '--bkbg-rs-rating-sz': a.ratingSize + 'px',
429 '--bkbg-rs-quote-sz': a.quoteSize + 'px',
430 '--bkbg-rs-quote-fw': a.quoteFontWeight,
431 '--bkbg-rs-quote-lh': a.quoteLineHeight,
432 '--bkbg-rs-star-sz': a.starSize + 'px',
433 '--bkbg-rs-pt': a.paddingTop + 'px',
434 '--bkbg-rs-pb': a.paddingBottom + 'px'
435 };
436 }
437
438 function renderStarsSave(rating, starColor, starSize) {
439 var stars = [];
440 for (var i = 1; i <= 5; i++) {
441 stars.push(el('span', {
442 key: i,
443 className: 'bkbg-rs-star',
444 style: { color: i <= Math.round(rating) ? starColor : '#e2e8f0', fontSize: starSize + 'px' }
445 }, i <= Math.round(rating) ? '\u2605' : '\u2606'));
446 }
447 return el('div', { className: 'bkbg-rs-stars' }, stars);
448 }
449
450 var activePlatforms = a.platforms.filter(function (p) { return p.active; });
451 var wrapClass = 'bkbg-rs-wrap bkbg-rs-layout--' + a.layout + ' bkbg-rs-style--' + a.style;
452 var blockProps = wp.blockEditor.useBlockProps.save({ className: wrapClass, style: wrapStyle(a) });
453
454 return el('div', blockProps,
455 a.showHeading && el('h2', { className: 'bkbg-rs-heading' }, a.heading),
456 a.showOverall && el('div', { className: 'bkbg-rs-overall' },
457 el('div', { className: 'bkbg-rs-rating-num' }, a.overallRating.toFixed(1)),
458 el('div', { className: 'bkbg-rs-overall-right' },
459 renderStarsSave(a.overallRating, a.starColor, a.starSize),
460 el('div', { className: 'bkbg-rs-count' }, a.reviewCount + ' reviews')
461 )
462 ),
463 el('div', { className: 'bkbg-rs-platforms' },
464 activePlatforms.map(function (plat) {
465 var cfg = PLATFORM_CONFIG[plat.logo] || { label: plat.name, color: '#666', badge: plat.name.slice(0, 2) };
466 return el('div', { key: plat.id, className: 'bkbg-rs-plat-card' },
467 el('div', { className: 'bkbg-rs-plat-top' },
468 el('div', { className: 'bkbg-rs-plat-logo', style: { background: cfg.color } }, cfg.badge),
469 el('div', { className: 'bkbg-rs-plat-name' }, plat.name)
470 ),
471 renderStarsSave(plat.rating, a.starColor, 14),
472 el('div', { className: 'bkbg-rs-plat-meta' },
473 el('strong', {}, plat.rating.toFixed(1)),
474 el('span', {}, ' / 5 \u00b7 ' + plat.count + ' reviews')
475 )
476 );
477 })
478 ),
479 a.showFeatured && el('div', { className: 'bkbg-rs-featured' },
480 el('div', { className: 'bkbg-rs-featured-inner' },
481 el('div', { className: 'bkbg-rs-feat-stars' }, renderStarsSave(5, a.starColor, a.starSize)),
482 el('p', { className: 'bkbg-rs-feat-quote' }, '\u201C' + a.featuredQuote + '\u201D'),
483 el('div', { className: 'bkbg-rs-feat-attr' },
484 el('div', { className: 'bkbg-rs-feat-avatar' }, a.featuredName.charAt(0)),
485 el('div', { className: 'bkbg-rs-feat-meta' },
486 el('div', { className: 'bkbg-rs-feat-name' }, a.featuredName),
487 el('div', { className: 'bkbg-rs-feat-role' }, a.featuredRole)
488 ),
489 (function () {
490 var cfg = PLATFORM_CONFIG[a.featuredPlatform];
491 return cfg ? el('div', { className: 'bkbg-rs-plat-logo bkbg-rs-feat-plat', style: { background: cfg.color } }, cfg.badge) : null;
492 })()
493 )
494 )
495 )
496 );
497 }
498 }]
499 });
500 }() );
501