| @@ -1,5 +1,5 @@ | ||
| 1 | -wp.domReady(function () { | |
| 1 | +( function () { | |
| 2 | 2 | var el = wp.element.createElement; |
| 3 | 3 | var useEffect = wp.element.useEffect; |
| 4 | 4 | var __ = wp.i18n.__; |
| 5 | 5 | var registerBlockType = wp.blocks.registerBlockType; |
| @@ -13,8 +13,16 @@ | ||
| 13 | 13 | var RangeControl = wp.components.RangeControl; |
| 14 | 14 | var BaseControl = wp.components.BaseControl; |
| 15 | 15 | var Button = wp.components.Button; |
| 16 | 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 | + | |
| 17 | 25 | // Custom Appender that opens sidebar inserter instead of popover |
| 18 | 26 | function SidebarInserterAppender(props) { |
| 19 | 27 | var rootClientId = props.rootClientId; |
| 20 | 28 | |
| @@ -87,8 +95,54 @@ | ||
| 87 | 95 | { label: __('L (32px)', 'blockenberg'), value: 'l' }, |
| 88 | 96 | { label: __('XL (48px)', 'blockenberg'), value: 'xl' } |
| 89 | 97 | ]; |
| 90 | 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 | + | |
| 91 | 145 | // Column icon |
| 92 | 146 | var columnIcon = el('svg', { |
| 93 | 147 | width: 24, |
| 94 | 148 | height: 24, |
| @@ -108,9 +162,9 @@ | ||
| 108 | 162 | |
| 109 | 163 | registerBlockType('blockenberg/column', { |
| 110 | 164 | title: __('Column', 'blockenberg'), |
| 111 | 165 | icon: columnIcon, |
| 112 | - category: 'blockenberg', | |
| 166 | + category: 'bkbg-layout', | |
| 113 | 167 | parent: ['blockenberg/row'], |
| 114 | 168 | description: __('A column container within a row for content.', 'blockenberg'), |
| 115 | 169 | |
| 116 | 170 | edit: function (props) { |
| @@ -119,8 +173,11 @@ | ||
| 119 | 173 | var clientId = props.clientId; |
| 120 | 174 | var a = attributes; |
| 121 | 175 | |
| 122 | 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; | |
| 123 | 180 | |
| 124 | 181 | // Generate UID if not set |
| 125 | 182 | useEffect(function () { |
| 126 | 183 | if (!a.uid) { |
| @@ -158,8 +215,52 @@ | ||
| 158 | 215 | if (newIndex < 0 || newIndex >= parentData.colCount) return; |
| 159 | 216 | moveBlockToPosition(clientId, parentData.rowClientId, parentData.rowClientId, newIndex); |
| 160 | 217 | } |
| 161 | 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 | + | |
| 162 | 263 | // Get width for current mode with fallback |
| 163 | 264 | function getWidth(mode) { |
| 164 | 265 | var widths = a.widths || { desktop: 50, tablet: 50, mobile: 100 }; |
| 165 | 266 | if (mode === 'mobile') { |
| @@ -183,14 +284,9 @@ | ||
| 183 | 284 | newWidths[mode] = value; |
| 184 | 285 | setAttributes({ widths: newWidths }); |
| 185 | 286 | } |
| 186 | 287 | |
| 187 | - var blockProps = useBlockProps({ | |
| 188 | - className: [ | |
| 189 | - 'bkbg-column', | |
| 190 | - 'bkbg-column--padding-' + a.paddingInner | |
| 191 | - ].join(' '), | |
| 192 | - style: { | |
| 288 | + var colStyle = Object.assign({}, { | |
| 193 | 289 | '--bkbg-col-desktop': (a.widths && a.widths.desktop || 50) + '%', |
| 194 | 290 | '--bkbg-col-tablet': (a.widths && a.widths.tablet || a.widths && a.widths.desktop || 50) + '%', |
| 195 | 291 | '--bkbg-col-mobile': (a.widths && a.widths.mobile || 100) + '%', |
| 196 | 292 | flexBasis: displayWidth + '%', |
| @@ -195,12 +291,79 @@ | ||
| 195 | 291 | '--bkbg-col-mobile': (a.widths && a.widths.mobile || 100) + '%', |
| 196 | 292 | flexBasis: displayWidth + '%', |
| 197 | 293 | maxWidth: displayWidth + '%', |
| 198 | 294 | width: displayWidth + '%' |
| 199 | - }, | |
| 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, | |
| 200 | 304 | 'data-uid': a.uid |
| 201 | 305 | }); |
| 202 | 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 | + | |
| 203 | 366 | // Inspector controls |
| 204 | 367 | var inspector = el(InspectorControls, {}, |
| 205 | 368 | el(PanelBody, { title: __('Column Settings', 'blockenberg'), initialOpen: true }, |
| 206 | 369 | el(BaseControl, { |
| @@ -240,13 +403,13 @@ | ||
| 240 | 403 | help: stackOnMobile ? __('Mobile width is overridden when "Stack on Mobile" is enabled in the row settings.', 'blockenberg') : '' |
| 241 | 404 | }) |
| 242 | 405 | ), |
| 243 | 406 | el(SelectControl, { |
| 244 | - label: __('Inner Padding', 'blockenberg'), | |
| 245 | - value: a.paddingInner, | |
| 246 | - options: paddingOptions, | |
| 247 | - onChange: function (v) { setAttributes({ paddingInner: v }); } | |
| 248 | - }) | |
| 407 | + label: __('Vertical Alignment', 'blockenberg'), | |
| 408 | + value: a.vAlign || 'top', | |
| 409 | + options: vAlignOptions, | |
| 410 | + onChange: function (v) { setAttributes({ vAlign: v }); } | |
| 411 | + }), | |
| 249 | 412 | ), |
| 250 | 413 | el(PanelBody, { title: __('Column Info', 'blockenberg'), initialOpen: false }, |
| 251 | 414 | el('div', { className: 'bkbg-column-info' }, |
| 252 | 415 | el('p', {}, |
| @@ -261,8 +424,14 @@ | ||
| 261 | 424 | el('strong', {}, __('UID:', 'blockenberg')), |
| 262 | 425 | ' ' + (a.uid || 'N/A') |
| 263 | 426 | ) |
| 264 | 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 | + })() | |
| 265 | 434 | ) |
| 266 | 435 | ); |
| 267 | 436 | |
| 268 | 437 | return el('div', blockProps, |
| @@ -277,8 +446,14 @@ | ||
| 277 | 446 | disabled: parentData.colIndex <= 0 |
| 278 | 447 | }), |
| 279 | 448 | el(Button, { |
| 280 | 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', | |
| 281 | 456 | icon: 'arrow-right-alt2', |
| 282 | 457 | label: __('Move column right', 'blockenberg'), |
| 283 | 458 | onClick: function () { moveSelf('right'); }, |
| 284 | 459 | disabled: parentData.colIndex >= parentData.colCount - 1 |
| @@ -301,18 +476,21 @@ | ||
| 301 | 476 | save: function (props) { |
| 302 | 477 | var a = props.attributes; |
| 303 | 478 | var widths = a.widths || { desktop: 50, tablet: 50, mobile: 100 }; |
| 304 | 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')); | |
| 305 | 486 | var blockProps = useBlockProps.save({ |
| 306 | 487 | className: [ |
| 307 | 488 | 'bkbg-column', |
| 308 | - 'bkbg-column--padding-' + a.paddingInner | |
| 489 | + 'bkbg-column--padding-' + a.paddingInner, | |
| 490 | + 'bkbg-column--valign-' + (a.vAlign || 'top') | |
| 309 | 491 | ].join(' '), |
| 310 | - style: { | |
| 311 | - '--bkbg-col-desktop': widths.desktop + '%', | |
| 312 | - '--bkbg-col-tablet': (widths.tablet || widths.desktop) + '%', | |
| 313 | - '--bkbg-col-mobile': (widths.mobile || 100) + '%' | |
| 314 | - }, | |
| 492 | + style: saveStyle, | |
| 315 | 493 | 'data-uid': a.uid |
| 316 | 494 | }); |
| 317 | 495 | |
| 318 | 496 | return el('div', blockProps, |
| @@ -321,5 +499,5 @@ | ||
| 321 | 499 | ) |
| 322 | 500 | ); |
| 323 | 501 | } |
| 324 | 502 | }); |
| 325 | -}); | |
| 503 | +}() ); | |