| 1 |
(function () { |
| 2 |
var el = window.wp.element.createElement; |
| 3 |
var __ = function (s) { return s; }; |
| 4 |
var registerBlockType = window.wp.blocks.registerBlockType; |
| 5 |
var InspectorControls = window.wp.blockEditor.InspectorControls; |
| 6 |
var PanelBody = window.wp.components.PanelBody; |
| 7 |
var PanelRow = window.wp.components.PanelRow; |
| 8 |
var ToggleControl = window.wp.components.ToggleControl; |
| 9 |
var SelectControl = window.wp.components.SelectControl; |
| 10 |
var RangeControl = window.wp.components.RangeControl; |
| 11 |
var TextControl = window.wp.components.TextControl; |
| 12 |
var Button = window.wp.components.Button; |
| 13 |
var PanelColorSettings = window.wp.blockEditor.PanelColorSettings; |
| 14 |
|
| 15 |
function getTypographyControl() { |
| 16 |
return (typeof window.bkbgTypographyControl !== 'undefined') ? window.bkbgTypographyControl : null; |
| 17 |
} |
| 18 |
function getTypoCssVars() { |
| 19 |
return (typeof window.bkbgTypoCssVars !== 'undefined') ? window.bkbgTypoCssVars : function () { return {}; }; |
| 20 |
} |
| 21 |
function _tv(typoObj, prefix) { return getTypoCssVars()(typoObj || {}, prefix); } |
| 22 |
|
| 23 |
var TYPE_ICONS = { |
| 24 |
address: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5A2.5 2.5 0 019.5 9 2.5 2.5 0 0112 6.5 2.5 2.5 0 0114.5 9 2.5 2.5 0 0112 11.5z"/></svg>', |
| 25 |
phone: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M6.62 10.79a15.05 15.05 0 006.59 6.59l2.2-2.2a1 1 0 011.01-.24 11.4 11.4 0 003.57.57 1 1 0 011 1V20a1 1 0 01-1 1A17 17 0 013 4a1 1 0 011-1h3.5a1 1 0 011 1 11.4 11.4 0 00.57 3.57 1 1 0 01-.25 1.01l-2.2 2.21z"/></svg>', |
| 26 |
email: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg>', |
| 27 |
website: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54A1.99 1.99 0 0016 14h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V5.07c3.95.49 7 3.85 7 7.93 0 2.08-.8 3.97-2.1 5.39z"/></svg>', |
| 28 |
hours: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67V7z"/></svg>', |
| 29 |
fax: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M17 2H7c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 18H7V4h10v16zM9 7h6v2H9zm0 4h6v2H9zm0 4h4v2H9z"/></svg>', |
| 30 |
custom: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/></svg>', |
| 31 |
}; |
| 32 |
|
| 33 |
function generateId() { |
| 34 |
return 'cti-' + Math.random().toString(36).slice(2, 9); |
| 35 |
} |
| 36 |
|
| 37 |
function updateItem(items, id, key, value, setA) { |
| 38 |
var newItems = items.map(function (it) { |
| 39 |
if (it.id !== id) return it; |
| 40 |
var clone = Object.assign({}, it); |
| 41 |
clone[key] = value; |
| 42 |
return clone; |
| 43 |
}); |
| 44 |
setA({ items: newItems }); |
| 45 |
} |
| 46 |
|
| 47 |
function renderIcon(type, iconStyle, iconBg, iconColor, iconSize, boxSize, radius) { |
| 48 |
var svgStr = TYPE_ICONS[type] || TYPE_ICONS.custom; |
| 49 |
var noBox = iconStyle === 'none'; |
| 50 |
if (noBox) { |
| 51 |
return el('span', { |
| 52 |
className: 'bkbg-cti-icon bkbg-cti-icon--plain', |
| 53 |
style: { color: iconColor, width: iconSize + 'px', height: iconSize + 'px', display: 'flex', flexShrink: 0 }, |
| 54 |
dangerouslySetInnerHTML: { __html: svgStr } |
| 55 |
}); |
| 56 |
} |
| 57 |
return el('span', { |
| 58 |
className: 'bkbg-cti-icon bkbg-cti-icon--box', |
| 59 |
style: { |
| 60 |
background: iconBg, |
| 61 |
color: iconColor, |
| 62 |
width: boxSize + 'px', |
| 63 |
height: boxSize + 'px', |
| 64 |
borderRadius: radius + 'px', |
| 65 |
display: 'flex', |
| 66 |
alignItems: 'center', |
| 67 |
justifyContent:'center', |
| 68 |
flexShrink: 0, |
| 69 |
padding: ((boxSize - iconSize) / 2) + 'px', |
| 70 |
}, |
| 71 |
dangerouslySetInnerHTML: { __html: svgStr } |
| 72 |
}); |
| 73 |
} |
| 74 |
|
| 75 |
registerBlockType('blockenberg/contact-info', { |
| 76 |
title: __('Contact Info'), |
| 77 |
description: __('Display contact details with icons and optional schema markup.'), |
| 78 |
category: 'bkbg-business', |
| 79 |
icon: el('svg', { viewBox: '0 0 24 24', fill: 'currentColor', width: 24, height: 24 }, |
| 80 |
el('path', { d: 'M6.62 10.79a15.05 15.05 0 006.59 6.59l2.2-2.2a1 1 0 011.01-.24 11.4 11.4 0 003.57.57 1 1 0 011 1V20a1 1 0 01-1 1A17 17 0 013 4a1 1 0 011-1h3.5a1 1 0 011 1 11.4 11.4 0 00.57 3.57 1 1 0 01-.25 1.01l-2.2 2.21z' }) |
| 81 |
), |
| 82 |
|
| 83 |
edit: function (props) { |
| 84 |
var a = props.attributes; |
| 85 |
var setA = props.setAttributes; |
| 86 |
|
| 87 |
var inspector = el(InspectorControls, null, |
| 88 |
el(PanelBody, { title: __('Layout'), initialOpen: true }, |
| 89 |
el(SelectControl, { |
| 90 |
label: __('Layout'), value: a.layout, |
| 91 |
options: [ |
| 92 |
{ label: 'Stacked (vertical)', value: 'stacked' }, |
| 93 |
{ label: 'Inline (horizontal)', value: 'inline' }, |
| 94 |
{ label: 'Grid', value: 'grid' }, |
| 95 |
], |
| 96 |
onChange: function (v) { setA({ layout: v }); }, |
| 97 |
}), |
| 98 |
a.layout === 'grid' && el(RangeControl, { |
| 99 |
label: __('Columns'), value: a.columns, min: 2, max: 4, |
| 100 |
onChange: function (v) { setA({ columns: v }); }, |
| 101 |
}), |
| 102 |
el(RangeControl, { |
| 103 |
label: __('Item Spacing (px)'), value: a.itemSpacing, min: 8, max: 60, |
| 104 |
onChange: function (v) { setA({ itemSpacing: v }); }, |
| 105 |
}), |
| 106 |
el(ToggleControl, { |
| 107 |
label: __('Show Labels'), checked: a.showLabel, |
| 108 |
onChange: function (v) { setA({ showLabel: v }); }, |
| 109 |
__nextHasNoMarginBottom: true, |
| 110 |
}), |
| 111 |
el(ToggleControl, { |
| 112 |
label: __('Inject Schema.org markup'), checked: a.schemaEnabled, |
| 113 |
onChange: function (v) { setA({ schemaEnabled: v }); }, |
| 114 |
__nextHasNoMarginBottom: true, |
| 115 |
}) |
| 116 |
), |
| 117 |
el(PanelBody, { title: __('Icon Style'), initialOpen: false }, |
| 118 |
el(SelectControl, { |
| 119 |
label: __('Icon Style'), value: a.iconStyle, |
| 120 |
options: [ |
| 121 |
{ label: 'Filled box', value: 'filled' }, |
| 122 |
{ label: 'Outline box', value: 'outline' }, |
| 123 |
{ label: 'No box', value: 'none' }, |
| 124 |
], |
| 125 |
onChange: function (v) { setA({ iconStyle: v }); }, |
| 126 |
}), |
| 127 |
el(RangeControl, { |
| 128 |
label: __('Icon Size'), value: a.iconSize, min: 12, max: 36, |
| 129 |
onChange: function (v) { setA({ iconSize: v }); }, |
| 130 |
}), |
| 131 |
el(RangeControl, { |
| 132 |
label: __('Box Size'), value: a.iconBoxSize, min: 24, max: 80, |
| 133 |
onChange: function (v) { setA({ iconBoxSize: v }); }, |
| 134 |
}), |
| 135 |
el(RangeControl, { |
| 136 |
label: __('Box Radius'), value: a.iconRadius, min: 0, max: 40, |
| 137 |
onChange: function (v) { setA({ iconRadius: v }); }, |
| 138 |
}), |
| 139 |
el(PanelColorSettings, { |
| 140 |
title: __('Icon Colors'), initialOpen: false, |
| 141 |
colorSettings: [ |
| 142 |
{ label: __('Icon Color'), value: a.iconColor, onChange: function (v) { setA({ iconColor: v || '#2563eb' }); } }, |
| 143 |
{ label: __('Box BG'), value: a.iconBg, onChange: function (v) { setA({ iconBg: v || '#eff6ff' }); } }, |
| 144 |
], |
| 145 |
}) |
| 146 |
), |
| 147 |
el(PanelBody, { title: __('Typography'), initialOpen: false }, |
| 148 |
(function () { |
| 149 |
var TC = getTypographyControl(); |
| 150 |
if (!TC) return el('p', { style: { color: '#999', fontSize: '12px', padding: '8px 0' } }, 'Typography control loading…'); |
| 151 |
return el(window.wp.element.Fragment, {}, |
| 152 |
el(TC, { |
| 153 |
label: 'Label', |
| 154 |
value: a.typoLabel || {}, |
| 155 |
onChange: function (v) { setA({ typoLabel: v }); } |
| 156 |
}), |
| 157 |
el(TC, { |
| 158 |
label: 'Value', |
| 159 |
value: a.typoValue || {}, |
| 160 |
onChange: function (v) { setA({ typoValue: v }); } |
| 161 |
}) |
| 162 |
); |
| 163 |
})(), |
| 164 |
el(PanelColorSettings, { |
| 165 |
title: __('Text Colors'), initialOpen: false, |
| 166 |
colorSettings: [ |
| 167 |
{ label: __('Label Color'), value: a.labelColor, onChange: function (v) { setA({ labelColor: v || '#6b7280' }); } }, |
| 168 |
{ label: __('Value Color'), value: a.valueColor, onChange: function (v) { setA({ valueColor: v || '#111827' }); } }, |
| 169 |
{ label: __('Link Color'), value: a.linkColor, onChange: function (v) { setA({ linkColor: v || '#2563eb' }); } }, |
| 170 |
], |
| 171 |
}) |
| 172 |
) |
| 173 |
); |
| 174 |
|
| 175 |
/* ── Item editor panels ── */ |
| 176 |
var itemPanels = el(PanelBody, { title: __('Contact Items'), initialOpen: true }, |
| 177 |
a.items.map(function (item, idx) { |
| 178 |
return el('div', { |
| 179 |
key: item.id, |
| 180 |
style: { border: '1px solid #e5e7eb', borderRadius: 6, padding: 12, marginBottom: 12 } |
| 181 |
}, |
| 182 |
el(SelectControl, { |
| 183 |
label: __('Type'), |
| 184 |
value: item.type, |
| 185 |
options: Object.keys(TYPE_ICONS).map(function (t) { return { label: t.charAt(0).toUpperCase() + t.slice(1), value: t }; }), |
| 186 |
onChange: function (v) { updateItem(a.items, item.id, 'type', v, setA); }, |
| 187 |
}), |
| 188 |
el(TextControl, { |
| 189 |
label: __('Label'), |
| 190 |
value: item.label, |
| 191 |
onChange: function (v) { updateItem(a.items, item.id, 'label', v, setA); }, |
| 192 |
}), |
| 193 |
el(TextControl, { |
| 194 |
label: __('Value'), |
| 195 |
value: item.value, |
| 196 |
onChange: function (v) { updateItem(a.items, item.id, 'value', v, setA); }, |
| 197 |
}), |
| 198 |
el(ToggleControl, { |
| 199 |
label: __('Make value a link'), |
| 200 |
checked: item.linkEnabled, |
| 201 |
onChange: function (v) { updateItem(a.items, item.id, 'linkEnabled', v, setA); }, |
| 202 |
__nextHasNoMarginBottom: true, |
| 203 |
}), |
| 204 |
el(Button, { |
| 205 |
variant: 'link', |
| 206 |
isDestructive: true, |
| 207 |
onClick: function () { |
| 208 |
setA({ items: a.items.filter(function (it) { return it.id !== item.id; }) }); |
| 209 |
} |
| 210 |
}, __('Remove')) |
| 211 |
); |
| 212 |
}), |
| 213 |
el(Button, { |
| 214 |
variant: 'secondary', |
| 215 |
onClick: function () { |
| 216 |
setA({ items: a.items.concat([{ id: generateId(), type: 'custom', label: 'Label', value: 'Value', linkEnabled: false }]) }); |
| 217 |
} |
| 218 |
}, __('+ Add Item')) |
| 219 |
); |
| 220 |
|
| 221 |
/* ── Preview ── */ |
| 222 |
var wrapStyle = Object.assign({ |
| 223 |
display: a.layout === 'inline' ? 'flex' : a.layout === 'grid' ? 'grid' : 'flex', |
| 224 |
flexDirection: a.layout === 'stacked' ? 'column' : undefined, |
| 225 |
flexWrap: a.layout === 'inline' ? 'wrap' : undefined, |
| 226 |
gridTemplateColumns: a.layout === 'grid' ? 'repeat(' + a.columns + ', 1fr)' : undefined, |
| 227 |
gap: a.itemSpacing + 'px', |
| 228 |
}, _tv(a.typoLabel, '--bkcti-label-'), _tv(a.typoValue, '--bkcti-value-')); |
| 229 |
|
| 230 |
var preview = el('div', { style: wrapStyle }, |
| 231 |
a.items.map(function (item) { |
| 232 |
var linkHref = item.type === 'phone' ? 'tel:' + item.value |
| 233 |
: item.type === 'email' ? 'mailto:' + item.value |
| 234 |
: item.value; |
| 235 |
return el('div', { key: item.id, className: 'bkbg-cti-item', style: { display: 'flex', alignItems: 'center', gap: 12 } }, |
| 236 |
renderIcon(item.type, a.iconStyle, a.iconBg, a.iconColor, a.iconSize, a.iconBoxSize, a.iconRadius), |
| 237 |
el('div', { style: { display: 'flex', flexDirection: 'column' } }, |
| 238 |
a.showLabel && el('span', { className: 'bkbg-cti-label', style: { color: a.labelColor, textTransform: 'uppercase', letterSpacing: '0.05em' } }, item.label), |
| 239 |
item.linkEnabled |
| 240 |
? el('a', { href: linkHref, className: 'bkbg-cti-link', style: { color: a.linkColor, textDecoration: 'none' } }, item.value) |
| 241 |
: el('span', { className: 'bkbg-cti-value', style: { color: a.valueColor } }, item.value) |
| 242 |
) |
| 243 |
); |
| 244 |
}) |
| 245 |
); |
| 246 |
|
| 247 |
return el('div', null, |
| 248 |
el(InspectorControls, null, itemPanels, inspector.props.children), |
| 249 |
preview |
| 250 |
); |
| 251 |
}, |
| 252 |
|
| 253 |
save: function (props) { |
| 254 |
var a = props.attributes; |
| 255 |
|
| 256 |
function getLinkHref(item) { |
| 257 |
if (item.type === 'phone') return 'tel:' + item.value.replace(/\s/g, ''); |
| 258 |
if (item.type === 'email') return 'mailto:' + item.value; |
| 259 |
if (item.type === 'website') return item.value; |
| 260 |
return item.value; |
| 261 |
} |
| 262 |
|
| 263 |
var svgMap = { |
| 264 |
address: TYPE_ICONS ? undefined : '', |
| 265 |
}; |
| 266 |
|
| 267 |
return el('div', { |
| 268 |
className: 'bkbg-cti-wrap bkbg-cti-layout--' + a.layout, |
| 269 |
style: Object.assign({ |
| 270 |
'--bkbg-cti-cols': a.columns, |
| 271 |
'--bkbg-cti-gap': a.itemSpacing + 'px', |
| 272 |
'--bkbg-cti-icon-c': a.iconColor, |
| 273 |
'--bkbg-cti-icon-bg': a.iconBg, |
| 274 |
'--bkbg-cti-label-c': a.labelColor, |
| 275 |
'--bkbg-cti-value-c': a.valueColor, |
| 276 |
'--bkbg-cti-link-c': a.linkColor, |
| 277 |
'--bkbg-cti-ls': a.labelSize + 'px', |
| 278 |
'--bkbg-cti-lw': a.labelWeight, |
| 279 |
'--bkbg-cti-vs': a.valueSize + 'px', |
| 280 |
'--bkbg-cti-vw': a.valueWeight, |
| 281 |
'--bkbg-cti-icon-sz': a.iconSize + 'px', |
| 282 |
'--bkbg-cti-box-sz': a.iconBoxSize + 'px', |
| 283 |
'--bkbg-cti-box-r': a.iconRadius + 'px', |
| 284 |
}, _tv(a.typoLabel, '--bkcti-label-'), _tv(a.typoValue, '--bkcti-value-')), |
| 285 |
'data-schema': a.schemaEnabled ? '1' : '0', |
| 286 |
'data-items': JSON.stringify(a.items), |
| 287 |
'data-icon-style': a.iconStyle, |
| 288 |
'data-show-label': a.showLabel ? '1' : '0', |
| 289 |
}); |
| 290 |
}, |
| 291 |
}); |
| 292 |
})(); |
| 293 |
|