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

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

300 lines 22.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 ( function () {
2 var el = wp.element.createElement;
3 var Fragment = wp.element.Fragment;
4 var useState = wp.element.useState;
5 var __ = wp.i18n.__;
6 var _fscTC, _fscTV;
7 function _tc() { return (_fscTC = _fscTC || window.bkbgTypographyControl); }
8 function _tv() { return (_fscTV = _fscTV || window.bkbgTypoCssVars); }
9 var registerBlockType = wp.blocks.registerBlockType;
10 var InspectorControls = wp.blockEditor.InspectorControls;
11 var MediaUpload = wp.blockEditor.MediaUpload;
12 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
13 var RichText = wp.blockEditor.RichText;
14 var useBlockProps = wp.blockEditor.useBlockProps;
15 var PanelBody = wp.components.PanelBody;
16 var Button = wp.components.Button;
17 var ToggleControl = wp.components.ToggleControl;
18 var RangeControl = wp.components.RangeControl;
19 var SelectControl = wp.components.SelectControl;
20 var TextControl = wp.components.TextControl;
21 var TextareaControl = wp.components.TextareaControl;
22 var ColorPicker = wp.components.ColorPicker;
23 var Popover = wp.components.Popover;
24
25 var TAG_OPTIONS = [
26 { label: 'H1', value: 'h1' }, { label: 'H2', value: 'h2' },
27 { label: 'H3', value: 'h3' }, { label: 'H4', value: 'h4' },
28 ];
29 var POSITION_OPTIONS = [
30 { label: __('Image right', 'blockenberg'), value: 'right' },
31 { label: __('Image left', 'blockenberg'), value: 'left' },
32 ];
33 var RATIO_OPTIONS = [
34 { label: '4:3', value: '4/3' }, { label: '3:2', value: '3/2' },
35 { label: '1:1', value: '1/1' }, { label: '16:9', value: '16/9' },
36 ];
37 var BULLET_OPTIONS = [
38 { label: __('Checkmark', 'blockenberg'), value: 'check' },
39 { label: __('Arrow', 'blockenberg'), value: 'arrow' },
40 { label: __('Dot', 'blockenberg'), value: 'dot' },
41 ];
42 var CTA_STYLE_OPTIONS = [
43 { label: __('Filled', 'blockenberg'), value: 'filled' },
44 { label: __('Outline', 'blockenberg'), value: 'outline' },
45 { label: __('Link', 'blockenberg'), value: 'link' },
46 ];
47
48 function makeId() { return 'fs' + Math.random().toString(36).substr(2, 6); }
49 function makeBId() { return 'fb' + Math.random().toString(36).substr(2, 6); }
50
51 function bulletChar(icon) {
52 if (icon === 'arrow') return '';
53 if (icon === 'dot') return '';
54 return '';
55 }
56
57 function renderColorControl(key, label, value, onChange, openKey, setOpenKey) {
58 var isOpen = openKey === key;
59 return el('div', { key: key, style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '4px 0', gap: '8px' } },
60 el('span', { style: { fontSize: '12px', color: '#1e1e1e', flex: 1, lineHeight: 1.4 } }, label),
61 el('div', { style: { position: 'relative', flexShrink: 0 } },
62 el('button', { type: 'button', title: value || 'default', 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 || '#cccccc' } }),
63 isOpen && el(Popover, { position: 'bottom left', onClose: function () { setOpenKey(null); } },
64 el('div', { style: { padding: '8px' } },
65 el(ColorPicker, { color: value, enableAlpha: true, onChange: onChange })
66 )
67 )
68 )
69 );
70 }
71
72 /* ── Single row preview ───────────────────────────────────────────────── */
73 function ShowcaseRow(props) {
74 var row = props.row;
75 var a = props.a;
76 var isLeft = row.imagePosition === 'left';
77 var aspectParts = a.imageRatio.split('/');
78 var aspectPadding = ((parseInt(aspectParts[1], 10) / parseInt(aspectParts[0], 10)) * 100) + '%';
79
80 var imageSide = el('div', { className: 'bkbg-fsc-img-col', style: { flex: (100 - a.imageWidth) + ' 0 0', maxWidth: (100 - a.imageWidth) + '%' } },
81 el('div', { style: { position: 'relative', paddingBottom: aspectPadding, borderRadius: a.imageRadius + 'px', overflow: 'hidden', background: '#e5e7eb', boxShadow: a.imageShadow ? '0 20px 60px rgba(0,0,0,0.14)' : 'none' } },
82 row.imageUrl && el('img', { src: row.imageUrl, alt: '', style: { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' } }),
83 !row.imageUrl && el('div', { style: { position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', flexDirection: 'column', gap: '8px', color: '#9ca3af' } },
84 el('span', { style: { fontSize: '32px' } }, '🖼️'),
85 el('span', { style: { fontSize: '12px' } }, __('Select image', 'blockenberg'))
86 )
87 )
88 );
89
90 var contentSide = el('div', { className: 'bkbg-fsc-text-col', style: { flex: a.imageWidth + ' 0 0', maxWidth: a.imageWidth + '%', display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: '16px' } },
91 a.showBadge && row.badge && el('span', { className: 'bkbg-fsc-badge', style: { alignSelf: 'flex-start', background: a.badgeBg, color: a.badgeColor } }, row.badge),
92 el(RichText, { tagName: a.headlineTag, className: 'bkbg-fsc-headline', value: row.headline, placeholder: __('Section headline…', 'blockenberg'), onChange: function (v) { props.onUpdateRow('headline', v); }, style: { color: a.headlineColor, margin: 0 } }),
93 el('p', { className: 'bkbg-fsc-body', style: { color: a.bodyColor, margin: 0 } }, row.body || __('Section body text…', 'blockenberg')),
94 a.showBullets && row.bullets && row.bullets.length > 0 && el('ul', { className: 'bkbg-fsc-bullets', style: { listStyle: 'none', padding: 0, margin: 0 } },
95 row.bullets.map(function (b) {
96 return el('li', { key: b.id, className: 'bkbg-fsc-bullet', style: { color: a.bulletColor } },
97 el('span', { className: 'bkbg-fsc-bullet-icon', style: { color: a.bulletIconColor } }, bulletChar(a.bulletIcon)),
98 el('span', null, b.text)
99 );
100 })
101 ),
102 row.ctaLabel && el('a', {
103 href: '#',
104 className: 'bkbg-fsc-cta',
105 style: Object.assign({ alignSelf: 'flex-start', borderRadius: a.ctaRadius + 'px', pointerEvents: 'none', cursor: 'default' },
106 row.ctaStyle === 'filled' ? { background: a.ctaBg, color: a.ctaColor } :
107 row.ctaStyle === 'outline' ? { border: '2px solid ' + a.ctaBg, color: a.ctaBg } :
108 { color: a.ctaBg, textDecoration: 'underline' }
109 )
110 }, row.ctaLabel)
111 );
112
113 return el('div', { className: 'bkbg-fsc-row bkbg-fsc-img-' + row.imagePosition, style: { display: 'flex', alignItems: 'center', gap: '5%', flexDirection: isLeft ? 'row' : 'row-reverse' } },
114 imageSide, contentSide
115 );
116 }
117
118 function buildWrapStyle(a) {
119 return Object.assign(
120 { paddingTop: a.paddingTop + 'px', paddingBottom: a.paddingBottom + 'px', backgroundColor: a.bgColor || undefined },
121 _tv()(a.typoHeadline, '--bkbg-fsc-hl-'),
122 _tv()(a.typoBody, '--bkbg-fsc-bd-'),
123 _tv()(a.typoBullet, '--bkbg-fsc-bl-'),
124 _tv()(a.typoBadge, '--bkbg-fsc-bg-')
125 );
126 }
127
128 registerBlockType('blockenberg/feature-showcase', {
129 title: __('Feature Showcase', 'blockenberg'),
130 icon: 'align-left',
131 category: 'bkbg-marketing',
132 description: __('Alternating zigzag image + text rows with bullets and CTA.', 'blockenberg'),
133
134 edit: function (props) {
135 var a = props.attributes;
136 var setAttributes = props.setAttributes;
137
138 var openColorKeyState = useState(null);
139 var openColorKey = openColorKeyState[0];
140 var setOpenColorKey = openColorKeyState[1];
141
142 var blockProps = useBlockProps({ className: 'bkbg-fsc-wrapper', style: buildWrapStyle(a) });
143
144 function cc(key, label, attrKey) {
145 return renderColorControl(key, label, a[attrKey], function (val) {
146 var upd = {}; upd[attrKey] = val; setAttributes(upd);
147 }, openColorKey, setOpenColorKey);
148 }
149
150 function updateRow(rowId, key, val) {
151 setAttributes({ rows: a.rows.map(function (r) { if (r.id !== rowId) return r; var u = Object.assign({}, r); u[key] = val; return u; }) });
152 }
153 function addBullet(rowId) {
154 setAttributes({ rows: a.rows.map(function (r) { if (r.id !== rowId) return r; return Object.assign({}, r, { bullets: (r.bullets || []).concat([{ id: makeBId(), text: 'Bullet point' }]) }); }) });
155 }
156 function removeBullet(rowId, bId) {
157 setAttributes({ rows: a.rows.map(function (r) { if (r.id !== rowId) return r; return Object.assign({}, r, { bullets: r.bullets.filter(function (b) { return b.id !== bId; }) }); }) });
158 }
159 function updateBullet(rowId, bId, text) {
160 setAttributes({ rows: a.rows.map(function (r) { if (r.id !== rowId) return r; return Object.assign({}, r, { bullets: r.bullets.map(function (b) { return b.id === bId ? Object.assign({}, b, { text: text }) : b; }) }); }) });
161 }
162 function addRow() {
163 var pos = a.rows.length % 2 === 0 ? 'right' : 'left';
164 setAttributes({ rows: a.rows.concat([{ id: makeId(), imageUrl: '', imageId: 0, imagePosition: pos, badge: 'New Feature', headline: 'Another headline here', body: 'Describe this feature in one or two sentences.', bullets: [{ id: makeBId(), text: 'Benefit one' }, { id: makeBId(), text: 'Benefit two' }], ctaLabel: 'Learn more', ctaUrl: '#', ctaTarget: false, ctaStyle: 'filled' }]) });
165 }
166 function removeRow(rowId) {
167 if (a.rows.length <= 1) return;
168 setAttributes({ rows: a.rows.filter(function (r) { return r.id !== rowId; }) });
169 }
170
171 return el(Fragment, null,
172 el(InspectorControls, null,
173 el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: true },
174 el(RangeControl, { label: __('Row gap (px)', 'blockenberg'), value: a.gap, min: 32, max: 200, onChange: function (v) { setAttributes({ gap: v }); } }),
175 el(RangeControl, { label: __('Image width (%)', 'blockenberg'), value: a.imageWidth, min: 30, max: 70, onChange: function (v) { setAttributes({ imageWidth: v }); } }),
176 el(SelectControl, { label: __('Image ratio', 'blockenberg'), value: a.imageRatio, options: RATIO_OPTIONS, onChange: function (v) { setAttributes({ imageRatio: v }); } }),
177 el(RangeControl, { label: __('Image radius (px)', 'blockenberg'), value: a.imageRadius, min: 0, max: 40, onChange: function (v) { setAttributes({ imageRadius: v }); } }),
178 el(ToggleControl, { label: __('Image shadow', 'blockenberg'), checked: a.imageShadow, onChange: function (v) { setAttributes({ imageShadow: v }); }, __nextHasNoMarginBottom: true })
179 ),
180 el(PanelBody, { title: __('Content', 'blockenberg'), initialOpen: false },
181 el(SelectControl, { label: __('Headline tag', 'blockenberg'), value: a.headlineTag, options: TAG_OPTIONS, onChange: function (v) { setAttributes({ headlineTag: v }); } }),
182 el(ToggleControl, { label: __('Show badge', 'blockenberg'), checked: a.showBadge, onChange: function (v) { setAttributes({ showBadge: v }); }, __nextHasNoMarginBottom: true }),
183 el(ToggleControl, { label: __('Show bullets', 'blockenberg'), checked: a.showBullets, onChange: function (v) { setAttributes({ showBullets: v }); }, __nextHasNoMarginBottom: true }),
184 a.showBullets && el(SelectControl, { label: __('Bullet icon', 'blockenberg'), value: a.bulletIcon, options: BULLET_OPTIONS, onChange: function (v) { setAttributes({ bulletIcon: v }); } }),
185 el(RangeControl, { label: __('CTA border radius (px)', 'blockenberg'), value: a.ctaRadius, min: 0, max: 99, onChange: function (v) { setAttributes({ ctaRadius: v }); } })
186 ),
187 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
188 _tc() && el(_tc(), { label: __('Headline', 'blockenberg'), value: a.typoHeadline, onChange: function (v) { setAttributes({ typoHeadline: v }); } }),
189 _tc() && el(_tc(), { label: __('Body', 'blockenberg'), value: a.typoBody, onChange: function (v) { setAttributes({ typoBody: v }); } }),
190 _tc() && el(_tc(), { label: __('Bullet / CTA', 'blockenberg'), value: a.typoBullet, onChange: function (v) { setAttributes({ typoBullet: v }); } }),
191 _tc() && el(_tc(), { label: __('Badge', 'blockenberg'), value: a.typoBadge, onChange: function (v) { setAttributes({ typoBadge: v }); } })
192 ),
193 el(PanelBody, { title: __('Colors', 'blockenberg'), initialOpen: false },
194 cc('headlineColor', __('Headline', 'blockenberg'), 'headlineColor'),
195 cc('bodyColor', __('Body text', 'blockenberg'), 'bodyColor'),
196 cc('bulletColor', __('Bullet text', 'blockenberg'), 'bulletColor'),
197 cc('bulletIconColor', __('Bullet icon', 'blockenberg'), 'bulletIconColor'),
198 cc('badgeColor', __('Badge text', 'blockenberg'), 'badgeColor'),
199 cc('badgeBg', __('Badge bg', 'blockenberg'), 'badgeBg'),
200 cc('ctaBg', __('CTA fill', 'blockenberg'), 'ctaBg'),
201 cc('ctaColor', __('CTA text', 'blockenberg'), 'ctaColor'),
202 cc('bgColor', __('Section bg', 'blockenberg'), 'bgColor')
203 ),
204 el(PanelBody, { title: __('Spacing', 'blockenberg'), initialOpen: false },
205 el(RangeControl, { label: __('Padding top (px)', 'blockenberg'), value: a.paddingTop, min: 0, max: 200, onChange: function (v) { setAttributes({ paddingTop: v }); } }),
206 el(RangeControl, { label: __('Padding bottom (px)', 'blockenberg'), value: a.paddingBottom, min: 0, max: 200, onChange: function (v) { setAttributes({ paddingBottom: v }); } })
207 ),
208 el(PanelBody, { title: __('Rows', 'blockenberg'), initialOpen: false },
209 a.rows.map(function (row, ri) {
210 return el(PanelBody, { key: row.id, title: (ri + 1) + '. ' + (row.headline || __('Row', 'blockenberg')), initialOpen: false },
211 el(SelectControl, { label: __('Image position', 'blockenberg'), value: row.imagePosition, options: POSITION_OPTIONS, onChange: function (v) { updateRow(row.id, 'imagePosition', v); } }),
212 el(MediaUploadCheck, null,
213 el(MediaUpload, {
214 onSelect: function (media) { setAttributes({ rows: a.rows.map(function (r) { return r.id !== row.id ? r : Object.assign({}, r, { imageUrl: media.url, imageId: media.id }); }) }); },
215 allowedTypes: ['image'], value: row.imageId,
216 render: function (p) {
217 return el('div', { style: { marginBottom: '8px' } },
218 row.imageUrl && el('img', { src: row.imageUrl, style: { width: '100%', height: '70px', objectFit: 'cover', borderRadius: '6px', marginBottom: '4px' } }),
219 el(Button, { onClick: p.open, variant: row.imageUrl ? 'secondary' : 'primary', size: 'compact' }, row.imageUrl ? __('Replace', 'blockenberg') : __('Select image', 'blockenberg'))
220 );
221 }
222 })
223 ),
224 el(TextControl, { label: __('Badge text', 'blockenberg'), value: row.badge, onChange: function (v) { updateRow(row.id, 'badge', v); } }),
225 el(TextControl, { label: __('Headline', 'blockenberg'), value: row.headline, onChange: function (v) { updateRow(row.id, 'headline', v); } }),
226 el(TextareaControl, { label: __('Body text', 'blockenberg'), value: row.body, onChange: function (v) { updateRow(row.id, 'body', v); } }),
227 el('p', { style: { fontWeight: 600, fontSize: '12px', margin: '8px 0 4px' } }, __('Bullets', 'blockenberg')),
228 (row.bullets || []).map(function (b) {
229 return el('div', { key: b.id, style: { display: 'flex', gap: '6px', marginBottom: '6px', alignItems: 'center' } },
230 el(TextControl, { value: b.text, onChange: function (v) { updateBullet(row.id, b.id, v); }, style: { flex: 1 } }),
231 el(Button, { onClick: function () { removeBullet(row.id, b.id); }, variant: 'tertiary', isDestructive: true, size: 'compact' }, '')
232 );
233 }),
234 el(Button, { onClick: function () { addBullet(row.id); }, variant: 'secondary', size: 'compact', style: { marginBottom: '8px' } }, __('+ Add bullet', 'blockenberg')),
235 el(TextControl, { label: __('CTA label', 'blockenberg'), value: row.ctaLabel, onChange: function (v) { updateRow(row.id, 'ctaLabel', v); } }),
236 el(TextControl, { label: __('CTA URL', 'blockenberg'), value: row.ctaUrl, onChange: function (v) { updateRow(row.id, 'ctaUrl', v); } }),
237 el(SelectControl, { label: __('CTA style', 'blockenberg'), value: row.ctaStyle, options: CTA_STYLE_OPTIONS, onChange: function (v) { updateRow(row.id, 'ctaStyle', v); } }),
238 el(ToggleControl, { label: __('Open in new tab', 'blockenberg'), checked: row.ctaTarget, onChange: function (v) { updateRow(row.id, 'ctaTarget', v); }, __nextHasNoMarginBottom: true }),
239 a.rows.length > 1 && el(Button, { onClick: function () { removeRow(row.id); }, variant: 'tertiary', isDestructive: true, size: 'compact', style: { marginTop: '6px' } }, __('Remove row', 'blockenberg'))
240 );
241 }),
242 el(Button, { onClick: addRow, variant: 'primary', style: { marginTop: '8px' } }, __('+ Add Row', 'blockenberg'))
243 )
244 ),
245
246 el('div', blockProps,
247 el('div', { className: 'bkbg-fsc-rows', style: { display: 'flex', flexDirection: 'column', gap: a.gap + 'px' } },
248 a.rows.map(function (row) {
249 return el(ShowcaseRow, { key: row.id, row: row, a: a, onUpdateRow: function (key, val) { updateRow(row.id, key, val); } });
250 })
251 )
252 )
253 );
254 },
255
256 save: function (props) {
257 var a = props.attributes;
258 return el('div', wp.blockEditor.useBlockProps.save({
259 className: 'bkbg-fsc-wrapper',
260 style: buildWrapStyle(a)
261 }),
262 el('div', { className: 'bkbg-fsc-rows', style: { display: 'flex', flexDirection: 'column', gap: a.gap + 'px' } },
263 a.rows.map(function (row) {
264 var isLeft = row.imagePosition === 'left';
265 var imgW = (100 - a.imageWidth) + '%';
266 var txtW = a.imageWidth + '%';
267 var aspectParts = a.imageRatio.split('/');
268 var aspectPadding = ((parseInt(aspectParts[1], 10) / parseInt(aspectParts[0], 10)) * 100) + '%';
269 var ctaStyle = {};
270 if (row.ctaStyle === 'filled') { ctaStyle = { background: a.ctaBg, color: a.ctaColor }; }
271 if (row.ctaStyle === 'outline') { ctaStyle = { border: '2px solid ' + a.ctaBg, color: a.ctaBg }; }
272 if (row.ctaStyle === 'link') { ctaStyle = { color: a.ctaBg, textDecoration: 'underline' }; }
273 return el('div', { key: row.id, className: 'bkbg-fsc-row bkbg-fsc-img-' + row.imagePosition },
274 el('div', { className: 'bkbg-fsc-img-col', style: { flex: '0 0 ' + imgW, maxWidth: imgW } },
275 el('div', { className: 'bkbg-fsc-img-wrap', style: { position: 'relative', paddingBottom: aspectPadding, borderRadius: a.imageRadius + 'px', overflow: 'hidden', boxShadow: a.imageShadow ? '0 20px 60px rgba(0,0,0,0.14)' : 'none' } },
276 row.imageUrl && el('img', { src: row.imageUrl, alt: '', loading: 'lazy', style: { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' } })
277 )
278 ),
279 el('div', { className: 'bkbg-fsc-text-col', style: { flex: '0 0 ' + txtW, maxWidth: txtW } },
280 a.showBadge && row.badge && el('span', { className: 'bkbg-fsc-badge', style: { background: a.badgeBg, color: a.badgeColor } }, row.badge),
281 el(RichText.Content, { tagName: a.headlineTag, className: 'bkbg-fsc-headline', value: row.headline, style: { color: a.headlineColor } }),
282 el('p', { className: 'bkbg-fsc-body', style: { color: a.bodyColor } }, row.body),
283 a.showBullets && row.bullets && row.bullets.length > 0 && el('ul', { className: 'bkbg-fsc-bullets' },
284 row.bullets.map(function (b) {
285 return el('li', { key: b.id, className: 'bkbg-fsc-bullet' },
286 el('span', { className: 'bkbg-fsc-bullet-icon', style: { color: a.bulletIconColor } }, bulletChar(a.bulletIcon)),
287 el('span', { style: { color: a.bulletColor } }, b.text)
288 );
289 })
290 ),
291 row.ctaLabel && el('a', { className: 'bkbg-fsc-cta bkbg-fsc-cta-' + row.ctaStyle, href: row.ctaUrl || '#', target: row.ctaTarget ? '_blank' : undefined, rel: row.ctaTarget ? 'noopener noreferrer' : undefined, style: Object.assign({ borderRadius: a.ctaRadius + 'px' }, ctaStyle) }, row.ctaLabel)
292 )
293 );
294 })
295 )
296 );
297 }
298 });
299 }() );
300