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

index.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.13, at blocks/column/index.js

504 lines 23.0 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 useEffect = wp.element.useEffect;
4 var __ = wp.i18n.__;
5 var registerBlockType = wp.blocks.registerBlockType;
6 var InspectorControls = wp.blockEditor.InspectorControls;
7 var useBlockProps = wp.blockEditor.useBlockProps;
8 var InnerBlocks = wp.blockEditor.InnerBlocks;
9 var useSelect = wp.data.useSelect;
10 var useDispatch = wp.data.useDispatch;
11 var PanelBody = wp.components.PanelBody;
12 var SelectControl = wp.components.SelectControl;
13 var RangeControl = wp.components.RangeControl;
14 var BaseControl = wp.components.BaseControl;
15 var Button = wp.components.Button;
16
17 function getTypographyControl() {
18 return (window.bkbgTypographyControl || function () { return null; });
19 }
20 var _tv = (function () {
21 var fn = window.bkbgTypoCssVars;
22 return fn ? fn : function () { return {}; };
23 })();
24
25 // Custom Appender that opens sidebar inserter instead of popover
26 function SidebarInserterAppender(props) {
27 var rootClientId = props.rootClientId;
28
29 // Get the inserter toggle function
30 var insertionPoint = useSelect(function (select) {
31 var blockEditor = select('core/block-editor');
32 var innerBlocks = blockEditor.getBlocks(rootClientId);
33 return {
34 index: innerBlocks.length
35 };
36 }, [rootClientId]);
37
38 function handleClick() {
39 // Toggle the global inserter sidebar (not the popover).
40 try {
41 var editPostDispatch = wp.data.dispatch('core/edit-post');
42 if (editPostDispatch && typeof editPostDispatch.setIsInserterOpened === 'function') {
43 editPostDispatch.setIsInserterOpened(true);
44 }
45 } catch (e) {}
46
47 try {
48 var editSiteDispatch = wp.data.dispatch('core/edit-site');
49 if (editSiteDispatch && typeof editSiteDispatch.setIsInserterOpened === 'function') {
50 editSiteDispatch.setIsInserterOpened(true);
51 }
52 } catch (e) {}
53
54 // Attempt to set an insertion point inside this column.
55 try {
56 var beDispatch = wp.data.dispatch('core/block-editor');
57 if (beDispatch) {
58 if (typeof beDispatch.setInsertionPoint === 'function') {
59 beDispatch.setInsertionPoint(rootClientId, insertionPoint.index);
60 } else if (typeof beDispatch.__unstableSetInsertionPoint === 'function') {
61 try {
62 beDispatch.__unstableSetInsertionPoint({ rootClientId: rootClientId, index: insertionPoint.index });
63 } catch (e2) {
64 beDispatch.__unstableSetInsertionPoint(rootClientId, insertionPoint.index);
65 }
66 }
67 }
68 } catch (e) {}
69 }
70
71 return el(Button, {
72 className: 'bkbg-sidebar-appender block-editor-button-block-appender',
73 onClick: handleClick,
74 label: __('Add block', 'blockenberg')
75 },
76 el('svg', {
77 xmlns: 'http://www.w3.org/2000/svg',
78 viewBox: '0 0 24 24',
79 width: '24',
80 height: '24',
81 'aria-hidden': 'true',
82 focusable: 'false'
83 },
84 el('path', { d: 'M11 12.5V17.5H12.5V12.5H17.5V11H12.5V6H11V11H6V12.5H11Z' })
85 )
86 );
87 }
88
89 // Spacing token scale for inner padding
90 var paddingOptions = [
91 { label: __('None', 'blockenberg'), value: 'none' },
92 { label: __('XS (8px)', 'blockenberg'), value: 'xs' },
93 { label: __('S (16px)', 'blockenberg'), value: 's' },
94 { label: __('M (24px)', 'blockenberg'), value: 'm' },
95 { label: __('L (32px)', 'blockenberg'), value: 'l' },
96 { label: __('XL (48px)', 'blockenberg'), value: 'xl' }
97 ];
98
99 var vAlignOptions = [
100 { label: __('Top', 'blockenberg'), value: 'top' },
101 { label: __('Middle', 'blockenberg'), value: 'middle' },
102 { label: __('Bottom', 'blockenberg'), value: 'bottom' }
103 ];
104
105 var spacingUnitOptions = [
106 { label: 'px', value: 'px' },
107 { label: '%', value: '%' },
108 { label: 'em', value: 'em' },
109 { label: 'rem', value: 'rem' },
110 { label: 'vw', value: 'vw' },
111 { label: 'vh', value: 'vh' },
112 { label: __('Custom', 'blockenberg'), value: 'custom' }
113 ];
114
115 function getSpacingSideValue(attrs, type, side) {
116 var cap = side.charAt(0).toUpperCase() + side.slice(1);
117 var value = attrs[type + cap];
118 var unit = attrs[type + cap + 'Unit'] || 'px';
119 var customUnit = attrs[type + cap + 'CustomUnit'] || '';
120
121 if (value === undefined || value === null) return undefined;
122 var raw = String(value).trim();
123 if (!raw) return undefined;
124
125 if (unit === 'custom') {
126 return customUnit ? raw + customUnit : raw;
127 }
128
129 return raw + unit;
130 }
131
132 function getSpacingStyles(attrs) {
133 return {
134 paddingTop: getSpacingSideValue(attrs, 'padding', 'top'),
135 paddingRight: getSpacingSideValue(attrs, 'padding', 'right'),
136 paddingBottom: getSpacingSideValue(attrs, 'padding', 'bottom'),
137 paddingLeft: getSpacingSideValue(attrs, 'padding', 'left'),
138 marginTop: getSpacingSideValue(attrs, 'margin', 'top'),
139 marginRight: getSpacingSideValue(attrs, 'margin', 'right'),
140 marginBottom: getSpacingSideValue(attrs, 'margin', 'bottom'),
141 marginLeft: getSpacingSideValue(attrs, 'margin', 'left')
142 };
143 }
144
145 // Column icon
146 var columnIcon = el('svg', {
147 width: 24,
148 height: 24,
149 viewBox: '0 0 24 24',
150 xmlns: 'http://www.w3.org/2000/svg'
151 },
152 el('path', {
153 d: 'M5 4h14v16H5V4zm2 2v12h10V6H7z',
154 fill: 'currentColor'
155 })
156 );
157
158 // Generate unique ID
159 function generateUid() {
160 return 'col-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
161 }
162
163 registerBlockType('blockenberg/column', {
164 title: __('Column', 'blockenberg'),
165 icon: columnIcon,
166 category: 'bkbg-layout',
167 parent: ['blockenberg/row'],
168 description: __('A column container within a row for content.', 'blockenberg'),
169
170 edit: function (props) {
171 var attributes = props.attributes;
172 var setAttributes = props.setAttributes;
173 var clientId = props.clientId;
174 var a = attributes;
175
176 var moveBlockToPosition = useDispatch('core/block-editor').moveBlockToPosition;
177 var replaceInnerBlocks = useDispatch('core/block-editor').replaceInnerBlocks;
178 var updateBlockAttributes = useDispatch('core/block-editor').updateBlockAttributes;
179 var createBlock = wp.blocks.createBlock;
180
181 // Generate UID if not set
182 useEffect(function () {
183 if (!a.uid) {
184 setAttributes({ uid: generateUid() });
185 }
186 }, []);
187
188 // Get parent row info (responsive mode + column index)
189 var parentData = useSelect(function (select) {
190 var parents = select('core/block-editor').getBlockParents(clientId);
191 for (var i = parents.length - 1; i >= 0; i--) {
192 var parent = select('core/block-editor').getBlock(parents[i]);
193 if (parent && parent.name === 'blockenberg/row') {
194 var siblings = parent.innerBlocks || [];
195 var colIndex = siblings.findIndex(function (b) { return b.clientId === clientId; });
196 return {
197 rowClientId: parents[i],
198 colIndex: colIndex,
199 colCount: siblings.length,
200 responsiveMode: parent.attributes.responsiveMode || 'desktop',
201 stackOnMobile: parent.attributes.stackOnMobile !== false
202 };
203 }
204 }
205 return { rowClientId: null, colIndex: -1, colCount: 0, responsiveMode: 'desktop', stackOnMobile: true };
206 }, [clientId]);
207
208 var responsiveMode = parentData.responsiveMode;
209 var stackOnMobile = parentData.stackOnMobile;
210
211 function moveSelf(direction) {
212 if (!parentData.rowClientId) return;
213 if (parentData.colIndex < 0) return;
214 var newIndex = direction === 'left' ? parentData.colIndex - 1 : parentData.colIndex + 1;
215 if (newIndex < 0 || newIndex >= parentData.colCount) return;
216 moveBlockToPosition(clientId, parentData.rowClientId, parentData.rowClientId, newIndex);
217 }
218
219 function removeSelfWithConfirm() {
220 if (!parentData.rowClientId) return;
221 if (parentData.colCount <= 1) return;
222
223 var msg = __('Delete this column? All content inside will be removed.', 'blockenberg');
224 if (typeof window !== 'undefined' && window.confirm && !window.confirm(msg)) return;
225
226 var blocks = wp.data.select('core/block-editor').getBlocks(parentData.rowClientId);
227 if (!blocks || !blocks.length) return;
228
229 var newCount = blocks.length - 1;
230 if (newCount < 1) return;
231
232 var equalWidth = Math.round((100 / newCount) * 10) / 10;
233 var newColumns = [];
234 var newIndex = 0;
235
236 for (var i = 0; i < blocks.length; i++) {
237 var existingCol = blocks[i];
238 if (!existingCol || !existingCol.clientId) continue;
239 if (existingCol.clientId === clientId) continue;
240
241 var width = equalWidth;
242 if (newIndex === newCount - 1) {
243 width = 100 - (equalWidth * (newCount - 1));
244 width = Math.round(width * 10) / 10;
245 }
246
247 var recreatedCol = createBlock('blockenberg/column', {
248 widths: { desktop: width, tablet: width, mobile: 100 },
249 uid: (existingCol.attributes && existingCol.attributes.uid) || ('col-' + Date.now() + '-' + newIndex),
250 paddingInner: (existingCol.attributes && existingCol.attributes.paddingInner) || 'none'
251 }, existingCol.innerBlocks);
252
253 recreatedCol.attributes = Object.assign({}, existingCol.attributes || {}, recreatedCol.attributes || {});
254
255 newColumns.push(recreatedCol);
256 newIndex++;
257 }
258
259 replaceInnerBlocks(parentData.rowClientId, newColumns, false);
260 updateBlockAttributes(parentData.rowClientId, { columnsCount: newCount });
261 }
262
263 // Get width for current mode with fallback
264 function getWidth(mode) {
265 var widths = a.widths || { desktop: 50, tablet: 50, mobile: 100 };
266 if (mode === 'mobile') {
267 return widths.mobile !== undefined ? widths.mobile :
268 (widths.tablet !== undefined ? widths.tablet :
269 (widths.desktop !== undefined ? widths.desktop : 50));
270 }
271 if (mode === 'tablet') {
272 return widths.tablet !== undefined ? widths.tablet :
273 (widths.desktop !== undefined ? widths.desktop : 50);
274 }
275 return widths.desktop !== undefined ? widths.desktop : 50;
276 }
277
278 var currentWidth = getWidth(responsiveMode);
279 var displayWidth = (responsiveMode === 'mobile' && stackOnMobile) ? 100 : currentWidth;
280
281 // Update width for specific mode
282 function updateWidth(mode, value) {
283 var newWidths = Object.assign({}, a.widths || { desktop: 50, tablet: 50, mobile: 100 });
284 newWidths[mode] = value;
285 setAttributes({ widths: newWidths });
286 }
287
288 var colStyle = Object.assign({}, {
289 '--bkbg-col-desktop': (a.widths && a.widths.desktop || 50) + '%',
290 '--bkbg-col-tablet': (a.widths && a.widths.tablet || a.widths && a.widths.desktop || 50) + '%',
291 '--bkbg-col-mobile': (a.widths && a.widths.mobile || 100) + '%',
292 flexBasis: displayWidth + '%',
293 maxWidth: displayWidth + '%',
294 width: displayWidth + '%'
295 }, getSpacingStyles(a));
296 Object.assign(colStyle, _tv(a.typoContent, '--bkbg-col-cn'));
297 var blockProps = useBlockProps({
298 className: [
299 'bkbg-column',
300 'bkbg-column--padding-' + a.paddingInner,
301 'bkbg-column--valign-' + (a.vAlign || 'top')
302 ].join(' '),
303 style: colStyle,
304 'data-uid': a.uid
305 });
306
307 function renderSpacingControl(type, label) {
308 var sides = [
309 { key: 'Top', label: __('Top', 'blockenberg') },
310 { key: 'Left', label: __('Left', 'blockenberg') },
311 { key: 'Bottom', label: __('Bottom', 'blockenberg') },
312 { key: 'Right', label: __('Right', 'blockenberg') }
313 ];
314
315 return el('div', { className: 'bkbg-spacing-control' },
316 el('div', { className: 'bkbg-spacing-control__title' }, label),
317 el('div', { className: 'bkbg-spacing-control__grid' },
318 sides.map(function (side) {
319 var valueKey = type + side.key;
320 var unitKey = type + side.key + 'Unit';
321 var customUnitKey = type + side.key + 'CustomUnit';
322
323 return el('div', { className: 'bkbg-spacing-control__item', key: valueKey },
324 el('label', { className: 'bkbg-spacing-control__label' }, side.label),
325 el('div', { className: 'bkbg-spacing-control__row' },
326 el('input', {
327 type: 'text',
328 className: 'components-text-control__input',
329 value: a[valueKey] || '',
330 placeholder: '0',
331 onChange: function (e) {
332 var next = {};
333 next[valueKey] = e.target.value;
334 setAttributes(next);
335 }
336 }),
337 el('select', {
338 className: 'components-select-control__input',
339 value: a[unitKey] || 'px',
340 onChange: function (e) {
341 var next = {};
342 next[unitKey] = e.target.value;
343 setAttributes(next);
344 }
345 }, spacingUnitOptions.map(function (opt) {
346 return el('option', { key: opt.value, value: opt.value }, opt.label);
347 }))
348 ),
349 (a[unitKey] || 'px') === 'custom' && el('input', {
350 type: 'text',
351 className: 'components-text-control__input bkbg-spacing-control__custom-unit',
352 value: a[customUnitKey] || '',
353 placeholder: __('unit, e.g. ch', 'blockenberg'),
354 onChange: function (e) {
355 var next = {};
356 next[customUnitKey] = e.target.value;
357 setAttributes(next);
358 }
359 })
360 );
361 })
362 )
363 );
364 }
365
366 // Inspector controls
367 var inspector = el(InspectorControls, {},
368 el(PanelBody, { title: __('Column Settings', 'blockenberg'), initialOpen: true },
369 el(BaseControl, {
370 label: __('Column Widths', 'blockenberg'),
371 help: __('Widths are typically controlled via drag handles on the row. You can fine-tune them here.', 'blockenberg')
372 }),
373 el('div', { className: 'bkbg-column-widths-controls' },
374 el(RangeControl, {
375 label: __('Desktop', 'blockenberg'),
376 value: a.widths && a.widths.desktop || 50,
377 onChange: function (v) { updateWidth('desktop', v); },
378 min: 5,
379 max: 100,
380 step: 0.5,
381 marks: [
382 { value: 25, label: '25%' },
383 { value: 50, label: '50%' },
384 { value: 75, label: '75%' },
385 { value: 100, label: '100%' }
386 ]
387 }),
388 el(RangeControl, {
389 label: __('Tablet', 'blockenberg'),
390 value: a.widths && a.widths.tablet || a.widths && a.widths.desktop || 50,
391 onChange: function (v) { updateWidth('tablet', v); },
392 min: 5,
393 max: 100,
394 step: 0.5
395 }),
396 el(RangeControl, {
397 label: __('Mobile', 'blockenberg'),
398 value: a.widths && a.widths.mobile || 100,
399 onChange: function (v) { updateWidth('mobile', v); },
400 min: 5,
401 max: 100,
402 step: 0.5,
403 help: stackOnMobile ? __('Mobile width is overridden when "Stack on Mobile" is enabled in the row settings.', 'blockenberg') : ''
404 })
405 ),
406 el(SelectControl, {
407 label: __('Vertical Alignment', 'blockenberg'),
408 value: a.vAlign || 'top',
409 options: vAlignOptions,
410 onChange: function (v) { setAttributes({ vAlign: v }); }
411 }),
412 ),
413 el(PanelBody, { title: __('Column Info', 'blockenberg'), initialOpen: false },
414 el('div', { className: 'bkbg-column-info' },
415 el('p', {},
416 el('strong', {}, __('Current Mode:', 'blockenberg')),
417 ' ' + responsiveMode.charAt(0).toUpperCase() + responsiveMode.slice(1)
418 ),
419 el('p', {},
420 el('strong', {}, __('Current Width:', 'blockenberg')),
421 ' ' + Math.round(currentWidth * 10) / 10 + '%'
422 ),
423 el('p', {},
424 el('strong', {}, __('UID:', 'blockenberg')),
425 ' ' + (a.uid || 'N/A')
426 )
427 )
428 ),
429 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
430 (function () {
431 var TC = getTypographyControl();
432 return el(TC, { label: __('Content', 'blockenberg'), value: a.typoContent || {}, onChange: function (v) { setAttributes({ typoContent: v }); } });
433 })()
434 )
435 );
436
437 return el('div', blockProps,
438 inspector,
439 // Move controls (hover)
440 parentData.colCount > 1 && el('div', { className: 'bkbg-column__move-actions' },
441 el(Button, {
442 className: 'bkbg-column__move-btn',
443 icon: 'arrow-left-alt2',
444 label: __('Move column left', 'blockenberg'),
445 onClick: function () { moveSelf('left'); },
446 disabled: parentData.colIndex <= 0
447 }),
448 el(Button, {
449 className: 'bkbg-column__move-btn',
450 icon: 'no-alt',
451 label: __('Delete column', 'blockenberg'),
452 onClick: removeSelfWithConfirm
453 }),
454 el(Button, {
455 className: 'bkbg-column__move-btn',
456 icon: 'arrow-right-alt2',
457 label: __('Move column right', 'blockenberg'),
458 onClick: function () { moveSelf('right'); },
459 disabled: parentData.colIndex >= parentData.colCount - 1
460 })
461 ),
462 el('div', { className: 'bkbg-column__inner' },
463 el(InnerBlocks, {
464 templateLock: false,
465 renderAppender: function () {
466 return el(SidebarInserterAppender, { rootClientId: clientId });
467 }
468 })
469 ),
470 el('div', { className: 'bkbg-column__width-label' },
471 Math.round(currentWidth) + '%'
472 )
473 );
474 },
475
476 save: function (props) {
477 var a = props.attributes;
478 var widths = a.widths || { desktop: 50, tablet: 50, mobile: 100 };
479
480 var saveStyle = Object.assign({}, {
481 '--bkbg-col-desktop': widths.desktop + '%',
482 '--bkbg-col-tablet': (widths.tablet || widths.desktop) + '%',
483 '--bkbg-col-mobile': (widths.mobile || 100) + '%'
484 }, getSpacingStyles(a));
485 Object.assign(saveStyle, _tv(a.typoContent, '--bkbg-col-cn'));
486 var blockProps = useBlockProps.save({
487 className: [
488 'bkbg-column',
489 'bkbg-column--padding-' + a.paddingInner,
490 'bkbg-column--valign-' + (a.vAlign || 'top')
491 ].join(' '),
492 style: saveStyle,
493 'data-uid': a.uid
494 });
495
496 return el('div', blockProps,
497 el('div', { className: 'bkbg-column__inner' },
498 el(InnerBlocks.Content)
499 )
500 );
501 }
502 });
503 }() );
504