| 1 |
(function () { |
| 2 |
'use strict'; |
| 3 |
|
| 4 |
var reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches; |
| 5 |
|
| 6 |
/* ── typography CSS-var helper ──────────────────────── */ |
| 7 |
var _typoKeys = { |
| 8 |
family:'font-family', weight:'font-weight', style:'font-style', |
| 9 |
decoration:'text-decoration', transform:'text-transform', |
| 10 |
sizeDesktop:'font-size-d', sizeTablet:'font-size-t', sizeMobile:'font-size-m', |
| 11 |
lineHeightDesktop:'line-height-d', lineHeightTablet:'line-height-t', lineHeightMobile:'line-height-m', |
| 12 |
letterSpacingDesktop:'letter-spacing-d', letterSpacingTablet:'letter-spacing-t', letterSpacingMobile:'letter-spacing-m', |
| 13 |
wordSpacingDesktop:'word-spacing-d', wordSpacingTablet:'word-spacing-t', wordSpacingMobile:'word-spacing-m' |
| 14 |
}; |
| 15 |
function typoCssVarsForEl(el, obj, prefix) { |
| 16 |
if (!obj || typeof obj !== 'object') return; |
| 17 |
Object.keys(_typoKeys).forEach(function (k) { |
| 18 |
var v = obj[k]; |
| 19 |
if (v === undefined || v === '' || v === null) return; |
| 20 |
if (k === 'sizeDesktop' || k === 'sizeTablet' || k === 'sizeMobile') v = v + (obj.sizeUnit || 'px'); |
| 21 |
else if (k === 'lineHeightDesktop' || k === 'lineHeightTablet' || k === 'lineHeightMobile') v = v + (obj.lineHeightUnit || ''); |
| 22 |
else if (k === 'letterSpacingDesktop' || k === 'letterSpacingTablet' || k === 'letterSpacingMobile') v = v + (obj.letterSpacingUnit || 'px'); |
| 23 |
else if (k === 'wordSpacingDesktop' || k === 'wordSpacingTablet' || k === 'wordSpacingMobile') v = v + (obj.wordSpacingUnit || 'px'); |
| 24 |
el.style.setProperty(prefix + _typoKeys[k], String(v)); |
| 25 |
}); |
| 26 |
} |
| 27 |
|
| 28 |
function initBlock(root) { |
| 29 |
var app = root.querySelector('.bkbg-is-app'); |
| 30 |
if (!app) return; |
| 31 |
|
| 32 |
var raw = app.getAttribute('data-opts') || '{}'; |
| 33 |
var o; |
| 34 |
try { o = JSON.parse(raw); } catch (e) { o = {}; } |
| 35 |
|
| 36 |
var steps = Array.isArray(o.steps) ? o.steps : []; |
| 37 |
var layout = o.layout || 'horizontal'; |
| 38 |
var isHoriz = layout !== 'vertical'; |
| 39 |
var showConnector = o.showConnector !== false; |
| 40 |
var animateContent = o.animateContent !== false; |
| 41 |
var initialStep = Number(o.initialStep) || 0; |
| 42 |
var stepSize = Number(o.stepSize) || 52; |
| 43 |
var stepFontSize = Number(o.stepFontSize) || 14; |
| 44 |
var headingSize = Number(o.headingSize) || 22; |
| 45 |
var bodySize = Number(o.bodySize) || 16; |
| 46 |
var connectorH = Number(o.connectorHeight) || 3; |
| 47 |
var contentRadius = Number(o.contentRadius) || 16; |
| 48 |
var contentPadding = Number(o.contentPadding) || 32; |
| 49 |
var activeBg = o.activeBg || '#6366f1'; |
| 50 |
var activeColor = o.activeColor || '#ffffff'; |
| 51 |
var inactiveBg = o.inactiveBg || '#e5e7eb'; |
| 52 |
var inactiveColor = o.inactiveColor || '#64748b'; |
| 53 |
var doneBg = o.doneBg || '#10b981'; |
| 54 |
var doneColor = o.doneColor || '#ffffff'; |
| 55 |
var connectorColor = o.connectorColor || '#e5e7eb'; |
| 56 |
var connectorDone = o.connectorDone || '#10b981'; |
| 57 |
var contentBg = o.contentBg || '#f8fafc'; |
| 58 |
var contentBorder = o.contentBorder || '#e5e7eb'; |
| 59 |
var headingColor = o.headingColor || '#0f172a'; |
| 60 |
var bodyColor = o.bodyColor || '#475569'; |
| 61 |
var labelColor = o.labelColor || '#0f172a'; |
| 62 |
var sectionBg = o.sectionBg || 'transparent'; |
| 63 |
|
| 64 |
if (steps.length === 0) return; |
| 65 |
|
| 66 |
var currentIdx = typeof initialStep === 'number' && initialStep < steps.length ? initialStep : 0; |
| 67 |
var total = steps.length; |
| 68 |
|
| 69 |
/* ── Build root container ───────────────────────────────────── */ |
| 70 |
var rootEl = document.createElement('div'); |
| 71 |
rootEl.className = 'bkbg-is-root ' + (isHoriz ? 'bkbg-is-horiz' : 'bkbg-is-vert'); |
| 72 |
rootEl.style.cssText = 'background:' + sectionBg + ';display:flex;' + |
| 73 |
(isHoriz ? 'flex-direction:column' : 'flex-direction:row;gap:32px'); |
| 74 |
|
| 75 |
typoCssVarsForEl(rootEl, o.headingTypo, '--bkbg-is-h-'); |
| 76 |
typoCssVarsForEl(rootEl, o.bodyTypo, '--bkbg-is-bd-'); |
| 77 |
|
| 78 |
/* ── Nav ────────────────────────────────────────────────────── */ |
| 79 |
var navEl = document.createElement('div'); |
| 80 |
navEl.className = 'bkbg-is-nav'; |
| 81 |
navEl.style.cssText = 'display:flex;' + |
| 82 |
(isHoriz |
| 83 |
? 'flex-direction:row;align-items:flex-start;margin-bottom:32px' |
| 84 |
: 'flex-direction:column;align-items:flex-start;flex-shrink:0'); |
| 85 |
|
| 86 |
var bubbles = []; |
| 87 |
var connectors = []; |
| 88 |
var labelEls = []; |
| 89 |
|
| 90 |
steps.forEach(function (step, idx) { |
| 91 |
/* Connector before */ |
| 92 |
if (idx > 0 && showConnector) { |
| 93 |
var conn = document.createElement('div'); |
| 94 |
conn.className = 'bkbg-is-connector'; |
| 95 |
if (isHoriz) { |
| 96 |
conn.style.cssText = 'flex:1;height:' + connectorH + 'px;background:' + connectorColor + ';align-self:center;min-width:20px;transition:background 0.4s ease'; |
| 97 |
} else { |
| 98 |
conn.style.cssText = 'height:24px;width:' + connectorH + 'px;background:' + connectorColor + |
| 99 |
';margin:0 0 0 ' + (stepSize / 2 - connectorH / 2) + 'px;transition:background 0.4s ease'; |
| 100 |
} |
| 101 |
navEl.appendChild(conn); |
| 102 |
connectors.push({ el: conn, idx: idx }); |
| 103 |
} |
| 104 |
|
| 105 |
/* Bubble + label wrapper */ |
| 106 |
var item = document.createElement('div'); |
| 107 |
item.className = 'bkbg-is-step-item'; |
| 108 |
item.style.cssText = 'display:flex;' + |
| 109 |
(isHoriz |
| 110 |
? 'flex-direction:column;align-items:center;gap:6px' |
| 111 |
: 'flex-direction:row;align-items:center;gap:10px'); |
| 112 |
|
| 113 |
var btn = document.createElement('button'); |
| 114 |
btn.className = 'bkbg-is-bubble'; |
| 115 |
btn.type = 'button'; |
| 116 |
btn.setAttribute('aria-label', 'Step ' + (idx + 1) + ': ' + (step.title || '')); |
| 117 |
btn.style.cssText = [ |
| 118 |
'width:' + stepSize + 'px', |
| 119 |
'height:' + stepSize + 'px', |
| 120 |
'font-size:' + stepFontSize + 'px', |
| 121 |
'background:' + inactiveBg, |
| 122 |
'color:' + inactiveColor |
| 123 |
].join(';'); |
| 124 |
var _IP = window.bkbgIconPicker; |
| 125 |
var _iType = step.iconType || 'custom-char'; |
| 126 |
if (_IP && _iType !== 'custom-char') { |
| 127 |
var _in = _IP.buildFrontendIcon(_iType, step.icon, step.iconDashicon, step.iconImageUrl, step.iconDashiconColor); |
| 128 |
if (_in) { btn.textContent = ''; btn.appendChild(_in); } else { btn.textContent = step.icon || (idx + 1); } |
| 129 |
} else { btn.textContent = step.icon || (idx + 1); } |
| 130 |
btn.addEventListener('click', function () { goTo(idx); }); |
| 131 |
item.appendChild(btn); |
| 132 |
bubbles.push(btn); |
| 133 |
|
| 134 |
var lbl = document.createElement('span'); |
| 135 |
lbl.className = 'bkbg-is-label'; |
| 136 |
lbl.textContent = step.title || ('Step ' + (idx + 1)); |
| 137 |
lbl.style.cssText = 'font-size:' + stepFontSize + 'px;color:' + labelColor + ';font-weight:500;white-space:nowrap'; |
| 138 |
item.appendChild(lbl); |
| 139 |
labelEls.push(lbl); |
| 140 |
|
| 141 |
navEl.appendChild(item); |
| 142 |
}); |
| 143 |
|
| 144 |
/* ── Panel ──────────────────────────────────────────────────── */ |
| 145 |
var panelEl = document.createElement('div'); |
| 146 |
panelEl.className = 'bkbg-is-panel'; |
| 147 |
panelEl.style.cssText = [ |
| 148 |
'flex:1', |
| 149 |
'background:' + contentBg, |
| 150 |
'border:1px solid ' + contentBorder, |
| 151 |
'border-radius:' + contentRadius + 'px', |
| 152 |
'padding:' + contentPadding + 'px' |
| 153 |
].join(';'); |
| 154 |
|
| 155 |
var headingEl = document.createElement('div'); |
| 156 |
headingEl.className = 'bkbg-is-heading'; |
| 157 |
headingEl.style.cssText = 'color:' + headingColor; |
| 158 |
|
| 159 |
var bodyEl = document.createElement('div'); |
| 160 |
bodyEl.className = 'bkbg-is-body'; |
| 161 |
bodyEl.style.cssText = 'color:' + bodyColor; |
| 162 |
|
| 163 |
var navBtns = document.createElement('div'); |
| 164 |
navBtns.className = 'bkbg-is-nav-btns'; |
| 165 |
|
| 166 |
var prevBtn = document.createElement('button'); |
| 167 |
prevBtn.type = 'button'; |
| 168 |
prevBtn.className = 'bkbg-is-btn-prev'; |
| 169 |
prevBtn.textContent = '← Previous'; |
| 170 |
prevBtn.style.cssText = 'padding:10px 24px;border-radius:8px;border:1px solid ' + contentBorder + |
| 171 |
';background:transparent;cursor:pointer;font-size:14px;color:' + bodyColor + ';font-weight:600'; |
| 172 |
prevBtn.addEventListener('click', function () { if (currentIdx > 0) goTo(currentIdx - 1); }); |
| 173 |
|
| 174 |
var nextBtn = document.createElement('button'); |
| 175 |
nextBtn.type = 'button'; |
| 176 |
nextBtn.className = 'bkbg-is-btn-next'; |
| 177 |
nextBtn.textContent = 'Next →'; |
| 178 |
nextBtn.style.cssText = 'padding:10px 24px;border-radius:8px;border:none;background:' + activeBg + |
| 179 |
';color:' + activeColor + ';cursor:pointer;font-size:14px;font-weight:700'; |
| 180 |
nextBtn.addEventListener('click', function () { if (currentIdx < total - 1) goTo(currentIdx + 1); }); |
| 181 |
|
| 182 |
navBtns.appendChild(prevBtn); |
| 183 |
navBtns.appendChild(nextBtn); |
| 184 |
panelEl.appendChild(headingEl); |
| 185 |
panelEl.appendChild(bodyEl); |
| 186 |
panelEl.appendChild(navBtns); |
| 187 |
|
| 188 |
rootEl.appendChild(navEl); |
| 189 |
rootEl.appendChild(panelEl); |
| 190 |
app.appendChild(rootEl); |
| 191 |
|
| 192 |
/* ── Update UI ──────────────────────────────────────────────── */ |
| 193 |
function updateBubbles() { |
| 194 |
bubbles.forEach(function (btn, i) { |
| 195 |
var isActive = i === currentIdx; |
| 196 |
var isDone = i < currentIdx; |
| 197 |
btn.style.background = isActive ? activeBg : (isDone ? doneBg : inactiveBg); |
| 198 |
btn.style.color = isActive ? activeColor : (isDone ? doneColor : inactiveColor); |
| 199 |
btn.style.transform = isActive ? 'scale(1.12)' : 'scale(1)'; |
| 200 |
btn.style.boxShadow = isActive ? '0 4px 16px rgba(99,102,241,0.4)' : 'none'; |
| 201 |
btn.textContent = ''; |
| 202 |
while (btn.firstChild) btn.removeChild(btn.firstChild); |
| 203 |
if (isDone) { |
| 204 |
btn.textContent = '✓'; |
| 205 |
} else { |
| 206 |
var _IP2 = window.bkbgIconPicker; |
| 207 |
var _iT2 = steps[i].iconType || 'custom-char'; |
| 208 |
if (_IP2 && _iT2 !== 'custom-char') { |
| 209 |
var _in2 = _IP2.buildFrontendIcon(_iT2, steps[i].icon, steps[i].iconDashicon, steps[i].iconImageUrl, steps[i].iconDashiconColor); |
| 210 |
if (_in2) btn.appendChild(_in2); else btn.textContent = steps[i].icon || (i + 1); |
| 211 |
} else { btn.textContent = steps[i].icon || (i + 1); } |
| 212 |
} |
| 213 |
btn.classList.toggle('bkbg-is-active', isActive); |
| 214 |
btn.classList.toggle('bkbg-is-done', isDone); |
| 215 |
labelEls[i].style.fontWeight = isActive ? '700' : '500'; |
| 216 |
labelEls[i].style.color = isActive ? activeBg : labelColor; |
| 217 |
}); |
| 218 |
connectors.forEach(function (c) { |
| 219 |
c.el.style.background = c.idx <= currentIdx ? connectorDone : connectorColor; |
| 220 |
}); |
| 221 |
prevBtn.style.display = currentIdx > 0 ? 'inline-flex' : 'none'; |
| 222 |
nextBtn.style.display = currentIdx < total - 1 ? 'inline-flex' : 'none'; |
| 223 |
} |
| 224 |
|
| 225 |
function updatePanel() { |
| 226 |
var step = steps[currentIdx]; |
| 227 |
headingEl.textContent = step.heading || ''; |
| 228 |
bodyEl.textContent = step.body || ''; |
| 229 |
} |
| 230 |
|
| 231 |
function goTo(idx) { |
| 232 |
if (idx === currentIdx) return; |
| 233 |
if (animateContent && !reduced) { |
| 234 |
panelEl.classList.add('bkbg-is-transitioning'); |
| 235 |
setTimeout(function () { |
| 236 |
currentIdx = idx; |
| 237 |
updatePanel(); |
| 238 |
updateBubbles(); |
| 239 |
panelEl.classList.remove('bkbg-is-transitioning'); |
| 240 |
}, 200); |
| 241 |
} else { |
| 242 |
currentIdx = idx; |
| 243 |
updatePanel(); |
| 244 |
updateBubbles(); |
| 245 |
} |
| 246 |
} |
| 247 |
|
| 248 |
updatePanel(); |
| 249 |
updateBubbles(); |
| 250 |
} |
| 251 |
|
| 252 |
function init() { |
| 253 |
document.querySelectorAll('.wp-block-blockenberg-interactive-steps').forEach(initBlock); |
| 254 |
} |
| 255 |
|
| 256 |
if (document.readyState === 'loading') { |
| 257 |
document.addEventListener('DOMContentLoaded', init); |
| 258 |
} else { |
| 259 |
init(); |
| 260 |
} |
| 261 |
})(); |
| 262 |
|