PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.11
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.11
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-columns / index.js

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

373 lines 19.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 RichText = wp.blockEditor.RichText;
10 var PanelBody = wp.components.PanelBody;
11 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
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 Button = wp.components.Button;
17
18 var _fcTC, _fcTV;
19 function _tc() { return _fcTC || (_fcTC = window.bkbgTypographyControl); }
20 function _tv() { return _fcTV || (_fcTV = window.bkbgTypoCssVars); }
21
22 var styleOptions = [
23 { label: __('Plain', 'blockenberg'), value: 'plain' },
24 { label: __('Card', 'blockenberg'), value: 'card' },
25 { label: __('Bordered', 'blockenberg'), value: 'bordered' },
26 { label: __('Filled Card', 'blockenberg'), value: 'filled' },
27 { label: __('Numbered', 'blockenberg'), value: 'numbered' }
28 ];
29
30 var iconStyleOptions = [
31 { label: __('Box', 'blockenberg'), value: 'box' },
32 { label: __('Circle', 'blockenberg'), value: 'circle' },
33 { label: __('Line / Underline', 'blockenberg'), value: 'line' },
34 { label: __('None / Inline', 'blockenberg'), value: 'none' }
35 ];
36
37 var alignOptions = [
38 { label: __('Left', 'blockenberg'), value: 'left' },
39 { label: __('Center', 'blockenberg'), value: 'center' }
40 ];
41
42 var columnOptions = [
43 { label: '2', value: 2 },
44 { label: '3', value: 3 },
45 { label: '4', value: 4 }
46 ];
47
48 // Dashicons available for features
49 var featureIcons = [
50 'performance', 'shield', 'admin-customizer', 'heart', 'networking',
51 'chart-line', 'chart-bar', 'star-filled', 'thumbs-up', 'smiley',
52 'admin-users', 'groups', 'clipboard', 'update', 'cloud',
53 'email-alt', 'megaphone', 'flag', 'info', 'yes-alt',
54 'category', 'tag', 'edit', 'trash', 'visibility',
55 'lock', 'unlock', 'rss', 'share', 'search',
56 'format-image', 'format-video', 'format-audio', 'media-document', 'code-standards'
57 ];
58
59 registerBlockType('blockenberg/feature-columns', {
60 title: __('Feature Columns', 'blockenberg'),
61 icon: 'columns',
62 category: 'bkbg-marketing',
63 description: __('Responsive 2–4 column feature grid with icon, title, and description.', 'blockenberg'),
64
65 edit: function (props) {
66 var a = props.attributes;
67 var setAttributes = props.setAttributes;
68 var openState = useState(null);
69 var openIndex = openState[0];
70 var setOpenIndex = openState[1];
71
72 function wrapStyle(a) {
73 return Object.assign({
74 '--bkbg-fc-cols': a.columns,
75 '--bkbg-fc-gap': a.gap + 'px',
76 '--bkbg-fc-row-gap': a.rowGap + 'px',
77 '--bkbg-fc-icon-c': a.iconColor,
78 '--bkbg-fc-icon-bg': a.iconBg,
79 '--bkbg-fc-icon-sz': a.iconSize + 'px',
80 '--bkbg-fc-num-sz': a.numberSize + 'px',
81 '--bkbg-fc-num-c': a.numberColor,
82 '--bkbg-fc-title-c': a.titleColor,
83 '--bkbg-fc-body-c': a.bodyColor,
84 '--bkbg-fc-accent': a.accentColor,
85 '--bkbg-fc-card-bg': a.cardBg,
86 '--bkbg-fc-card-border': a.cardBorder,
87 '--bkbg-fc-card-p': a.cardPadding + 'px',
88 '--bkbg-fc-card-r': a.cardRadius + 'px',
89 '--bkbg-fc-pt': a.paddingTop + 'px',
90 '--bkbg-fc-pb': a.paddingBottom + 'px'
91 }, _tv()(a.typoTitle, '--bkbg-fc-tt-'), _tv()(a.typoBody, '--bkbg-fc-bd-'));
92 }
93
94 function updateFeature(index, key, value) {
95 var updated = a.features.slice();
96 updated[index] = Object.assign({}, updated[index]);
97 updated[index][key] = value;
98 setAttributes({ features: updated });
99 }
100
101 function addFeature() {
102 var icons = featureIcons;
103 var newIcon = icons[a.features.length % icons.length];
104 setAttributes({
105 features: a.features.concat([{
106 id: 'f' + Date.now(),
107 icon: newIcon,
108 title: __('Feature Title', 'blockenberg'),
109 body: __('Describe this feature in one or two short sentences.', 'blockenberg')
110 }])
111 });
112 }
113
114 function removeFeature(index) {
115 setAttributes({ features: a.features.filter(function (_, i) { return i !== index; }) });
116 }
117
118 function moveFeature(index, dir) {
119 var newIndex = index + dir;
120 if (newIndex < 0 || newIndex >= a.features.length) return;
121 var updated = a.features.slice();
122 var tmp = updated[index]; updated[index] = updated[newIndex]; updated[newIndex] = tmp;
123 setAttributes({ features: updated });
124 }
125
126 var inspector = el(InspectorControls, {},
127 el(PanelBody, { title: __('Features', 'blockenberg'), initialOpen: true },
128 a.features.map(function (feat, index) {
129 var isOpen = openIndex === index;
130 return el('div', { key: feat.id, className: 'bkbg-fc-feat-ctrl' },
131 el('div', { className: 'bkbg-fc-feat-head', onClick: function () { setOpenIndex(isOpen ? null : index); } },
132 el('span', { className: 'dashicons dashicons-' + feat.icon, style: { color: a.iconColor } }),
133 el('strong', {}, feat.title || __('Feature', 'blockenberg') + ' ' + (index + 1)),
134 el('span', { className: 'dashicons dashicons-' + (isOpen ? 'arrow-up-alt2' : 'arrow-down-alt2') })
135 ),
136 isOpen && el('div', { className: 'bkbg-fc-feat-body' },
137 el(SelectControl, {
138 label: __('Icon', 'blockenberg'),
139 value: feat.icon,
140 options: featureIcons.map(function (ic) { return { label: ic, value: ic }; }),
141 onChange: function (v) { updateFeature(index, 'icon', v); }
142 }),
143 el(TextControl, {
144 label: __('Title', 'blockenberg'),
145 value: feat.title,
146 onChange: function (v) { updateFeature(index, 'title', v); }
147 }),
148 el('div', { className: 'bkbg-fc-feat-actions' },
149 el(Button, { icon: 'arrow-up-alt2', size: 'small', disabled: index === 0, onClick: function () { moveFeature(index, -1); } }),
150 el(Button, { icon: 'arrow-down-alt2', size: 'small', disabled: index === a.features.length - 1, onClick: function () { moveFeature(index, 1); } }),
151 el(Button, { icon: 'trash', size: 'small', isDestructive: true, onClick: function () { removeFeature(index); } })
152 )
153 )
154 );
155 }),
156 el(Button, { variant: 'secondary', onClick: addFeature }, __('+ Add Feature', 'blockenberg'))
157 ),
158
159 el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: false },
160 el(SelectControl, {
161 label: __('Columns', 'blockenberg'),
162 value: a.columns,
163 options: columnOptions,
164 onChange: function (v) { setAttributes({ columns: parseInt(v, 10) }); }
165 }),
166 el(SelectControl, {
167 label: __('Visual Style', 'blockenberg'),
168 value: a.style,
169 options: styleOptions,
170 onChange: function (v) { setAttributes({ style: v }); }
171 }),
172 el(SelectControl, {
173 label: __('Icon Style', 'blockenberg'),
174 value: a.iconStyle,
175 options: iconStyleOptions,
176 onChange: function (v) { setAttributes({ iconStyle: v }); }
177 }),
178 el(SelectControl, {
179 label: __('Text Alignment', 'blockenberg'),
180 value: a.align,
181 options: alignOptions,
182 onChange: function (v) { setAttributes({ align: v }); }
183 }),
184 el(ToggleControl, {
185 label: __('Show Icon', 'blockenberg'),
186 checked: a.showIcon,
187 __nextHasNoMarginBottom: true,
188 onChange: function (v) { setAttributes({ showIcon: v }); }
189 }),
190 el(ToggleControl, {
191 label: __('Show Step Numbers', 'blockenberg'),
192 checked: a.showNumber,
193 __nextHasNoMarginBottom: true,
194 onChange: function (v) { setAttributes({ showNumber: v }); }
195 }),
196 el(RangeControl, {
197 label: __('Column Gap (px)', 'blockenberg'),
198 value: a.gap,
199 onChange: function (v) { setAttributes({ gap: v }); },
200 min: 8, max: 80
201 }),
202 el(RangeControl, {
203 label: __('Row Gap (px)', 'blockenberg'),
204 value: a.rowGap,
205 onChange: function (v) { setAttributes({ rowGap: v }); },
206 min: 8, max: 80
207 }),
208 el(RangeControl, {
209 label: __('Card Padding (px)', 'blockenberg'),
210 value: a.cardPadding,
211 onChange: function (v) { setAttributes({ cardPadding: v }); },
212 min: 0, max: 60
213 }),
214 el(RangeControl, {
215 label: __('Card Radius (px)', 'blockenberg'),
216 value: a.cardRadius,
217 onChange: function (v) { setAttributes({ cardRadius: v }); },
218 min: 0, max: 32
219 }),
220 el(RangeControl, {
221 label: __('Padding Top (px)', 'blockenberg'),
222 value: a.paddingTop,
223 onChange: function (v) { setAttributes({ paddingTop: v }); },
224 min: 0, max: 120
225 }),
226 el(RangeControl, {
227 label: __('Padding Bottom (px)', 'blockenberg'),
228 value: a.paddingBottom,
229 onChange: function (v) { setAttributes({ paddingBottom: v }); },
230 min: 0, max: 120
231 })
232 ),
233
234 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
235 _tc() && el(_tc(), {
236 label: __('Title Typography', 'blockenberg'),
237 value: a.typoTitle,
238 onChange: function (v) { setAttributes({ typoTitle: v }); }
239 }),
240 _tc() && el(_tc(), {
241 label: __('Body Typography', 'blockenberg'),
242 value: a.typoBody,
243 onChange: function (v) { setAttributes({ typoBody: v }); }
244 }),
245 el(RangeControl, {
246 label: __('Icon Size (px)', 'blockenberg'),
247 value: a.iconSize,
248 onChange: function (v) { setAttributes({ iconSize: v }); },
249 min: 14, max: 56
250 }),
251 el(RangeControl, {
252 label: __('Number Size (px)', 'blockenberg'),
253 value: a.numberSize,
254 onChange: function (v) { setAttributes({ numberSize: v }); },
255 min: 20, max: 72
256 })
257 ),
258
259 el(PanelColorSettings, {
260 title: __('Colors', 'blockenberg'),
261 initialOpen: false,
262 colorSettings: [
263 { value: a.iconColor, onChange: function (v) { setAttributes({ iconColor: v || '#6c3fb5' }); }, label: __('Icon Color', 'blockenberg') },
264 { value: a.iconBg, onChange: function (v) { setAttributes({ iconBg: v || '#f0ebff' }); }, label: __('Icon Background', 'blockenberg') },
265 { value: a.titleColor, onChange: function (v) { setAttributes({ titleColor: v || '#0f172a' }); }, label: __('Title Color', 'blockenberg') },
266 { value: a.bodyColor, onChange: function (v) { setAttributes({ bodyColor: v || '#64748b' }); }, label: __('Body Color', 'blockenberg') },
267 { value: a.numberColor, onChange: function (v) { setAttributes({ numberColor: v || '#6c3fb5' }); }, label: __('Number Color', 'blockenberg') },
268 { value: a.accentColor, onChange: function (v) { setAttributes({ accentColor: v || '#6c3fb5' }); }, label: __('Accent Color', 'blockenberg') },
269 { value: a.cardBg, onChange: function (v) { setAttributes({ cardBg: v || '#ffffff' }); }, label: __('Card Background', 'blockenberg') },
270 { value: a.cardBorder, onChange: function (v) { setAttributes({ cardBorder: v || '#e2e8f0' }); }, label: __('Card Border', 'blockenberg') }
271 ]
272 })
273 );
274
275 function renderFeature(feat, index) {
276 var iconEl = null;
277 if (a.showNumber) {
278 iconEl = el('div', { className: 'bkbg-fc-number bkbg-fc-icon-style--' + a.iconStyle }, index + 1);
279 } else if (a.showIcon) {
280 iconEl = el('div', { className: 'bkbg-fc-icon-wrap bkbg-fc-icon-style--' + a.iconStyle },
281 el('span', { className: 'bkbg-fc-icon dashicons dashicons-' + feat.icon })
282 );
283 }
284 return el('div', { key: feat.id, className: 'bkbg-fc-feature' },
285 iconEl,
286 el('div', { className: 'bkbg-fc-text' },
287 el(RichText, {
288 tagName: 'h3',
289 className: 'bkbg-fc-title',
290 value: feat.title,
291 onChange: function (v) { updateFeature(index, 'title', v); },
292 placeholder: __('Feature Title', 'blockenberg')
293 }),
294 el(RichText, {
295 tagName: 'p',
296 className: 'bkbg-fc-body',
297 value: feat.body,
298 onChange: function (v) { updateFeature(index, 'body', v); },
299 placeholder: __('Feature description…', 'blockenberg')
300 })
301 )
302 );
303 }
304
305 var wrapClass = 'bkbg-fc-wrap bkbg-fc-style--' + a.style + ' bkbg-fc-align--' + a.align;
306 var blockProps = useBlockProps({ className: wrapClass, style: wrapStyle(a) });
307
308 return el(Fragment, {},
309 inspector,
310 el('div', blockProps,
311 el('div', { className: 'bkbg-fc-grid' },
312 a.features.map(function (feat, index) { return renderFeature(feat, index); })
313 ),
314 el('div', { className: 'bkbg-editor-actions' },
315 el(Button, { variant: 'secondary', icon: 'plus-alt2', onClick: addFeature }, __('Add Feature', 'blockenberg'))
316 )
317 )
318 );
319 },
320
321 save: function (props) {
322 var a = props.attributes;
323
324 function wrapStyle(a) {
325 return Object.assign({
326 '--bkbg-fc-cols': a.columns,
327 '--bkbg-fc-gap': a.gap + 'px',
328 '--bkbg-fc-row-gap': a.rowGap + 'px',
329 '--bkbg-fc-icon-c': a.iconColor,
330 '--bkbg-fc-icon-bg': a.iconBg,
331 '--bkbg-fc-icon-sz': a.iconSize + 'px',
332 '--bkbg-fc-num-sz': a.numberSize + 'px',
333 '--bkbg-fc-num-c': a.numberColor,
334 '--bkbg-fc-title-c': a.titleColor,
335 '--bkbg-fc-body-c': a.bodyColor,
336 '--bkbg-fc-accent': a.accentColor,
337 '--bkbg-fc-card-bg': a.cardBg,
338 '--bkbg-fc-card-border': a.cardBorder,
339 '--bkbg-fc-card-p': a.cardPadding + 'px',
340 '--bkbg-fc-card-r': a.cardRadius + 'px',
341 '--bkbg-fc-pt': a.paddingTop + 'px',
342 '--bkbg-fc-pb': a.paddingBottom + 'px'
343 }, _tv()(a.typoTitle, '--bkbg-fc-tt-'), _tv()(a.typoBody, '--bkbg-fc-bd-'));
344 }
345
346 var wrapClass = 'bkbg-fc-wrap bkbg-fc-style--' + a.style + ' bkbg-fc-align--' + a.align;
347 var blockProps = wp.blockEditor.useBlockProps.save({ className: wrapClass, style: wrapStyle(a) });
348
349 return el('div', blockProps,
350 el('div', { className: 'bkbg-fc-grid' },
351 a.features.map(function (feat, index) {
352 var iconEl = null;
353 if (a.showNumber) {
354 iconEl = el('div', { className: 'bkbg-fc-number bkbg-fc-icon-style--' + a.iconStyle }, index + 1);
355 } else if (a.showIcon) {
356 iconEl = el('div', { className: 'bkbg-fc-icon-wrap bkbg-fc-icon-style--' + a.iconStyle },
357 el('span', { className: 'bkbg-fc-icon dashicons dashicons-' + feat.icon })
358 );
359 }
360 return el('div', { key: feat.id, className: 'bkbg-fc-feature' },
361 iconEl,
362 el('div', { className: 'bkbg-fc-text' },
363 el(RichText.Content, { tagName: 'h3', className: 'bkbg-fc-title', value: feat.title }),
364 el(RichText.Content, { tagName: 'p', className: 'bkbg-fc-body', value: feat.body })
365 )
366 );
367 })
368 )
369 );
370 }
371 });
372 }() );
373