PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.3
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.3
2.0.14 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.3, at blocks/column/index.js

377 lines 16.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 wp.domReady(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 // Custom Appender that opens sidebar inserter instead of popover
18 function SidebarInserterAppender(props) {
19 var rootClientId = props.rootClientId;
20
21 // Get the inserter toggle function
22 var insertionPoint = useSelect(function (select) {
23 var blockEditor = select('core/block-editor');
24 var innerBlocks = blockEditor.getBlocks(rootClientId);
25 return {
26 index: innerBlocks.length
27 };
28 }, [rootClientId]);
29
30 function handleClick() {
31 // Toggle the global inserter sidebar (not the popover).
32 try {
33 var editPostDispatch = wp.data.dispatch('core/edit-post');
34 if (editPostDispatch && typeof editPostDispatch.setIsInserterOpened === 'function') {
35 editPostDispatch.setIsInserterOpened(true);
36 }
37 } catch (e) {}
38
39 try {
40 var editSiteDispatch = wp.data.dispatch('core/edit-site');
41 if (editSiteDispatch && typeof editSiteDispatch.setIsInserterOpened === 'function') {
42 editSiteDispatch.setIsInserterOpened(true);
43 }
44 } catch (e) {}
45
46 // Attempt to set an insertion point inside this column.
47 try {
48 var beDispatch = wp.data.dispatch('core/block-editor');
49 if (beDispatch) {
50 if (typeof beDispatch.setInsertionPoint === 'function') {
51 beDispatch.setInsertionPoint(rootClientId, insertionPoint.index);
52 } else if (typeof beDispatch.__unstableSetInsertionPoint === 'function') {
53 try {
54 beDispatch.__unstableSetInsertionPoint({ rootClientId: rootClientId, index: insertionPoint.index });
55 } catch (e2) {
56 beDispatch.__unstableSetInsertionPoint(rootClientId, insertionPoint.index);
57 }
58 }
59 }
60 } catch (e) {}
61 }
62
63 return el(Button, {
64 className: 'bkbg-sidebar-appender block-editor-button-block-appender',
65 onClick: handleClick,
66 label: __('Add block', 'blockenberg')
67 },
68 el('svg', {
69 xmlns: 'http://www.w3.org/2000/svg',
70 viewBox: '0 0 24 24',
71 width: '24',
72 height: '24',
73 'aria-hidden': 'true',
74 focusable: 'false'
75 },
76 el('path', { d: 'M11 12.5V17.5H12.5V12.5H17.5V11H12.5V6H11V11H6V12.5H11Z' })
77 )
78 );
79 }
80
81 // Spacing token scale for inner padding
82 var paddingOptions = [
83 { label: __('None', 'blockenberg'), value: 'none' },
84 { label: __('XS (8px)', 'blockenberg'), value: 'xs' },
85 { label: __('S (16px)', 'blockenberg'), value: 's' },
86 { label: __('M (24px)', 'blockenberg'), value: 'm' },
87 { label: __('L (32px)', 'blockenberg'), value: 'l' },
88 { label: __('XL (48px)', 'blockenberg'), value: 'xl' }
89 ];
90
91 // Column icon
92 var columnIcon = el('svg', {
93 width: 24,
94 height: 24,
95 viewBox: '0 0 24 24',
96 xmlns: 'http://www.w3.org/2000/svg'
97 },
98 el('path', {
99 d: 'M5 4h14v16H5V4zm2 2v12h10V6H7z',
100 fill: 'currentColor'
101 })
102 );
103
104 // Generate unique ID
105 function generateUid() {
106 return 'col-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
107 }
108
109 registerBlockType('blockenberg/column', {
110 title: __('Column', 'blockenberg'),
111 icon: columnIcon,
112 category: 'blockenberg',
113 parent: ['blockenberg/row'],
114 description: __('A column container within a row for content.', 'blockenberg'),
115
116 edit: function (props) {
117 var attributes = props.attributes;
118 var setAttributes = props.setAttributes;
119 var clientId = props.clientId;
120 var a = attributes;
121
122 var moveBlockToPosition = useDispatch('core/block-editor').moveBlockToPosition;
123 var replaceInnerBlocks = useDispatch('core/block-editor').replaceInnerBlocks;
124 var updateBlockAttributes = useDispatch('core/block-editor').updateBlockAttributes;
125 var createBlock = wp.blocks.createBlock;
126
127 // Generate UID if not set
128 useEffect(function () {
129 if (!a.uid) {
130 setAttributes({ uid: generateUid() });
131 }
132 }, []);
133
134 // Get parent row info (responsive mode + column index)
135 var parentData = useSelect(function (select) {
136 var parents = select('core/block-editor').getBlockParents(clientId);
137 for (var i = parents.length - 1; i >= 0; i--) {
138 var parent = select('core/block-editor').getBlock(parents[i]);
139 if (parent && parent.name === 'blockenberg/row') {
140 var siblings = parent.innerBlocks || [];
141 var colIndex = siblings.findIndex(function (b) { return b.clientId === clientId; });
142 return {
143 rowClientId: parents[i],
144 colIndex: colIndex,
145 colCount: siblings.length,
146 responsiveMode: parent.attributes.responsiveMode || 'desktop',
147 stackOnMobile: parent.attributes.stackOnMobile !== false
148 };
149 }
150 }
151 return { rowClientId: null, colIndex: -1, colCount: 0, responsiveMode: 'desktop', stackOnMobile: true };
152 }, [clientId]);
153
154 var responsiveMode = parentData.responsiveMode;
155 var stackOnMobile = parentData.stackOnMobile;
156
157 function moveSelf(direction) {
158 if (!parentData.rowClientId) return;
159 if (parentData.colIndex < 0) return;
160 var newIndex = direction === 'left' ? parentData.colIndex - 1 : parentData.colIndex + 1;
161 if (newIndex < 0 || newIndex >= parentData.colCount) return;
162 moveBlockToPosition(clientId, parentData.rowClientId, parentData.rowClientId, newIndex);
163 }
164
165 function removeSelfWithConfirm() {
166 if (!parentData.rowClientId) return;
167 if (parentData.colCount <= 1) return;
168
169 var msg = __('Delete this column? All content inside will be removed.', 'blockenberg');
170 if (typeof window !== 'undefined' && window.confirm && !window.confirm(msg)) return;
171
172 var blocks = wp.data.select('core/block-editor').getBlocks(parentData.rowClientId);
173 if (!blocks || !blocks.length) return;
174
175 var newCount = blocks.length - 1;
176 if (newCount < 1) return;
177
178 var equalWidth = Math.round((100 / newCount) * 10) / 10;
179 var newColumns = [];
180 var newIndex = 0;
181
182 for (var i = 0; i < blocks.length; i++) {
183 var existingCol = blocks[i];
184 if (!existingCol || !existingCol.clientId) continue;
185 if (existingCol.clientId === clientId) continue;
186
187 var width = equalWidth;
188 if (newIndex === newCount - 1) {
189 width = 100 - (equalWidth * (newCount - 1));
190 width = Math.round(width * 10) / 10;
191 }
192
193 var recreatedCol = createBlock('blockenberg/column', {
194 widths: { desktop: width, tablet: width, mobile: 100 },
195 uid: (existingCol.attributes && existingCol.attributes.uid) || ('col-' + Date.now() + '-' + newIndex),
196 paddingInner: (existingCol.attributes && existingCol.attributes.paddingInner) || 'none'
197 }, existingCol.innerBlocks);
198
199 newColumns.push(recreatedCol);
200 newIndex++;
201 }
202
203 replaceInnerBlocks(parentData.rowClientId, newColumns, false);
204 updateBlockAttributes(parentData.rowClientId, { columnsCount: newCount });
205 }
206
207 // Get width for current mode with fallback
208 function getWidth(mode) {
209 var widths = a.widths || { desktop: 50, tablet: 50, mobile: 100 };
210 if (mode === 'mobile') {
211 return widths.mobile !== undefined ? widths.mobile :
212 (widths.tablet !== undefined ? widths.tablet :
213 (widths.desktop !== undefined ? widths.desktop : 50));
214 }
215 if (mode === 'tablet') {
216 return widths.tablet !== undefined ? widths.tablet :
217 (widths.desktop !== undefined ? widths.desktop : 50);
218 }
219 return widths.desktop !== undefined ? widths.desktop : 50;
220 }
221
222 var currentWidth = getWidth(responsiveMode);
223 var displayWidth = (responsiveMode === 'mobile' && stackOnMobile) ? 100 : currentWidth;
224
225 // Update width for specific mode
226 function updateWidth(mode, value) {
227 var newWidths = Object.assign({}, a.widths || { desktop: 50, tablet: 50, mobile: 100 });
228 newWidths[mode] = value;
229 setAttributes({ widths: newWidths });
230 }
231
232 var blockProps = useBlockProps({
233 className: [
234 'bkbg-column',
235 'bkbg-column--padding-' + a.paddingInner
236 ].join(' '),
237 style: {
238 '--bkbg-col-desktop': (a.widths && a.widths.desktop || 50) + '%',
239 '--bkbg-col-tablet': (a.widths && a.widths.tablet || a.widths && a.widths.desktop || 50) + '%',
240 '--bkbg-col-mobile': (a.widths && a.widths.mobile || 100) + '%',
241 flexBasis: displayWidth + '%',
242 maxWidth: displayWidth + '%',
243 width: displayWidth + '%'
244 },
245 'data-uid': a.uid
246 });
247
248 // Inspector controls
249 var inspector = el(InspectorControls, {},
250 el(PanelBody, { title: __('Column Settings', 'blockenberg'), initialOpen: true },
251 el(BaseControl, {
252 label: __('Column Widths', 'blockenberg'),
253 help: __('Widths are typically controlled via drag handles on the row. You can fine-tune them here.', 'blockenberg')
254 }),
255 el('div', { className: 'bkbg-column-widths-controls' },
256 el(RangeControl, {
257 label: __('Desktop', 'blockenberg'),
258 value: a.widths && a.widths.desktop || 50,
259 onChange: function (v) { updateWidth('desktop', v); },
260 min: 5,
261 max: 100,
262 step: 0.5,
263 marks: [
264 { value: 25, label: '25%' },
265 { value: 50, label: '50%' },
266 { value: 75, label: '75%' },
267 { value: 100, label: '100%' }
268 ]
269 }),
270 el(RangeControl, {
271 label: __('Tablet', 'blockenberg'),
272 value: a.widths && a.widths.tablet || a.widths && a.widths.desktop || 50,
273 onChange: function (v) { updateWidth('tablet', v); },
274 min: 5,
275 max: 100,
276 step: 0.5
277 }),
278 el(RangeControl, {
279 label: __('Mobile', 'blockenberg'),
280 value: a.widths && a.widths.mobile || 100,
281 onChange: function (v) { updateWidth('mobile', v); },
282 min: 5,
283 max: 100,
284 step: 0.5,
285 help: stackOnMobile ? __('Mobile width is overridden when "Stack on Mobile" is enabled in the row settings.', 'blockenberg') : ''
286 })
287 ),
288 el(SelectControl, {
289 label: __('Inner Padding', 'blockenberg'),
290 value: a.paddingInner,
291 options: paddingOptions,
292 onChange: function (v) { setAttributes({ paddingInner: v }); }
293 })
294 ),
295 el(PanelBody, { title: __('Column Info', 'blockenberg'), initialOpen: false },
296 el('div', { className: 'bkbg-column-info' },
297 el('p', {},
298 el('strong', {}, __('Current Mode:', 'blockenberg')),
299 ' ' + responsiveMode.charAt(0).toUpperCase() + responsiveMode.slice(1)
300 ),
301 el('p', {},
302 el('strong', {}, __('Current Width:', 'blockenberg')),
303 ' ' + Math.round(currentWidth * 10) / 10 + '%'
304 ),
305 el('p', {},
306 el('strong', {}, __('UID:', 'blockenberg')),
307 ' ' + (a.uid || 'N/A')
308 )
309 )
310 )
311 );
312
313 return el('div', blockProps,
314 inspector,
315 // Move controls (hover)
316 parentData.colCount > 1 && el('div', { className: 'bkbg-column__move-actions' },
317 el(Button, {
318 className: 'bkbg-column__move-btn',
319 icon: 'arrow-left-alt2',
320 label: __('Move column left', 'blockenberg'),
321 onClick: function () { moveSelf('left'); },
322 disabled: parentData.colIndex <= 0
323 }),
324 el(Button, {
325 className: 'bkbg-column__move-btn',
326 icon: 'no-alt',
327 label: __('Delete column', 'blockenberg'),
328 onClick: removeSelfWithConfirm
329 }),
330 el(Button, {
331 className: 'bkbg-column__move-btn',
332 icon: 'arrow-right-alt2',
333 label: __('Move column right', 'blockenberg'),
334 onClick: function () { moveSelf('right'); },
335 disabled: parentData.colIndex >= parentData.colCount - 1
336 })
337 ),
338 el('div', { className: 'bkbg-column__inner' },
339 el(InnerBlocks, {
340 templateLock: false,
341 renderAppender: function () {
342 return el(SidebarInserterAppender, { rootClientId: clientId });
343 }
344 })
345 ),
346 el('div', { className: 'bkbg-column__width-label' },
347 Math.round(currentWidth) + '%'
348 )
349 );
350 },
351
352 save: function (props) {
353 var a = props.attributes;
354 var widths = a.widths || { desktop: 50, tablet: 50, mobile: 100 };
355
356 var blockProps = useBlockProps.save({
357 className: [
358 'bkbg-column',
359 'bkbg-column--padding-' + a.paddingInner
360 ].join(' '),
361 style: {
362 '--bkbg-col-desktop': widths.desktop + '%',
363 '--bkbg-col-tablet': (widths.tablet || widths.desktop) + '%',
364 '--bkbg-col-mobile': (widths.mobile || 100) + '%'
365 },
366 'data-uid': a.uid
367 });
368
369 return el('div', blockProps,
370 el('div', { className: 'bkbg-column__inner' },
371 el(InnerBlocks.Content)
372 )
373 );
374 }
375 });
376 });
377