| 1 |
( function () { |
| 2 |
var el = wp.element.createElement; |
| 3 |
var Fragment = wp.element.Fragment; |
| 4 |
var useState = wp.element.useState; |
| 5 |
var __ = wp.i18n.__; |
| 6 |
var registerBlockType = wp.blocks.registerBlockType; |
| 7 |
var InspectorControls = wp.blockEditor.InspectorControls; |
| 8 |
var RichText = wp.blockEditor.RichText; |
| 9 |
var useBlockProps = wp.blockEditor.useBlockProps; |
| 10 |
var PanelBody = wp.components.PanelBody; |
| 11 |
var Button = wp.components.Button; |
| 12 |
var ToggleControl = wp.components.ToggleControl; |
| 13 |
var RangeControl = wp.components.RangeControl; |
| 14 |
var SelectControl = wp.components.SelectControl; |
| 15 |
var TextControl = wp.components.TextControl; |
| 16 |
var URLInput = wp.blockEditor.URLInput; |
| 17 |
var ColorPicker = wp.components.ColorPicker; |
| 18 |
var Popover = wp.components.Popover; |
| 19 |
|
| 20 |
/* ── typography lazy-getters ── */ |
| 21 |
function _tc() { return window.bkbgTypographyControl || null; } |
| 22 |
function _tv() { return window.bkbgTypoCssVars || function () { return {}; }; } |
| 23 |
|
| 24 |
var BTN_STYLE_OPTIONS = [ |
| 25 |
{ label: __('Filled', 'blockenberg'), value: 'filled' }, |
| 26 |
{ label: __('Outline', 'blockenberg'), value: 'outline' }, |
| 27 |
{ label: __('Ghost', 'blockenberg'), value: 'ghost' }, |
| 28 |
]; |
| 29 |
var ALIGN_OPTIONS = [ |
| 30 |
{ label: __('Center', 'blockenberg'), value: 'center' }, |
| 31 |
{ label: __('Left', 'blockenberg'), value: 'left' }, |
| 32 |
{ label: __('Right', 'blockenberg'), value: 'right' }, |
| 33 |
]; |
| 34 |
var TAG_OPTIONS = [ |
| 35 |
{ label: 'H1', value: 'h1' }, |
| 36 |
{ label: 'H2', value: 'h2' }, |
| 37 |
{ label: 'H3', value: 'h3' }, |
| 38 |
{ label: 'H4', value: 'h4' }, |
| 39 |
]; |
| 40 |
|
| 41 |
function makeId() { return 'gs' + Math.random().toString(36).substr(2, 5); } |
| 42 |
|
| 43 |
function buildGradientCSS(gradient, angle) { |
| 44 |
if (!gradient || !gradient.length) return 'linear-gradient(' + angle + 'deg, #6c3fb5, #3b82f6)'; |
| 45 |
var stops = gradient.map(function (g) { return g.color; }).join(', '); |
| 46 |
return 'linear-gradient(' + angle + 'deg, ' + stops + ')'; |
| 47 |
} |
| 48 |
|
| 49 |
function buildWrapStyle(a) { |
| 50 |
var grad = buildGradientCSS(a.gradient, a.gradientAngle); |
| 51 |
return Object.assign({ |
| 52 |
'--bkbg-gs-headline-color': a.headlineColor, |
| 53 |
'--bkbg-gs-subtext-color': a.subtextColor, |
| 54 |
'--bkbg-gs-badge-bg': a.badgeBg, |
| 55 |
'--bkbg-gs-badge-color': a.badgeColor, |
| 56 |
'--bkbg-gs-badge-r': a.badgeRadius + 'px', |
| 57 |
'--bkbg-gs-btn-bg': a.btnBg, |
| 58 |
'--bkbg-gs-btn-color': a.btnColor, |
| 59 |
'--bkbg-gs-btn2-color': a.btn2Color, |
| 60 |
'--bkbg-gs-btn2-border': a.btn2BorderColor, |
| 61 |
'--bkbg-gs-btn-r': a.btnRadius + 'px', |
| 62 |
'--bkbg-gs-gradient': grad, |
| 63 |
'--bkbg-gs-angle': a.gradientAngle + 'deg', |
| 64 |
'--bkbg-gs-anim-dur': a.animationDuration + 's', |
| 65 |
'--bkbg-gs-btn-sz': a.btnSize + 'px', |
| 66 |
'--bkbg-gs-min-height': a.minHeight + 'px', |
| 67 |
background: grad, |
| 68 |
paddingTop: a.paddingTop + 'px', |
| 69 |
paddingBottom: a.paddingBottom + 'px', |
| 70 |
}, _tv()(a.typoHeadline, '--bkbg-gs-hl-'), _tv()(a.typoSubtext, '--bkbg-gs-st-'), _tv()(a.typoBadge, '--bkbg-gs-bg-')); |
| 71 |
} |
| 72 |
|
| 73 |
function renderColorControl(key, label, value, onChange, openKey, setOpenKey) { |
| 74 |
var isOpen = openKey === key; |
| 75 |
return el('div', { key: key, style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '4px 0', gap: '8px' } }, |
| 76 |
el('span', { style: { fontSize: '12px', color: '#1e1e1e', flex: 1, lineHeight: 1.4 } }, label), |
| 77 |
el('div', { style: { position: 'relative', flexShrink: 0 } }, |
| 78 |
el('button', { type: 'button', onClick: function () { setOpenKey(isOpen ? null : key); }, style: { width: '28px', height: '28px', borderRadius: '4px', border: isOpen ? '2px solid #007cba' : '2px solid #ddd', cursor: 'pointer', padding: 0, background: value || '#ccc' } }), |
| 79 |
isOpen && el(Popover, { position: 'bottom left', onClose: function () { setOpenKey(null); } }, |
| 80 |
el('div', { style: { padding: '8px' } }, |
| 81 |
el(ColorPicker, { color: value, enableAlpha: true, onChange: onChange }) |
| 82 |
) |
| 83 |
) |
| 84 |
) |
| 85 |
); |
| 86 |
} |
| 87 |
|
| 88 |
registerBlockType('blockenberg/gradient-section', { |
| 89 |
title: __('Gradient Section', 'blockenberg'), |
| 90 |
icon: 'art', |
| 91 |
category: 'bkbg-layout', |
| 92 |
|
| 93 |
edit: function (props) { |
| 94 |
var a = props.attributes; |
| 95 |
var setAttributes = props.setAttributes; |
| 96 |
|
| 97 |
var openColorKeyState = useState(null); |
| 98 |
var openColorKey = openColorKeyState[0]; |
| 99 |
var setOpenColorKey = openColorKeyState[1]; |
| 100 |
|
| 101 |
var blockProps = useBlockProps({ style: buildWrapStyle(a), className: 'bkbg-gs-wrapper' + (a.animate ? ' bkbg-gs--animate' : ''), 'data-animate': a.animate ? 'true' : undefined }); |
| 102 |
|
| 103 |
function cc(key, label, attrKey) { |
| 104 |
return renderColorControl(key, label, a[attrKey], function (val) { |
| 105 |
var upd = {}; upd[attrKey] = val; setAttributes(upd); |
| 106 |
}, openColorKey, setOpenColorKey); |
| 107 |
} |
| 108 |
|
| 109 |
function updateGradientColor(id, color) { |
| 110 |
setAttributes({ gradient: a.gradient.map(function (g) { if (g.id !== id) return g; return Object.assign({}, g, { color: color }); }) }); |
| 111 |
} |
| 112 |
|
| 113 |
var headlineTag = a.headlineTag || 'h2'; |
| 114 |
var overlayStyle = a.overlay ? { position: 'absolute', inset: 0, background: a.overlayColor, pointerEvents: 'none', zIndex: 0 } : null; |
| 115 |
var innerStyle = { position: 'relative', zIndex: 1, maxWidth: a.contentMaxWidth + 'px', margin: '0 auto', minHeight: a.minHeight + 'px', display: 'flex', flexDirection: 'column', alignItems: a.textAlign === 'center' ? 'center' : (a.textAlign === 'right' ? 'flex-end' : 'flex-start'), justifyContent: 'center', textAlign: a.textAlign, padding: '40px 24px', boxSizing: 'border-box' }; |
| 116 |
|
| 117 |
var btnBase = { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '0 28px', height: (a.btnSize + 22) + 'px', borderRadius: a.btnRadius + 'px', fontSize: a.btnSize + 'px', fontWeight: 700, fontFamily: 'inherit', cursor: 'pointer', textDecoration: 'none', boxSizing: 'border-box', transition: 'opacity 0.2s' }; |
| 118 |
var btn1Style = Object.assign({}, btnBase, { background: a.btnBg, color: a.btnColor, border: '2px solid transparent' }); |
| 119 |
var btn2Style = Object.assign({}, btnBase, { background: 'transparent', color: a.btn2Color, border: '2px solid ' + a.btn2BorderColor }); |
| 120 |
|
| 121 |
return el(Fragment, null, |
| 122 |
el(InspectorControls, null, |
| 123 |
el(PanelBody, { title: __('Gradient', 'blockenberg'), initialOpen: true }, |
| 124 |
el('p', { style: { fontSize: '12px', fontWeight: 600, margin: '0 0 6px' } }, __('Gradient colors', 'blockenberg')), |
| 125 |
el('div', { style: { display: 'flex', flexWrap: 'wrap', gap: '8px', marginBottom: '12px' } }, |
| 126 |
a.gradient.map(function (g) { |
| 127 |
var isOpenG = openColorKey === g.id; |
| 128 |
return el('div', { key: g.id, style: { position: 'relative', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '4px' } }, |
| 129 |
el('button', { type: 'button', onClick: function () { setOpenColorKey(isOpenG ? null : g.id); }, style: { width: '36px', height: '36px', borderRadius: '50%', border: isOpenG ? '3px solid #007cba' : '2px solid #ddd', cursor: 'pointer', background: g.color, padding: 0 } }), |
| 130 |
isOpenG && el(Popover, { position: 'bottom left', onClose: function () { setOpenColorKey(null); } }, |
| 131 |
el('div', { style: { padding: '8px' } }, |
| 132 |
el(ColorPicker, { color: g.color, onChange: function (val) { updateGradientColor(g.id, val); } }) |
| 133 |
) |
| 134 |
), |
| 135 |
a.gradient.length > 2 && el('button', { type: 'button', onClick: function () { setAttributes({ gradient: a.gradient.filter(function (x) { return x.id !== g.id; }) }); }, style: { fontSize: '10px', color: '#dc3535', background: 'none', border: 'none', cursor: 'pointer', padding: 0 } }, '✕') |
| 136 |
); |
| 137 |
}), |
| 138 |
a.gradient.length < 5 && el('button', { type: 'button', onClick: function () { setAttributes({ gradient: a.gradient.concat([{ id: makeId(), color: '#ffffff' }]) }); }, style: { width: '36px', height: '36px', borderRadius: '50%', border: '2px dashed #ccc', background: 'none', cursor: 'pointer', fontSize: '20px', color: '#999', display: 'flex', alignItems: 'center', justifyContent: 'center' } }, '+') |
| 139 |
), |
| 140 |
el(RangeControl, { label: __('Gradient angle (°)', 'blockenberg'), value: a.gradientAngle, min: 0, max: 360, onChange: function (v) { setAttributes({ gradientAngle: v }); } }), |
| 141 |
el(ToggleControl, { label: __('Animate gradient', 'blockenberg'), checked: a.animate, onChange: function (v) { setAttributes({ animate: v }); }, __nextHasNoMarginBottom: true }), |
| 142 |
a.animate && el(RangeControl, { label: __('Animation speed (s)', 'blockenberg'), value: a.animationDuration, min: 3, max: 20, onChange: function (v) { setAttributes({ animationDuration: v }); } }), |
| 143 |
el(ToggleControl, { label: __('Overlay', 'blockenberg'), checked: a.overlay, onChange: function (v) { setAttributes({ overlay: v }); }, __nextHasNoMarginBottom: true }), |
| 144 |
a.overlay && cc('overlayColor', __('Overlay color', 'blockenberg'), 'overlayColor') |
| 145 |
), |
| 146 |
el(PanelBody, { title: __('Content', 'blockenberg'), initialOpen: false }, |
| 147 |
el(ToggleControl, { label: __('Show badge', 'blockenberg'), checked: a.showBadge, onChange: function (v) { setAttributes({ showBadge: v }); }, __nextHasNoMarginBottom: true }), |
| 148 |
a.showBadge && el(TextControl, { label: __('Badge text', 'blockenberg'), value: a.badge, onChange: function (v) { setAttributes({ badge: v }); } }), |
| 149 |
el(SelectControl, { label: __('Headline tag', 'blockenberg'), value: a.headlineTag, options: TAG_OPTIONS, onChange: function (v) { setAttributes({ headlineTag: v }); } }), |
| 150 |
el(TextControl, { label: __('Button 1 label', 'blockenberg'), value: a.btnLabel, onChange: function (v) { setAttributes({ btnLabel: v }); } }), |
| 151 |
el(TextControl, { label: __('Button 1 URL', 'blockenberg'), value: a.btnUrl, onChange: function (v) { setAttributes({ btnUrl: v }); } }), |
| 152 |
el(SelectControl, { label: __('Button 1 style', 'blockenberg'), value: a.btnStyle, options: BTN_STYLE_OPTIONS, onChange: function (v) { setAttributes({ btnStyle: v }); } }), |
| 153 |
el(ToggleControl, { label: __('Show second button', 'blockenberg'), checked: a.showSecondBtn, onChange: function (v) { setAttributes({ showSecondBtn: v }); }, __nextHasNoMarginBottom: true }), |
| 154 |
a.showSecondBtn && el(TextControl, { label: __('Button 2 label', 'blockenberg'), value: a.btn2Label, onChange: function (v) { setAttributes({ btn2Label: v }); } }), |
| 155 |
a.showSecondBtn && el(TextControl, { label: __('Button 2 URL', 'blockenberg'), value: a.btn2Url, onChange: function (v) { setAttributes({ btn2Url: v }); } }) |
| 156 |
), |
| 157 |
el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: false }, |
| 158 |
el(SelectControl, { label: __('Text align', 'blockenberg'), value: a.textAlign, options: ALIGN_OPTIONS, onChange: function (v) { setAttributes({ textAlign: v }); } }), |
| 159 |
el(RangeControl, { label: __('Min height (px)', 'blockenberg'), value: a.minHeight, min: 200, max: 900, onChange: function (v) { setAttributes({ minHeight: v }); } }), |
| 160 |
el(RangeControl, { label: __('Content max width', 'blockenberg'), value: a.contentMaxWidth, min: 400, max: 1400, onChange: function (v) { setAttributes({ contentMaxWidth: v }); } }), |
| 161 |
el(RangeControl, { label: __('Button radius (px)','blockenberg'), value: a.btnRadius, min: 0, max: 99, onChange: function (v) { setAttributes({ btnRadius: v }); } }), |
| 162 |
el(RangeControl, { label: __('Badge radius (px)', 'blockenberg'), value: a.badgeRadius, min: 0, max: 99, onChange: function (v) { setAttributes({ badgeRadius: v }); } }) |
| 163 |
), |
| 164 |
el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false }, |
| 165 |
_tc() && el(_tc(), { label:__('Headline','blockenberg'), value:a.typoHeadline, onChange:function(v){ setAttributes({typoHeadline:v}); } }), |
| 166 |
_tc() && el(_tc(), { label:__('Subtext','blockenberg'), value:a.typoSubtext, onChange:function(v){ setAttributes({typoSubtext:v}); } }), |
| 167 |
_tc() && el(_tc(), { label:__('Badge','blockenberg'), value:a.typoBadge, onChange:function(v){ setAttributes({typoBadge:v}); } }), |
| 168 |
el(RangeControl, { label: __('Button (px)', 'blockenberg'), value: a.btnSize, min: 13, max: 22, onChange: function (v) { setAttributes({ btnSize: v }); } }) |
| 169 |
), |
| 170 |
el(PanelBody, { title: __('Colors', 'blockenberg'), initialOpen: false }, |
| 171 |
cc('headlineColor', __('Headline', 'blockenberg'), 'headlineColor'), |
| 172 |
cc('subtextColor', __('Subtext', 'blockenberg'), 'subtextColor'), |
| 173 |
cc('badgeBg', __('Badge background', 'blockenberg'), 'badgeBg'), |
| 174 |
cc('badgeColor', __('Badge text', 'blockenberg'), 'badgeColor'), |
| 175 |
cc('btnBg', __('Button 1 bg', 'blockenberg'), 'btnBg'), |
| 176 |
cc('btnColor', __('Button 1 text', 'blockenberg'), 'btnColor'), |
| 177 |
cc('btn2Color', __('Button 2 text', 'blockenberg'), 'btn2Color'), |
| 178 |
cc('btn2BorderColor', __('Button 2 border', 'blockenberg'), 'btn2BorderColor') |
| 179 |
), |
| 180 |
el(PanelBody, { title: __('Spacing', 'blockenberg'), initialOpen: false }, |
| 181 |
el(RangeControl, { label: __('Padding top (px)', 'blockenberg'), value: a.paddingTop, min: 0, max: 200, onChange: function (v) { setAttributes({ paddingTop: v }); } }), |
| 182 |
el(RangeControl, { label: __('Padding bottom (px)', 'blockenberg'), value: a.paddingBottom, min: 0, max: 200, onChange: function (v) { setAttributes({ paddingBottom: v }); } }) |
| 183 |
) |
| 184 |
), |
| 185 |
|
| 186 |
el('div', blockProps, |
| 187 |
a.overlay && el('div', { className: 'bkbg-gs-overlay', style: overlayStyle }), |
| 188 |
el('div', { className: 'bkbg-gs-inner', style: innerStyle }, |
| 189 |
a.showBadge && el('span', { className: 'bkbg-gs-badge', style: { display: 'inline-flex', alignItems: 'center', padding: '6px 16px', borderRadius: a.badgeRadius + 'px', background: a.badgeBg, color: a.badgeColor, marginBottom: '20px', backdropFilter: 'blur(10px)' } }, a.badge), |
| 190 |
el(RichText, { tagName: headlineTag, className: 'bkbg-gs-headline', value: a.headline, onChange: function (v) { setAttributes({ headline: v }); }, placeholder: __('Headline…', 'blockenberg'), style: { color: a.headlineColor, margin: '0 0 16px', maxWidth: '100%' } }), |
| 191 |
el(RichText, { tagName: 'p', className: 'bkbg-gs-subtext', value: a.subtext, onChange: function (v) { setAttributes({ subtext: v }); }, placeholder: __('Subtext description…', 'blockenberg'), style: { color: a.subtextColor, margin: '0 0 32px', maxWidth: '580px' } }), |
| 192 |
el('div', { className: 'bkbg-gs-actions', style: { display: 'flex', gap: '12px', flexWrap: 'wrap', justifyContent: a.textAlign === 'center' ? 'center' : (a.textAlign === 'right' ? 'flex-end' : 'flex-start') } }, |
| 193 |
a.btnLabel && el('span', { className: 'bkbg-gs-btn bkbg-gs-btn--primary', style: btn1Style }, a.btnLabel), |
| 194 |
a.showSecondBtn && a.btn2Label && el('span', { className: 'bkbg-gs-btn bkbg-gs-btn--secondary', style: btn2Style }, a.btn2Label) |
| 195 |
) |
| 196 |
) |
| 197 |
) |
| 198 |
); |
| 199 |
}, |
| 200 |
|
| 201 |
save: function (props) { |
| 202 |
var a = props.attributes; |
| 203 |
var wrapStyle = buildWrapStyle(a); |
| 204 |
var headlineTag = a.headlineTag || 'h2'; |
| 205 |
return el('div', wp.blockEditor.useBlockProps.save({ className: 'bkbg-gs-wrapper' + (a.animate ? ' bkbg-gs--animate' : ''), style: wrapStyle, 'data-animate': a.animate ? 'true' : undefined }), |
| 206 |
a.overlay && el('div', { className: 'bkbg-gs-overlay', 'aria-hidden': 'true' }), |
| 207 |
el('div', { className: 'bkbg-gs-inner' }, |
| 208 |
a.showBadge && el('span', { className: 'bkbg-gs-badge' }, a.badge), |
| 209 |
el(RichText.Content, { tagName: headlineTag, className: 'bkbg-gs-headline', value: a.headline }), |
| 210 |
el(RichText.Content, { tagName: 'p', className: 'bkbg-gs-subtext', value: a.subtext }), |
| 211 |
el('div', { className: 'bkbg-gs-actions' }, |
| 212 |
a.btnLabel && el('a', { href: a.btnUrl || '#', className: 'bkbg-gs-btn bkbg-gs-btn--primary bkbg-gs-btn--' + a.btnStyle }, a.btnLabel), |
| 213 |
a.showSecondBtn && a.btn2Label && el('a', { href: a.btn2Url || '#', className: 'bkbg-gs-btn bkbg-gs-btn--secondary' }, a.btn2Label) |
| 214 |
) |
| 215 |
) |
| 216 |
); |
| 217 |
} |
| 218 |
}); |
| 219 |
}() ); |
| 220 |
|