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 / affiliate-box / index.js

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

401 lines 28.4 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 useState = wp.element.useState;
4 var __ = wp.i18n.__;
5 var registerBlockType = wp.blocks.registerBlockType;
6 var InspectorControls = wp.blockEditor.InspectorControls;
7 var useBlockProps = wp.blockEditor.useBlockProps;
8 var MediaUpload = wp.blockEditor.MediaUpload;
9 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
10 var PanelBody = wp.components.PanelBody;
11 var Button = wp.components.Button;
12 var ToggleControl = wp.components.ToggleControl;
13 var RangeControl = wp.components.RangeControl;
14 var SelectControl = wp.components.SelectControl;
15 var TextControl = wp.components.TextControl;
16 var ColorPicker = wp.components.ColorPicker;
17 var Popover = wp.components.Popover;
18
19 // Lazy lookup so the typography control is resolved at render time
20 function getTypographyControl() {
21 return (typeof window.bkbgTypographyControl !== 'undefined') ? window.bkbgTypographyControl : null;
22 }
23 function getTypoCssVars() {
24 return (typeof window.bkbgTypoCssVars !== 'undefined') ? window.bkbgTypoCssVars : function() { return {}; };
25 }
26
27 var LAYOUT_OPTIONS = [
28 { label: __('Horizontal (image left)', 'blockenberg'), value: 'horizontal' },
29 { label: __('Vertical (image top)', 'blockenberg'), value: 'vertical' },
30 ];
31 var CTA_TARGET_OPTIONS = [
32 { label: __('New Tab', 'blockenberg'), value: '_blank' },
33 { label: __('Same Tab', 'blockenberg'), value: '_self' },
34 ];
35
36 function makeId() { return 'ab' + Math.random().toString(36).substr(2, 6); }
37
38 function buildWrapStyle(a) {
39 var s = {
40 '--bkbg-ab-score-bg': a.scoreBg,
41 '--bkbg-ab-score-color': a.scoreColor,
42 '--bkbg-ab-pros-hd-color': a.prosHeaderColor,
43 '--bkbg-ab-pros-icon': a.prosIconColor,
44 '--bkbg-ab-pros-bg': a.prosBg,
45 '--bkbg-ab-cons-hd-color': a.consHeaderColor,
46 '--bkbg-ab-cons-icon': a.consIconColor,
47 '--bkbg-ab-cons-bg': a.consBg,
48 '--bkbg-ab-cta-bg': a.ctaBg,
49 '--bkbg-ab-cta-color': a.ctaColor,
50 '--bkbg-ab-verdict-bg': a.verdictBg,
51 '--bkbg-ab-verdict-border': a.verdictBorderColor,
52 '--bkbg-ab-verdict-color': a.verdictColor,
53 '--bkbg-ab-name-color': a.nameColor,
54 '--bkbg-ab-tagline-color': a.taglineColor,
55 '--bkbg-ab-card-bg': a.cardBg,
56 '--bkbg-ab-border-color': a.borderColor,
57 '--bkbg-ab-name-sz': a.namSize + 'px',
58 '--bkbg-ab-tagline-sz': a.taglineSize + 'px',
59 '--bkbg-ab-score-sz': a.scoreSize + 'px',
60 '--bkbg-ab-verdict-sz': a.verdictSize + 'px',
61 '--bkbg-ab-list-sz': a.listItemSize + 'px',
62 '--bkbg-ab-card-r': a.cardRadius + 'px',
63 '--bkbg-ab-card-pad': a.cardPadding + 'px',
64 '--bkbg-ab-score-r': a.scoreRadius + 'px',
65 '--bkbg-ab-verdict-r': a.verdictRadius + 'px',
66 '--bkbg-ab-cta-r': a.ctaRadius + 'px',
67 '--bkbg-ab-img-r': a.imageRadius + 'px',
68 paddingTop: a.paddingTop + 'px',
69 paddingBottom: a.paddingBottom + 'px',
70 backgroundColor: a.bgColor || undefined,
71 };
72 var _tv = getTypoCssVars();
73 Object.assign(s, _tv(a.nameTypo || {}, '--bkbg-ab-name-'));
74 Object.assign(s, _tv(a.taglineTypo || {}, '--bkbg-ab-tagline-'));
75 Object.assign(s, _tv(a.listTypo || {}, '--bkbg-ab-list-'));
76 Object.assign(s, _tv(a.verdictTypo || {}, '--bkbg-ab-verdict-'));
77 return s;
78 }
79
80 function renderColorControl(key, label, value, onChange, openKey, setOpenKey) {
81 var isOpen = openKey === key;
82 return el('div', { key: key, style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '4px 0', gap: '8px' } },
83 el('span', { style: { fontSize: '12px', color: '#1e1e1e', flex: 1, lineHeight: 1.4 } }, label),
84 el('div', { style: { position: 'relative', flexShrink: 0 } },
85 el('button', { type: 'button', onClick: function () { setOpenKey(isOpen ? null : key); }, style: { width: '28px', height: '28px', borderRadius: '4px', border: isOpen ? '2px solid #007cba' : '2px solid #ddd', cursor: 'pointer', padding: 0, background: value || '#ccc' } }),
86 isOpen && el(Popover, { position: 'bottom left', onClose: function () { setOpenKey(null); } },
87 el('div', { style: { padding: '8px' } },
88 el(ColorPicker, { color: value, enableAlpha: true, onChange: onChange })
89 )
90 )
91 )
92 );
93 }
94
95 registerBlockType('blockenberg/affiliate-box', {
96 edit: function (props) {
97 var a = props.attributes;
98 var setA = props.setAttributes;
99 var openColor = useState(null);
100 var openKey = openColor[0];
101 var setOpenKey = openColor[1];
102
103 function setColor(key) {
104 return function (v) { var o = {}; o[key] = v; setA(o); };
105 }
106 function cc(key, label, val) {
107 return renderColorControl(key, label, val, setColor(key), openKey, setOpenKey);
108 }
109
110 function updateListItem(listKey, list, id, val) {
111 var o = {}; o[listKey] = list.map(function (it) { return it.id === id ? Object.assign({}, it, { text: val }) : it; }); setA(o);
112 }
113 function removeListItem(listKey, list, id) {
114 var o = {}; o[listKey] = list.filter(function (it) { return it.id !== id; }); setA(o);
115 }
116 function addListItem(listKey, list, placeholder) {
117 var o = {}; o[listKey] = list.concat([{ id: makeId(), text: placeholder }]); setA(o);
118 }
119
120 var inspector = el(InspectorControls, null,
121
122 /* Product Info */
123 el(PanelBody, { title: __('Product Info', 'blockenberg'), initialOpen: true },
124 el(TextControl, { label: __('Product Name', 'blockenberg'), value: a.productName, onChange: function (v) { setA({ productName: v }); } }),
125 el(TextControl, { label: __('Tagline', 'blockenberg'), value: a.tagline, onChange: function (v) { setA({ tagline: v }); } }),
126 el(ToggleControl, { label: __('Show Product Image', 'blockenberg'), checked: a.showImage, onChange: function (v) { setA({ showImage: v }); }, __nextHasNoMarginBottom: true }),
127 a.showImage && el(MediaUploadCheck, null,
128 el(MediaUpload, {
129 onSelect: function (m) { setA({ imageUrl: m.url, imageId: m.id }); },
130 allowedTypes: ['image'],
131 value: a.imageId,
132 render: function (ref) {
133 return el('div', null,
134 a.imageUrl && el('img', { src: a.imageUrl, style: { width: '100%', borderRadius: '6px', marginBottom: '8px' } }),
135 el(Button, { onClick: ref.open, variant: a.imageId ? 'secondary' : 'primary', style: { marginBottom: '4px' } },
136 a.imageId ? __('Replace Image', 'blockenberg') : __('Select Image', 'blockenberg')
137 ),
138 a.imageId && el(Button, { onClick: function () { setA({ imageUrl: '', imageId: 0 }); }, variant: 'link', isDestructive: true }, __('Remove', 'blockenberg'))
139 );
140 }
141 })
142 )
143 ),
144
145 /* Score */
146 el(PanelBody, { title: __('Score & Verdict', 'blockenberg'), initialOpen: false },
147 el(ToggleControl, { label: __('Show Score Badge', 'blockenberg'), checked: a.showScore, onChange: function (v) { setA({ showScore: v }); }, __nextHasNoMarginBottom: true }),
148 el(RangeControl, { label: __('Overall Score', 'blockenberg'), value: a.overallScore, min: 0, max: a.maxScore, step: 0.1, onChange: function (v) { setA({ overallScore: v }); } }),
149 el(RangeControl, { label: __('Max Score', 'blockenberg'), value: a.maxScore, min: 5, max: 10, step: 1, onChange: function (v) { setA({ maxScore: v }); } }),
150 el(TextControl, { label: __('Score Label', 'blockenberg'), value: a.scoreLabel, onChange: function (v) { setA({ scoreLabel: v }); } }),
151 el(ToggleControl, { label: __('Show Verdict Box', 'blockenberg'), checked: a.showVerdict, onChange: function (v) { setA({ showVerdict: v }); }, __nextHasNoMarginBottom: true }),
152 el(TextControl, { label: __('Verdict Label', 'blockenberg'), value: a.verdictLabel, onChange: function (v) { setA({ verdictLabel: v }); } }),
153 el(TextControl, { label: __('Verdict Text', 'blockenberg'), value: a.verdict, onChange: function (v) { setA({ verdict: v }); } })
154 ),
155
156 /* Pros */
157 el(PanelBody, { title: __('Pros', 'blockenberg'), initialOpen: false },
158 el(TextControl, { label: __('Section Label', 'blockenberg'), value: a.prosLabel, onChange: function (v) { setA({ prosLabel: v }); } }),
159 a.pros.map(function (p) {
160 return el('div', { key: p.id, style: { display: 'flex', gap: '6px', marginBottom: '6px', alignItems: 'center' } },
161 el(TextControl, { value: p.text, onChange: function (v) { updateListItem('pros', a.pros, p.id, v); }, style: { flex: 1, margin: 0 } }),
162 el(Button, { onClick: function () { removeListItem('pros', a.pros, p.id); }, icon: 'trash', isDestructive: true, label: __('Remove', 'blockenberg') })
163 );
164 }),
165 el(Button, { variant: 'secondary', onClick: function () { addListItem('pros', a.pros, 'New advantage'); } }, __('+ Add Pro', 'blockenberg'))
166 ),
167
168 /* Cons */
169 el(PanelBody, { title: __('Cons', 'blockenberg'), initialOpen: false },
170 el(TextControl, { label: __('Section Label', 'blockenberg'), value: a.consLabel, onChange: function (v) { setA({ consLabel: v }); } }),
171 a.cons.map(function (c) {
172 return el('div', { key: c.id, style: { display: 'flex', gap: '6px', marginBottom: '6px', alignItems: 'center' } },
173 el(TextControl, { value: c.text, onChange: function (v) { updateListItem('cons', a.cons, c.id, v); }, style: { flex: 1, margin: 0 } }),
174 el(Button, { onClick: function () { removeListItem('cons', a.cons, c.id); }, icon: 'trash', isDestructive: true, label: __('Remove', 'blockenberg') })
175 );
176 }),
177 el(Button, { variant: 'secondary', onClick: function () { addListItem('cons', a.cons, 'New disadvantage'); } }, __('+ Add Con', 'blockenberg'))
178 ),
179
180 /* Price & CTA */
181 el(PanelBody, { title: __('Price & CTA', 'blockenberg'), initialOpen: false },
182 el(ToggleControl, { label: __('Show Price', 'blockenberg'), checked: a.showPrice, onChange: function (v) { setA({ showPrice: v }); }, __nextHasNoMarginBottom: true }),
183 el(TextControl, { label: __('Currency Symbol', 'blockenberg'), value: a.currency, onChange: function (v) { setA({ currency: v }); } }),
184 el(TextControl, { label: __('Price', 'blockenberg'), value: a.price, onChange: function (v) { setA({ price: v }); } }),
185 el(TextControl, { label: __('Original Price', 'blockenberg'), value: a.originalPrice, onChange: function (v) { setA({ originalPrice: v }); } }),
186 el(ToggleControl, { label: __('Show Strikethrough Price', 'blockenberg'), checked: a.showOriginalPrice, onChange: function (v) { setA({ showOriginalPrice: v }); }, __nextHasNoMarginBottom: true }),
187 el(TextControl, { label: __('CTA Button Label', 'blockenberg'), value: a.ctaLabel, onChange: function (v) { setA({ ctaLabel: v }); } }),
188 el(TextControl, { label: __('CTA URL', 'blockenberg'), value: a.ctaUrl, onChange: function (v) { setA({ ctaUrl: v }); } }),
189 el(SelectControl, { label: __('Open Link In', 'blockenberg'), value: a.ctaTarget, options: CTA_TARGET_OPTIONS, onChange: function (v) { setA({ ctaTarget: v }); } }),
190 el(ToggleControl, { label: __('Show Affiliate Disclosure', 'blockenberg'), checked: a.showAffiliateNote, onChange: function (v) { setA({ showAffiliateNote: v }); }, __nextHasNoMarginBottom: true }),
191 a.showAffiliateNote && el(TextControl, { label: __('Disclosure Text', 'blockenberg'), value: a.affiliateNote, onChange: function (v) { setA({ affiliateNote: v }); } })
192 ),
193
194 /* Layout */
195 el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: false },
196 el(SelectControl, { label: __('Layout', 'blockenberg'), value: a.layout, options: LAYOUT_OPTIONS, onChange: function (v) { setA({ layout: v }); } }),
197 el(RangeControl, { label: __('Card Border Radius', 'blockenberg'), value: a.cardRadius, min: 0, max: 40, onChange: function (v) { setA({ cardRadius: v }); } }),
198 el(RangeControl, { label: __('Card Padding', 'blockenberg'), value: a.cardPadding, min: 8, max: 80, onChange: function (v) { setA({ cardPadding: v }); } }),
199 el(RangeControl, { label: __('Image Border Radius', 'blockenberg'), value: a.imageRadius, min: 0, max: 40, onChange: function (v) { setA({ imageRadius: v }); } }),
200 el(RangeControl, { label: __('Score Badge Radius', 'blockenberg'), value: a.scoreRadius, min: 0, max: 40, onChange: function (v) { setA({ scoreRadius: v }); } }),
201 el(RangeControl, { label: __('Verdict Box Radius', 'blockenberg'), value: a.verdictRadius, min: 0, max: 40, onChange: function (v) { setA({ verdictRadius: v }); } }),
202 el(RangeControl, { label: __('Button Radius', 'blockenberg'), value: a.ctaRadius, min: 0, max: 40, onChange: function (v) { setA({ ctaRadius: v }); } })
203 ),
204
205 /* Typography */
206 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
207 (function () {
208 var TC = getTypographyControl();
209 if (!TC) return el('p', { style: { color: '#999', fontSize: '12px', padding: '8px 0' } }, __('Typography control loading…', 'blockenberg'));
210 return el(wp.element.Fragment, {},
211 el(TC, { label: __('Product Name', 'blockenberg'), value: a.nameTypo || {}, onChange: function (v) { setA({ nameTypo: v }); } }),
212 el(TC, { label: __('Tagline', 'blockenberg'), value: a.taglineTypo || {}, onChange: function (v) { setA({ taglineTypo: v }); } }),
213 el(TC, { label: __('List Items', 'blockenberg'), value: a.listTypo || {}, onChange: function (v) { setA({ listTypo: v }); } }),
214 el(TC, { label: __('Verdict', 'blockenberg'), value: a.verdictTypo || {}, onChange: function (v) { setA({ verdictTypo: v }); } }),
215 el(RangeControl, { label: __('Score Number Size', 'blockenberg'), value: a.scoreSize, min: 28, max: 80, onChange: function (v) { setA({ scoreSize: v }); } })
216 );
217 })()
218 ),
219
220 /* Colors */
221 el(PanelBody, { title: __('Colors', 'blockenberg'), initialOpen: false },
222 cc('bgColor', __('Section Background', 'blockenberg'), a.bgColor),
223 cc('cardBg', __('Card Background', 'blockenberg'), a.cardBg),
224 cc('borderColor', __('Border Color', 'blockenberg'), a.borderColor),
225 cc('nameColor', __('Product Name', 'blockenberg'), a.nameColor),
226 cc('taglineColor', __('Tagline', 'blockenberg'), a.taglineColor),
227 cc('scoreBg', __('Score Badge Background', 'blockenberg'), a.scoreBg),
228 cc('scoreColor', __('Score Badge Text', 'blockenberg'), a.scoreColor),
229 cc('prosBg', __('Pros Box Background', 'blockenberg'), a.prosBg),
230 cc('prosHeaderColor', __('Pros Label Color', 'blockenberg'), a.prosHeaderColor),
231 cc('prosIconColor', __('Pros Check Icon', 'blockenberg'), a.prosIconColor),
232 cc('consBg', __('Cons Box Background', 'blockenberg'), a.consBg),
233 cc('consHeaderColor', __('Cons Label Color', 'blockenberg'), a.consHeaderColor),
234 cc('consIconColor', __('Cons X Icon', 'blockenberg'), a.consIconColor),
235 cc('verdictBg', __('Verdict Box Background', 'blockenberg'), a.verdictBg),
236 cc('verdictBorderColor', __('Verdict Border', 'blockenberg'), a.verdictBorderColor),
237 cc('verdictColor', __('Verdict Text', 'blockenberg'), a.verdictColor),
238 cc('ctaBg', __('Button Background', 'blockenberg'), a.ctaBg),
239 cc('ctaColor', __('Button Text', 'blockenberg'), a.ctaColor)
240 ),
241
242 /* Spacing */
243 el(PanelBody, { title: __('Spacing', 'blockenberg'), initialOpen: false },
244 el(RangeControl, { label: __('Padding Top (px)', 'blockenberg'), value: a.paddingTop, min: 0, max: 200, onChange: function (v) { setA({ paddingTop: v }); } }),
245 el(RangeControl, { label: __('Padding Bottom (px)', 'blockenberg'), value: a.paddingBottom, min: 0, max: 200, onChange: function (v) { setA({ paddingBottom: v }); } })
246 )
247 );
248
249 /* — Preview — */
250 var isHorizontal = a.layout === 'horizontal';
251
252 var scorePct = Math.round((a.overallScore / a.maxScore) * 100);
253
254 var previewStyle = {
255 background: a.cardBg,
256 border: '1px solid ' + a.borderColor,
257 borderRadius: a.cardRadius + 'px',
258 overflow: 'hidden',
259 fontFamily: 'sans-serif',
260 };
261
262 /* Top header row: image + name/tagline + score */
263 var headerRow = el('div', { style: { display: 'flex', gap: '20px', alignItems: 'center', padding: a.cardPadding + 'px', borderBottom: '1px solid ' + a.borderColor, flexDirection: isHorizontal ? 'row' : 'column' } },
264 a.showImage && a.imageUrl && el('img', { src: a.imageUrl, style: { width: isHorizontal ? '140px' : '100%', height: isHorizontal ? '140px' : '200px', objectFit: 'cover', borderRadius: a.imageRadius + 'px', flexShrink: 0 } }),
265 el('div', { style: { flex: 1, minWidth: 0 } },
266 el('div', { className: 'bkbg-ab-name', style: { color: a.nameColor, marginBottom: '6px' } }, a.productName),
267 a.tagline && el('div', { className: 'bkbg-ab-tagline', style: { color: a.taglineColor } }, a.tagline)
268 ),
269 a.showScore && el('div', { style: { textAlign: 'center', flexShrink: 0 } },
270 el('div', { style: { background: a.scoreBg, color: a.scoreColor, borderRadius: a.scoreRadius + 'px', padding: '12px 20px', marginBottom: '4px' } },
271 el('span', { style: { fontSize: a.scoreSize + 'px', fontWeight: 900, lineHeight: 1 } }, a.overallScore.toFixed(1)),
272 el('span', { style: { fontSize: (a.scoreSize * 0.45) + 'px', opacity: 0.8 } }, '/' + a.maxScore)
273 ),
274 el('div', { style: { fontSize: '11px', color: a.taglineColor, fontWeight: 600 } }, a.scoreLabel),
275 el('div', { style: { height: '6px', background: '#e5e7eb', borderRadius: '99px', overflow: 'hidden', marginTop: '6px', width: '80px', marginLeft: 'auto', marginRight: 'auto' } },
276 el('div', { style: { width: scorePct + '%', height: '100%', background: a.scoreBg, borderRadius: 'inherit' } })
277 )
278 )
279 );
280
281 /* Pros / Cons two columns */
282 var prosConsRow = el('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 0, borderBottom: '1px solid ' + a.borderColor } },
283 el('div', { style: { background: a.prosBg, padding: a.cardPadding + 'px', borderRight: '1px solid ' + a.borderColor } },
284 el('div', { className: 'bkbg-ab-pros-header', style: { color: a.prosHeaderColor, marginBottom: '10px' } }, '' + a.prosLabel),
285 el('ul', { className: 'bkbg-ab-list', style: { margin: 0, padding: 0, listStyle: 'none' } },
286 a.pros.map(function (p) {
287 return el('li', { key: p.id, style: { display: 'flex', gap: '8px', color: '#374151', marginBottom: '7px' } },
288 el('span', { style: { color: a.prosIconColor, fontWeight: 700, flexShrink: 0 } }, ''),
289 p.text
290 );
291 })
292 )
293 ),
294 el('div', { style: { background: a.consBg, padding: a.cardPadding + 'px' } },
295 el('div', { className: 'bkbg-ab-cons-header', style: { color: a.consHeaderColor, marginBottom: '10px' } }, '' + a.consLabel),
296 el('ul', { className: 'bkbg-ab-list', style: { margin: 0, padding: 0, listStyle: 'none' } },
297 a.cons.map(function (c) {
298 return el('li', { key: c.id, style: { display: 'flex', gap: '8px', color: '#374151', marginBottom: '7px' } },
299 el('span', { style: { color: a.consIconColor, fontWeight: 700, flexShrink: 0 } }, ''),
300 c.text
301 );
302 })
303 )
304 )
305 );
306
307 /* Verdict */
308 var verdictBox = a.showVerdict && el('div', { style: { padding: a.cardPadding + 'px', borderBottom: '1px solid ' + a.borderColor } },
309 el('div', { style: { background: a.verdictBg, borderLeft: '4px solid ' + a.verdictBorderColor, borderRadius: a.verdictRadius + 'px', padding: '14px 18px' } },
310 el('div', { style: { fontSize: '11px', fontWeight: 700, color: a.verdictBorderColor, textTransform: 'uppercase', letterSpacing: '0.07em', marginBottom: '6px' } }, a.verdictLabel),
311 el('div', { className: 'bkbg-ab-verdict-text', style: { color: a.verdictColor } }, a.verdict)
312 )
313 );
314
315 /* CTA */
316 var ctaRow = el('div', { style: { padding: a.cardPadding + 'px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexWrap: 'wrap', gap: '12px' } },
317 a.showPrice && el('div', null,
318 el('span', { style: { fontSize: '26px', fontWeight: 800, color: a.nameColor } }, a.currency + a.price),
319 a.showOriginalPrice && a.originalPrice && el('span', { style: { fontSize: '16px', color: a.taglineColor, textDecoration: 'line-through', marginLeft: '10px' } }, a.currency + a.originalPrice)
320 ),
321 el('a', { href: a.ctaUrl || '#', style: { display: 'inline-block', padding: '12px 28px', background: a.ctaBg, color: a.ctaColor, borderRadius: a.ctaRadius + 'px', fontWeight: 700, fontSize: '15px', textDecoration: 'none' } }, a.ctaLabel)
322 );
323
324 var affiliateNote = a.showAffiliateNote && el('div', { style: { padding: '8px ' + a.cardPadding + 'px', fontSize: '11px', color: '#9ca3af', fontStyle: 'italic', borderTop: '1px solid ' + a.borderColor } }, a.affiliateNote);
325
326 return el('div', useBlockProps({ className: 'bkbg-ab-wrap', style: buildWrapStyle(a) }),
327 inspector,
328 el('div', { style: previewStyle },
329 headerRow,
330 prosConsRow,
331 verdictBox,
332 ctaRow,
333 affiliateNote
334 )
335 );
336 },
337
338 save: function (props) {
339 var a = props.attributes;
340 return el('div', Object.assign({}, useBlockProps.save(), {
341 className: 'bkbg-ab-wrap',
342 style: buildWrapStyle(a),
343 'data-layout': a.layout,
344 }),
345 el('div', { className: 'bkbg-ab-card' },
346 /* Header */
347 el('div', { className: 'bkbg-ab-header bkbg-ab-header--' + a.layout },
348 a.showImage && a.imageUrl && el('img', { className: 'bkbg-ab-image', src: a.imageUrl, alt: a.productName, loading: 'lazy' }),
349 el('div', { className: 'bkbg-ab-product-info' },
350 el('h3', { className: 'bkbg-ab-name' }, a.productName),
351 a.tagline && el('p', { className: 'bkbg-ab-tagline' }, a.tagline)
352 ),
353 a.showScore && el('div', { className: 'bkbg-ab-score-wrap' },
354 el('div', { className: 'bkbg-ab-score-badge' },
355 el('span', { className: 'bkbg-ab-score-num' }, a.overallScore.toFixed(1)),
356 el('span', { className: 'bkbg-ab-score-denom' }, '/' + a.maxScore)
357 ),
358 el('div', { className: 'bkbg-ab-score-label' }, a.scoreLabel),
359 el('div', { className: 'bkbg-ab-score-bar-wrap' },
360 el('div', { className: 'bkbg-ab-score-bar', style: { width: Math.round((a.overallScore / a.maxScore) * 100) + '%' } })
361 )
362 )
363 ),
364 /* Pros & Cons */
365 el('div', { className: 'bkbg-ab-pros-cons' },
366 el('div', { className: 'bkbg-ab-pros' },
367 el('div', { className: 'bkbg-ab-pros-header' }, '' + a.prosLabel),
368 el('ul', { className: 'bkbg-ab-list' },
369 a.pros.map(function (p) { return el('li', { key: p.id }, el('span', { className: 'bkbg-ab-list-icon bkbg-ab-list-icon--pro' }, ''), p.text); })
370 )
371 ),
372 el('div', { className: 'bkbg-ab-cons' },
373 el('div', { className: 'bkbg-ab-cons-header' }, '' + a.consLabel),
374 el('ul', { className: 'bkbg-ab-list' },
375 a.cons.map(function (c) { return el('li', { key: c.id }, el('span', { className: 'bkbg-ab-list-icon bkbg-ab-list-icon--con' }, ''), c.text); })
376 )
377 )
378 ),
379 /* Verdict */
380 a.showVerdict && el('div', { className: 'bkbg-ab-verdict-wrap' },
381 el('div', { className: 'bkbg-ab-verdict-box' },
382 el('div', { className: 'bkbg-ab-verdict-label' }, a.verdictLabel),
383 el('p', { className: 'bkbg-ab-verdict-text' }, a.verdict)
384 )
385 ),
386 /* CTA */
387 el('div', { className: 'bkbg-ab-cta-row' },
388 a.showPrice && el('div', { className: 'bkbg-ab-price-display' },
389 el('span', { className: 'bkbg-ab-price' }, a.currency + a.price),
390 a.showOriginalPrice && a.originalPrice && el('span', { className: 'bkbg-ab-orig-price' }, a.currency + a.originalPrice)
391 ),
392 el('a', { href: a.ctaUrl || '#', className: 'bkbg-ab-cta-btn', target: a.ctaTarget, rel: 'noopener noreferrer nofollow' }, a.ctaLabel)
393 ),
394 /* Affiliate note */
395 a.showAffiliateNote && el('p', { className: 'bkbg-ab-affiliate-note' }, a.affiliateNote)
396 )
397 );
398 },
399 });
400 }() );
401