| 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 useBlockProps = wp.blockEditor.useBlockProps; |
| 9 |
var PanelBody = wp.components.PanelBody; |
| 10 |
var Button = wp.components.Button; |
| 11 |
var ToggleControl = wp.components.ToggleControl; |
| 12 |
var RangeControl = wp.components.RangeControl; |
| 13 |
var SelectControl = wp.components.SelectControl; |
| 14 |
var TextControl = wp.components.TextControl; |
| 15 |
var ColorPicker = wp.components.ColorPicker; |
| 16 |
var Popover = wp.components.Popover; |
| 17 |
|
| 18 |
/* ── Typography helpers (lazy) ───────────────────────────────── */ |
| 19 |
var _tc, _tvf; |
| 20 |
Object.defineProperty(window, '__bkwm_tc', { get: function () { return _tc || (_tc = window.bkbgTypographyControl); } }); |
| 21 |
Object.defineProperty(window, '__bkwm_tvf', { get: function () { return _tvf || (_tvf = window.bkbgTypoCssVars); } }); |
| 22 |
function getTypoControl(label, typoObj, setAttributes, attrName) { |
| 23 |
var fn = window.__bkwm_tc; |
| 24 |
return fn ? fn({ label: label, value: typoObj || {}, onChange: function (v) { var o = {}; o[attrName] = v; setAttributes(o); } }) : null; |
| 25 |
} |
| 26 |
function getTypoCssVarsObj(a) { |
| 27 |
var fn = window.__bkwm_tvf; |
| 28 |
var s = {}; |
| 29 |
if (fn) { |
| 30 |
Object.assign(s, fn(a.titleTypo || {}, '--bkwm-tt-')); |
| 31 |
Object.assign(s, fn(a.subtitleTypo || {}, '--bkwm-st-')); |
| 32 |
} |
| 33 |
return s; |
| 34 |
} |
| 35 |
|
| 36 |
function makeId() { return 'wm' + Math.random().toString(36).substr(2,8); } |
| 37 |
|
| 38 |
/* ── Helpers ───────────────────────────────────────────────────── */ |
| 39 |
function buildWrapStyle(a) { |
| 40 |
var s = { |
| 41 |
'--bkbg-wm-accent': a.accentColor, |
| 42 |
'--bkbg-wm-title': a.titleColor, |
| 43 |
'--bkbg-wm-subtitle': a.subtitleColor, |
| 44 |
'--bkbg-wm-map-bg': a.mapBg, |
| 45 |
'--bkbg-wm-list-bg': a.listBg, |
| 46 |
'--bkbg-wm-list-border': a.listBorder, |
| 47 |
paddingTop: a.paddingTop + 'px', |
| 48 |
paddingBottom: a.paddingBottom + 'px', |
| 49 |
backgroundColor: a.bgColor || undefined, |
| 50 |
}; |
| 51 |
Object.assign(s, getTypoCssVarsObj(a)); |
| 52 |
return s; |
| 53 |
} |
| 54 |
|
| 55 |
function renderColorControl(key, label, value, onChange, openKey, setOpenKey) { |
| 56 |
var isOpen = openKey === key; |
| 57 |
return el('div', { key: key, style: { display:'flex', alignItems:'center', justifyContent:'space-between', padding:'4px 0', gap:'8px' } }, |
| 58 |
el('span', { style: { fontSize:'12px', color:'#1e1e1e', flex:1, lineHeight:1.4 } }, label), |
| 59 |
el('div', { style: { position:'relative', flexShrink:0 } }, |
| 60 |
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' } }), |
| 61 |
isOpen && el(Popover, { position:'bottom left', onClose: function () { setOpenKey(null); } }, |
| 62 |
el('div', { style: { padding:'8px' } }, |
| 63 |
el(ColorPicker, { color: value, enableAlpha: true, onChange: onChange }) |
| 64 |
) |
| 65 |
) |
| 66 |
) |
| 67 |
); |
| 68 |
} |
| 69 |
|
| 70 |
/* ── Country editor row ─────────────────────────────────────────── */ |
| 71 |
function CountryRow(props) { |
| 72 |
var item = props.item; |
| 73 |
var onChange = props.onChange; |
| 74 |
var onRemove = props.onRemove; |
| 75 |
var openKey = props.openKey; |
| 76 |
var setOpenKey = props.setOpenKey; |
| 77 |
|
| 78 |
function upd(key, val) { |
| 79 |
var copy = Object.assign({}, item); |
| 80 |
copy[key] = val; |
| 81 |
onChange(copy); |
| 82 |
} |
| 83 |
|
| 84 |
return el('div', { style: { background:'#f9f9f9', borderRadius:'8px', padding:'10px', marginBottom:'10px', border:'1px solid #e9e9e9' } }, |
| 85 |
el('div', { style: { display:'flex', gap:'8px', marginBottom:'8px' } }, |
| 86 |
el(TextControl, { label:__('Country name','blockenberg'), value: item.name, onChange: function(v){upd('name',v);}, style:{ flex:1 } }), |
| 87 |
el(TextControl, { label:__('Code (ISO)','blockenberg'), value: item.code, onChange: function(v){upd('code',v.toUpperCase().substr(0,2));}, style:{ width:'60px', flex:'none' } }) |
| 88 |
), |
| 89 |
el('div', { style: { display:'flex', gap:'8px', marginBottom:'8px', alignItems:'flex-end' } }, |
| 90 |
el(TextControl, { label:__('Value / Count','blockenberg'), value: String(item.value), onChange: function(v){upd('value', parseFloat(v)||0);}, type:'number', style:{ flex:1 } }), |
| 91 |
el(TextControl, { label:__('Link URL','blockenberg'), value: item.url || '', onChange: function(v){upd('url',v);}, style:{ flex:2 } }) |
| 92 |
), |
| 93 |
el('div', { style:{ display:'flex', alignItems:'center', justifyContent:'space-between', marginTop:'4px' } }, |
| 94 |
renderColorControl('item-'+item.id, __('Custom colour','blockenberg'), item.color, function(v){upd('color',v);}, openKey, setOpenKey), |
| 95 |
el(Button, { variant:'tertiary', size:'compact', isDestructive:true, onClick:onRemove, style:{marginLeft:'8px'} }, '✕') |
| 96 |
) |
| 97 |
); |
| 98 |
} |
| 99 |
|
| 100 |
/* ── Preview card ───────────────────────────────────────────────── */ |
| 101 |
function MapPreview(props) { |
| 102 |
var a = props.a; |
| 103 |
var countries = a.countries || []; |
| 104 |
|
| 105 |
return el('div', { style: { display:'flex', flexDirection:'column', gap:'24px' } }, |
| 106 |
/* Header */ |
| 107 |
el('div', { className: 'bkbg-wm-header', style:{ textAlign:'center' } }, |
| 108 |
a.showTitle && el('div', { className: 'bkbg-wm-title', style:{ color: a.titleColor||'#1e1b4b', marginBottom:'8px' } }, a.title || __('Global Presence','blockenberg')), |
| 109 |
a.showSubtitle && el('p', { className: 'bkbg-wm-subtitle', style:{ color: a.subtitleColor||'#6b7280', marginTop:0 } }, a.subtitle || __('Where our customers are','blockenberg')) |
| 110 |
), |
| 111 |
|
| 112 |
/* Map placeholder */ |
| 113 |
el('div', { style:{ |
| 114 |
background: a.mapBg || '#f0f4f8', |
| 115 |
borderRadius: a.cardRadius+'px', |
| 116 |
minHeight: '220px', |
| 117 |
display:'flex', alignItems:'center', justifyContent:'center', |
| 118 |
flexDirection:'column', gap:'8px', |
| 119 |
border: '2px dashed #cbd5e1' |
| 120 |
} }, |
| 121 |
el('span', { style:{ fontSize:'48px' } }, '🗺️'), |
| 122 |
el('p', { style:{ margin:0, color:'#94a3b8', fontWeight:600, fontSize:'14px' } }, __('Interactive map renders on the frontend','blockenberg')), |
| 123 |
el('p', { style:{ margin:0, color:'#94a3b8', fontSize:'12px' } }, countries.length + ' ' + __('countries configured','blockenberg')) |
| 124 |
), |
| 125 |
|
| 126 |
/* Country tags grid */ |
| 127 |
countries.length > 0 && el('div', { style:{ display:'flex', flexWrap:'wrap', gap:'8px' } }, |
| 128 |
countries.map(function(c) { |
| 129 |
return el('div', { key:c.id, style:{ |
| 130 |
display:'inline-flex', alignItems:'center', gap:'6px', |
| 131 |
padding:'4px 10px', borderRadius:'999px', |
| 132 |
background: c.color || (a.accentColor || '#ede9fe'), |
| 133 |
color: '#fff', fontSize:'13px', fontWeight:600, |
| 134 |
} }, |
| 135 |
el('span', null, c.code || '??'), |
| 136 |
el('span', { style:{ fontWeight:400 } }, c.name), |
| 137 |
el('span', { style:{ background:'rgba(255,255,255,0.25)', borderRadius:'999px', padding:'1px 6px', fontSize:'11px', marginLeft:'2px' } }, c.value) |
| 138 |
); |
| 139 |
}) |
| 140 |
) |
| 141 |
); |
| 142 |
} |
| 143 |
|
| 144 |
/* ── Register ──────────────────────────────────────────────────── */ |
| 145 |
registerBlockType('blockenberg/world-map', { |
| 146 |
title: __('World Map','blockenberg'), |
| 147 |
icon: 'location-alt', |
| 148 |
category: 'bkbg-charts', |
| 149 |
|
| 150 |
edit: function (props) { |
| 151 |
var a = props.attributes; |
| 152 |
var setAttributes = props.setAttributes; |
| 153 |
|
| 154 |
var openColorState = useState(null); |
| 155 |
var openColorKey = openColorState[0]; |
| 156 |
var setOpenColorKey = openColorState[1]; |
| 157 |
|
| 158 |
var blockProps = useBlockProps({ style: buildWrapStyle(a) }); |
| 159 |
|
| 160 |
function cc(key, label, value, attrKey) { |
| 161 |
return renderColorControl(key, label, value, function(v){ var u={}; u[attrKey]=v; setAttributes(u); }, openColorKey, setOpenColorKey); |
| 162 |
} |
| 163 |
|
| 164 |
function updateCountry(id, updated) { |
| 165 |
setAttributes({ countries: a.countries.map(function(c){ return c.id===id ? updated : c; }) }); |
| 166 |
} |
| 167 |
|
| 168 |
function removeCountry(id) { |
| 169 |
setAttributes({ countries: a.countries.filter(function(c){ return c.id!==id; }) }); |
| 170 |
} |
| 171 |
|
| 172 |
function addCountry() { |
| 173 |
setAttributes({ countries: a.countries.concat([{ id:makeId(), code:'XX', name:__('New Country','blockenberg'), value:100, color:'', url:'' }]) }); |
| 174 |
} |
| 175 |
|
| 176 |
return el(Fragment, null, |
| 177 |
el(InspectorControls, null, |
| 178 |
el(PanelBody, { title:__('Countries','blockenberg'), initialOpen:true }, |
| 179 |
(a.countries||[]).map(function(item) { |
| 180 |
return el(CountryRow, { key:item.id, item:item, onChange:function(upd){ updateCountry(item.id, upd); }, onRemove:function(){ removeCountry(item.id); }, openKey:openColorKey, setOpenKey:setOpenColorKey }); |
| 181 |
}), |
| 182 |
el(Button, { variant:'primary', onClick:addCountry }, '+ ' + __('Add Country','blockenberg')) |
| 183 |
), |
| 184 |
el(PanelBody, { title:__('Display','blockenberg'), initialOpen:false }, |
| 185 |
el(ToggleControl, { label:__('Show title','blockenberg'), checked:a.showTitle, onChange:function(v){setAttributes({showTitle:v});}, __nextHasNoMarginBottom:true }), |
| 186 |
a.showTitle && el(TextControl, { label:__('Title','blockenberg'), value:a.title, onChange:function(v){setAttributes({title:v});} }), |
| 187 |
el(ToggleControl, { label:__('Show subtitle','blockenberg'), checked:a.showSubtitle, onChange:function(v){setAttributes({showSubtitle:v});}, __nextHasNoMarginBottom:true }), |
| 188 |
a.showSubtitle && el(TextControl, { label:__('Subtitle','blockenberg'), value:a.subtitle, onChange:function(v){setAttributes({subtitle:v});} }), |
| 189 |
el(ToggleControl, { label:__('Show legend','blockenberg'), checked:a.showLegend, onChange:function(v){setAttributes({showLegend:v});}, __nextHasNoMarginBottom:true }), |
| 190 |
a.showLegend && el(TextControl, { label:__('Legend title','blockenberg'), value:a.legendTitle, onChange:function(v){setAttributes({legendTitle:v});} }), |
| 191 |
el(ToggleControl, { label:__('Show tooltips on hover','blockenberg'), checked:a.showTooltips, onChange:function(v){setAttributes({showTooltips:v});}, __nextHasNoMarginBottom:true }), |
| 192 |
el(ToggleControl, { label:__('Show country list below map','blockenberg'), checked:a.showCountryList, onChange:function(v){setAttributes({showCountryList:v});}, __nextHasNoMarginBottom:true }), |
| 193 |
el(RangeControl, { label:__('Map height (px)','blockenberg'), value:a.mapHeight, min:200, max:800, onChange:function(v){setAttributes({mapHeight:v});} }) |
| 194 |
), |
| 195 |
el(PanelBody, { title:__('Map Style','blockenberg'), initialOpen:false }, |
| 196 |
el(SelectControl, { label:__('Color mode','blockenberg'), value:a.colorMode, options:[{ label:__('Gradient by value','blockenberg'), value:'gradient' },{ label:__('Uniform fill','blockenberg'), value:'uniform' },{ label:__('Per-country custom color','blockenberg'), value:'custom' }], onChange:function(v){setAttributes({colorMode:v});} }), |
| 197 |
a.colorMode==='gradient' && el(Fragment, null, |
| 198 |
cc('lowColor', __('Low value colour','blockenberg'), a.lowColor, 'lowColor'), |
| 199 |
cc('highColor', __('High value colour','blockenberg'), a.highColor, 'highColor') |
| 200 |
), |
| 201 |
cc('defaultFill', __('Default country fill','blockenberg'), a.defaultFill, 'defaultFill'), |
| 202 |
cc('hoverFill', __('Hover fill','blockenberg'), a.hoverFill, 'hoverFill'), |
| 203 |
cc('borderColor', __('Border colour','blockenberg'), a.borderColor, 'borderColor'), |
| 204 |
cc('mapBg', __('Map background','blockenberg'), a.mapBg, 'mapBg'), |
| 205 |
el(SelectControl, { label:__('Projection','blockenberg'), value:a.projection, options:[{ label:'Natural Earth', value:'NaturalEarth1' },{ label:'Mercator', value:'Mercator' },{ label:'Equirectangular', value:'Equirectangular' },{ label:'Robinson', value:'Robinson' }], onChange:function(v){setAttributes({projection:v});} }) |
| 206 |
), |
| 207 |
el(PanelBody, { title:__('Tooltip Style','blockenberg'), initialOpen:false }, |
| 208 |
cc('tooltipBg', __('Tooltip background','blockenberg'), a.tooltipBg, 'tooltipBg'), |
| 209 |
cc('tooltipColor', __('Tooltip text','blockenberg'), a.tooltipColor, 'tooltipColor') |
| 210 |
), |
| 211 |
el(PanelBody, { title:__('Country List Style','blockenberg'), initialOpen:false }, |
| 212 |
cc('listBg', __('List background','blockenberg'), a.listBg, 'listBg'), |
| 213 |
cc('listBorder', __('List border','blockenberg'), a.listBorder, 'listBorder') |
| 214 |
), |
| 215 |
el(PanelBody, { title:__('Colours','blockenberg'), initialOpen:false }, |
| 216 |
cc('accentColor', __('Accent','blockenberg'), a.accentColor, 'accentColor'), |
| 217 |
cc('titleColor', __('Title','blockenberg'), a.titleColor, 'titleColor'), |
| 218 |
cc('subtitleColor', __('Subtitle','blockenberg'), a.subtitleColor, 'subtitleColor'), |
| 219 |
cc('bgColor', __('Section background','blockenberg'), a.bgColor, 'bgColor') |
| 220 |
), |
| 221 |
el(PanelBody, { title:__('Appearance','blockenberg'), initialOpen:false }, |
| 222 |
el(RangeControl, { label:__('Card radius (px)','blockenberg'), value:a.cardRadius, min:0, max:40, onChange:function(v){setAttributes({cardRadius:v});} }) |
| 223 |
), |
| 224 |
el(PanelBody, { title:__('Spacing','blockenberg'), initialOpen:false }, |
| 225 |
el(RangeControl, { label:__('Padding top (px)','blockenberg'), value:a.paddingTop, min:0, max:200, onChange:function(v){setAttributes({paddingTop:v});} }), |
| 226 |
el(RangeControl, { label:__('Padding bottom (px)','blockenberg'), value:a.paddingBottom, min:0, max:200, onChange:function(v){setAttributes({paddingBottom:v});} }) |
| 227 |
), |
| 228 |
el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false }, |
| 229 |
getTypoControl('Title', a.titleTypo, setAttributes, 'titleTypo'), |
| 230 |
getTypoControl('Subtitle', a.subtitleTypo, setAttributes, 'subtitleTypo') |
| 231 |
) |
| 232 |
), |
| 233 |
|
| 234 |
el('div', blockProps, |
| 235 |
el(MapPreview, { a: a }) |
| 236 |
) |
| 237 |
); |
| 238 |
}, |
| 239 |
|
| 240 |
save: function (props) { |
| 241 |
var a = props.attributes; |
| 242 |
return el('div', wp.blockEditor.useBlockProps.save({ |
| 243 |
className: 'bkbg-wm-wrapper', |
| 244 |
style: buildWrapStyle(a), |
| 245 |
}), |
| 246 |
(a.showTitle || a.showSubtitle) && el('div', { className:'bkbg-wm-header', style:{ textAlign:'center', marginBottom:'32px' } }, |
| 247 |
a.showTitle && el('h2', { className:'bkbg-wm-title', style:{ margin:'0 0 8px' } }, a.title), |
| 248 |
a.showSubtitle && el('p', { className:'bkbg-wm-subtitle', style:{ margin:0 } }, a.subtitle) |
| 249 |
), |
| 250 |
el('div', { |
| 251 |
className: 'bkbg-wm-map-holder', |
| 252 |
'data-countries': JSON.stringify(a.countries || []), |
| 253 |
'data-opts': JSON.stringify({ |
| 254 |
colorMode: a.colorMode, |
| 255 |
lowColor: a.lowColor, |
| 256 |
highColor: a.highColor, |
| 257 |
defaultFill: a.defaultFill, |
| 258 |
hoverFill: a.hoverFill, |
| 259 |
borderColor: a.borderColor, |
| 260 |
projection: a.projection, |
| 261 |
showLegend: a.showLegend, |
| 262 |
legendTitle: a.legendTitle, |
| 263 |
showTooltips: a.showTooltips, |
| 264 |
tooltipBg: a.tooltipBg, |
| 265 |
tooltipColor: a.tooltipColor, |
| 266 |
mapHeight: a.mapHeight, |
| 267 |
mapBg: a.mapBg, |
| 268 |
cardRadius: a.cardRadius, |
| 269 |
}), |
| 270 |
style: { minHeight: a.mapHeight+'px', borderRadius: a.cardRadius+'px', background: a.mapBg } |
| 271 |
}), |
| 272 |
a.showCountryList && el('div', { className:'bkbg-wm-country-list', style:{ marginTop:'32px' } }, |
| 273 |
el('div', { style:{ display:'flex', flexWrap:'wrap', gap:'8px' } }, |
| 274 |
(a.countries||[]).map(function(c) { |
| 275 |
return el('span', { key:c.id, className:'bkbg-wm-country-tag' }, |
| 276 |
el('strong', null, c.code), ' ', c.name, ' — ', c.value |
| 277 |
); |
| 278 |
}) |
| 279 |
) |
| 280 |
) |
| 281 |
); |
| 282 |
} |
| 283 |
}); |
| 284 |
}() ); |
| 285 |
|