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

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

766 lines 55.5 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 RichText = wp.blockEditor.RichText;
8 var useBlockProps = wp.blockEditor.useBlockProps;
9 var MediaUpload = wp.blockEditor.MediaUpload;
10 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
11 var PanelBody = wp.components.PanelBody;
12 var Button = wp.components.Button;
13 var ToggleControl = wp.components.ToggleControl;
14 var RangeControl = wp.components.RangeControl;
15 var SelectControl = wp.components.SelectControl;
16 var TextControl = wp.components.TextControl;
17 var ColorPicker = wp.components.ColorPicker;
18 var Popover = wp.components.Popover;
19
20 var _tc; function getTypoControl(){ return _tc || (_tc = window.bkbgTypographyControl); }
21 var _tv; function getTypoCssVars(){ return _tv || (_tv = window.bkbgTypoCssVars); }
22 var IP = function () { return window.bkbgIconPicker; };
23
24 var IMAGE_POSITION_OPTIONS = [
25 { label: __('Left', 'blockenberg'), value: 'left' },
26 { label: __('Right', 'blockenberg'), value: 'right' },
27 ];
28 var RATIO_OPTIONS = [
29 { label: __('Square (1:1)', 'blockenberg'), value: '1/1' },
30 { label: __('Portrait (3:4)', 'blockenberg'), value: '3/4' },
31 { label: __('Landscape (4:3)', 'blockenberg'), value: '4/3' },
32 { label: __('Wide (16:9)', 'blockenberg'), value: '16/9' },
33 ];
34 var CURRENCY_POS_OPTIONS = [
35 { label: __('Before price ($99)', 'blockenberg'), value: 'before' },
36 { label: __('After price (99$)', 'blockenberg'), value: 'after' },
37 ];
38 var VARIANT_TYPE_OPTIONS = [
39 { label: __('Color Swatches', 'blockenberg'), value: 'color' },
40 { label: __('Button Labels', 'blockenberg'), value: 'button' },
41 ];
42
43 function makeId() { return 'ps' + Math.random().toString(36).substr(2, 6); }
44
45 function buildWrapStyle(a) {
46 return {
47 '--bkbg-ps-name-color': a.nameColor,
48 '--bkbg-ps-tagline-color': a.taglineColor,
49 '--bkbg-ps-price-color': a.priceColor,
50 '--bkbg-ps-orig-price-c': a.originalPriceColor,
51 '--bkbg-ps-rating-star': a.ratingStarColor,
52 '--bkbg-ps-instock-color': a.inStockColor,
53 '--bkbg-ps-outstock-color':a.outStockColor,
54 '--bkbg-ps-feature-color': a.featureColor,
55 '--bkbg-ps-feature-icon': a.featureIconColor,
56 '--bkbg-ps-cta-bg': a.ctaBg,
57 '--bkbg-ps-cta-color': a.ctaColor,
58 '--bkbg-ps-sec-cta-bg': a.secCtaBg,
59 '--bkbg-ps-sec-cta-color': a.secCtaColor,
60 '--bkbg-ps-tab-active-bg': a.tabActiveBg,
61 '--bkbg-ps-tab-active-c': a.tabActiveColor,
62 '--bkbg-ps-tab-idle-bg': a.tabIdleBg,
63 '--bkbg-ps-tab-idle-c': a.tabIdleColor,
64 '--bkbg-ps-card-bg': a.cardBg,
65 '--bkbg-ps-border-color': a.borderColor,
66 '--bkbg-ps-badge-bg': a.badgeBg,
67 '--bkbg-ps-badge-color': a.badgeColor,
68 '--bkbg-ps-gap': a.gap + 'px',
69 '--bkbg-ps-card-r': a.cardRadius + 'px',
70 '--bkbg-ps-card-pad': a.cardPadding + 'px',
71 '--bkbg-ps-img-r': a.imageRadius + 'px',
72 '--bkbg-ps-cta-r': a.ctaRadius + 'px',
73 '--bkbg-ps-badge-r': a.badgeRadius + 'px',
74 paddingTop: a.paddingTop + 'px',
75 paddingBottom: a.paddingBottom + 'px',
76 backgroundColor: a.bgColor || undefined,
77 };
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 function StarRating(props) {
96 var score = props.score || 0;
97 var color = props.color || '#f59e0b';
98 var stars = [];
99 for (var i = 1; i <= 5; i++) {
100 var pct = Math.min(1, Math.max(0, score - (i - 1))) * 100;
101 stars.push(
102 el('span', { key: i, style: { position: 'relative', display: 'inline-block', fontSize: '16px', lineHeight: 1 } },
103 el('span', { style: { color: '#d1d5db' } }, '�
104 '),
105 el('span', { style: { position: 'absolute', left: 0, top: 0, overflow: 'hidden', width: pct + '%', color: color } }, '�
106 ')
107 )
108 );
109 }
110 return el('div', { style: { display: 'flex', gap: '2px' } }, stars);
111 }
112
113 /* ── deprecated: v1 save (old scalar typo attrs, old CSS vars) ── */
114 function buildWrapStyleV1(a) {
115 return {
116 '--bkbg-ps-name-color': a.nameColor,
117 '--bkbg-ps-tagline-color': a.taglineColor,
118 '--bkbg-ps-price-color': a.priceColor,
119 '--bkbg-ps-orig-price-c': a.originalPriceColor,
120 '--bkbg-ps-rating-star': a.ratingStarColor,
121 '--bkbg-ps-instock-color': a.inStockColor,
122 '--bkbg-ps-outstock-color':a.outStockColor,
123 '--bkbg-ps-feature-color': a.featureColor,
124 '--bkbg-ps-feature-icon': a.featureIconColor,
125 '--bkbg-ps-cta-bg': a.ctaBg,
126 '--bkbg-ps-cta-color': a.ctaColor,
127 '--bkbg-ps-sec-cta-bg': a.secCtaBg,
128 '--bkbg-ps-sec-cta-color': a.secCtaColor,
129 '--bkbg-ps-tab-active-bg': a.tabActiveBg,
130 '--bkbg-ps-tab-active-c': a.tabActiveColor,
131 '--bkbg-ps-tab-idle-bg': a.tabIdleBg,
132 '--bkbg-ps-tab-idle-c': a.tabIdleColor,
133 '--bkbg-ps-card-bg': a.cardBg,
134 '--bkbg-ps-border-color': a.borderColor,
135 '--bkbg-ps-badge-bg': a.badgeBg,
136 '--bkbg-ps-badge-color': a.badgeColor,
137 '--bkbg-ps-name-sz': (a.nameSize||28) + 'px',
138 '--bkbg-ps-tagline-sz': (a.taglineSize||15) + 'px',
139 '--bkbg-ps-price-sz': (a.priceSize||36) + 'px',
140 '--bkbg-ps-feature-sz': (a.featureSize||14) + 'px',
141 '--bkbg-ps-rating-sz': (a.ratingSize||13) + 'px',
142 '--bkbg-ps-tab-label-sz': (a.tabLabelSize||14) + 'px',
143 '--bkbg-ps-gap': a.gap + 'px',
144 '--bkbg-ps-card-r': a.cardRadius + 'px',
145 '--bkbg-ps-card-pad': a.cardPadding + 'px',
146 '--bkbg-ps-img-r': a.imageRadius + 'px',
147 '--bkbg-ps-cta-r': a.ctaRadius + 'px',
148 '--bkbg-ps-badge-r': a.badgeRadius + 'px',
149 paddingTop: a.paddingTop + 'px',
150 paddingBottom: a.paddingBottom + 'px',
151 backgroundColor: a.bgColor || undefined,
152 };
153 }
154
155 var v1Attributes = {
156 mainImageUrl:{type:'string',default:''},mainImageId:{type:'integer',default:0},
157 gallery:{type:'array',default:[],items:{type:'object'}},
158 productName:{type:'string',default:'Premium Wireless Headphones'},
159 tagline:{type:'string',default:'Studio-quality sound, redefined.'},
160 sku:{type:'string',default:'SKU-WH-001'},showSku:{type:'boolean',default:false},
161 price:{type:'string',default:'249.00'},originalPrice:{type:'string',default:'349.00'},
162 showOriginalPrice:{type:'boolean',default:true},
163 currency:{type:'string',default:'$'},currencyPos:{type:'string',default:'before'},
164 rating:{type:'number',default:4.7},reviewCount:{type:'integer',default:2134},showRating:{type:'boolean',default:true},
165 inStock:{type:'boolean',default:true},stockText:{type:'string',default:'In Stock \u2013 ships in 24h'},showStock:{type:'boolean',default:true},
166 badge:{type:'string',default:'Best Seller'},showBadge:{type:'boolean',default:true},
167 features:{type:'array',default:[],items:{type:'object'}},showFeatures:{type:'boolean',default:true},
168 featuresTitle:{type:'string',default:'Why you\'ll love it'},featureIcon:{type:'string',default:'\u2713'},
169 tabs:{type:'array',default:[],items:{type:'object'}},showTabs:{type:'boolean',default:true},
170 variantGroups:{type:'array',default:[],items:{type:'object'}},showVariants:{type:'boolean',default:true},
171 trustBadges:{type:'array',default:[],items:{type:'object'}},showTrustBadges:{type:'boolean',default:true},
172 ctaLabel:{type:'string',default:'Add to Cart'},ctaUrl:{type:'string',default:'#'},
173 showSecCta:{type:'boolean',default:true},secCtaLabel:{type:'string',default:'Add to Wishlist'},secCtaUrl:{type:'string',default:'#'},
174 imagePosition:{type:'string',default:'left'},imageRatio:{type:'string',default:'1/1'},
175 gap:{type:'integer',default:48},cardRadius:{type:'integer',default:20},cardPadding:{type:'integer',default:40},
176 imageRadius:{type:'integer',default:16},ctaRadius:{type:'integer',default:10},badgeRadius:{type:'integer',default:99},
177 nameSize:{type:'integer',default:28},taglineSize:{type:'integer',default:15},
178 priceSize:{type:'integer',default:36},featureSize:{type:'integer',default:14},
179 ratingSize:{type:'integer',default:13},tabLabelSize:{type:'integer',default:14},
180 nameColor:{type:'string',default:'#111827'},priceColor:{type:'string',default:'#6c3fb5'},
181 originalPriceColor:{type:'string',default:'#9ca3af'},taglineColor:{type:'string',default:'#6b7280'},
182 ratingStarColor:{type:'string',default:'#f59e0b'},inStockColor:{type:'string',default:'#16a34a'},
183 outStockColor:{type:'string',default:'#dc2626'},featureColor:{type:'string',default:'#374151'},
184 featureIconColor:{type:'string',default:'#6c3fb5'},
185 ctaBg:{type:'string',default:'#6c3fb5'},ctaColor:{type:'string',default:'#ffffff'},
186 secCtaBg:{type:'string',default:'#f3f4f6'},secCtaColor:{type:'string',default:'#374151'},
187 tabActiveBg:{type:'string',default:'#6c3fb5'},tabActiveColor:{type:'string',default:'#ffffff'},
188 tabIdleBg:{type:'string',default:'#f3f4f6'},tabIdleColor:{type:'string',default:'#374151'},
189 cardBg:{type:'string',default:'#ffffff'},borderColor:{type:'string',default:'#e5e7eb'},
190 badgeBg:{type:'string',default:'#6c3fb5'},badgeColor:{type:'string',default:'#ffffff'},
191 bgColor:{type:'string',default:''},
192 paddingTop:{type:'integer',default:64},paddingBottom:{type:'integer',default:64},
193 nameFontWeight:{type:'string',default:'800'},taglineFontWeight:{type:'string',default:'400'},
194 };
195
196 var deprecated = [{
197 attributes: v1Attributes,
198 save: function(props) {
199 var a = props.attributes;
200 return el('div', Object.assign({}, useBlockProps.save(), {
201 className: 'bkbg-ps-wrap',
202 style: buildWrapStyleV1(a),
203 'data-image-position': a.imagePosition,
204 'data-image-ratio': a.imageRatio,
205 'data-show-tabs': a.showTabs ? '1' : '0',
206 'data-tabs': JSON.stringify(a.tabs),
207 'data-variants': JSON.stringify(a.variantGroups),
208 'data-gallery': JSON.stringify(a.gallery),
209 }),
210 el('div', { className: 'bkbg-ps-layout bkbg-ps-img-' + a.imagePosition },
211 el('div', { className: 'bkbg-ps-image-col' },
212 el('div', { className: 'bkbg-ps-main-image-wrap' },
213 a.mainImageUrl && el('img', { className: 'bkbg-ps-main-img', src: a.mainImageUrl, alt: a.productName, loading: 'lazy' })
214 ),
215 a.gallery.length > 0 && el('div', { className: 'bkbg-ps-gallery' },
216 a.gallery.map(function (g, idx) {
217 return el('img', { key: idx, className: 'bkbg-ps-thumb' + (idx === 0 ? ' bkbg-ps-thumb--active' : ''), src: g.url, alt: (a.productName + ' ' + (idx + 1)), loading: 'lazy', 'data-full': g.url });
218 })
219 )
220 ),
221 el('div', { className: 'bkbg-ps-info-col' },
222 a.showBadge && a.badge && el('span', { className: 'bkbg-ps-badge' }, a.badge),
223 el('h2', { className: 'bkbg-ps-name' }, a.productName),
224 a.tagline && el('p', { className: 'bkbg-ps-tagline' }, a.tagline),
225 a.showRating && el('div', { className: 'bkbg-ps-rating', 'data-score': a.rating, 'data-count': a.reviewCount }),
226 el('div', { className: 'bkbg-ps-price-row' },
227 el('span', { className: 'bkbg-ps-price' }, a.currencyPos === 'before' ? a.currency + a.price : a.price + a.currency),
228 a.showOriginalPrice && a.originalPrice && el('span', { className: 'bkbg-ps-orig-price' }, a.currencyPos === 'before' ? a.currency + a.originalPrice : a.originalPrice + a.currency)
229 ),
230 a.showSku && a.sku && el('div', { className: 'bkbg-ps-sku' }, 'SKU: ' + a.sku),
231 a.showStock && el('div', { className: 'bkbg-ps-stock bkbg-ps-stock--' + (a.inStock ? 'in' : 'out') }, a.stockText),
232 a.showVariants && a.variantGroups.length > 0 && el('div', { className: 'bkbg-ps-variants' }),
233 el('div', { className: 'bkbg-ps-ctas' },
234 a.ctaLabel && el('a', { href: a.ctaUrl || '#', className: 'bkbg-ps-cta bkbg-ps-cta--primary' }, a.ctaLabel),
235 a.showSecCta && a.secCtaLabel && el('a', { href: a.secCtaUrl || '#', className: 'bkbg-ps-cta bkbg-ps-cta--secondary' }, a.secCtaLabel)
236 ),
237 a.showTrustBadges && a.trustBadges.length > 0 && el('div', { className: 'bkbg-ps-trust-badges' },
238 a.trustBadges.map(function (b) {
239 return el('div', { key: b.id, className: 'bkbg-ps-trust-badge' },
240 el('span', { className: 'bkbg-ps-trust-icon' }, b.icon),
241 el('span', { className: 'bkbg-ps-trust-text' }, b.text)
242 );
243 })
244 ),
245 a.showFeatures && a.features.length > 0 && el('div', { className: 'bkbg-ps-features' },
246 a.featuresTitle && el('div', { className: 'bkbg-ps-features-title' }, a.featuresTitle),
247 el('ul', { className: 'bkbg-ps-feature-list' },
248 a.features.map(function (f) {
249 return el('li', { key: f.id },
250 el('span', { className: 'bkbg-ps-feature-icon' }, a.featureIcon),
251 f.text
252 );
253 })
254 )
255 ),
256 a.showTabs && a.tabs.length > 0 && el('div', { className: 'bkbg-ps-tabs-wrap' },
257 el('div', { className: 'bkbg-ps-tab-bar' },
258 a.tabs.map(function (t, idx) {
259 return el('button', { key: t.id, className: 'bkbg-ps-tab-btn' + (idx === 0 ? ' bkbg-ps-tab-btn--active' : ''), 'data-tab': t.id }, t.label);
260 })
261 ),
262 el('div', { className: 'bkbg-ps-tab-panels' },
263 a.tabs.map(function (t, idx) {
264 return el('div', { key: t.id, className: 'bkbg-ps-tab-panel' + (idx === 0 ? ' bkbg-ps-tab-panel--active' : ''), 'data-tab-panel': t.id }, t.content);
265 })
266 )
267 )
268 )
269 )
270 );
271 }
272 }];
273
274 registerBlockType('blockenberg/product-showcase', {
275 deprecated: deprecated,
276 edit: function (props) {
277 var a = props.attributes;
278 var setA = props.setAttributes;
279 var TC = getTypoControl();
280 var openColor = useState(null);
281 var openColorKey = openColor[0];
282 var setOpenColorKey = openColor[1];
283 var activeTab = useState(0);
284 var activeTabIdx = activeTab[0];
285 var setActiveTabIdx = activeTab[1];
286
287 function setColor(key) {
288 return function (v) { var o = {}; o[key] = v; setA(o); };
289 }
290 function cc(key, label, val) {
291 return renderColorControl(key, label, val, setColor(key), openColorKey, setOpenColorKey);
292 }
293
294 /* — Array helpers — */
295 function updateFeature(id, val) {
296 setA({ features: a.features.map(function (f) { return f.id === id ? Object.assign({}, f, { text: val }) : f; }) });
297 }
298 function removeFeature(id) {
299 setA({ features: a.features.filter(function (f) { return f.id !== id; }) });
300 }
301 function addFeature() {
302 setA({ features: a.features.concat([{ id: makeId(), text: 'New feature point' }]) });
303 }
304 function updateTabField(id, field, val) {
305 setA({ tabs: a.tabs.map(function (t) { return t.id === id ? Object.assign({}, t, { [field]: val }) : t; }) });
306 }
307 function removeTab(id) {
308 setA({ tabs: a.tabs.filter(function (t) { return t.id !== id; }) });
309 }
310 function addTab() {
311 setA({ tabs: a.tabs.concat([{ id: makeId(), label: 'New Tab', content: 'Tab content goes here.' }]) });
312 }
313 function updateTrustBadge(id, field, val) {
314 setA({ trustBadges: a.trustBadges.map(function (b) { return b.id === id ? Object.assign({}, b, { [field]: val }) : b; }) });
315 }
316 function removeTrustBadge(id) {
317 setA({ trustBadges: a.trustBadges.filter(function (b) { return b.id !== id; }) });
318 }
319 function addTrustBadge() {
320 setA({ trustBadges: a.trustBadges.concat([{ id: makeId(), icon: '🔒', iconType: 'custom-char', iconDashicon: '', iconImageUrl: '', text: 'Trust Point' }]) });
321 }
322 function updateVariantGroupField(id, field, val) {
323 setA({ variantGroups: a.variantGroups.map(function (g) { return g.id === id ? Object.assign({}, g, { [field]: val }) : g; }) });
324 }
325 function removeVariantGroup(id) {
326 setA({ variantGroups: a.variantGroups.filter(function (g) { return g.id !== id; }) });
327 }
328 function addVariantGroup() {
329 setA({ variantGroups: a.variantGroups.concat([{ id: makeId(), label: 'Option', type: 'button', options: [{ id: makeId(), label: 'A', value: '#cccccc' }] }]) });
330 }
331 function addVariantOption(groupId) {
332 setA({ variantGroups: a.variantGroups.map(function (g) {
333 if (g.id !== groupId) return g;
334 return Object.assign({}, g, { options: (g.options || []).concat([{ id: makeId(), label: 'Option', value: '#cccccc' }]) });
335 }) });
336 }
337 function updateVariantOption(groupId, optId, field, val) {
338 setA({ variantGroups: a.variantGroups.map(function (g) {
339 if (g.id !== groupId) return g;
340 return Object.assign({}, g, { options: (g.options || []).map(function (o) { return o.id === optId ? Object.assign({}, o, { [field]: val }) : o; }) });
341 }) });
342 }
343 function removeVariantOption(groupId, optId) {
344 setA({ variantGroups: a.variantGroups.map(function (g) {
345 if (g.id !== groupId) return g;
346 return Object.assign({}, g, { options: (g.options || []).filter(function (o) { return o.id !== optId; }) });
347 }) });
348 }
349 function updateGalleryItem(idx, field, val) {
350 var newGallery = a.gallery.slice();
351 newGallery[idx] = Object.assign({}, newGallery[idx], { [field]: val });
352 setA({ gallery: newGallery });
353 }
354 function removeGalleryItem(idx) {
355 setA({ gallery: a.gallery.filter(function (_, i) { return i !== idx; }) });
356 }
357
358 /* − Inspector − */
359 var inspector = el(InspectorControls, null,
360
361 /* Product Info */
362 el(PanelBody, { title: __('Product Info', 'blockenberg'), initialOpen: true },
363 el(TextControl, { label: __('Product Name', 'blockenberg'), value: a.productName, onChange: function (v) { setA({ productName: v }); } }),
364 el(TextControl, { label: __('Tagline', 'blockenberg'), value: a.tagline, onChange: function (v) { setA({ tagline: v }); } }),
365 el(TextControl, { label: __('Badge Text', 'blockenberg'), value: a.badge, onChange: function (v) { setA({ badge: v }); } }),
366 el(ToggleControl, { label: __('Show Badge', 'blockenberg'), checked: a.showBadge, onChange: function (v) { setA({ showBadge: v }); }, __nextHasNoMarginBottom: true }),
367 el(TextControl, { label: __('SKU', 'blockenberg'), value: a.sku, onChange: function (v) { setA({ sku: v }); } }),
368 el(ToggleControl, { label: __('Show SKU', 'blockenberg'), checked: a.showSku, onChange: function (v) { setA({ showSku: v }); }, __nextHasNoMarginBottom: true })
369 ),
370
371 /* Pricing */
372 el(PanelBody, { title: __('Pricing', 'blockenberg'), initialOpen: false },
373 el(TextControl, { label: __('Currency Symbol', 'blockenberg'), value: a.currency, onChange: function (v) { setA({ currency: v }); } }),
374 el(SelectControl, { label: __('Currency Position', 'blockenberg'), value: a.currencyPos, options: CURRENCY_POS_OPTIONS, onChange: function (v) { setA({ currencyPos: v }); } }),
375 el(TextControl, { label: __('Price', 'blockenberg'), value: a.price, onChange: function (v) { setA({ price: v }); } }),
376 el(TextControl, { label: __('Original Price (strikethrough)', 'blockenberg'), value: a.originalPrice, onChange: function (v) { setA({ originalPrice: v }); } }),
377 el(ToggleControl, { label: __('Show Original Price', 'blockenberg'), checked: a.showOriginalPrice, onChange: function (v) { setA({ showOriginalPrice: v }); }, __nextHasNoMarginBottom: true })
378 ),
379
380 /* Rating & Stock */
381 el(PanelBody, { title: __('Rating & Stock', 'blockenberg'), initialOpen: false },
382 el(RangeControl, { label: __('Rating (out of 5)', 'blockenberg'), value: a.rating, min: 0, max: 5, step: 0.1, onChange: function (v) { setA({ rating: v }); } }),
383 el(TextControl, { label: __('Review Count', 'blockenberg'), value: String(a.reviewCount), onChange: function (v) { setA({ reviewCount: parseInt(v) || 0 }); } }),
384 el(ToggleControl, { label: __('Show Rating', 'blockenberg'), checked: a.showRating, onChange: function (v) { setA({ showRating: v }); }, __nextHasNoMarginBottom: true }),
385 el(ToggleControl, { label: __('In Stock', 'blockenberg'), checked: a.inStock, onChange: function (v) { setA({ inStock: v }); }, __nextHasNoMarginBottom: true }),
386 el(TextControl, { label: __('Stock Label', 'blockenberg'), value: a.stockText, onChange: function (v) { setA({ stockText: v }); } }),
387 el(ToggleControl, { label: __('Show Stock Status', 'blockenberg'), checked: a.showStock, onChange: function (v) { setA({ showStock: v }); }, __nextHasNoMarginBottom: true })
388 ),
389
390 /* Features */
391 el(PanelBody, { title: __('Features List', 'blockenberg'), initialOpen: false },
392 el(TextControl, { label: __('Section Title', 'blockenberg'), value: a.featuresTitle, onChange: function (v) { setA({ featuresTitle: v }); } }),
393 el(IP().IconPickerControl, IP().iconPickerProps(a, setA, { charAttr: 'featureIcon', typeAttr: 'featureIconType', dashiconAttr: 'featureIconDashicon', imageUrlAttr: 'featureIconImageUrl', colorAttr: 'featureIconDashiconColor' })),
394 el(ToggleControl, { label: __('Show Features', 'blockenberg'), checked: a.showFeatures, onChange: function (v) { setA({ showFeatures: v }); }, __nextHasNoMarginBottom: true }),
395 a.features.map(function (f) {
396 return el('div', { key: f.id, style: { display: 'flex', gap: '6px', marginBottom: '6px', alignItems: 'center' } },
397 el(TextControl, { value: f.text, onChange: function (v) { updateFeature(f.id, v); }, style: { flex: 1, margin: 0 } }),
398 el(Button, { onClick: function () { removeFeature(f.id); }, icon: 'trash', isDestructive: true, label: __('Remove', 'blockenberg') })
399 );
400 }),
401 el(Button, { variant: 'secondary', onClick: addFeature }, __('+ Add Feature', 'blockenberg'))
402 ),
403
404 /* Tabs */
405 el(PanelBody, { title: __('Product Tabs', 'blockenberg'), initialOpen: false },
406 el(ToggleControl, { label: __('Show Tabs', 'blockenberg'), checked: a.showTabs, onChange: function (v) { setA({ showTabs: v }); }, __nextHasNoMarginBottom: true }),
407 a.tabs.map(function (t) {
408 return el(PanelBody, { key: t.id, title: t.label || __('Tab', 'blockenberg'), initialOpen: false },
409 el(TextControl, { label: __('Tab Label', 'blockenberg'), value: t.label, onChange: function (v) { updateTabField(t.id, 'label', v); } }),
410 el(TextControl, { label: __('Content', 'blockenberg'), value: t.content, onChange: function (v) { updateTabField(t.id, 'content', v); } }),
411 el(Button, { variant: 'link', isDestructive: true, onClick: function () { removeTab(t.id); } }, __('Remove Tab', 'blockenberg'))
412 );
413 }),
414 el(Button, { variant: 'secondary', onClick: addTab }, __('+ Add Tab', 'blockenberg'))
415 ),
416
417 /* Variants */
418 el(PanelBody, { title: __('Variant Selectors', 'blockenberg'), initialOpen: false },
419 el(ToggleControl, { label: __('Show Variants', 'blockenberg'), checked: a.showVariants, onChange: function (v) { setA({ showVariants: v }); }, __nextHasNoMarginBottom: true }),
420 a.variantGroups.map(function (g) {
421 return el(PanelBody, { key: g.id, title: g.label || __('Variant Group', 'blockenberg'), initialOpen: false },
422 el(TextControl, { label: __('Group Label', 'blockenberg'), value: g.label, onChange: function (v) { updateVariantGroupField(g.id, 'label', v); } }),
423 el(SelectControl, { label: __('Display Style', 'blockenberg'), value: g.type, options: VARIANT_TYPE_OPTIONS, onChange: function (v) { updateVariantGroupField(g.id, 'type', v); } }),
424 (g.options || []).map(function (o) {
425 return el('div', { key: o.id, style: { display: 'flex', gap: '6px', marginBottom: '6px', alignItems: 'center' } },
426 el(TextControl, { label: g.type === 'color' ? __('Name', 'blockenberg') : __('Label', 'blockenberg'), value: o.label, onChange: function (v) { updateVariantOption(g.id, o.id, 'label', v); }, style: { flex: 1, margin: 0 } }),
427 g.type === 'color' && el(TextControl, { label: __('Hex', 'blockenberg'), value: o.value, onChange: function (v) { updateVariantOption(g.id, o.id, 'value', v); }, style: { width: '80px', margin: 0 } }),
428 el(Button, { onClick: function () { removeVariantOption(g.id, o.id); }, icon: 'trash', isDestructive: true, label: __('Remove', 'blockenberg') })
429 );
430 }),
431 el(Button, { variant: 'secondary', onClick: function () { addVariantOption(g.id); } }, __('+ Add Option', 'blockenberg')),
432 el(Button, { variant: 'link', isDestructive: true, onClick: function () { removeVariantGroup(g.id); } }, __('Remove Group', 'blockenberg'))
433 );
434 }),
435 el(Button, { variant: 'secondary', onClick: addVariantGroup }, __('+ Add Variant Group', 'blockenberg'))
436 ),
437
438 /* Trust Badges */
439 el(PanelBody, { title: __('Trust Badges', 'blockenberg'), initialOpen: false },
440 el(ToggleControl, { label: __('Show Trust Badges', 'blockenberg'), checked: a.showTrustBadges, onChange: function (v) { setA({ showTrustBadges: v }); }, __nextHasNoMarginBottom: true }),
441 a.trustBadges.map(function (b) {
442 return el('div', { key: b.id, style: { border: '1px solid #e5e7eb', borderRadius: '6px', padding: '8px', marginBottom: '6px' } },
443 el(IP().IconPickerControl, {
444 iconType: b.iconType || 'custom-char',
445 customChar: b.icon,
446 dashicon: b.iconDashicon || '',
447 imageUrl: b.iconImageUrl || '',
448 onChangeType: function (v) { updateTrustBadge(b.id, 'iconType', v); },
449 onChangeChar: function (v) { updateTrustBadge(b.id, 'icon', v); },
450 onChangeDashicon: function (v) { updateTrustBadge(b.id, 'iconDashicon', v); },
451 onChangeImageUrl: function (v) { updateTrustBadge(b.id, 'iconImageUrl', v); }
452 }),
453 el(TextControl, { label: __('Text', 'blockenberg'), value: b.text, onChange: function (v) { updateTrustBadge(b.id, 'text', v); } }),
454 el(Button, { variant: 'link', isDestructive: true, onClick: function () { removeTrustBadge(b.id); } }, __('Remove', 'blockenberg'))
455 );
456 }),
457 el(Button, { variant: 'secondary', onClick: addTrustBadge }, __('+ Add Badge', 'blockenberg'))
458 ),
459
460 /* CTAs */
461 el(PanelBody, { title: __('Call to Action Buttons', 'blockenberg'), initialOpen: false },
462 el(TextControl, { label: __('Primary Button Label', 'blockenberg'), value: a.ctaLabel, onChange: function (v) { setA({ ctaLabel: v }); } }),
463 el(TextControl, { label: __('Primary Button URL', 'blockenberg'), value: a.ctaUrl, onChange: function (v) { setA({ ctaUrl: v }); } }),
464 el(ToggleControl, { label: __('Show Secondary Button', 'blockenberg'), checked: a.showSecCta, onChange: function (v) { setA({ showSecCta: v }); }, __nextHasNoMarginBottom: true }),
465 a.showSecCta && el(TextControl, { label: __('Secondary Button Label', 'blockenberg'), value: a.secCtaLabel, onChange: function (v) { setA({ secCtaLabel: v }); } }),
466 a.showSecCta && el(TextControl, { label: __('Secondary Button URL', 'blockenberg'), value: a.secCtaUrl, onChange: function (v) { setA({ secCtaUrl: v }); } })
467 ),
468
469 /* Image Gallery */
470 el(PanelBody, { title: __('Product Images', 'blockenberg'), initialOpen: false },
471 el(MediaUploadCheck, null,
472 el(MediaUpload, {
473 onSelect: function (m) { setA({ mainImageUrl: m.url, mainImageId: m.id }); },
474 allowedTypes: ['image'],
475 value: a.mainImageId,
476 render: function (ref) {
477 return el('div', { style: { marginBottom: '12px' } },
478 a.mainImageUrl && el('img', { src: a.mainImageUrl, style: { width: '100%', borderRadius: '6px', marginBottom: '8px' } }),
479 el(Button, { onClick: ref.open, variant: a.mainImageId ? 'secondary' : 'primary', style: { marginBottom: '6px' } },
480 a.mainImageId ? __('Replace Main Image', 'blockenberg') : __('Select Main Image', 'blockenberg')
481 ),
482 a.mainImageId && el(Button, { onClick: function () { setA({ mainImageUrl: '', mainImageId: 0 }); }, variant: 'link', isDestructive: true }, __('Remove', 'blockenberg'))
483 );
484 }
485 })
486 ),
487 el('hr', { style: { margin: '8px 0' } }),
488 el('p', { style: { fontSize: '12px', color: '#6b7280', margin: '0 0 8px' } }, __('Gallery thumbnails', 'blockenberg')),
489 a.gallery.map(function (g, idx) {
490 return el('div', { key: idx, style: { display: 'flex', gap: '6px', alignItems: 'center', marginBottom: '6px' } },
491 g.url && el('img', { src: g.url, style: { width: '40px', height: '40px', objectFit: 'cover', borderRadius: '4px' } }),
492 el(Button, { variant: 'link', isDestructive: true, onClick: function () { removeGalleryItem(idx); } }, __('Remove', 'blockenberg'))
493 );
494 }),
495 el(MediaUploadCheck, null,
496 el(MediaUpload, {
497 onSelect: function (m) { setA({ gallery: a.gallery.concat([{ url: m.url, id: m.id }]) }); },
498 allowedTypes: ['image'],
499 render: function (ref) {
500 return el(Button, { variant: 'secondary', onClick: ref.open }, __('+ Add Gallery Image', 'blockenberg'));
501 }
502 })
503 )
504 ),
505
506 /* Layout */
507 el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: false },
508 el(SelectControl, { label: __('Image Position', 'blockenberg'), value: a.imagePosition, options: IMAGE_POSITION_OPTIONS, onChange: function (v) { setA({ imagePosition: v }); } }),
509 el(SelectControl, { label: __('Image Aspect Ratio', 'blockenberg'), value: a.imageRatio, options: RATIO_OPTIONS, onChange: function (v) { setA({ imageRatio: v }); } }),
510 el(RangeControl, { label: __('Column Gap (px)', 'blockenberg'), value: a.gap, min: 0, max: 120, onChange: function (v) { setA({ gap: v }); } }),
511 el(RangeControl, { label: __('Card Border Radius', 'blockenberg'), value: a.cardRadius, min: 0, max: 40, onChange: function (v) { setA({ cardRadius: v }); } }),
512 el(RangeControl, { label: __('Card Padding', 'blockenberg'), value: a.cardPadding, min: 8, max: 80, onChange: function (v) { setA({ cardPadding: v }); } }),
513 el(RangeControl, { label: __('Image Border Radius', 'blockenberg'), value: a.imageRadius, min: 0, max: 40, onChange: function (v) { setA({ imageRadius: v }); } }),
514 el(RangeControl, { label: __('Button Radius', 'blockenberg'), value: a.ctaRadius, min: 0, max: 40, onChange: function (v) { setA({ ctaRadius: v }); } }),
515 el(RangeControl, { label: __('Badge Radius', 'blockenberg'), value: a.badgeRadius, min: 0, max: 99, onChange: function (v) { setA({ badgeRadius: v }); } })
516 ),
517
518 /* Typography */
519 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
520 TC && el(TC, {label:__('Product Name','blockenberg'),typo:a.nameTypo||{},onChange:function(v){setA({nameTypo:v});}}),
521 TC && el(TC, {label:__('Tagline','blockenberg'),typo:a.taglineTypo||{},onChange:function(v){setA({taglineTypo:v});}}),
522 TC && el(TC, {label:__('Price','blockenberg'),typo:a.priceTypo||{},onChange:function(v){setA({priceTypo:v});}}),
523 TC && el(TC, {label:__('Feature Items','blockenberg'),typo:a.featureTypo||{},onChange:function(v){setA({featureTypo:v});}}),
524 TC && el(TC, {label:__('CTA Buttons','blockenberg'),typo:a.ctaTypo||{},onChange:function(v){setA({ctaTypo:v});}})
525 ),
526
527 /* Colors */
528 el(PanelBody, { title: __('Colors', 'blockenberg'), initialOpen: false },
529 cc('bgColor', __('Section Background', 'blockenberg'), a.bgColor),
530 cc('cardBg', __('Card Background', 'blockenberg'), a.cardBg),
531 cc('borderColor', __('Border Color', 'blockenberg'), a.borderColor),
532 cc('nameColor', __('Product Name', 'blockenberg'), a.nameColor),
533 cc('taglineColor', __('Tagline', 'blockenberg'), a.taglineColor),
534 cc('priceColor', __('Price', 'blockenberg'), a.priceColor),
535 cc('originalPriceColor', __('Original Price', 'blockenberg'), a.originalPriceColor),
536 cc('ratingStarColor', __('Rating Stars', 'blockenberg'), a.ratingStarColor),
537 cc('inStockColor', __('In Stock Text', 'blockenberg'), a.inStockColor),
538 cc('outStockColor', __('Out of Stock Text', 'blockenberg'), a.outStockColor),
539 cc('featureColor', __('Feature Text', 'blockenberg'), a.featureColor),
540 cc('featureIconColor', __('Feature Icon', 'blockenberg'), a.featureIconColor),
541 cc('badgeBg', __('Badge Background', 'blockenberg'), a.badgeBg),
542 cc('badgeColor', __('Badge Text', 'blockenberg'), a.badgeColor),
543 cc('tabActiveBg', __('Active Tab Background', 'blockenberg'),a.tabActiveBg),
544 cc('tabActiveColor', __('Active Tab Text', 'blockenberg'), a.tabActiveColor),
545 cc('tabIdleBg', __('Idle Tab Background', 'blockenberg'), a.tabIdleBg),
546 cc('tabIdleColor', __('Idle Tab Text', 'blockenberg'), a.tabIdleColor),
547 cc('ctaBg', __('Primary Button BG', 'blockenberg'), a.ctaBg),
548 cc('ctaColor', __('Primary Button Text', 'blockenberg'), a.ctaColor),
549 cc('secCtaBg', __('Secondary Button BG', 'blockenberg'), a.secCtaBg),
550 cc('secCtaColor', __('Secondary Button Text', 'blockenberg'),a.secCtaColor)
551 ),
552
553 /* Spacing */
554 el(PanelBody, { title: __('Spacing', 'blockenberg'), initialOpen: false },
555 el(RangeControl, { label: __('Padding Top (px)', 'blockenberg'), value: a.paddingTop, min: 0, max: 200, onChange: function (v) { setA({ paddingTop: v }); } }),
556 el(RangeControl, { label: __('Padding Bottom (px)', 'blockenberg'), value: a.paddingBottom, min: 0, max: 200, onChange: function (v) { setA({ paddingBottom: v }); } })
557 )
558 );
559
560 /* — Preview — */
561 var formatPrice = function (p) {
562 if (!p) return '';
563 return a.currencyPos === 'before' ? a.currency + p : p + a.currency;
564 };
565
566 var isRight = a.imagePosition === 'right';
567
568 var imageCol = el('div', { className: 'bkbg-ps-image-col' },
569 el('div', { className: 'bkbg-ps-main-image-wrap', style: { aspectRatio: a.imageRatio, borderRadius: a.imageRadius + 'px', overflow: 'hidden', background: '#f3f4f6', display: 'flex', alignItems: 'center', justifyContent: 'center' } },
570 a.mainImageUrl
571 ? el('img', { src: a.mainImageUrl, style: { width: '100%', height: '100%', objectFit: 'cover' } })
572 : el('span', { style: { fontSize: '48px', opacity: 0.3 } }, '🖼')
573 ),
574 a.gallery.length > 0 && el('div', { style: { display: 'flex', gap: '8px', marginTop: '12px', flexWrap: 'wrap' } },
575 a.gallery.map(function (g, idx) {
576 return el('img', { key: idx, src: g.url, style: { width: '64px', height: '64px', objectFit: 'cover', borderRadius: '6px', border: '2px solid ' + a.borderColor, cursor: 'pointer' } });
577 })
578 )
579 );
580
581 var infoCol = el('div', { className: 'bkbg-ps-info-col', style: { flex: 1, minWidth: 0 } },
582 /* Badge */
583 a.showBadge && a.badge && el('span', { style: { display: 'inline-block', background: a.badgeBg, color: a.badgeColor, borderRadius: a.badgeRadius + 'px', fontSize: '12px', fontWeight: 700, padding: '4px 12px', marginBottom: '12px', letterSpacing: '0.05em', textTransform: 'uppercase' } }, a.badge),
584 /* Name */
585 el('div', { className: 'bkbg-ps-name', style: { color: a.nameColor, lineHeight: 1.2, marginBottom: '8px' } }, a.productName),
586 /* Tagline */
587 a.tagline && el('div', { className: 'bkbg-ps-tagline', style: { color: a.taglineColor, marginBottom: '12px' } }, a.tagline),
588 /* Rating */
589 a.showRating && el('div', { style: { display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '12px' } },
590 el(StarRating, { score: a.rating, color: a.ratingStarColor }),
591 el('span', { style: { fontSize: '13px', color: a.taglineColor } }, a.rating.toFixed(1) + ' (' + a.reviewCount.toLocaleString() + ' reviews)')
592 ),
593 /* Price row */
594 el('div', { style: { display: 'flex', alignItems: 'baseline', gap: '12px', marginBottom: '8px' } },
595 el('span', { className: 'bkbg-ps-price', style: { color: a.priceColor } }, formatPrice(a.price)),
596 a.showOriginalPrice && a.originalPrice && el('span', { style: { fontSize: (a.priceSize * 0.62) + 'px', color: a.originalPriceColor, textDecoration: 'line-through' } }, formatPrice(a.originalPrice))
597 ),
598 /* SKU */
599 a.showSku && a.sku && el('div', { style: { fontSize: '12px', color: a.taglineColor, marginBottom: '8px' } }, 'SKU: ' + a.sku),
600 /* Stock */
601 a.showStock && el('div', { style: { fontSize: '13px', fontWeight: 600, color: a.inStock ? a.inStockColor : a.outStockColor, marginBottom: '16px' } },
602 (a.inStock ? '' : '') + a.stockText
603 ),
604 /* Variants */
605 a.showVariants && a.variantGroups.length > 0 && el('div', { style: { marginBottom: '16px' } },
606 a.variantGroups.map(function (g) {
607 return el('div', { key: g.id, style: { marginBottom: '10px' } },
608 el('div', { style: { fontSize: '13px', fontWeight: 600, color: a.nameColor, marginBottom: '6px' } }, g.label + ':'),
609 el('div', { style: { display: 'flex', gap: '8px', flexWrap: 'wrap' } },
610 (g.options || []).map(function (o, oidx) {
611 return g.type === 'color'
612 ? el('div', { key: o.id, title: o.label, style: { width: '28px', height: '28px', borderRadius: '50%', background: o.value, border: oidx === 0 ? '3px solid ' + a.ctaBg : '2px solid ' + a.borderColor, cursor: 'pointer' } })
613 : el('button', { key: o.id, style: { padding: '5px 14px', borderRadius: '6px', fontSize: '13px', cursor: 'pointer', background: oidx === 0 ? a.ctaBg : 'transparent', color: oidx === 0 ? a.ctaColor : a.nameColor, border: oidx === 0 ? '2px solid ' + a.ctaBg : '2px solid ' + a.borderColor, fontWeight: 600 } }, o.label);
614 })
615 )
616 );
617 })
618 ),
619 /* CTAs */
620 el('div', { style: { display: 'flex', gap: '10px', marginBottom: '20px', flexWrap: 'wrap' } },
621 a.ctaLabel && el('a', { href: a.ctaUrl || '#', className: 'bkbg-ps-cta bkbg-ps-cta--primary', style: { display: 'inline-block', padding: '12px 28px', background: a.ctaBg, color: a.ctaColor, borderRadius: a.ctaRadius + 'px' } }, a.ctaLabel),
622 a.showSecCta && a.secCtaLabel && el('a', { href: a.secCtaUrl || '#', className: 'bkbg-ps-cta bkbg-ps-cta--secondary', style: { display: 'inline-block', padding: '12px 28px', background: a.secCtaBg, color: a.secCtaColor, borderRadius: a.ctaRadius + 'px' } }, a.secCtaLabel)
623 ),
624 /* Trust Badges */
625 a.showTrustBadges && a.trustBadges.length > 0 && el('div', { style: { display: 'flex', gap: '12px', flexWrap: 'wrap', borderTop: '1px solid ' + a.borderColor, paddingTop: '16px', marginBottom: '20px' } },
626 a.trustBadges.map(function (b) {
627 return el('div', { key: b.id, style: { display: 'flex', alignItems: 'center', gap: '6px', fontSize: '12px', color: a.featureColor } },
628 el('span', { className: 'bkbg-ps-trust-icon' }, (b.iconType || 'custom-char') !== 'custom-char' ? IP().buildEditorIcon(b.iconType, b.icon, b.iconDashicon, b.iconImageUrl, b.iconDashiconColor) : b.icon),
629 el('span', null, b.text)
630 );
631 })
632 ),
633 /* Features */
634 a.showFeatures && a.features.length > 0 && el('div', { style: { marginBottom: '20px' } },
635 a.featuresTitle && el('div', { style: { fontSize: '13px', fontWeight: 700, color: a.nameColor, marginBottom: '10px' } }, a.featuresTitle),
636 el('ul', { style: { margin: 0, padding: 0, listStyle: 'none' } },
637 a.features.map(function (f) {
638 return el('li', { key: f.id, style: { display: 'flex', gap: '8px', alignItems: 'flex-start', color: a.featureColor, marginBottom: '6px' } },
639 el('span', { style: { color: a.featureIconColor, fontWeight: 700, flexShrink: 0 } }, (a.featureIconType || 'custom-char') !== 'custom-char' ? IP().buildEditorIcon(a.featureIconType, a.featureIcon, a.featureIconDashicon, a.featureIconImageUrl, a.featureIconDashiconColor) : a.featureIcon),
640 el('span', null, f.text)
641 );
642 })
643 )
644 ),
645 /* Tabs */
646 a.showTabs && a.tabs.length > 0 && el('div', { style: { borderTop: '1px solid ' + a.borderColor, paddingTop: '16px' } },
647 el('div', { style: { display: 'flex', gap: '4px', marginBottom: '12px', flexWrap: 'wrap' } },
648 a.tabs.map(function (t, idx) {
649 var isActive = idx === activeTabIdx;
650 return el('button', { key: t.id, onClick: function () { setActiveTabIdx(idx); }, style: { padding: '6px 16px', borderRadius: '6px', fontSize: '14px', fontWeight: 600, cursor: 'pointer', border: 'none', background: isActive ? a.tabActiveBg : a.tabIdleBg, color: isActive ? a.tabActiveColor : a.tabIdleColor } }, t.label);
651 })
652 ),
653 a.tabs[activeTabIdx] && el('div', { style: { fontSize: '14px', color: a.featureColor, lineHeight: 1.7 } }, a.tabs[activeTabIdx].content)
654 )
655 );
656
657 var blockProps = useBlockProps((function(){
658 var fn = getTypoCssVars();
659 var s = Object.assign({}, buildWrapStyle(a), { maxWidth: '100%' });
660 if(fn){
661 Object.assign(s, fn(a.nameTypo||{}, '--bkbg-ps-nm-'));
662 Object.assign(s, fn(a.taglineTypo||{}, '--bkbg-ps-tg-'));
663 Object.assign(s, fn(a.priceTypo||{}, '--bkbg-ps-pr-'));
664 Object.assign(s, fn(a.featureTypo||{}, '--bkbg-ps-ft-'));
665 Object.assign(s, fn(a.ctaTypo||{}, '--bkbg-ps-ct-'));
666 }
667 return { style: s };
668 })());
669
670 return el('div', blockProps,
671 inspector,
672 el('div', { className: 'bkbg-ps-layout bkbg-ps-img-' + a.imagePosition, style: { display: 'flex', gap: a.gap + 'px', alignItems: 'flex-start', flexDirection: isRight ? 'row-reverse' : 'row' } },
673 imageCol,
674 infoCol
675 )
676 );
677 },
678
679 save: function (props) {
680 var a = props.attributes;
681 var fn = getTypoCssVars();
682 var s = Object.assign({}, buildWrapStyle(a));
683 if(fn){
684 Object.assign(s, fn(a.nameTypo||{}, '--bkbg-ps-nm-'));
685 Object.assign(s, fn(a.taglineTypo||{}, '--bkbg-ps-tg-'));
686 Object.assign(s, fn(a.priceTypo||{}, '--bkbg-ps-pr-'));
687 Object.assign(s, fn(a.featureTypo||{}, '--bkbg-ps-ft-'));
688 Object.assign(s, fn(a.ctaTypo||{}, '--bkbg-ps-ct-'));
689 }
690 return el('div', Object.assign({}, useBlockProps.save(), {
691 className: 'bkbg-ps-wrap',
692 style: s,
693 'data-image-position': a.imagePosition,
694 'data-image-ratio': a.imageRatio,
695 'data-show-tabs': a.showTabs ? '1' : '0',
696 'data-tabs': JSON.stringify(a.tabs),
697 'data-variants': JSON.stringify(a.variantGroups),
698 'data-gallery': JSON.stringify(a.gallery),
699 }),
700 el('div', { className: 'bkbg-ps-layout bkbg-ps-img-' + a.imagePosition },
701 /* Image column */
702 el('div', { className: 'bkbg-ps-image-col' },
703 el('div', { className: 'bkbg-ps-main-image-wrap' },
704 a.mainImageUrl && el('img', { className: 'bkbg-ps-main-img', src: a.mainImageUrl, alt: a.productName, loading: 'lazy' })
705 ),
706 a.gallery.length > 0 && el('div', { className: 'bkbg-ps-gallery' },
707 a.gallery.map(function (g, idx) {
708 return el('img', { key: idx, className: 'bkbg-ps-thumb' + (idx === 0 ? ' bkbg-ps-thumb--active' : ''), src: g.url, alt: (a.productName + ' ' + (idx + 1)), loading: 'lazy', 'data-full': g.url });
709 })
710 )
711 ),
712 /* Info column */
713 el('div', { className: 'bkbg-ps-info-col' },
714 a.showBadge && a.badge && el('span', { className: 'bkbg-ps-badge' }, a.badge),
715 el('h2', { className: 'bkbg-ps-name' }, a.productName),
716 a.tagline && el('p', { className: 'bkbg-ps-tagline' }, a.tagline),
717 a.showRating && el('div', { className: 'bkbg-ps-rating', 'data-score': a.rating, 'data-count': a.reviewCount }),
718 el('div', { className: 'bkbg-ps-price-row' },
719 el('span', { className: 'bkbg-ps-price' }, a.currencyPos === 'before' ? a.currency + a.price : a.price + a.currency),
720 a.showOriginalPrice && a.originalPrice && el('span', { className: 'bkbg-ps-orig-price' }, a.currencyPos === 'before' ? a.currency + a.originalPrice : a.originalPrice + a.currency)
721 ),
722 a.showSku && a.sku && el('div', { className: 'bkbg-ps-sku' }, 'SKU: ' + a.sku),
723 a.showStock && el('div', { className: 'bkbg-ps-stock bkbg-ps-stock--' + (a.inStock ? 'in' : 'out') }, a.stockText),
724 a.showVariants && a.variantGroups.length > 0 && el('div', { className: 'bkbg-ps-variants' }),
725 el('div', { className: 'bkbg-ps-ctas' },
726 a.ctaLabel && el('a', { href: a.ctaUrl || '#', className: 'bkbg-ps-cta bkbg-ps-cta--primary' }, a.ctaLabel),
727 a.showSecCta && a.secCtaLabel && el('a', { href: a.secCtaUrl || '#', className: 'bkbg-ps-cta bkbg-ps-cta--secondary' }, a.secCtaLabel)
728 ),
729 a.showTrustBadges && a.trustBadges.length > 0 && el('div', { className: 'bkbg-ps-trust-badges' },
730 a.trustBadges.map(function (b) {
731 return el('div', { key: b.id, className: 'bkbg-ps-trust-badge' },
732 el('span', { className: 'bkbg-ps-trust-icon' }, (b.iconType || 'custom-char') !== 'custom-char' ? IP().buildSaveIcon(b.iconType, b.icon, b.iconDashicon, b.iconImageUrl, b.iconDashiconColor) : b.icon),
733 el('span', { className: 'bkbg-ps-trust-text' }, b.text)
734 );
735 })
736 ),
737 a.showFeatures && a.features.length > 0 && el('div', { className: 'bkbg-ps-features' },
738 a.featuresTitle && el('div', { className: 'bkbg-ps-features-title' }, a.featuresTitle),
739 el('ul', { className: 'bkbg-ps-feature-list' },
740 a.features.map(function (f) {
741 return el('li', { key: f.id },
742 el('span', { className: 'bkbg-ps-feature-icon' }, (a.featureIconType || 'custom-char') !== 'custom-char' ? IP().buildSaveIcon(a.featureIconType, a.featureIcon, a.featureIconDashicon, a.featureIconImageUrl, a.featureIconDashiconColor) : a.featureIcon),
743 f.text
744 );
745 })
746 )
747 ),
748 a.showTabs && a.tabs.length > 0 && el('div', { className: 'bkbg-ps-tabs-wrap' },
749 el('div', { className: 'bkbg-ps-tab-bar' },
750 a.tabs.map(function (t, idx) {
751 return el('button', { key: t.id, className: 'bkbg-ps-tab-btn' + (idx === 0 ? ' bkbg-ps-tab-btn--active' : ''), 'data-tab': t.id }, t.label);
752 })
753 ),
754 el('div', { className: 'bkbg-ps-tab-panels' },
755 a.tabs.map(function (t, idx) {
756 return el('div', { key: t.id, className: 'bkbg-ps-tab-panel' + (idx === 0 ? ' bkbg-ps-tab-panel--active' : ''), 'data-tab-panel': t.id }, t.content);
757 })
758 )
759 )
760 )
761 )
762 );
763 },
764 });
765 }() );
766