| 1 |
(function () { |
| 2 |
'use strict'; |
| 3 |
|
| 4 |
function mk(tag, cls, styles) { |
| 5 |
var d = document.createElement(tag); |
| 6 |
if (cls) d.className = cls; |
| 7 |
if (styles) Object.keys(styles).forEach(function (k) { d.style[k] = styles[k]; }); |
| 8 |
return d; |
| 9 |
} |
| 10 |
function tx(tag, cls, text, styles) { |
| 11 |
var d = mk(tag, cls, styles); |
| 12 |
d.textContent = text; |
| 13 |
return d; |
| 14 |
} |
| 15 |
function ap(p) { |
| 16 |
Array.prototype.slice.call(arguments, 1).forEach(function (c) { if (c) p.appendChild(c); }); |
| 17 |
return p; |
| 18 |
} |
| 19 |
|
| 20 |
var _typoKeys = { |
| 21 |
family:'font-family', weight:'font-weight', style:'font-style', |
| 22 |
transform:'text-transform', decoration:'text-decoration', |
| 23 |
sizeDesktop:'font-size-d', sizeTablet:'font-size-t', sizeMobile:'font-size-m', |
| 24 |
lineHeightDesktop:'line-height-d', lineHeightTablet:'line-height-t', lineHeightMobile:'line-height-m', |
| 25 |
letterSpacingDesktop:'letter-spacing-d', letterSpacingTablet:'letter-spacing-t', letterSpacingMobile:'letter-spacing-m', |
| 26 |
wordSpacingDesktop:'word-spacing-d', wordSpacingTablet:'word-spacing-t', wordSpacingMobile:'word-spacing-m' |
| 27 |
}; |
| 28 |
var _typoUnits = { size:'sizeUnit', lineHeight:'lineHeightUnit', letterSpacing:'letterSpacingUnit', wordSpacing:'wordSpacingUnit' }; |
| 29 |
var _typoUnitDefaults = { size:'px', lineHeight:'', letterSpacing:'px', wordSpacing:'px' }; |
| 30 |
function typoCssVarsForEl(el, obj, prefix) { |
| 31 |
if (!obj || typeof obj !== 'object') return; |
| 32 |
Object.keys(_typoKeys).forEach(function (k) { |
| 33 |
var v = obj[k]; if (v === undefined || v === '') return; |
| 34 |
var prop = _typoKeys[k]; |
| 35 |
var base = k.replace(/Desktop|Tablet|Mobile/, ''); |
| 36 |
var uKey = _typoUnits[base]; |
| 37 |
if (uKey && typeof v === 'number') v = v + (obj[uKey] || _typoUnitDefaults[base] || ''); |
| 38 |
el.style.setProperty(prefix + prop, v); |
| 39 |
}); |
| 40 |
} |
| 41 |
|
| 42 |
function buildBlock(appEl) { |
| 43 |
if (appEl.dataset.rendered) return; |
| 44 |
appEl.dataset.rendered = '1'; |
| 45 |
|
| 46 |
var a; |
| 47 |
try { a = JSON.parse(appEl.dataset.opts || '{}'); } catch (e) { a = {}; } |
| 48 |
|
| 49 |
var reactions = a.reactions || []; |
| 50 |
var isCards = a.layout === 'cards'; |
| 51 |
var isCompact = a.layout === 'compact'; |
| 52 |
var emojiSize = a.emojiSize || 22; |
| 53 |
var itemRadius = (a.itemRadius || 100) + 'px'; |
| 54 |
|
| 55 |
// state: which index is "active" (toggled) |
| 56 |
var activeIdx = -1; |
| 57 |
|
| 58 |
// Wrapper |
| 59 |
appEl.style.background = a.bgColor || '#ffffff'; |
| 60 |
appEl.style.border = '1px solid ' + (a.borderColor || '#e5e7eb'); |
| 61 |
appEl.style.borderRadius = (a.borderRadius || 16) + 'px'; |
| 62 |
appEl.style.padding = '20px 24px'; |
| 63 |
appEl.style.fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'; |
| 64 |
|
| 65 |
typoCssVarsForEl(appEl, a.titleTypo || {}, '--bkrab-tt-'); |
| 66 |
typoCssVarsForEl(appEl, a.countTypo || {}, '--bkrab-ct-'); |
| 67 |
|
| 68 |
// Title |
| 69 |
if (a.showTitle && a.title) { |
| 70 |
var titleEl = tx('div', 'bkrab-title', a.title, { |
| 71 |
color: a.titleColor || '#111827', |
| 72 |
marginBottom: '14px' |
| 73 |
}); |
| 74 |
ap(appEl, titleEl); |
| 75 |
} |
| 76 |
|
| 77 |
// Reactions container |
| 78 |
var container = mk('div', '', { |
| 79 |
display: isCards ? 'grid' : 'flex', |
| 80 |
gridTemplateColumns: isCards ? 'repeat(auto-fill, minmax(90px, 1fr))' : '', |
| 81 |
flexWrap: 'wrap', |
| 82 |
gap: '8px' |
| 83 |
}); |
| 84 |
|
| 85 |
// Total tracker |
| 86 |
var totalCounts = reactions.map(function (r) { return r.count || 0; }); |
| 87 |
|
| 88 |
// Total display |
| 89 |
var totalEl = null; |
| 90 |
var totalSum = totalCounts.reduce(function (s, c) { return s + c; }, 0); |
| 91 |
if (a.showTotal) { |
| 92 |
totalEl = tx('div', 'bkrab-total', totalSum + ' ' + (a.totalLabel || 'reactions'), { |
| 93 |
marginTop: '12px', |
| 94 |
color: a.totalColor || '#9ca3af' |
| 95 |
}); |
| 96 |
} |
| 97 |
|
| 98 |
// Build each reaction item |
| 99 |
var itemEls = reactions.map(function (r, idx) { |
| 100 |
var isActive = false; |
| 101 |
|
| 102 |
var itemWrap = mk('div', '', { |
| 103 |
display: 'inline-flex', alignItems: 'center', |
| 104 |
justifyContent: isCards ? 'center' : 'flex-start', |
| 105 |
flexDirection: isCards ? 'column' : 'row', |
| 106 |
gap: '6px', |
| 107 |
padding: isCards ? '16px 12px' : isCompact ? '4px 12px' : '7px 14px', |
| 108 |
borderRadius: isCards ? '12px' : itemRadius, |
| 109 |
border: '1.5px solid ' + (a.itemBorderColor || '#e5e7eb'), |
| 110 |
background: a.itemBg || '#f9fafb', |
| 111 |
cursor: 'pointer', userSelect: 'none', |
| 112 |
transition: 'all .15s ease', |
| 113 |
flexShrink: '0' |
| 114 |
}); |
| 115 |
|
| 116 |
var emojiEl = tx('span', '', r.emoji || '', { fontSize: emojiSize + 'px', lineHeight: '1' }); |
| 117 |
var countSpan = tx('span', 'bkrab-count', String(r.count || 0), { |
| 118 |
color: a.countColor || '#374151' |
| 119 |
}); |
| 120 |
var labelSpan = null; |
| 121 |
if (a.showLabel && !isCompact) { |
| 122 |
labelSpan = tx('span', 'bkrab-label', r.label || '', { |
| 123 |
color: a.labelColor || '#6b7280' |
| 124 |
}); |
| 125 |
} |
| 126 |
|
| 127 |
var metaRow = mk('div', '', { |
| 128 |
display: 'flex', flexDirection: isCards ? 'column' : 'row', |
| 129 |
alignItems: 'center', gap: '4px' |
| 130 |
}); |
| 131 |
if (a.showCount) ap(metaRow, countSpan); |
| 132 |
if (labelSpan) ap(metaRow, labelSpan); |
| 133 |
ap(itemWrap, emojiEl, metaRow); |
| 134 |
|
| 135 |
// Animate helper |
| 136 |
function applyActive(active) { |
| 137 |
var bg = active ? (a.activeItemBg || '#eff6ff') : (a.itemBg || '#f9fafb'); |
| 138 |
var border = active ? (a.activeItemBorder || '#bfdbfe') : (a.itemBorderColor || '#e5e7eb'); |
| 139 |
var cc = active ? (a.activeCountColor || '#1d4ed8') : (a.countColor || '#374151'); |
| 140 |
itemWrap.style.background = bg; |
| 141 |
itemWrap.style.borderColor = border; |
| 142 |
countSpan.style.color = cc; |
| 143 |
} |
| 144 |
|
| 145 |
itemWrap.addEventListener('click', function () { |
| 146 |
isActive = !isActive; |
| 147 |
applyActive(isActive); |
| 148 |
|
| 149 |
// Update count |
| 150 |
var delta = isActive ? 1 : -1; |
| 151 |
totalCounts[idx] += delta; |
| 152 |
countSpan.textContent = String(totalCounts[idx]); |
| 153 |
|
| 154 |
// Bounce animation |
| 155 |
if (a.animate !== false) { |
| 156 |
emojiEl.style.transform = 'scale(1.4)'; |
| 157 |
setTimeout(function () { emojiEl.style.transform = 'scale(1)'; }, 200); |
| 158 |
} |
| 159 |
|
| 160 |
// Update total |
| 161 |
if (totalEl) { |
| 162 |
var sum = totalCounts.reduce(function (s, c) { return s + c; }, 0); |
| 163 |
totalEl.textContent = sum + ' ' + (a.totalLabel || 'reactions'); |
| 164 |
} |
| 165 |
}); |
| 166 |
|
| 167 |
itemWrap.addEventListener('mouseenter', function () { |
| 168 |
if (!isActive) itemWrap.style.background = a.itemHoverBg || '#f3f4f6'; |
| 169 |
}); |
| 170 |
itemWrap.addEventListener('mouseleave', function () { |
| 171 |
if (!isActive) itemWrap.style.background = a.itemBg || '#f9fafb'; |
| 172 |
}); |
| 173 |
|
| 174 |
return itemWrap; |
| 175 |
}); |
| 176 |
|
| 177 |
itemEls.forEach(function (el) { ap(container, el); }); |
| 178 |
ap(appEl, container); |
| 179 |
if (totalEl) ap(appEl, totalEl); |
| 180 |
} |
| 181 |
|
| 182 |
function init() { |
| 183 |
document.querySelectorAll('.bkbg-reaction-bar-app').forEach(buildBlock); |
| 184 |
} |
| 185 |
if (document.readyState !== 'loading') { init(); } else { document.addEventListener('DOMContentLoaded', init); } |
| 186 |
})(); |
| 187 |
|