| 1 |
function typoCssVarsForEl(typo, prefix, el) { |
| 2 |
if (!typo || typeof typo !== 'object') return; |
| 3 |
var m = { |
| 4 |
family: 'font-family', |
| 5 |
weight: 'font-weight', |
| 6 |
style: 'font-style', |
| 7 |
decoration: 'text-decoration', |
| 8 |
transform: 'text-transform', |
| 9 |
sizeDesktop: 'font-size-d', |
| 10 |
sizeTablet: 'font-size-t', |
| 11 |
sizeMobile: 'font-size-m', |
| 12 |
lineHeightDesktop: 'line-height-d', |
| 13 |
lineHeightTablet: 'line-height-t', |
| 14 |
lineHeightMobile: 'line-height-m', |
| 15 |
letterSpacingDesktop: 'letter-spacing-d', |
| 16 |
letterSpacingTablet: 'letter-spacing-t', |
| 17 |
letterSpacingMobile: 'letter-spacing-m', |
| 18 |
wordSpacingDesktop: 'word-spacing-d', |
| 19 |
wordSpacingTablet: 'word-spacing-t', |
| 20 |
wordSpacingMobile: 'word-spacing-m' |
| 21 |
}; |
| 22 |
Object.keys(m).forEach(function (k) { |
| 23 |
var v = typo[k]; |
| 24 |
if (v !== undefined && v !== '' && v !== null) { |
| 25 |
el.style.setProperty(prefix + m[k], String(v)); |
| 26 |
} |
| 27 |
}); |
| 28 |
} |
| 29 |
|
| 30 |
document.addEventListener('DOMContentLoaded', function () { |
| 31 |
document.querySelectorAll('.bkbg-ht-app').forEach(function (root) { |
| 32 |
var opts = JSON.parse(root.dataset.opts || '{}'); |
| 33 |
initHorizontalTimeline(root, opts); |
| 34 |
}); |
| 35 |
}); |
| 36 |
|
| 37 |
function initHorizontalTimeline(root, a) { |
| 38 |
var items = a.items || []; |
| 39 |
if (!items.length) return; |
| 40 |
|
| 41 |
var containerH = a.containerHeight || 420; |
| 42 |
var halfH = Math.round(containerH / 2); |
| 43 |
var itemWidth = a.itemWidth || 220; |
| 44 |
var itemGap = a.itemGap || 40; |
| 45 |
var dotSize = a.dotSize || 16; |
| 46 |
var trackPad = 48; |
| 47 |
|
| 48 |
/* CSS vars */ |
| 49 |
root.style.setProperty('--ht-line-color', a.lineColor || '#6366f1'); |
| 50 |
root.style.setProperty('--ht-section-bg', a.sectionBg || '#f8fafc'); |
| 51 |
|
| 52 |
/* typography CSS vars */ |
| 53 |
typoCssVarsForEl(a.titleTypo, '--bkbg-ht-tt-', root); |
| 54 |
typoCssVarsForEl(a.descTypo, '--bkbg-ht-desc-', root); |
| 55 |
typoCssVarsForEl(a.dateTypo, '--bkbg-ht-date-', root); |
| 56 |
/* legacy fallback vars */ |
| 57 |
if (a.titleFontSize && a.titleFontSize !== 13) root.style.setProperty('--bkbg-ht-tt-sz', a.titleFontSize + 'px'); |
| 58 |
if (a.titleFontWeight && a.titleFontWeight !== '600') root.style.setProperty('--bkbg-ht-tt-fw', a.titleFontWeight); |
| 59 |
if (a.titleLineHeight && a.titleLineHeight !== 1.3) root.style.setProperty('--bkbg-ht-tt-lh', String(a.titleLineHeight)); |
| 60 |
if (a.descFontSize && a.descFontSize !== 11) root.style.setProperty('--bkbg-ht-desc-sz', a.descFontSize + 'px'); |
| 61 |
if (a.descLineHeight && a.descLineHeight !== 1.5) root.style.setProperty('--bkbg-ht-desc-lh', String(a.descLineHeight)); |
| 62 |
if (a.dateFontSize && a.dateFontSize !== 11) root.style.setProperty('--bkbg-ht-date-sz', a.dateFontSize + 'px'); |
| 63 |
|
| 64 |
/* section */ |
| 65 |
var section = document.createElement('div'); |
| 66 |
section.className = 'bkbg-ht-section'; |
| 67 |
section.style.cssText = 'background:' + (a.sectionBg || '#f8fafc') + ';padding:20px 0;'; |
| 68 |
|
| 69 |
/* track wrap */ |
| 70 |
var trackWrap = document.createElement('div'); |
| 71 |
trackWrap.className = 'bkbg-ht-track-wrap'; |
| 72 |
trackWrap.style.height = containerH + 'px'; |
| 73 |
|
| 74 |
/* line */ |
| 75 |
var line = document.createElement('div'); |
| 76 |
line.className = 'bkbg-ht-line'; |
| 77 |
line.style.cssText = 'top:' + halfH + 'px;height:' + (a.lineThickness || 3) + 'px;background:' + (a.lineColor || '#6366f1') + ';'; |
| 78 |
trackWrap.appendChild(line); |
| 79 |
|
| 80 |
/* track */ |
| 81 |
var track = document.createElement('div'); |
| 82 |
track.className = 'bkbg-ht-track'; |
| 83 |
track.style.cssText = 'gap:' + itemGap + 'px;padding:0 ' + trackPad + 'px;height:' + containerH + 'px;min-width:max-content;'; |
| 84 |
trackWrap.appendChild(track); |
| 85 |
|
| 86 |
/* build items */ |
| 87 |
items.forEach(function (item, idx) { |
| 88 |
var isTop = a.layout === 'top' || (a.layout === 'alternating' ? idx % 2 === 0 : false); |
| 89 |
if (a.layout === 'bottom') isTop = false; |
| 90 |
|
| 91 |
var col = document.createElement('div'); |
| 92 |
col.className = 'bkbg-ht-item-col ' + (isTop ? 'bkbg-ht-top' : 'bkbg-ht-bottom'); |
| 93 |
col.style.cssText = 'height:' + containerH + 'px;width:' + itemWidth + 'px;'; |
| 94 |
|
| 95 |
/* card half-height = from center to top/bottom edge minus dot */ |
| 96 |
var cardAvailH = halfH - Math.round(dotSize / 2) - 8; |
| 97 |
|
| 98 |
/* card */ |
| 99 |
var card = document.createElement('div'); |
| 100 |
card.className = 'bkbg-ht-card' + (a.animateOnScroll ? ' bkbg-ht-hidden' : ''); |
| 101 |
card.style.cssText = [ |
| 102 |
'width:' + itemWidth + 'px', |
| 103 |
'max-height:' + (cardAvailH - 24) + 'px', |
| 104 |
'overflow:hidden', |
| 105 |
'background:' + (a.cardBg || '#fff'), |
| 106 |
'border:1px solid ' + (a.cardBorderColor || '#e2e8f0'), |
| 107 |
'border-radius:' + (a.cardRadius || 16) + 'px', |
| 108 |
'padding:' + (a.cardPadding || 20) + 'px', |
| 109 |
a.cardShadow ? 'box-shadow:0 4px 16px rgba(0,0,0,0.08)' : '', |
| 110 |
'box-sizing:border-box', |
| 111 |
].filter(Boolean).join(';'); |
| 112 |
|
| 113 |
if (item.imageUrl) { |
| 114 |
var img = document.createElement('img'); |
| 115 |
img.src = item.imageUrl; |
| 116 |
img.className = 'bkbg-ht-card-img'; |
| 117 |
img.alt = item.title || ''; |
| 118 |
img.style.maxHeight = '80px'; |
| 119 |
card.appendChild(img); |
| 120 |
} |
| 121 |
|
| 122 |
if (a.showIcons && item.icon) { |
| 123 |
var iconEl = document.createElement('div'); |
| 124 |
iconEl.className = 'bkbg-ht-card-icon'; |
| 125 |
iconEl.style.fontSize = (a.iconSize || 28) + 'px'; |
| 126 |
var _IP = window.bkbgIconPicker; |
| 127 |
var _iType = item.iconType || 'custom-char'; |
| 128 |
if (_IP && _iType !== 'custom-char') { |
| 129 |
var _in = _IP.buildFrontendIcon(_iType, item.icon, item.iconDashicon, item.iconImageUrl, item.iconDashiconColor); |
| 130 |
if (_in) iconEl.appendChild(_in); else iconEl.textContent = item.icon || ''; |
| 131 |
} else { |
| 132 |
iconEl.textContent = item.icon || ''; |
| 133 |
} |
| 134 |
card.appendChild(iconEl); |
| 135 |
} |
| 136 |
|
| 137 |
if (a.showDates && item.date) { |
| 138 |
var dateEl = document.createElement('div'); |
| 139 |
dateEl.className = 'bkbg-ht-card-date'; |
| 140 |
dateEl.style.color = a.dateColor || '#6366f1'; |
| 141 |
dateEl.textContent = item.date; |
| 142 |
card.appendChild(dateEl); |
| 143 |
} |
| 144 |
|
| 145 |
if (item.title) { |
| 146 |
var titleEl = document.createElement('div'); |
| 147 |
titleEl.className = 'bkbg-ht-card-title'; |
| 148 |
titleEl.style.color = a.titleColor || '#1e293b'; |
| 149 |
titleEl.textContent = item.title; |
| 150 |
card.appendChild(titleEl); |
| 151 |
} |
| 152 |
|
| 153 |
if (item.description) { |
| 154 |
var descEl = document.createElement('div'); |
| 155 |
descEl.className = 'bkbg-ht-card-desc'; |
| 156 |
descEl.style.color = a.descColor || '#64748b'; |
| 157 |
descEl.textContent = item.description; |
| 158 |
card.appendChild(descEl); |
| 159 |
} |
| 160 |
|
| 161 |
/* connector rod */ |
| 162 |
var rod = document.createElement('div'); |
| 163 |
rod.className = 'bkbg-ht-connector'; |
| 164 |
rod.style.height = '8px'; |
| 165 |
|
| 166 |
/* dot */ |
| 167 |
var dot = document.createElement('div'); |
| 168 |
dot.className = 'bkbg-ht-dot'; |
| 169 |
dot.style.cssText = [ |
| 170 |
'width:' + dotSize + 'px', |
| 171 |
'height:' + dotSize + 'px', |
| 172 |
'background:' + (a.dotColor || '#6366f1'), |
| 173 |
'border:3px solid ' + (a.dotBorderColor || '#fff'), |
| 174 |
'box-shadow:0 0 0 3px ' + (a.dotColor || '#6366f1') + '33', |
| 175 |
].join(';'); |
| 176 |
|
| 177 |
if (isTop) { |
| 178 |
col.appendChild(card); |
| 179 |
col.appendChild(rod); |
| 180 |
col.appendChild(dot); |
| 181 |
} else { |
| 182 |
col.appendChild(dot); |
| 183 |
col.appendChild(rod); |
| 184 |
col.appendChild(card); |
| 185 |
} |
| 186 |
|
| 187 |
/* spacer to fill remaining height */ |
| 188 |
col.style.justifyContent = isTop ? 'flex-start' : 'flex-end'; |
| 189 |
|
| 190 |
track.appendChild(col); |
| 191 |
}); |
| 192 |
|
| 193 |
section.appendChild(trackWrap); |
| 194 |
|
| 195 |
/* arrows */ |
| 196 |
if (a.showArrows) { |
| 197 |
var arrowsDiv = document.createElement('div'); |
| 198 |
arrowsDiv.className = 'bkbg-ht-arrows'; |
| 199 |
|
| 200 |
var btnLeft = document.createElement('button'); |
| 201 |
var btnRight = document.createElement('button'); |
| 202 |
[btnLeft, btnRight].forEach(function (btn) { |
| 203 |
btn.className = 'bkbg-ht-arrow'; |
| 204 |
btn.style.cssText = 'background:' + (a.arrowBg || '#6366f1') + ';color:' + (a.arrowColor || '#fff') + ';'; |
| 205 |
}); |
| 206 |
btnLeft.textContent = '←'; |
| 207 |
btnRight.textContent = '→'; |
| 208 |
|
| 209 |
btnLeft.addEventListener('click', function () { |
| 210 |
trackWrap.scrollBy({ left: -(itemWidth + itemGap) * 2, behavior: 'smooth' }); |
| 211 |
}); |
| 212 |
btnRight.addEventListener('click', function () { |
| 213 |
trackWrap.scrollBy({ left: (itemWidth + itemGap) * 2, behavior: 'smooth' }); |
| 214 |
}); |
| 215 |
|
| 216 |
arrowsDiv.appendChild(btnLeft); |
| 217 |
arrowsDiv.appendChild(btnRight); |
| 218 |
section.appendChild(arrowsDiv); |
| 219 |
} |
| 220 |
|
| 221 |
root.appendChild(section); |
| 222 |
|
| 223 |
/* drag-to-scroll */ |
| 224 |
if (a.draggable !== false) { |
| 225 |
var dragState = { active: false, startX: 0, scrollLeft: 0 }; |
| 226 |
trackWrap.addEventListener('mousedown', function (e) { |
| 227 |
dragState.active = true; |
| 228 |
dragState.startX = e.pageX - trackWrap.offsetLeft; |
| 229 |
dragState.scrollLeft = trackWrap.scrollLeft; |
| 230 |
trackWrap.classList.add('bkbg-ht-dragging'); |
| 231 |
}); |
| 232 |
document.addEventListener('mouseup', function () { |
| 233 |
dragState.active = false; |
| 234 |
trackWrap.classList.remove('bkbg-ht-dragging'); |
| 235 |
}); |
| 236 |
trackWrap.addEventListener('mousemove', function (e) { |
| 237 |
if (!dragState.active) return; |
| 238 |
e.preventDefault(); |
| 239 |
var x = e.pageX - trackWrap.offsetLeft; |
| 240 |
var walk = (x - dragState.startX) * 1.4; |
| 241 |
trackWrap.scrollLeft = dragState.scrollLeft - walk; |
| 242 |
}); |
| 243 |
/* touch */ |
| 244 |
var touchStartX = 0, touchScrollLeft = 0; |
| 245 |
trackWrap.addEventListener('touchstart', function (e) { |
| 246 |
touchStartX = e.touches[0].pageX; |
| 247 |
touchScrollLeft = trackWrap.scrollLeft; |
| 248 |
}, { passive: true }); |
| 249 |
trackWrap.addEventListener('touchmove', function (e) { |
| 250 |
var dx = touchStartX - e.touches[0].pageX; |
| 251 |
trackWrap.scrollLeft = touchScrollLeft + dx; |
| 252 |
}, { passive: true }); |
| 253 |
} |
| 254 |
|
| 255 |
/* scroll-reveal */ |
| 256 |
if (a.animateOnScroll) { |
| 257 |
var observer = new IntersectionObserver(function (entries) { |
| 258 |
entries.forEach(function (entry) { |
| 259 |
if (entry.isIntersecting) { |
| 260 |
var idx = Array.from(track.children).indexOf(entry.target); |
| 261 |
setTimeout(function () { |
| 262 |
var card = entry.target.querySelector('.bkbg-ht-card'); |
| 263 |
if (card) { card.classList.remove('bkbg-ht-hidden'); card.classList.add('bkbg-ht-visible'); } |
| 264 |
}, idx * 80); |
| 265 |
observer.unobserve(entry.target); |
| 266 |
} |
| 267 |
}); |
| 268 |
}, { threshold: 0.1 }); |
| 269 |
|
| 270 |
track.querySelectorAll('.bkbg-ht-item-col').forEach(function (col) { observer.observe(col); }); |
| 271 |
} |
| 272 |
} |
| 273 |
|