| 1 |
( function () { |
| 2 |
var el = wp.element.createElement; |
| 3 |
var Fragment = wp.element.Fragment; |
| 4 |
var registerBlockType = wp.blocks.registerBlockType; |
| 5 |
var __ = wp.i18n.__; |
| 6 |
var InspectorControls = wp.blockEditor.InspectorControls; |
| 7 |
var PanelColorSettings = wp.blockEditor.PanelColorSettings; |
| 8 |
var InnerBlocks = wp.blockEditor.InnerBlocks; |
| 9 |
var useBlockProps = wp.blockEditor.useBlockProps; |
| 10 |
var useInnerBlocksProps = wp.blockEditor.useInnerBlocksProps; |
| 11 |
var PanelBody = wp.components.PanelBody; |
| 12 |
var ToggleControl = wp.components.ToggleControl; |
| 13 |
var RangeControl = wp.components.RangeControl; |
| 14 |
|
| 15 |
var TEMPLATE = [ |
| 16 |
['core/heading', { level: 2, placeholder: __('Section heading…', 'blockenberg'), textAlign: 'center' }], |
| 17 |
['core/paragraph', { placeholder: __('Add your content here…', 'blockenberg'), align: 'center' }], |
| 18 |
['core/buttons', { layout: { type: 'flex', justifyContent: 'center' } }, [ |
| 19 |
['core/button', { text: __('Get started', 'blockenberg') }], |
| 20 |
]], |
| 21 |
]; |
| 22 |
|
| 23 |
// CSS for animated blob (editor preview) |
| 24 |
var BLOB_ANIM = [ |
| 25 |
{ top: '10%', left: '15%', animDelay: '0s' }, |
| 26 |
{ top: '60%', left: '65%', animDelay: '-4s' }, |
| 27 |
{ top: '35%', left: '50%', animDelay: '-7s' }, |
| 28 |
{ top: '75%', left: '10%', animDelay: '-2s' }, |
| 29 |
{ top: '20%', left: '75%', animDelay: '-9s' }, |
| 30 |
]; |
| 31 |
|
| 32 |
function buildWrapStyle(a) { |
| 33 |
return { |
| 34 |
position: 'relative', |
| 35 |
overflow: 'hidden', |
| 36 |
minHeight: a.minHeight + 'px', |
| 37 |
paddingTop: a.paddingTop + 'px', |
| 38 |
paddingBottom: a.paddingBottom + 'px', |
| 39 |
background: a.bgColor, |
| 40 |
color: a.textColor || undefined, |
| 41 |
}; |
| 42 |
} |
| 43 |
|
| 44 |
function buildInnerStyle(a) { |
| 45 |
return { |
| 46 |
position: 'relative', |
| 47 |
zIndex: 1, |
| 48 |
maxWidth: a.contentMaxWidth + 'px', |
| 49 |
margin: a.centerContent ? '0 auto' : undefined, |
| 50 |
}; |
| 51 |
} |
| 52 |
|
| 53 |
function buildBlobStyle(a, idx) { |
| 54 |
var info = BLOB_ANIM[idx % BLOB_ANIM.length]; |
| 55 |
var colors = [a.color1, a.color2, a.color3]; |
| 56 |
var color = colors[idx % colors.length]; |
| 57 |
var size = a.blobSize; |
| 58 |
return { |
| 59 |
position: 'absolute', |
| 60 |
top: info.top, |
| 61 |
left: info.left, |
| 62 |
width: size + '%', |
| 63 |
height: size + '%', |
| 64 |
background: color, |
| 65 |
borderRadius: '60% 40% 30% 70% / 60% 30% 70% 40%', |
| 66 |
opacity: (a.blobOpacity / 100), |
| 67 |
filter: 'blur(' + a.blobBlur + 'px)', |
| 68 |
willChange: 'transform, border-radius', |
| 69 |
animationName: 'bkgb-morph', |
| 70 |
animationDuration: a.animSpeed + 's', |
| 71 |
animationDelay: info.animDelay, |
| 72 |
animationTimingFunction: 'ease-in-out', |
| 73 |
animationIterationCount: 'infinite', |
| 74 |
animationPlayState: a.paused ? 'paused' : 'running', |
| 75 |
transformOrigin: 'center', |
| 76 |
pointerEvents: 'none', |
| 77 |
}; |
| 78 |
} |
| 79 |
|
| 80 |
registerBlockType('blockenberg/gradient-blob', { |
| 81 |
title: __('Gradient Blob', 'blockenberg'), |
| 82 |
description: __('Section with animated CSS blobs as background.', 'blockenberg'), |
| 83 |
category: 'bkbg-effects', |
| 84 |
icon: 'art', |
| 85 |
|
| 86 |
edit: function (props) { |
| 87 |
var attributes = props.attributes; |
| 88 |
var setAttributes = props.setAttributes; |
| 89 |
var numBlobs = Math.min(attributes.numBlobs || 3, 5); |
| 90 |
|
| 91 |
var wrapStyle = buildWrapStyle(attributes); |
| 92 |
var innerStyle = buildInnerStyle(attributes); |
| 93 |
|
| 94 |
var blockProps = useBlockProps({ style: wrapStyle, className: 'bkgb-section' }); |
| 95 |
var innerBlocksProps = useInnerBlocksProps({ style: innerStyle, className: 'bkgb-inner' }, { |
| 96 |
template: TEMPLATE, |
| 97 |
templateLock: false, |
| 98 |
}); |
| 99 |
|
| 100 |
return el(Fragment, null, |
| 101 |
el(InspectorControls, null, |
| 102 |
/* ── Blobs ──────────────────────────────────────────────── */ |
| 103 |
el(PanelBody, { title: __('Blobs', 'blockenberg'), initialOpen: true }, |
| 104 |
el(RangeControl, { |
| 105 |
label: __('Number of blobs', 'blockenberg'), |
| 106 |
value: attributes.numBlobs, |
| 107 |
min: 1, max: 5, |
| 108 |
onChange: function (v) { setAttributes({ numBlobs: v }); }, |
| 109 |
}), |
| 110 |
el(RangeControl, { |
| 111 |
label: __('Blob size (%)', 'blockenberg'), |
| 112 |
value: attributes.blobSize, |
| 113 |
min: 10, max: 120, |
| 114 |
onChange: function (v) { setAttributes({ blobSize: v }); }, |
| 115 |
}), |
| 116 |
el(RangeControl, { |
| 117 |
label: __('Opacity (%)', 'blockenberg'), |
| 118 |
value: attributes.blobOpacity, |
| 119 |
min: 5, max: 100, |
| 120 |
onChange: function (v) { setAttributes({ blobOpacity: v }); }, |
| 121 |
}), |
| 122 |
el(RangeControl, { |
| 123 |
label: __('Blur (px)', 'blockenberg'), |
| 124 |
value: attributes.blobBlur, |
| 125 |
min: 0, max: 200, |
| 126 |
onChange: function (v) { setAttributes({ blobBlur: v }); }, |
| 127 |
}), |
| 128 |
el(RangeControl, { |
| 129 |
label: __('Animation speed (s)', 'blockenberg'), |
| 130 |
value: attributes.animSpeed, |
| 131 |
min: 3, max: 40, |
| 132 |
onChange: function (v) { setAttributes({ animSpeed: v }); }, |
| 133 |
}), |
| 134 |
el(ToggleControl, { |
| 135 |
label: __('Pause animation', 'blockenberg'), |
| 136 |
checked: attributes.paused, |
| 137 |
onChange: function (v) { setAttributes({ paused: v }); }, |
| 138 |
__nextHasNoMarginBottom: true, |
| 139 |
}) |
| 140 |
), |
| 141 |
/* ── Layout ─────────────────────────────────────────────── */ |
| 142 |
el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: false }, |
| 143 |
el(RangeControl, { |
| 144 |
label: __('Min height (px)', 'blockenberg'), |
| 145 |
value: attributes.minHeight, |
| 146 |
min: 100, max: 1200, |
| 147 |
onChange: function (v) { setAttributes({ minHeight: v }); }, |
| 148 |
}), |
| 149 |
el(RangeControl, { |
| 150 |
label: __('Padding top (px)', 'blockenberg'), |
| 151 |
value: attributes.paddingTop, |
| 152 |
min: 0, max: 200, |
| 153 |
onChange: function (v) { setAttributes({ paddingTop: v }); }, |
| 154 |
}), |
| 155 |
el(RangeControl, { |
| 156 |
label: __('Padding bottom (px)', 'blockenberg'), |
| 157 |
value: attributes.paddingBottom, |
| 158 |
min: 0, max: 200, |
| 159 |
onChange: function (v) { setAttributes({ paddingBottom: v }); }, |
| 160 |
}), |
| 161 |
el(RangeControl, { |
| 162 |
label: __('Content max width (px)', 'blockenberg'), |
| 163 |
value: attributes.contentMaxWidth, |
| 164 |
min: 200, max: 1400, |
| 165 |
onChange: function (v) { setAttributes({ contentMaxWidth: v }); }, |
| 166 |
}), |
| 167 |
el(ToggleControl, { |
| 168 |
label: __('Center content', 'blockenberg'), |
| 169 |
checked: attributes.centerContent, |
| 170 |
onChange: function (v) { setAttributes({ centerContent: v }); }, |
| 171 |
__nextHasNoMarginBottom: true, |
| 172 |
}) |
| 173 |
), |
| 174 |
/* ── Colors ─────────────────────────────────────────────── */ |
| 175 |
el(PanelColorSettings, { |
| 176 |
title: __('Colors', 'blockenberg'), |
| 177 |
initialOpen: false, |
| 178 |
colorSettings: [ |
| 179 |
{ |
| 180 |
value: attributes.color1, |
| 181 |
onChange: function (v) { setAttributes({ color1: v || '#c084fc' }); }, |
| 182 |
label: __('Blob color 1', 'blockenberg'), |
| 183 |
}, |
| 184 |
{ |
| 185 |
value: attributes.color2, |
| 186 |
onChange: function (v) { setAttributes({ color2: v || '#818cf8' }); }, |
| 187 |
label: __('Blob color 2', 'blockenberg'), |
| 188 |
}, |
| 189 |
{ |
| 190 |
value: attributes.color3, |
| 191 |
onChange: function (v) { setAttributes({ color3: v || '#f0abfc' }); }, |
| 192 |
label: __('Blob color 3', 'blockenberg'), |
| 193 |
}, |
| 194 |
{ |
| 195 |
value: attributes.bgColor, |
| 196 |
onChange: function (v) { setAttributes({ bgColor: v || '#faf5ff' }); }, |
| 197 |
label: __('Background color', 'blockenberg'), |
| 198 |
}, |
| 199 |
{ |
| 200 |
value: attributes.textColor, |
| 201 |
onChange: function (v) { setAttributes({ textColor: v || '' }); }, |
| 202 |
label: __('Text color', 'blockenberg'), |
| 203 |
}, |
| 204 |
], |
| 205 |
}) |
| 206 |
), |
| 207 |
el('section', blockProps, |
| 208 |
/* Blob layers */ |
| 209 |
Array.from({ length: numBlobs }, function (_, i) { |
| 210 |
return el('div', { |
| 211 |
key: i, |
| 212 |
'aria-hidden': 'true', |
| 213 |
className: 'bkgb-blob bkgb-blob-' + (i + 1), |
| 214 |
style: buildBlobStyle(attributes, i), |
| 215 |
}); |
| 216 |
}), |
| 217 |
/* InnerBlocks content */ |
| 218 |
el('div', innerBlocksProps) |
| 219 |
) |
| 220 |
); |
| 221 |
}, |
| 222 |
|
| 223 |
save: function (props) { |
| 224 |
var a = props.attributes; |
| 225 |
var numBlobs = Math.min(a.numBlobs || 3, 5); |
| 226 |
var colors = [a.color1, a.color2, a.color3]; |
| 227 |
var blobPositions = [ |
| 228 |
{ top: '10%', left: '15%' }, |
| 229 |
{ top: '60%', left: '65%' }, |
| 230 |
{ top: '35%', left: '50%' }, |
| 231 |
{ top: '75%', left: '10%' }, |
| 232 |
{ top: '20%', left: '75%' }, |
| 233 |
]; |
| 234 |
var blobDelays = ['0s', '-4s', '-7s', '-2s', '-9s']; |
| 235 |
|
| 236 |
return el('section', { |
| 237 |
className: 'bkgb-section', |
| 238 |
'data-speed': a.animSpeed, |
| 239 |
'data-paused': a.paused ? 'true' : 'false', |
| 240 |
style: { |
| 241 |
position: 'relative', |
| 242 |
overflow: 'hidden', |
| 243 |
minHeight: a.minHeight + 'px', |
| 244 |
paddingTop: a.paddingTop + 'px', |
| 245 |
paddingBottom: a.paddingBottom + 'px', |
| 246 |
background: a.bgColor, |
| 247 |
color: a.textColor || undefined, |
| 248 |
'--bkgb-speed': a.animSpeed + 's', |
| 249 |
'--bkgb-blur': a.blobBlur + 'px', |
| 250 |
'--bkgb-opacity': a.blobOpacity / 100, |
| 251 |
'--bkgb-size': a.blobSize + '%', |
| 252 |
}, |
| 253 |
}, |
| 254 |
Array.from({ length: numBlobs }, function (_, i) { |
| 255 |
var pos = blobPositions[i % blobPositions.length]; |
| 256 |
return el('div', { |
| 257 |
key: i, |
| 258 |
'aria-hidden': 'true', |
| 259 |
className: 'bkgb-blob bkgb-blob-' + (i + 1), |
| 260 |
style: { |
| 261 |
'--bkgb-c': colors[i % colors.length], |
| 262 |
'--bkgb-delay': blobDelays[i % blobDelays.length], |
| 263 |
top: pos.top, |
| 264 |
left: pos.left, |
| 265 |
}, |
| 266 |
}); |
| 267 |
}), |
| 268 |
el('div', { |
| 269 |
className: 'bkgb-inner', |
| 270 |
style: { |
| 271 |
position: 'relative', |
| 272 |
zIndex: 1, |
| 273 |
maxWidth: a.contentMaxWidth + 'px', |
| 274 |
margin: a.centerContent ? '0 auto' : undefined, |
| 275 |
}, |
| 276 |
}, el(InnerBlocks.Content)) |
| 277 |
); |
| 278 |
}, |
| 279 |
}); |
| 280 |
}() ); |
| 281 |
|