| 1 |
/* ==================================================== |
| 2 |
Client Showcase Block — editor (index.js) |
| 3 |
Block: blockenberg/client-showcase |
| 4 |
==================================================== */ |
| 5 |
( function () { |
| 6 |
var el = wp.element.createElement; |
| 7 |
var Fragment = wp.element.Fragment; |
| 8 |
var useState = wp.element.useState; |
| 9 |
var RichText = wp.blockEditor.RichText; |
| 10 |
var MediaUpload = wp.blockEditor.MediaUpload; |
| 11 |
var MediaUploadCheck = wp.blockEditor.MediaUploadCheck; |
| 12 |
var registerBlockType = wp.blocks.registerBlockType; |
| 13 |
var InspectorControls = wp.blockEditor.InspectorControls; |
| 14 |
var useBlockProps = wp.blockEditor.useBlockProps; |
| 15 |
var PanelBody = wp.components.PanelBody; |
| 16 |
var PanelColorSettings = wp.blockEditor.PanelColorSettings; |
| 17 |
var RangeControl = wp.components.RangeControl; |
| 18 |
var SelectControl = wp.components.SelectControl; |
| 19 |
var ToggleControl = wp.components.ToggleControl; |
| 20 |
var TextControl = wp.components.TextControl; |
| 21 |
var Button = wp.components.Button; |
| 22 |
var __ = wp.i18n.__; |
| 23 |
|
| 24 |
function getTypographyControl() { return (window.bkbgTypographyControl || function () { return null; }); } |
| 25 |
function _tv() { var fn = window.bkbgTypoCssVars; return fn ? fn.apply(null, arguments) : {}; } |
| 26 |
|
| 27 |
function uid() { return 'l' + Math.random().toString(36).slice(2,7); } |
| 28 |
function move(arr, from, to) { var a=arr.slice(); a.splice(to,0,a.splice(from,1)[0]); return a; } |
| 29 |
|
| 30 |
function buildWrapStyle(a) { |
| 31 |
var s = { |
| 32 |
'--bkbg-cshw-cols': a.columns, |
| 33 |
'--bkbg-cshw-gap': a.gap + 'px', |
| 34 |
'--bkbg-cshw-h-gap': a.headerGap + 'px', |
| 35 |
'--bkbg-cshw-q-gap': a.quoteGap + 'px', |
| 36 |
'--bkbg-cshw-logo-h': a.logoHeight + 'px', |
| 37 |
'--bkbg-cshw-logo-r': a.logoRadius + 'px', |
| 38 |
'--bkbg-cshw-logo-pad': a.logoPadding + 'px', |
| 39 |
'--bkbg-cshw-pt': a.paddingTop + 'px', |
| 40 |
'--bkbg-cshw-pb': a.paddingBottom + 'px', |
| 41 |
'--bkbg-cshw-h-color': a.headlineColor, |
| 42 |
'--bkbg-cshw-sub-color': a.subColor, |
| 43 |
'--bkbg-cshw-logo-bg': a.logoBgColor, |
| 44 |
'--bkbg-cshw-logo-brd': a.logoBorderColor, |
| 45 |
'--bkbg-cshw-logo-tint': a.logoTintColor, |
| 46 |
'--bkbg-cshw-q-bg': a.quoteBg, |
| 47 |
'--bkbg-cshw-q-brd': a.quoteBorder, |
| 48 |
'--bkbg-cshw-q-text': a.quoteTextColor, |
| 49 |
'--bkbg-cshw-q-meta': a.quoteMetaColor, |
| 50 |
'--bkbg-cshw-accent': a.accentColor, |
| 51 |
background: a.sectionBg || undefined, |
| 52 |
}; |
| 53 |
Object.assign(s, _tv(a.typoHeadline, '--bkbg-cshw-hl')); |
| 54 |
Object.assign(s, _tv(a.typoSubhead, '--bkbg-cshw-sh')); |
| 55 |
Object.assign(s, _tv(a.typoQuote, '--bkbg-cshw-qt')); |
| 56 |
return s; |
| 57 |
} |
| 58 |
|
| 59 |
/* ── Logo item (editor) ── */ |
| 60 |
function LogoItem(props) { |
| 61 |
var logo = props.logo; |
| 62 |
var a = props.a; |
| 63 |
var onChange = props.onChange; |
| 64 |
|
| 65 |
var imgEl = logo.imageUrl |
| 66 |
? el('img', { src: logo.imageUrl, alt: logo.name, className: 'bkbg-cshw-logo-img' }) |
| 67 |
: el('span', { className: 'bkbg-cshw-logo-placeholder' }, logo.name || 'Logo'); |
| 68 |
|
| 69 |
var uploadBtn = el(MediaUploadCheck, null, |
| 70 |
el(MediaUpload, { |
| 71 |
onSelect: function(m){ onChange({ imageUrl: m.url, imageId: m.id }); }, |
| 72 |
allowedTypes: ['image'], |
| 73 |
value: logo.imageId, |
| 74 |
render: function(o){ return el(Button, { onClick: o.open, className: 'bkbg-cshw-logo-upload-btn', isSmall: true }, logo.imageUrl ? __('Replace','blockenberg') : __('Upload','blockenberg')); } |
| 75 |
}) |
| 76 |
); |
| 77 |
|
| 78 |
return el('div', { className: 'bkbg-cshw-logo-item' + (a.logoBg ? ' bkbg-cshw-logo-bg' : '') + (a.logoGrayscale ? ' bkbg-cshw-grayscale' : ''), title: logo.name }, |
| 79 |
imgEl, |
| 80 |
uploadBtn |
| 81 |
); |
| 82 |
} |
| 83 |
|
| 84 |
/* ── REGISTER ── */ |
| 85 |
registerBlockType('blockenberg/client-showcase', { |
| 86 |
edit: function (props) { |
| 87 |
var a = props.attributes; |
| 88 |
var setAttr = props.setAttributes; |
| 89 |
var logos = a.logos; |
| 90 |
|
| 91 |
var _act = useState(null); |
| 92 |
var activeId = _act[0]; var setActiveId = _act[1]; |
| 93 |
|
| 94 |
function updateLogo(id, patch) { |
| 95 |
setAttr({ logos: logos.map(function(l){ return l.id===id ? Object.assign({},l,patch) : l; }) }); |
| 96 |
} |
| 97 |
|
| 98 |
var blockProps = useBlockProps({ |
| 99 |
className: 'bkbg-cshw-wrap bkbg-cshw-layout--' + a.layout, |
| 100 |
style: buildWrapStyle(a) |
| 101 |
}); |
| 102 |
|
| 103 |
var inspector = el(InspectorControls, null, |
| 104 |
/* Logos */ |
| 105 |
el(PanelBody, { title:__('Logos','blockenberg'), initialOpen:true }, |
| 106 |
logos.map(function(logo, idx) { |
| 107 |
var isActive = activeId === logo.id; |
| 108 |
return el('div', { key: logo.id }, |
| 109 |
el('div', { |
| 110 |
style: { display:'flex', alignItems:'center', gap:'6px', background: isActive ? '#f3f0ff' : '#f8f9fa', border: isActive ? '1px solid #6c3fb5' : '1px solid #e2e8f0', borderRadius:'6px', padding:'6px 8px', marginBottom:'4px', cursor:'pointer' }, |
| 111 |
onClick: function(){ setActiveId(isActive ? null : logo.id); } |
| 112 |
}, |
| 113 |
logo.imageUrl ? el('img', { src:logo.imageUrl, style:{ height:'20px', objectFit:'contain', marginRight:'4px' } }) : null, |
| 114 |
el('span', { style:{ flex:1, fontSize:'13px' } }, logo.name || '(untitled)'), |
| 115 |
el(Button, { icon:'arrow-up', isSmall:true, disabled:idx===0, onClick:function(e){ e.stopPropagation(); setAttr({logos:move(logos,idx,idx-1)}); } }), |
| 116 |
el(Button, { icon:'arrow-down', isSmall:true, disabled:idx===logos.length-1, onClick:function(e){ e.stopPropagation(); setAttr({logos:move(logos,idx,idx+1)}); } }), |
| 117 |
el(Button, { icon:'no-alt', isSmall:true, isDestructive:true, onClick:function(e){ e.stopPropagation(); setAttr({logos:logos.filter(function(_,i){ return i!==idx; })}); if(activeId===logo.id) setActiveId(null); } }) |
| 118 |
), |
| 119 |
isActive ? el('div', { style:{ padding:'8px', background:'#faf8ff', border:'1px solid #6c3fb5', borderTop:'none', borderRadius:'0 0 6px 6px', marginBottom:'4px' }}, |
| 120 |
el(TextControl, { label:__('Company name','blockenberg'), value:logo.name, onChange:function(v){ updateLogo(logo.id,{name:v}); } }), |
| 121 |
el(MediaUploadCheck, null, |
| 122 |
el(MediaUpload, { |
| 123 |
onSelect: function(m){ updateLogo(logo.id,{imageUrl:m.url,imageId:m.id}); }, |
| 124 |
allowedTypes:['image'], value:logo.imageId, |
| 125 |
render:function(o){ return el(Button, { onClick:o.open, variant:'secondary', isSmall:true }, logo.imageUrl ? __('Change logo','blockenberg') : __('Upload logo','blockenberg')); } |
| 126 |
}) |
| 127 |
), |
| 128 |
logo.imageUrl ? el(Button, { isDestructive:true, variant:'tertiary', isSmall:true, style:{marginTop:'4px'}, onClick:function(){ updateLogo(logo.id,{imageUrl:'',imageId:0}); } }, __('Remove image','blockenberg')) : null |
| 129 |
) : null |
| 130 |
); |
| 131 |
}), |
| 132 |
el(Button, { variant:'primary', style:{ marginTop:'8px', width:'100%' }, |
| 133 |
onClick:function(){ var n={id:uid(),name:'New Client',imageUrl:'',imageId:0}; setAttr({logos:logos.concat([n])}); setActiveId(n.id); } |
| 134 |
}, __('+ Add Logo','blockenberg')) |
| 135 |
), |
| 136 |
|
| 137 |
/* Header / layout */ |
| 138 |
el(PanelBody, { title:__('Header & Layout','blockenberg'), initialOpen:false }, |
| 139 |
el(ToggleControl, { label:__('Show section header','blockenberg'), checked:a.showHeader, onChange:function(v){ setAttr({showHeader:v}); } }), |
| 140 |
el(ToggleControl, { label:__('Show pull-quote','blockenberg'), checked:a.showQuote, onChange:function(v){ setAttr({showQuote:v}); } }), |
| 141 |
el(SelectControl, { label:__('Layout','blockenberg'), value:a.layout, options:[ |
| 142 |
{label:__('Row (centered)','blockenberg'), value:'row'}, |
| 143 |
{label:__('Grid (fixed columns)','blockenberg'), value:'grid'}, |
| 144 |
], onChange:function(v){ setAttr({layout:v}); } }), |
| 145 |
el(SelectControl, { label:__('Header alignment','blockenberg'), value:a.headerAlign, options:[ |
| 146 |
{label:'Left',value:'left'},{label:'Center',value:'center'},{label:'Right',value:'right'} |
| 147 |
], onChange:function(v){ setAttr({headerAlign:v}); } }), |
| 148 |
el(RangeControl, { label:__('Columns','blockenberg'), value:a.columns, min:2, max:8, onChange:function(v){ setAttr({columns:v}); } }), |
| 149 |
el(RangeControl, { label:__('Logo gap (px)','blockenberg'), value:a.gap, min:8, max:80, onChange:function(v){ setAttr({gap:v}); } }), |
| 150 |
el(RangeControl, { label:__('Logo height (px)','blockenberg'), value:a.logoHeight, min:20, max:100, onChange:function(v){ setAttr({logoHeight:v}); } }), |
| 151 |
el(RangeControl, { label:__('Logo padding (px)','blockenberg'), value:a.logoPadding, min:0, max:40, onChange:function(v){ setAttr({logoPadding:v}); } }), |
| 152 |
el(RangeControl, { label:__('Logo border radius (px)','blockenberg'), value:a.logoRadius, min:0, max:20, onChange:function(v){ setAttr({logoRadius:v}); } }), |
| 153 |
el(ToggleControl, { label:__('Logo background box','blockenberg'), checked:a.logoBg, onChange:function(v){ setAttr({logoBg:v}); } }), |
| 154 |
el(ToggleControl, { label:__('Grayscale logos','blockenberg'), checked:a.logoGrayscale, onChange:function(v){ setAttr({logoGrayscale:v}); } }), |
| 155 |
el(ToggleControl, { label:__('Color on hover','blockenberg'), checked:a.logoHoverColor, onChange:function(v){ setAttr({logoHoverColor:v}); } }) |
| 156 |
), |
| 157 |
|
| 158 |
/* Quote */ |
| 159 |
a.showQuote ? el(PanelBody, { title:__('Pull Quote','blockenberg'), initialOpen:false }, |
| 160 |
el(TextControl, { label:__('Quote text','blockenberg'), value:a.quoteText, onChange:function(v){ setAttr({quoteText:v}); } }), |
| 161 |
el(TextControl, { label:__('Author name','blockenberg'), value:a.quoteAuthor, onChange:function(v){ setAttr({quoteAuthor:v}); } }), |
| 162 |
el(TextControl, { label:__('Author role','blockenberg'), value:a.quoteRole, onChange:function(v){ setAttr({quoteRole:v}); } }), |
| 163 |
el(MediaUploadCheck, null, |
| 164 |
el(MediaUpload, { |
| 165 |
onSelect:function(m){ setAttr({quoteAvatarUrl:m.url,quoteAvatarId:m.id}); }, |
| 166 |
allowedTypes:['image'], value:a.quoteAvatarId, |
| 167 |
render:function(o){ return el(Button, { onClick:o.open, variant:'secondary', isSmall:true }, a.quoteAvatarUrl ? __('Change avatar','blockenberg') : __('Upload avatar','blockenberg')); } |
| 168 |
}) |
| 169 |
) |
| 170 |
) : null, |
| 171 |
|
| 172 |
/* Spacing */ |
| 173 |
el(PanelBody, { title:__('Spacing','blockenberg'), initialOpen:false }, |
| 174 |
el(RangeControl, { label:__('Padding top (px)','blockenberg'), value:a.paddingTop, min:0, max:160, onChange:function(v){ setAttr({paddingTop:v}); } }), |
| 175 |
el(RangeControl, { label:__('Padding bottom (px)','blockenberg'), value:a.paddingBottom, min:0, max:160, onChange:function(v){ setAttr({paddingBottom:v}); } }), |
| 176 |
el(RangeControl, { label:__('Header gap (px)','blockenberg'), value:a.headerGap, min:0, max:80, onChange:function(v){ setAttr({headerGap:v}); } }), |
| 177 |
el(RangeControl, { label:__('Quote gap (px)','blockenberg'), value:a.quoteGap, min:0, max:80, onChange:function(v){ setAttr({quoteGap:v}); } }) |
| 178 |
), |
| 179 |
|
| 180 |
/* Typography */ |
| 181 |
el(PanelBody, { title:__('Typography','blockenberg'), initialOpen:false }, |
| 182 |
el(getTypographyControl(), { label:__('Headline','blockenberg'), value:a.typoHeadline, onChange:function(v){ setAttr({typoHeadline:v}); } }), |
| 183 |
el(getTypographyControl(), { label:__('Subheadline','blockenberg'), value:a.typoSubhead, onChange:function(v){ setAttr({typoSubhead:v}); } }), |
| 184 |
el(getTypographyControl(), { label:__('Quote Text','blockenberg'), value:a.typoQuote, onChange:function(v){ setAttr({typoQuote:v}); } }) |
| 185 |
), |
| 186 |
|
| 187 |
/* Colors */ |
| 188 |
el(PanelColorSettings, { title:__('Colors','blockenberg'), initialOpen:false, colorSettings:[ |
| 189 |
{label:__('Section background','blockenberg'), value:a.sectionBg, onChange:function(v){ setAttr({sectionBg:v||''}); }}, |
| 190 |
{label:__('Headline color','blockenberg'), value:a.headlineColor, onChange:function(v){ setAttr({headlineColor:v||''}); }}, |
| 191 |
{label:__('Subhead color','blockenberg'), value:a.subColor, onChange:function(v){ setAttr({subColor:v||''}); }}, |
| 192 |
{label:__('Logo box bg','blockenberg'), value:a.logoBgColor, onChange:function(v){ setAttr({logoBgColor:v||''}); }}, |
| 193 |
{label:__('Logo box border','blockenberg'), value:a.logoBorderColor,onChange:function(v){ setAttr({logoBorderColor:v||''}); }}, |
| 194 |
{label:__('Logo tint (grayscale)','blockenberg'), value:a.logoTintColor, onChange:function(v){ setAttr({logoTintColor:v||''}); }}, |
| 195 |
{label:__('Quote background','blockenberg'), value:a.quoteBg, onChange:function(v){ setAttr({quoteBg:v||''}); }}, |
| 196 |
{label:__('Quote border','blockenberg'), value:a.quoteBorder, onChange:function(v){ setAttr({quoteBorder:v||''}); }}, |
| 197 |
{label:__('Quote text','blockenberg'), value:a.quoteTextColor, onChange:function(v){ setAttr({quoteTextColor:v||''}); }}, |
| 198 |
{label:__('Quote meta','blockenberg'), value:a.quoteMetaColor, onChange:function(v){ setAttr({quoteMetaColor:v||''}); }}, |
| 199 |
{label:__('Accent (quote mark)','blockenberg'),value:a.accentColor, onChange:function(v){ setAttr({accentColor:v||''}); }}, |
| 200 |
] }) |
| 201 |
); |
| 202 |
|
| 203 |
/* Header */ |
| 204 |
var headerEl = a.showHeader ? el('div', { className:'bkbg-cshw-header', style:{ textAlign:a.headerAlign, marginBottom: a.headerGap + 'px' } }, |
| 205 |
el(RichText, { tagName:'p', className:'bkbg-cshw-headline', value:a.headline, onChange:function(v){ setAttr({headline:v}); }, placeholder:__('Trusted by…','blockenberg') }), |
| 206 |
el(RichText, { tagName:'p', className:'bkbg-cshw-subhead', value:a.subheadline, onChange:function(v){ setAttr({subheadline:v}); }, placeholder:__('Subtitle…','blockenberg') }) |
| 207 |
) : null; |
| 208 |
|
| 209 |
/* Logo grid */ |
| 210 |
var logoGrid = el('div', { className:'bkbg-cshw-logos' + (a.logoHoverColor && a.logoGrayscale ? ' bkbg-cshw-hover-color' : '') }, |
| 211 |
logos.map(function(logo) { |
| 212 |
return el(LogoItem, { key:logo.id, logo:logo, a:a, onChange:function(patch){ updateLogo(logo.id,patch); } }); |
| 213 |
}) |
| 214 |
); |
| 215 |
|
| 216 |
/* Quote */ |
| 217 |
var quoteEl = a.showQuote ? el('figure', { className:'bkbg-cshw-quote', style:{ marginTop: a.quoteGap + 'px' } }, |
| 218 |
el('blockquote', { className:'bkbg-cshw-quote-text' }, el('p', null, a.quoteText)), |
| 219 |
el('figcaption', { className:'bkbg-cshw-quote-author' }, |
| 220 |
a.quoteAvatarUrl ? el('img', { src:a.quoteAvatarUrl, alt:a.quoteAuthor, className:'bkbg-cshw-quote-avatar' }) : null, |
| 221 |
el('span', { className:'bkbg-cshw-quote-meta' }, |
| 222 |
el('strong', { className:'bkbg-cshw-quote-name' }, a.quoteAuthor), |
| 223 |
el('span', { className:'bkbg-cshw-quote-role' }, a.quoteRole) |
| 224 |
) |
| 225 |
) |
| 226 |
) : null; |
| 227 |
|
| 228 |
return el(Fragment, null, |
| 229 |
inspector, |
| 230 |
el('div', blockProps, headerEl, logoGrid, quoteEl) |
| 231 |
); |
| 232 |
}, |
| 233 |
|
| 234 |
save: function (props) { |
| 235 |
var a = props.attributes; |
| 236 |
var blockProps = wp.blockEditor.useBlockProps.save({ |
| 237 |
className: 'bkbg-cshw-wrap bkbg-cshw-layout--' + a.layout, |
| 238 |
style: buildWrapStyle(a) |
| 239 |
}); |
| 240 |
|
| 241 |
var headerEl = a.showHeader ? el('div', { className:'bkbg-cshw-header', style:{ textAlign:a.headerAlign, marginBottom:a.headerGap+'px' } }, |
| 242 |
el(RichText.Content, { tagName:'p', className:'bkbg-cshw-headline', value:a.headline }), |
| 243 |
el(RichText.Content, { tagName:'p', className:'bkbg-cshw-subhead', value:a.subheadline }) |
| 244 |
) : null; |
| 245 |
|
| 246 |
var logoGrid = el('div', { className:'bkbg-cshw-logos' + (a.logoHoverColor && a.logoGrayscale ? ' bkbg-cshw-hover-color' : '') }, |
| 247 |
a.logos.map(function(logo) { |
| 248 |
return el('div', { key:logo.id, className:'bkbg-cshw-logo-item' + (a.logoBg ? ' bkbg-cshw-logo-bg' : '') + (a.logoGrayscale ? ' bkbg-cshw-grayscale' : ''), title:logo.name }, |
| 249 |
logo.imageUrl ? el('img', { src:logo.imageUrl, alt:logo.name, className:'bkbg-cshw-logo-img', loading:'lazy' }) : el('span', { className:'bkbg-cshw-logo-placeholder' }, logo.name) |
| 250 |
); |
| 251 |
}) |
| 252 |
); |
| 253 |
|
| 254 |
var quoteEl = a.showQuote ? el('figure', { className:'bkbg-cshw-quote', style:{ marginTop:a.quoteGap+'px' } }, |
| 255 |
el('blockquote', { className:'bkbg-cshw-quote-text' }, el('p', null, a.quoteText)), |
| 256 |
el('figcaption', { className:'bkbg-cshw-quote-author' }, |
| 257 |
a.quoteAvatarUrl ? el('img', { src:a.quoteAvatarUrl, alt:a.quoteAuthor, className:'bkbg-cshw-quote-avatar', loading:'lazy' }) : null, |
| 258 |
el('span', { className:'bkbg-cshw-quote-meta' }, |
| 259 |
el('strong', { className:'bkbg-cshw-quote-name' }, a.quoteAuthor), |
| 260 |
el('span', { className:'bkbg-cshw-quote-role' }, a.quoteRole) |
| 261 |
) |
| 262 |
) |
| 263 |
) : null; |
| 264 |
|
| 265 |
return el('div', blockProps, headerEl, logoGrid, quoteEl); |
| 266 |
} |
| 267 |
}); |
| 268 |
}() ); |
| 269 |
|