| 1 |
( function () { |
| 2 |
var el = wp.element.createElement; |
| 3 |
var useState = wp.element.useState; |
| 4 |
var useRef = wp.element.useRef; |
| 5 |
var useEffect = wp.element.useEffect; |
| 6 |
var useCallback = wp.element.useCallback; |
| 7 |
var Fragment = wp.element.Fragment; |
| 8 |
var __ = wp.i18n.__; |
| 9 |
var registerBlockType = wp.blocks.registerBlockType; |
| 10 |
var InspectorControls = wp.blockEditor.InspectorControls; |
| 11 |
var BlockControls = wp.blockEditor.BlockControls; |
| 12 |
var useBlockProps = wp.blockEditor.useBlockProps; |
| 13 |
var InnerBlocks = wp.blockEditor.InnerBlocks; |
| 14 |
var useSelect = wp.data.useSelect; |
| 15 |
var useDispatch = wp.data.useDispatch; |
| 16 |
var PanelBody = wp.components.PanelBody; |
| 17 |
var SelectControl = wp.components.SelectControl; |
| 18 |
var ToggleControl = wp.components.ToggleControl; |
| 19 |
var RangeControl = wp.components.RangeControl; |
| 20 |
var ToolbarGroup = wp.components.ToolbarGroup; |
| 21 |
var ToolbarButton = wp.components.ToolbarButton; |
| 22 |
var ToolbarDropdownMenu = wp.components.ToolbarDropdownMenu; |
| 23 |
var Button = wp.components.Button; |
| 24 |
var ButtonGroup = wp.components.ButtonGroup; |
| 25 |
var Placeholder = wp.components.Placeholder; |
| 26 |
var Popover = wp.components.Popover; |
| 27 |
|
| 28 |
// Gap token scale (legacy support in saved attributes/classes) |
| 29 |
|
| 30 |
// Responsive mode options |
| 31 |
var responsiveModes = [ |
| 32 |
{ value: 'desktop', label: __('Desktop', 'blockenberg'), icon: 'desktop' }, |
| 33 |
{ value: 'tablet', label: __('Tablet', 'blockenberg'), icon: 'tablet' }, |
| 34 |
{ value: 'mobile', label: __('Mobile', 'blockenberg'), icon: 'smartphone' } |
| 35 |
]; |
| 36 |
|
| 37 |
var spacingUnitOptions = [ |
| 38 |
{ label: 'px', value: 'px' }, |
| 39 |
{ label: '%', value: '%' }, |
| 40 |
{ label: 'em', value: 'em' }, |
| 41 |
{ label: 'rem', value: 'rem' }, |
| 42 |
{ label: 'vw', value: 'vw' }, |
| 43 |
{ label: 'vh', value: 'vh' }, |
| 44 |
{ label: __('Custom', 'blockenberg'), value: 'custom' } |
| 45 |
]; |
| 46 |
|
| 47 |
function getSpacingSideValue(attrs, type, side) { |
| 48 |
var cap = side.charAt(0).toUpperCase() + side.slice(1); |
| 49 |
var value = attrs[type + cap]; |
| 50 |
var unit = attrs[type + cap + 'Unit'] || 'px'; |
| 51 |
var customUnit = attrs[type + cap + 'CustomUnit'] || ''; |
| 52 |
|
| 53 |
if (value === undefined || value === null) return undefined; |
| 54 |
var raw = String(value).trim(); |
| 55 |
if (!raw) return undefined; |
| 56 |
|
| 57 |
if (unit === 'custom') { |
| 58 |
return customUnit ? raw + customUnit : raw; |
| 59 |
} |
| 60 |
|
| 61 |
return raw + unit; |
| 62 |
} |
| 63 |
|
| 64 |
function getSpacingStyles(attrs) { |
| 65 |
return { |
| 66 |
paddingTop: getSpacingSideValue(attrs, 'padding', 'top'), |
| 67 |
paddingRight: getSpacingSideValue(attrs, 'padding', 'right'), |
| 68 |
paddingBottom: getSpacingSideValue(attrs, 'padding', 'bottom'), |
| 69 |
paddingLeft: getSpacingSideValue(attrs, 'padding', 'left'), |
| 70 |
marginTop: getSpacingSideValue(attrs, 'margin', 'top'), |
| 71 |
marginRight: getSpacingSideValue(attrs, 'margin', 'right'), |
| 72 |
marginBottom: getSpacingSideValue(attrs, 'margin', 'bottom'), |
| 73 |
marginLeft: getSpacingSideValue(attrs, 'margin', 'left') |
| 74 |
}; |
| 75 |
} |
| 76 |
|
| 77 |
// Column presets for quick setup |
| 78 |
var columnPresets = [ |
| 79 |
{ label: '1 Column (100)', value: '1-full', widths: [100], icon: '⬜' }, |
| 80 |
{ label: '2 Columns (50/50)', value: '2-equal', widths: [50, 50], icon: '⬜⬜' }, |
| 81 |
{ label: '3 Columns (33/33/33)', value: '3-equal', widths: [33.33, 33.33, 33.34], icon: '⬜⬜⬜' }, |
| 82 |
{ label: '4 Columns (25/25/25/25)', value: '4-equal', widths: [25, 25, 25, 25], icon: '⬜⬜⬜⬜' }, |
| 83 |
{ label: '2 Columns (66/33)', value: '2-wide-left', widths: [66.66, 33.34], icon: '⬛⬜' }, |
| 84 |
{ label: '2 Columns (33/66)', value: '2-wide-right', widths: [33.34, 66.66], icon: '⬜⬛' }, |
| 85 |
{ label: '3 Columns (25/50/25)', value: '3-wide-center', widths: [25, 50, 25], icon: '⬜⬛⬜' } |
| 86 |
]; |
| 87 |
|
| 88 |
// Row icon |
| 89 |
var rowIcon = el('svg', { |
| 90 |
width: 24, |
| 91 |
height: 24, |
| 92 |
viewBox: '0 0 24 24', |
| 93 |
xmlns: 'http://www.w3.org/2000/svg' |
| 94 |
}, |
| 95 |
el('path', { |
| 96 |
d: 'M4 4h16v16H4V4zm2 2v12h5V6H6zm7 0v12h5V6h-5z', |
| 97 |
fill: 'currentColor' |
| 98 |
}) |
| 99 |
); |
| 100 |
|
| 101 |
// Global cleanup function for resize state |
| 102 |
function cleanupAllResizeStates() { |
| 103 |
var allResizing = document.querySelectorAll('.bkbg-row--is-resizing'); |
| 104 |
allResizing.forEach(function(el) { |
| 105 |
el.classList.remove('bkbg-row--is-resizing'); |
| 106 |
}); |
| 107 |
// Also clean in iframe if exists |
| 108 |
var editorCanvas = document.querySelector('iframe[name="editor-canvas"]'); |
| 109 |
if (editorCanvas && editorCanvas.contentDocument) { |
| 110 |
var iframeResizing = editorCanvas.contentDocument.querySelectorAll('.bkbg-row--is-resizing'); |
| 111 |
iframeResizing.forEach(function(el) { |
| 112 |
el.classList.remove('bkbg-row--is-resizing'); |
| 113 |
}); |
| 114 |
} |
| 115 |
} |
| 116 |
|
| 117 |
// Resize Handle Component |
| 118 |
function ResizeHandle(props) { |
| 119 |
var onResizeStart = props.onResizeStart; |
| 120 |
var onResize = props.onResize; |
| 121 |
var onResizeEnd = props.onResizeEnd; |
| 122 |
var leftIndex = props.leftIndex; |
| 123 |
var minPct = props.minPct; |
| 124 |
var leftWidth = props.leftWidth; |
| 125 |
var rightWidth = props.rightWidth; |
| 126 |
var rowClientId = props.rowClientId; |
| 127 |
|
| 128 |
var handleRef = useRef(null); |
| 129 |
var rowRef = useRef(null); |
| 130 |
var isDragging = useRef(false); |
| 131 |
var startXRef = useRef(0); |
| 132 |
var startLeftRef = useRef(0); |
| 133 |
var startRightRef = useRef(0); |
| 134 |
var rowWidthRef = useRef(0); |
| 135 |
var cleanupFnRef = useRef(null); |
| 136 |
var documentRef = useRef(null); |
| 137 |
|
| 138 |
// Cleanup on unmount or when component loses focus |
| 139 |
useEffect(function() { |
| 140 |
// Get the document (might be inside iframe) |
| 141 |
var ownerDoc = handleRef.current ? handleRef.current.ownerDocument : document; |
| 142 |
var ownerWin = ownerDoc.defaultView || window; |
| 143 |
documentRef.current = ownerDoc; |
| 144 |
|
| 145 |
// Global mouseup handler to catch any missed events |
| 146 |
function globalMouseUp() { |
| 147 |
if (isDragging.current) { |
| 148 |
isDragging.current = false; |
| 149 |
cleanupAllResizeStates(); |
| 150 |
onResizeEnd(); |
| 151 |
} |
| 152 |
} |
| 153 |
|
| 154 |
// Also cleanup on window blur (user switched tabs/apps) |
| 155 |
function windowBlur() { |
| 156 |
if (isDragging.current) { |
| 157 |
isDragging.current = false; |
| 158 |
cleanupAllResizeStates(); |
| 159 |
onResizeEnd(); |
| 160 |
} |
| 161 |
} |
| 162 |
|
| 163 |
// Listen on both the owner document and window |
| 164 |
ownerDoc.addEventListener('mouseup', globalMouseUp); |
| 165 |
ownerWin.addEventListener('blur', windowBlur); |
| 166 |
// Also on parent window if in iframe |
| 167 |
if (ownerWin !== window) { |
| 168 |
window.addEventListener('mouseup', globalMouseUp); |
| 169 |
window.addEventListener('blur', windowBlur); |
| 170 |
} |
| 171 |
|
| 172 |
return function() { |
| 173 |
ownerDoc.removeEventListener('mouseup', globalMouseUp); |
| 174 |
ownerWin.removeEventListener('blur', windowBlur); |
| 175 |
if (ownerWin !== window) { |
| 176 |
window.removeEventListener('mouseup', globalMouseUp); |
| 177 |
window.removeEventListener('blur', windowBlur); |
| 178 |
} |
| 179 |
// Cleanup on unmount |
| 180 |
cleanupAllResizeStates(); |
| 181 |
}; |
| 182 |
}, [onResizeEnd]); |
| 183 |
|
| 184 |
var onMouseDown = useCallback(function (e) { |
| 185 |
e.preventDefault(); |
| 186 |
e.stopPropagation(); |
| 187 |
|
| 188 |
var handle = handleRef.current; |
| 189 |
if (!handle) return; |
| 190 |
|
| 191 |
var row = handle.closest('.bkbg-row'); |
| 192 |
if (!row) return; |
| 193 |
|
| 194 |
// Get the correct document (handles iframe case) |
| 195 |
var ownerDoc = handle.ownerDocument || document; |
| 196 |
|
| 197 |
// Store row reference for cleanup |
| 198 |
rowRef.current = row; |
| 199 |
isDragging.current = true; |
| 200 |
rowWidthRef.current = row.offsetWidth; |
| 201 |
startXRef.current = e.clientX; |
| 202 |
startLeftRef.current = leftWidth; |
| 203 |
startRightRef.current = rightWidth; |
| 204 |
|
| 205 |
onResizeStart(); |
| 206 |
|
| 207 |
// Add dragging class to row |
| 208 |
row.classList.add('bkbg-row--is-resizing'); |
| 209 |
|
| 210 |
function onMouseMove(moveEvent) { |
| 211 |
if (!isDragging.current) return; |
| 212 |
|
| 213 |
var deltaPx = moveEvent.clientX - startXRef.current; |
| 214 |
var deltaPct = (deltaPx / rowWidthRef.current) * 100; |
| 215 |
|
| 216 |
// Apply shift for snap to 1% |
| 217 |
if (moveEvent.shiftKey) { |
| 218 |
deltaPct = Math.round(deltaPct); |
| 219 |
} |
| 220 |
|
| 221 |
var newLeft = startLeftRef.current + deltaPct; |
| 222 |
var newRight = startRightRef.current - deltaPct; |
| 223 |
|
| 224 |
// Enforce minimum width |
| 225 |
if (newLeft < minPct) { |
| 226 |
newLeft = minPct; |
| 227 |
newRight = startLeftRef.current + startRightRef.current - minPct; |
| 228 |
} |
| 229 |
if (newRight < minPct) { |
| 230 |
newRight = minPct; |
| 231 |
newLeft = startLeftRef.current + startRightRef.current - minPct; |
| 232 |
} |
| 233 |
|
| 234 |
// Round to 1 decimal place |
| 235 |
newLeft = Math.round(newLeft * 10) / 10; |
| 236 |
newRight = Math.round(newRight * 10) / 10; |
| 237 |
|
| 238 |
onResize(leftIndex, newLeft, newRight); |
| 239 |
} |
| 240 |
|
| 241 |
function onMouseUp() { |
| 242 |
isDragging.current = false; |
| 243 |
|
| 244 |
// Remove dragging class using stored reference |
| 245 |
if (rowRef.current) { |
| 246 |
rowRef.current.classList.remove('bkbg-row--is-resizing'); |
| 247 |
rowRef.current = null; |
| 248 |
} |
| 249 |
|
| 250 |
// Global cleanup for safety |
| 251 |
cleanupAllResizeStates(); |
| 252 |
|
| 253 |
onResizeEnd(); |
| 254 |
ownerDoc.removeEventListener('mousemove', onMouseMove); |
| 255 |
ownerDoc.removeEventListener('mouseup', onMouseUp); |
| 256 |
} |
| 257 |
|
| 258 |
// Store cleanup function |
| 259 |
cleanupFnRef.current = onMouseUp; |
| 260 |
|
| 261 |
ownerDoc.addEventListener('mousemove', onMouseMove); |
| 262 |
ownerDoc.addEventListener('mouseup', onMouseUp); |
| 263 |
}, [leftWidth, rightWidth, minPct, leftIndex, onResize, onResizeEnd, onResizeStart]); |
| 264 |
|
| 265 |
// Double-click to equalize pair |
| 266 |
var onDoubleClick = useCallback(function (e) { |
| 267 |
e.preventDefault(); |
| 268 |
e.stopPropagation(); |
| 269 |
var avg = (leftWidth + rightWidth) / 2; |
| 270 |
avg = Math.round(avg * 10) / 10; |
| 271 |
onResize(leftIndex, avg, avg); |
| 272 |
}, [leftWidth, rightWidth, leftIndex, onResize]); |
| 273 |
|
| 274 |
return el('div', { |
| 275 |
ref: handleRef, |
| 276 |
className: 'bkbg-row__resize-handle', |
| 277 |
onMouseDown: onMouseDown, |
| 278 |
onDoubleClick: onDoubleClick, |
| 279 |
title: __('Drag to resize. Double-click to equalize.', 'blockenberg') |
| 280 |
}, |
| 281 |
el('div', { className: 'bkbg-row__resize-handle-line' }), |
| 282 |
el('div', { className: 'bkbg-row__resize-indicator' }, |
| 283 |
el('span', { className: 'bkbg-row__resize-pct bkbg-row__resize-pct--left' }, |
| 284 |
Math.round(leftWidth) + '%' |
| 285 |
), |
| 286 |
el('span', { className: 'bkbg-row__resize-pct bkbg-row__resize-pct--right' }, |
| 287 |
Math.round(rightWidth) + '%' |
| 288 |
) |
| 289 |
) |
| 290 |
); |
| 291 |
} |
| 292 |
|
| 293 |
registerBlockType('blockenberg/row', { |
| 294 |
title: __('Row', 'blockenberg'), |
| 295 |
icon: rowIcon, |
| 296 |
category: 'bkbg-layout', |
| 297 |
parent: ['blockenberg/section'], |
| 298 |
description: __('A row container for columns with drag-resize support.', 'blockenberg'), |
| 299 |
|
| 300 |
edit: function (props) { |
| 301 |
var attributes = props.attributes; |
| 302 |
var setAttributes = props.setAttributes; |
| 303 |
var clientId = props.clientId; |
| 304 |
var a = attributes; |
| 305 |
|
| 306 |
var isResizingState = useState(false); |
| 307 |
var isResizing = isResizingState[0]; |
| 308 |
var setIsResizing = isResizingState[1]; |
| 309 |
|
| 310 |
var hasSelectedLayoutState = useState(false); |
| 311 |
var hasSelectedLayout = hasSelectedLayoutState[0]; |
| 312 |
var setHasSelectedLayout = hasSelectedLayoutState[1]; |
| 313 |
|
| 314 |
var isStructureOpenState = useState(false); |
| 315 |
var isStructureOpen = isStructureOpenState[0]; |
| 316 |
var setIsStructureOpen = isStructureOpenState[1]; |
| 317 |
|
| 318 |
var updateBlockAttributes = useDispatch('core/block-editor').updateBlockAttributes; |
| 319 |
var selectBlock = useDispatch('core/block-editor').selectBlock; |
| 320 |
var insertBlock = useDispatch('core/block-editor').insertBlock; |
| 321 |
var replaceInnerBlocks = useDispatch('core/block-editor').replaceInnerBlocks; |
| 322 |
var removeBlock = useDispatch('core/block-editor').removeBlock; |
| 323 |
var moveBlockToPosition = useDispatch('core/block-editor').moveBlockToPosition; |
| 324 |
var createBlock = wp.blocks.createBlock; |
| 325 |
|
| 326 |
function openRowSettings(e) { |
| 327 |
if (e && e.preventDefault) e.preventDefault(); |
| 328 |
if (e && e.stopPropagation) e.stopPropagation(); |
| 329 |
|
| 330 |
if (typeof selectBlock === 'function') { |
| 331 |
selectBlock(clientId); |
| 332 |
} else { |
| 333 |
try { |
| 334 |
wp.data.dispatch('core/block-editor').selectBlock(clientId); |
| 335 |
} catch (err) {} |
| 336 |
} |
| 337 |
|
| 338 |
// Try to open the block settings sidebar (works in Post Editor; best-effort elsewhere) |
| 339 |
try { |
| 340 |
var editPostDispatch = wp.data.dispatch('core/edit-post'); |
| 341 |
if (editPostDispatch && typeof editPostDispatch.openGeneralSidebar === 'function') { |
| 342 |
editPostDispatch.openGeneralSidebar('edit-post/block'); |
| 343 |
} |
| 344 |
} catch (err) {} |
| 345 |
|
| 346 |
try { |
| 347 |
var editSiteDispatch = wp.data.dispatch('core/edit-site'); |
| 348 |
if (editSiteDispatch && typeof editSiteDispatch.openGeneralSidebar === 'function') { |
| 349 |
editSiteDispatch.openGeneralSidebar('edit-site/block-inspector'); |
| 350 |
} |
| 351 |
} catch (err) {} |
| 352 |
|
| 353 |
try { |
| 354 |
var interfaceDispatch = wp.data.dispatch('core/interface'); |
| 355 |
if (interfaceDispatch && typeof interfaceDispatch.enableComplementaryArea === 'function') { |
| 356 |
interfaceDispatch.enableComplementaryArea('core/edit-post', 'edit-post/block'); |
| 357 |
} |
| 358 |
} catch (err) {} |
| 359 |
} |
| 360 |
|
| 361 |
// Get inner blocks (columns) |
| 362 |
var innerBlocksData = useSelect(function (select) { |
| 363 |
var innerBlocks = select('core/block-editor').getBlocks(clientId); |
| 364 |
return innerBlocks.map(function (block) { |
| 365 |
return { |
| 366 |
clientId: block.clientId, |
| 367 |
widths: block.attributes.widths || { desktop: 50, tablet: 50, mobile: 100 }, |
| 368 |
uid: block.attributes.uid |
| 369 |
}; |
| 370 |
}); |
| 371 |
}, [clientId]); |
| 372 |
|
| 373 |
// Get parent section info and row position |
| 374 |
var parentSectionData = useSelect(function (select) { |
| 375 |
var parents = select('core/block-editor').getBlockParents(clientId); |
| 376 |
for (var i = parents.length - 1; i >= 0; i--) { |
| 377 |
var parent = select('core/block-editor').getBlock(parents[i]); |
| 378 |
if (parent && parent.name === 'blockenberg/section') { |
| 379 |
var siblingRows = parent.innerBlocks || []; |
| 380 |
var rowIndex = siblingRows.findIndex(function (block) { return block.clientId === clientId; }); |
| 381 |
return { |
| 382 |
parentClientId: parents[i], |
| 383 |
rowIndex: rowIndex, |
| 384 |
rowCount: siblingRows.length |
| 385 |
}; |
| 386 |
} |
| 387 |
} |
| 388 |
return { parentClientId: null, rowIndex: -1, rowCount: 0 }; |
| 389 |
}, [clientId]); |
| 390 |
|
| 391 |
var columnCount = innerBlocksData.length; |
| 392 |
|
| 393 |
// Check if we need to show layout selector |
| 394 |
var showLayoutSelector = columnCount === 0 && !hasSelectedLayout; |
| 395 |
|
| 396 |
// Select a preset layout |
| 397 |
function selectLayout(preset) { |
| 398 |
var presetData = columnPresets.find(function (p) { return p.value === preset; }); |
| 399 |
if (!presetData) return; |
| 400 |
|
| 401 |
var columns = presetData.widths.map(function (width, index) { |
| 402 |
return createBlock('blockenberg/column', { |
| 403 |
widths: { desktop: width, tablet: width, mobile: 100 }, |
| 404 |
uid: 'col-' + Date.now() + '-' + index |
| 405 |
}); |
| 406 |
}); |
| 407 |
|
| 408 |
replaceInnerBlocks(clientId, columns); |
| 409 |
setAttributes({ columnsCount: presetData.widths.length }); |
| 410 |
setHasSelectedLayout(true); |
| 411 |
} |
| 412 |
|
| 413 |
// Get widths for current responsive mode |
| 414 |
function getColumnWidth(columnData, mode) { |
| 415 |
var widths = columnData.widths || {}; |
| 416 |
// Fallback chain: requested mode -> tablet -> desktop |
| 417 |
if (mode === 'mobile') { |
| 418 |
return widths.mobile !== undefined ? widths.mobile : |
| 419 |
(widths.tablet !== undefined ? widths.tablet : |
| 420 |
(widths.desktop !== undefined ? widths.desktop : 50)); |
| 421 |
} |
| 422 |
if (mode === 'tablet') { |
| 423 |
return widths.tablet !== undefined ? widths.tablet : |
| 424 |
(widths.desktop !== undefined ? widths.desktop : 50); |
| 425 |
} |
| 426 |
return widths.desktop !== undefined ? widths.desktop : 50; |
| 427 |
} |
| 428 |
|
| 429 |
// Get all current widths |
| 430 |
function getCurrentWidths() { |
| 431 |
return innerBlocksData.map(function (col) { |
| 432 |
return getColumnWidth(col, a.responsiveMode); |
| 433 |
}); |
| 434 |
} |
| 435 |
|
| 436 |
function setWidthLabelsVisibilityDuringResize(show) { |
| 437 |
var selector = '[data-block="' + clientId + '"] .bkbg-column__width-label'; |
| 438 |
|
| 439 |
function updateInDocument(doc) { |
| 440 |
if (!doc || !doc.querySelectorAll) return; |
| 441 |
var labels = doc.querySelectorAll(selector); |
| 442 |
labels.forEach(function (label) { |
| 443 |
if (show) { |
| 444 |
label.style.opacity = '1'; |
| 445 |
} else { |
| 446 |
label.style.opacity = ''; |
| 447 |
} |
| 448 |
}); |
| 449 |
} |
| 450 |
|
| 451 |
updateInDocument(document); |
| 452 |
|
| 453 |
var editorCanvas = document.querySelector('iframe[name="editor-canvas"]'); |
| 454 |
if (editorCanvas && editorCanvas.contentDocument) { |
| 455 |
updateInDocument(editorCanvas.contentDocument); |
| 456 |
} |
| 457 |
} |
| 458 |
|
| 459 |
// Handle resize start |
| 460 |
function handleResizeStart() { |
| 461 |
setIsResizing(true); |
| 462 |
setWidthLabelsVisibilityDuringResize(true); |
| 463 |
} |
| 464 |
|
| 465 |
// Handle resize - update attributes immediately |
| 466 |
function handleResize(leftIndex, newLeftPct, newRightPct) { |
| 467 |
var mode = a.responsiveMode; |
| 468 |
var leftCol = innerBlocksData[leftIndex]; |
| 469 |
var rightCol = innerBlocksData[leftIndex + 1]; |
| 470 |
|
| 471 |
if (leftCol) { |
| 472 |
var leftWidths = Object.assign({}, leftCol.widths || { desktop: 50, tablet: 50, mobile: 100 }); |
| 473 |
leftWidths[mode] = newLeftPct; |
| 474 |
updateBlockAttributes(leftCol.clientId, { widths: leftWidths }); |
| 475 |
} |
| 476 |
|
| 477 |
if (rightCol) { |
| 478 |
var rightWidths = Object.assign({}, rightCol.widths || { desktop: 50, tablet: 50, mobile: 100 }); |
| 479 |
rightWidths[mode] = newRightPct; |
| 480 |
updateBlockAttributes(rightCol.clientId, { widths: rightWidths }); |
| 481 |
} |
| 482 |
} |
| 483 |
|
| 484 |
// Handle resize end |
| 485 |
function handleResizeEnd() { |
| 486 |
setIsResizing(false); |
| 487 |
setWidthLabelsVisibilityDuringResize(false); |
| 488 |
// Ensure CSS class is also cleaned up |
| 489 |
cleanupAllResizeStates(); |
| 490 |
} |
| 491 |
|
| 492 |
// Add column - rebuild all columns with new widths |
| 493 |
function addColumn() { |
| 494 |
var newCount = columnCount + 1; |
| 495 |
if (newCount > 6) return; |
| 496 |
|
| 497 |
// Calculate equal widths |
| 498 |
var equalWidth = Math.round((100 / newCount) * 10) / 10; |
| 499 |
var lastWidth = 100 - (equalWidth * (newCount - 1)); |
| 500 |
lastWidth = Math.round(lastWidth * 10) / 10; |
| 501 |
|
| 502 |
// Get existing column inner blocks content |
| 503 |
var existingBlocks = wp.data.select('core/block-editor').getBlocks(clientId); |
| 504 |
|
| 505 |
// Create new columns array |
| 506 |
var newColumns = []; |
| 507 |
|
| 508 |
// Recreate existing columns with new widths |
| 509 |
for (var i = 0; i < existingBlocks.length; i++) { |
| 510 |
var existingCol = existingBlocks[i]; |
| 511 |
var width = (i === newCount - 1) ? lastWidth : equalWidth; |
| 512 |
|
| 513 |
var newCol = createBlock('blockenberg/column', { |
| 514 |
widths: { desktop: width, tablet: width, mobile: 100 }, |
| 515 |
uid: existingCol.attributes.uid || ('col-' + Date.now() + '-' + i), |
| 516 |
paddingInner: existingCol.attributes.paddingInner || 'none' |
| 517 |
}, existingCol.innerBlocks); |
| 518 |
|
| 519 |
newCol.attributes = Object.assign({}, existingCol.attributes || {}, newCol.attributes || {}); |
| 520 |
|
| 521 |
newColumns.push(newCol); |
| 522 |
} |
| 523 |
|
| 524 |
// Add new empty column |
| 525 |
var newColWidth = (newCount - 1 === columnCount) ? lastWidth : equalWidth; |
| 526 |
var newColumn = createBlock('blockenberg/column', { |
| 527 |
widths: { desktop: newColWidth, tablet: newColWidth, mobile: 100 }, |
| 528 |
uid: 'col-' + Date.now() |
| 529 |
}); |
| 530 |
newColumns.push(newColumn); |
| 531 |
|
| 532 |
// Replace all inner blocks |
| 533 |
replaceInnerBlocks(clientId, newColumns, false); |
| 534 |
setAttributes({ columnsCount: newCount }); |
| 535 |
} |
| 536 |
|
| 537 |
// Equalize all columns |
| 538 |
function equalizeColumns() { |
| 539 |
if (columnCount === 0) return; |
| 540 |
|
| 541 |
var mode = a.responsiveMode; |
| 542 |
var equalWidth = Math.round((100 / columnCount) * 100) / 100; |
| 543 |
|
| 544 |
// Adjust last column to ensure sum is exactly 100 |
| 545 |
var widthsArray = []; |
| 546 |
for (var i = 0; i < columnCount - 1; i++) { |
| 547 |
widthsArray.push(equalWidth); |
| 548 |
} |
| 549 |
widthsArray.push(100 - (equalWidth * (columnCount - 1))); |
| 550 |
|
| 551 |
innerBlocksData.forEach(function (col, index) { |
| 552 |
var newWidths = Object.assign({}, col.widths); |
| 553 |
newWidths[mode] = widthsArray[index]; |
| 554 |
updateBlockAttributes(col.clientId, { widths: newWidths }); |
| 555 |
}); |
| 556 |
} |
| 557 |
|
| 558 |
// Insert column at specific position (before/after index) |
| 559 |
function insertColumnAt(index, position) { |
| 560 |
if (columnCount >= 6) return; |
| 561 |
|
| 562 |
var newCount = columnCount + 1; |
| 563 |
var equalWidth = Math.round((100 / newCount) * 10) / 10; |
| 564 |
|
| 565 |
// Get existing column inner blocks content |
| 566 |
var existingBlocks = wp.data.select('core/block-editor').getBlocks(clientId); |
| 567 |
|
| 568 |
// Determine insert position |
| 569 |
var insertIndex = position === 'before' ? index : index + 1; |
| 570 |
|
| 571 |
// Create new columns array with recalculated widths |
| 572 |
var newColumns = []; |
| 573 |
var colIndex = 0; |
| 574 |
|
| 575 |
for (var i = 0; i < newCount; i++) { |
| 576 |
var width = equalWidth; |
| 577 |
if (i === newCount - 1) { |
| 578 |
width = 100 - (equalWidth * (newCount - 1)); |
| 579 |
width = Math.round(width * 10) / 10; |
| 580 |
} |
| 581 |
|
| 582 |
if (i === insertIndex) { |
| 583 |
// Insert new column here |
| 584 |
var newCol = createBlock('blockenberg/column', { |
| 585 |
widths: { desktop: width, tablet: width, mobile: 100 }, |
| 586 |
uid: 'col-' + Date.now() |
| 587 |
}); |
| 588 |
newColumns.push(newCol); |
| 589 |
} else { |
| 590 |
// Use existing column |
| 591 |
var existingCol = existingBlocks[colIndex]; |
| 592 |
if (existingCol) { |
| 593 |
var recreatedCol = createBlock('blockenberg/column', { |
| 594 |
widths: { desktop: width, tablet: width, mobile: 100 }, |
| 595 |
uid: existingCol.attributes.uid || ('col-' + Date.now() + '-' + colIndex), |
| 596 |
paddingInner: existingCol.attributes.paddingInner || 'none' |
| 597 |
}, existingCol.innerBlocks); |
| 598 |
|
| 599 |
recreatedCol.attributes = Object.assign({}, existingCol.attributes || {}, recreatedCol.attributes || {}); |
| 600 |
newColumns.push(recreatedCol); |
| 601 |
colIndex++; |
| 602 |
} |
| 603 |
} |
| 604 |
} |
| 605 |
|
| 606 |
replaceInnerBlocks(clientId, newColumns, false); |
| 607 |
setAttributes({ columnsCount: newCount }); |
| 608 |
} |
| 609 |
|
| 610 |
// Move column left or right |
| 611 |
function moveColumn(index, direction) { |
| 612 |
var newIndex = direction === 'left' ? index - 1 : index + 1; |
| 613 |
|
| 614 |
// Check bounds |
| 615 |
if (newIndex < 0 || newIndex >= columnCount) return; |
| 616 |
|
| 617 |
// Get the column block to move |
| 618 |
var blocks = wp.data.select('core/block-editor').getBlocks(clientId); |
| 619 |
var blockToMove = blocks[index]; |
| 620 |
|
| 621 |
if (blockToMove) { |
| 622 |
moveBlockToPosition(blockToMove.clientId, clientId, clientId, newIndex); |
| 623 |
} |
| 624 |
} |
| 625 |
|
| 626 |
// Remove column at index |
| 627 |
function removeColumnAt(index) { |
| 628 |
if (columnCount <= 1) return; |
| 629 |
|
| 630 |
var blocks = wp.data.select('core/block-editor').getBlocks(clientId); |
| 631 |
var blockToRemove = blocks[index]; |
| 632 |
|
| 633 |
if (blockToRemove) { |
| 634 |
var newCount = columnCount - 1; |
| 635 |
var equalWidth = Math.round((100 / newCount) * 10) / 10; |
| 636 |
|
| 637 |
// Rebuild columns without the removed one |
| 638 |
var newColumns = []; |
| 639 |
var newIndex = 0; |
| 640 |
|
| 641 |
for (var i = 0; i < blocks.length; i++) { |
| 642 |
if (i === index) continue; |
| 643 |
|
| 644 |
var existingCol = blocks[i]; |
| 645 |
var width = equalWidth; |
| 646 |
if (newIndex === newCount - 1) { |
| 647 |
width = 100 - (equalWidth * (newCount - 1)); |
| 648 |
width = Math.round(width * 10) / 10; |
| 649 |
} |
| 650 |
|
| 651 |
var recreatedCol = createBlock('blockenberg/column', { |
| 652 |
widths: { desktop: width, tablet: width, mobile: 100 }, |
| 653 |
uid: existingCol.attributes.uid || ('col-' + Date.now() + '-' + newIndex), |
| 654 |
paddingInner: existingCol.attributes.paddingInner || 'none' |
| 655 |
}, existingCol.innerBlocks); |
| 656 |
|
| 657 |
recreatedCol.attributes = Object.assign({}, existingCol.attributes || {}, recreatedCol.attributes || {}); |
| 658 |
newColumns.push(recreatedCol); |
| 659 |
newIndex++; |
| 660 |
} |
| 661 |
|
| 662 |
replaceInnerBlocks(clientId, newColumns, false); |
| 663 |
setAttributes({ columnsCount: newCount }); |
| 664 |
} |
| 665 |
} |
| 666 |
|
| 667 |
// ======================================== |
| 668 |
// Row Management Functions |
| 669 |
// ======================================== |
| 670 |
|
| 671 |
// Insert new row above or below current row |
| 672 |
function insertRowAt(position) { |
| 673 |
if (!parentSectionData.parentClientId) return; |
| 674 |
|
| 675 |
var insertIndex = position === 'before' |
| 676 |
? parentSectionData.rowIndex |
| 677 |
: parentSectionData.rowIndex + 1; |
| 678 |
|
| 679 |
var newRow = createBlock('blockenberg/row', { columnsCount: 1 }, [ |
| 680 |
createBlock('blockenberg/column', { |
| 681 |
widths: { desktop: 100, tablet: 100, mobile: 100 }, |
| 682 |
uid: 'col-' + Date.now() + '-0' |
| 683 |
}) |
| 684 |
]); |
| 685 |
|
| 686 |
insertBlock(newRow, insertIndex, parentSectionData.parentClientId); |
| 687 |
} |
| 688 |
|
| 689 |
// Move row up or down within section |
| 690 |
function moveRow(direction) { |
| 691 |
if (!parentSectionData.parentClientId) return; |
| 692 |
|
| 693 |
var newIndex = direction === 'up' |
| 694 |
? parentSectionData.rowIndex - 1 |
| 695 |
: parentSectionData.rowIndex + 1; |
| 696 |
|
| 697 |
// Check bounds |
| 698 |
if (newIndex < 0 || newIndex >= parentSectionData.rowCount) return; |
| 699 |
|
| 700 |
moveBlockToPosition( |
| 701 |
clientId, |
| 702 |
parentSectionData.parentClientId, |
| 703 |
parentSectionData.parentClientId, |
| 704 |
newIndex |
| 705 |
); |
| 706 |
} |
| 707 |
|
| 708 |
// Apply preset |
| 709 |
function applyPreset(preset) { |
| 710 |
var presetData = columnPresets.find(function (p) { return p.value === preset; }); |
| 711 |
if (!presetData) return; |
| 712 |
|
| 713 |
var targetCount = presetData.widths.length; |
| 714 |
var mode = a.responsiveMode; |
| 715 |
|
| 716 |
// Add or remove columns to match preset |
| 717 |
if (columnCount < targetCount) { |
| 718 |
// Add columns |
| 719 |
for (var i = columnCount; i < targetCount; i++) { |
| 720 |
var newColumn = createBlock('blockenberg/column', { |
| 721 |
widths: { |
| 722 |
desktop: presetData.widths[i], |
| 723 |
tablet: presetData.widths[i], |
| 724 |
mobile: 100 |
| 725 |
}, |
| 726 |
uid: 'col-' + Date.now() + '-' + i |
| 727 |
}); |
| 728 |
insertBlock(newColumn, i, clientId); |
| 729 |
} |
| 730 |
} |
| 731 |
|
| 732 |
if (columnCount > targetCount) { |
| 733 |
// Remove extra columns from the end |
| 734 |
var blocksToTrim = wp.data.select('core/block-editor').getBlocks(clientId); |
| 735 |
for (var r = blocksToTrim.length - 1; r >= targetCount; r--) { |
| 736 |
if (blocksToTrim[r] && blocksToTrim[r].clientId) { |
| 737 |
removeBlock(blocksToTrim[r].clientId); |
| 738 |
} |
| 739 |
} |
| 740 |
} |
| 741 |
|
| 742 |
// Update existing column widths |
| 743 |
setTimeout(function () { |
| 744 |
var blocks = wp.data.select('core/block-editor').getBlocks(clientId); |
| 745 |
blocks.slice(0, targetCount).forEach(function (block, index) { |
| 746 |
var newWidths = Object.assign({}, block.attributes.widths || {}); |
| 747 |
newWidths[mode] = presetData.widths[index]; |
| 748 |
updateBlockAttributes(block.clientId, { widths: newWidths }); |
| 749 |
}); |
| 750 |
}, 100); |
| 751 |
|
| 752 |
setAttributes({ columnsCount: targetCount }); |
| 753 |
} |
| 754 |
|
| 755 |
var blockProps = useBlockProps({ |
| 756 |
className: [ |
| 757 |
'bkbg-row', |
| 758 |
'bkbg-row--gap-' + a.gap, |
| 759 |
'bkbg-row--mode-' + a.responsiveMode, |
| 760 |
a.stackOnMobile ? 'bkbg-row--stack-mobile' : '', |
| 761 |
isResizing ? 'bkbg-row--is-resizing' : '' |
| 762 |
].filter(Boolean).join(' '), |
| 763 |
style: getSpacingStyles(a), |
| 764 |
'data-responsive-mode': a.responsiveMode |
| 765 |
}); |
| 766 |
|
| 767 |
function renderSpacingControl(type, label) { |
| 768 |
var sides = [ |
| 769 |
{ key: 'Top', label: __('Top', 'blockenberg') }, |
| 770 |
{ key: 'Left', label: __('Left', 'blockenberg') }, |
| 771 |
{ key: 'Bottom', label: __('Bottom', 'blockenberg') }, |
| 772 |
{ key: 'Right', label: __('Right', 'blockenberg') } |
| 773 |
]; |
| 774 |
|
| 775 |
return el('div', { className: 'bkbg-spacing-control' }, |
| 776 |
el('div', { className: 'bkbg-spacing-control__title' }, label), |
| 777 |
el('div', { className: 'bkbg-spacing-control__grid' }, |
| 778 |
sides.map(function (side) { |
| 779 |
var valueKey = type + side.key; |
| 780 |
var unitKey = type + side.key + 'Unit'; |
| 781 |
var customUnitKey = type + side.key + 'CustomUnit'; |
| 782 |
|
| 783 |
return el('div', { className: 'bkbg-spacing-control__item', key: valueKey }, |
| 784 |
el('label', { className: 'bkbg-spacing-control__label' }, side.label), |
| 785 |
el('div', { className: 'bkbg-spacing-control__row' }, |
| 786 |
el('input', { |
| 787 |
type: 'text', |
| 788 |
className: 'components-text-control__input', |
| 789 |
value: a[valueKey] || '', |
| 790 |
placeholder: '0', |
| 791 |
onChange: function (e) { |
| 792 |
var next = {}; |
| 793 |
next[valueKey] = e.target.value; |
| 794 |
setAttributes(next); |
| 795 |
} |
| 796 |
}), |
| 797 |
el('select', { |
| 798 |
className: 'components-select-control__input', |
| 799 |
value: a[unitKey] || 'px', |
| 800 |
onChange: function (e) { |
| 801 |
var next = {}; |
| 802 |
next[unitKey] = e.target.value; |
| 803 |
setAttributes(next); |
| 804 |
} |
| 805 |
}, spacingUnitOptions.map(function (opt) { |
| 806 |
return el('option', { key: opt.value, value: opt.value }, opt.label); |
| 807 |
})) |
| 808 |
), |
| 809 |
(a[unitKey] || 'px') === 'custom' && el('input', { |
| 810 |
type: 'text', |
| 811 |
className: 'components-text-control__input bkbg-spacing-control__custom-unit', |
| 812 |
value: a[customUnitKey] || '', |
| 813 |
placeholder: __('unit, e.g. ch', 'blockenberg'), |
| 814 |
onChange: function (e) { |
| 815 |
var next = {}; |
| 816 |
next[customUnitKey] = e.target.value; |
| 817 |
setAttributes(next); |
| 818 |
} |
| 819 |
}) |
| 820 |
); |
| 821 |
}) |
| 822 |
) |
| 823 |
); |
| 824 |
} |
| 825 |
|
| 826 |
// Get responsive mode icon |
| 827 |
function getModeIcon(mode) { |
| 828 |
switch (mode) { |
| 829 |
case 'tablet': return 'tablet'; |
| 830 |
case 'mobile': return 'smartphone'; |
| 831 |
default: return 'desktop'; |
| 832 |
} |
| 833 |
} |
| 834 |
|
| 835 |
// Toolbar controls |
| 836 |
var toolbar = el(BlockControls, {}, |
| 837 |
// Responsive mode switcher |
| 838 |
el(ToolbarGroup, {}, |
| 839 |
el(ToolbarDropdownMenu, { |
| 840 |
icon: getModeIcon(a.responsiveMode), |
| 841 |
label: __('Responsive Mode', 'blockenberg'), |
| 842 |
controls: responsiveModes.map(function (mode) { |
| 843 |
return { |
| 844 |
title: mode.label, |
| 845 |
icon: mode.icon, |
| 846 |
isActive: a.responsiveMode === mode.value, |
| 847 |
onClick: function () { setAttributes({ responsiveMode: mode.value }); } |
| 848 |
}; |
| 849 |
}) |
| 850 |
}) |
| 851 |
), |
| 852 |
// Column actions |
| 853 |
el(ToolbarGroup, {}, |
| 854 |
el(ToolbarButton, { |
| 855 |
icon: 'plus', |
| 856 |
label: __('Add Column', 'blockenberg'), |
| 857 |
onClick: addColumn, |
| 858 |
disabled: columnCount >= 6 |
| 859 |
}), |
| 860 |
el(ToolbarButton, { |
| 861 |
icon: 'editor-contract', |
| 862 |
label: __('Equalize Columns', 'blockenberg'), |
| 863 |
onClick: equalizeColumns, |
| 864 |
disabled: columnCount < 2 |
| 865 |
}) |
| 866 |
), |
| 867 |
null |
| 868 |
); |
| 869 |
|
| 870 |
// Inspector controls |
| 871 |
var inspector = el(InspectorControls, {}, |
| 872 |
el(PanelBody, { title: __('Row Settings', 'blockenberg'), initialOpen: true }, |
| 873 |
el(ToggleControl, { |
| 874 |
label: __('Stack on Mobile', 'blockenberg'), |
| 875 |
help: __('Columns will stack vertically on mobile devices.', 'blockenberg'), |
| 876 |
checked: a.stackOnMobile, |
| 877 |
__nextHasNoMarginBottom: true, |
| 878 |
onChange: function (v) { setAttributes({ stackOnMobile: v }); } |
| 879 |
}), |
| 880 |
el(RangeControl, { |
| 881 |
label: __('Minimum Column Width (%)', 'blockenberg'), |
| 882 |
value: a.minColumnPct, |
| 883 |
onChange: function (v) { setAttributes({ minColumnPct: v }); }, |
| 884 |
min: 5, |
| 885 |
max: 25, |
| 886 |
step: 1 |
| 887 |
}), |
| 888 |
), |
| 889 |
el(PanelBody, { title: __('Column Presets', 'blockenberg'), initialOpen: false }, |
| 890 |
el('div', { className: 'bkbg-row-presets' }, |
| 891 |
columnPresets.map(function (preset) { |
| 892 |
return el(Button, { |
| 893 |
key: preset.value, |
| 894 |
variant: 'secondary', |
| 895 |
className: 'bkbg-row-preset-btn', |
| 896 |
onClick: function () { applyPreset(preset.value); } |
| 897 |
}, preset.label); |
| 898 |
}) |
| 899 |
) |
| 900 |
) |
| 901 |
); |
| 902 |
|
| 903 |
// Build inner blocks with resize handles |
| 904 |
var currentWidths = getCurrentWidths(); |
| 905 |
|
| 906 |
// No appender in Row - columns are added via toolbar button |
| 907 |
|
| 908 |
// Generate column template based on count |
| 909 |
function getColumnTemplate() { |
| 910 |
var count = Math.max(1, a.columnsCount || 1); |
| 911 |
var template = []; |
| 912 |
var defaultWidth = Math.round((100 / count) * 100) / 100; |
| 913 |
for (var i = 0; i < count; i++) { |
| 914 |
var w = i === count - 1 ? (100 - defaultWidth * (count - 1)) : defaultWidth; |
| 915 |
template.push(['blockenberg/column', { |
| 916 |
widths: { desktop: w, tablet: w, mobile: 100 }, |
| 917 |
uid: 'col-' + i |
| 918 |
}]); |
| 919 |
} |
| 920 |
return template; |
| 921 |
} |
| 922 |
|
| 923 |
// Layout selector for initial setup |
| 924 |
if (showLayoutSelector) { |
| 925 |
return el('div', blockProps, |
| 926 |
el(Placeholder, { |
| 927 |
icon: rowIcon, |
| 928 |
label: __('Row', 'blockenberg'), |
| 929 |
instructions: __('Select a column layout to start.', 'blockenberg'), |
| 930 |
className: 'bkbg-row-layout-selector' |
| 931 |
}, |
| 932 |
el('div', { className: 'bkbg-row-layout-options' }, |
| 933 |
columnPresets.map(function (preset) { |
| 934 |
return el(Button, { |
| 935 |
key: preset.value, |
| 936 |
variant: 'secondary', |
| 937 |
className: 'bkbg-row-layout-option', |
| 938 |
onClick: function () { selectLayout(preset.value); } |
| 939 |
}, |
| 940 |
el('span', { className: 'bkbg-row-layout-icon' }, preset.icon), |
| 941 |
el('span', { className: 'bkbg-row-layout-label' }, preset.label) |
| 942 |
); |
| 943 |
}) |
| 944 |
), |
| 945 |
el(Button, { |
| 946 |
variant: 'link', |
| 947 |
className: 'bkbg-row-skip-layout', |
| 948 |
onClick: function () { setHasSelectedLayout(true); } |
| 949 |
}, __('Skip and add columns manually', 'blockenberg')) |
| 950 |
) |
| 951 |
); |
| 952 |
} |
| 953 |
|
| 954 |
return el('div', blockProps, |
| 955 |
toolbar, |
| 956 |
inspector, |
| 957 |
el('div', { |
| 958 |
className: 'bkbg-row__inner' |
| 959 |
}, |
| 960 |
el(InnerBlocks, { |
| 961 |
allowedBlocks: ['blockenberg/column'], |
| 962 |
template: getColumnTemplate(), |
| 963 |
templateLock: false, |
| 964 |
orientation: 'horizontal', |
| 965 |
renderAppender: false |
| 966 |
}) |
| 967 |
), |
| 968 |
// Render resize handles overlay (hide in mobile mode) |
| 969 |
columnCount > 1 && a.responsiveMode !== 'mobile' && el('div', { className: 'bkbg-row__handles-overlay' }, |
| 970 |
currentWidths.slice(0, -1).map(function (leftWidth, index) { |
| 971 |
var rightWidth = currentWidths[index + 1]; |
| 972 |
var leftOffset = currentWidths.slice(0, index + 1).reduce(function (sum, w) { return sum + w; }, 0); |
| 973 |
return el('div', { |
| 974 |
key: 'handle-' + index, |
| 975 |
className: 'bkbg-row__handle-wrapper', |
| 976 |
style: { left: leftOffset + '%' } |
| 977 |
}, |
| 978 |
el(ResizeHandle, { |
| 979 |
leftIndex: index, |
| 980 |
leftWidth: leftWidth, |
| 981 |
rightWidth: rightWidth, |
| 982 |
minPct: a.minColumnPct, |
| 983 |
onResizeStart: handleResizeStart, |
| 984 |
onResize: handleResize, |
| 985 |
onResizeEnd: handleResizeEnd, |
| 986 |
rowClientId: clientId |
| 987 |
}) |
| 988 |
); |
| 989 |
}) |
| 990 |
), |
| 991 |
// Floating structure popup trigger |
| 992 |
el('div', { className: 'bkbg-row__structure-trigger' }, |
| 993 |
el(Button, { |
| 994 |
className: 'bkbg-row__structure-btn', |
| 995 |
icon: 'layout', |
| 996 |
label: __('Row structure', 'blockenberg'), |
| 997 |
onClick: function() { setIsStructureOpen(!isStructureOpen); } |
| 998 |
}), |
| 999 |
isStructureOpen && el(Popover, { |
| 1000 |
className: 'bkbg-row__structure-popover', |
| 1001 |
position: 'bottom center', |
| 1002 |
onClose: function() { setIsStructureOpen(false); }, |
| 1003 |
focusOnMount: 'container' |
| 1004 |
}, |
| 1005 |
el('div', { className: 'bkbg-row__structure-content' }, |
| 1006 |
// Header |
| 1007 |
el('div', { className: 'bkbg-row__structure-header' }, |
| 1008 |
el('span', { className: 'bkbg-row__structure-title' }, __('Row Structure', 'blockenberg')), |
| 1009 |
el(Button, { |
| 1010 |
className: 'bkbg-row__structure-close', |
| 1011 |
icon: 'no-alt', |
| 1012 |
label: __('Close', 'blockenberg'), |
| 1013 |
onClick: function() { setIsStructureOpen(false); } |
| 1014 |
}) |
| 1015 |
), |
| 1016 |
// Visual column bars |
| 1017 |
el('div', { className: 'bkbg-row__structure-columns' }, |
| 1018 |
currentWidths.map(function(width, index) { |
| 1019 |
return el('div', { |
| 1020 |
key: 'col-' + index, |
| 1021 |
className: 'bkbg-row__structure-col', |
| 1022 |
style: { width: width + '%' } |
| 1023 |
}, |
| 1024 |
el('span', { className: 'bkbg-row__structure-col-label' }, Math.round(width) + '%') |
| 1025 |
); |
| 1026 |
}) |
| 1027 |
), |
| 1028 |
// Column actions |
| 1029 |
el('div', { className: 'bkbg-row__structure-actions' }, |
| 1030 |
el(Button, { |
| 1031 |
className: 'bkbg-row__structure-action', |
| 1032 |
icon: 'plus', |
| 1033 |
onClick: addColumn, |
| 1034 |
disabled: columnCount >= 6 |
| 1035 |
}, __('Add Column', 'blockenberg')), |
| 1036 |
el(Button, { |
| 1037 |
className: 'bkbg-row__structure-action', |
| 1038 |
icon: 'editor-contract', |
| 1039 |
onClick: equalizeColumns, |
| 1040 |
disabled: columnCount < 2 |
| 1041 |
}, __('Equalize', 'blockenberg')) |
| 1042 |
), |
| 1043 |
// Row actions |
| 1044 |
el('div', { className: 'bkbg-row__structure-row-actions' }, |
| 1045 |
el(Button, { |
| 1046 |
className: 'bkbg-row__structure-action bkbg-row__structure-action--secondary', |
| 1047 |
icon: 'insert-before', |
| 1048 |
onClick: function() { insertRowAt('before'); setIsStructureOpen(false); } |
| 1049 |
}, __('Row Above', 'blockenberg')), |
| 1050 |
el(Button, { |
| 1051 |
className: 'bkbg-row__structure-action bkbg-row__structure-action--secondary', |
| 1052 |
icon: 'insert-after', |
| 1053 |
onClick: function() { insertRowAt('after'); setIsStructureOpen(false); } |
| 1054 |
}, __('Row Below', 'blockenberg')) |
| 1055 |
) |
| 1056 |
) |
| 1057 |
) |
| 1058 |
) |
| 1059 |
); |
| 1060 |
}, |
| 1061 |
|
| 1062 |
save: function (props) { |
| 1063 |
var a = props.attributes; |
| 1064 |
var blockProps = useBlockProps.save({ |
| 1065 |
className: [ |
| 1066 |
'bkbg-row', |
| 1067 |
'bkbg-row--gap-' + a.gap, |
| 1068 |
a.stackOnMobile ? 'bkbg-row--stack-mobile' : '' |
| 1069 |
].filter(Boolean).join(' '), |
| 1070 |
style: getSpacingStyles(a), |
| 1071 |
'data-stack-mobile': a.stackOnMobile ? '1' : '0' |
| 1072 |
}); |
| 1073 |
|
| 1074 |
return el('div', blockProps, |
| 1075 |
el('div', { className: 'bkbg-row__inner' }, |
| 1076 |
el(InnerBlocks.Content) |
| 1077 |
) |
| 1078 |
); |
| 1079 |
} |
| 1080 |
}); |
| 1081 |
}() ); |
| 1082 |
|