| 1 |
(function () { |
| 2 |
'use strict'; |
| 3 |
|
| 4 |
var EMOJI_POOL = ['� |
| 5 |
','📌','🔖','⭐','💡','🎯','📝','🔑','🏆','💎','🌟','🔥']; |
| 6 |
|
| 7 |
function buildBlock(root) { |
| 8 |
var o; |
| 9 |
try { o = JSON.parse(root.getAttribute('data-opts')); } catch (e) { return; } |
| 10 |
|
| 11 |
// CSS vars |
| 12 |
root.style.setProperty('--cl-accent', o.accentColor || '#10b981'); |
| 13 |
root.style.setProperty('--cl-check-color', o.checkColor || '#ffffff'); |
| 14 |
root.style.setProperty('--cl-done-color', o.doneTextColor || '#9ca3af'); |
| 15 |
root.style.setProperty('--cl-prog-bg', o.progressBg || '#d1fae5'); |
| 16 |
root.style.setProperty('--cl-section-bg', o.sectionBg || '#f0fdf4'); |
| 17 |
root.style.setProperty('--cl-item-bg', o.itemBg || '#f9fafb'); |
| 18 |
root.style.setProperty('--cl-title-color', o.titleColor || '#111827'); |
| 19 |
root.style.setProperty('--cl-subtitle-color',o.subtitleColor|| '#6b7280'); |
| 20 |
root.style.setProperty('--cl-label-color', o.labelColor || '#374151'); |
| 21 |
root.style.setProperty('--cl-max-width', (o.contentMaxWidth||600) + 'px'); |
| 22 |
var STORAGE_KEY = 'bkbg-cl-' + (root.closest('[id]') || root).id; |
| 23 |
|
| 24 |
// Load persisted state |
| 25 |
var doneSet = {}; |
| 26 |
var extraItems = []; |
| 27 |
if (o.rememberState) { |
| 28 |
try { |
| 29 |
var saved = JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}'); |
| 30 |
doneSet = saved.doneSet || {}; |
| 31 |
extraItems = saved.extraItems || []; |
| 32 |
} catch (e) {} |
| 33 |
} |
| 34 |
|
| 35 |
// Merge items |
| 36 |
var items = (o.items || []).map(function (i) { |
| 37 |
return Object.assign({}, i, { done: !!doneSet[i.id] }); |
| 38 |
}).concat(extraItems.map(function (i) { |
| 39 |
return Object.assign({}, i, { done: !!doneSet[i.id] }); |
| 40 |
})); |
| 41 |
|
| 42 |
function persist() { |
| 43 |
if (!o.rememberState) return; |
| 44 |
var ds = {}; |
| 45 |
items.forEach(function (i) { if (i.done) ds[i.id] = true; }); |
| 46 |
try { localStorage.setItem(STORAGE_KEY, JSON.stringify({ doneSet: ds, extraItems: extraItems })); } catch (e) {} |
| 47 |
} |
| 48 |
|
| 49 |
// Build wrap |
| 50 |
function typoCssVarsForEl(typo, prefix, el) { |
| 51 |
if (!typo) return; |
| 52 |
if (typo.family) el.style.setProperty(prefix + 'font-family', "'" + typo.family + "', sans-serif"); |
| 53 |
if (typo.weight) el.style.setProperty(prefix + 'font-weight', typo.weight); |
| 54 |
if (typo.transform) el.style.setProperty(prefix + 'text-transform', typo.transform); |
| 55 |
if (typo.style) el.style.setProperty(prefix + 'font-style', typo.style); |
| 56 |
if (typo.decoration) el.style.setProperty(prefix + 'text-decoration', typo.decoration); |
| 57 |
var su = typo.sizeUnit || 'px'; |
| 58 |
if (typo.sizeDesktop !== '' && typo.sizeDesktop != null) el.style.setProperty(prefix + 'font-size-d', typo.sizeDesktop + su); |
| 59 |
if (typo.sizeTablet !== '' && typo.sizeTablet != null) el.style.setProperty(prefix + 'font-size-t', typo.sizeTablet + su); |
| 60 |
if (typo.sizeMobile !== '' && typo.sizeMobile != null) el.style.setProperty(prefix + 'font-size-m', typo.sizeMobile + su); |
| 61 |
var lhu = typo.lineHeightUnit || ''; |
| 62 |
if (typo.lineHeightDesktop !== '' && typo.lineHeightDesktop != null) el.style.setProperty(prefix + 'line-height-d', typo.lineHeightDesktop + lhu); |
| 63 |
if (typo.lineHeightTablet !== '' && typo.lineHeightTablet != null) el.style.setProperty(prefix + 'line-height-t', typo.lineHeightTablet + lhu); |
| 64 |
if (typo.lineHeightMobile !== '' && typo.lineHeightMobile != null) el.style.setProperty(prefix + 'line-height-m', typo.lineHeightMobile + lhu); |
| 65 |
var lsu = typo.letterSpacingUnit || 'px'; |
| 66 |
if (typo.letterSpacingDesktop !== '' && typo.letterSpacingDesktop != null) el.style.setProperty(prefix + 'letter-spacing-d', typo.letterSpacingDesktop + lsu); |
| 67 |
if (typo.letterSpacingTablet !== '' && typo.letterSpacingTablet != null) el.style.setProperty(prefix + 'letter-spacing-t', typo.letterSpacingTablet + lsu); |
| 68 |
if (typo.letterSpacingMobile !== '' && typo.letterSpacingMobile != null) el.style.setProperty(prefix + 'letter-spacing-m', typo.letterSpacingMobile + lsu); |
| 69 |
var wsu = typo.wordSpacingUnit || 'px'; |
| 70 |
if (typo.wordSpacingDesktop !== '' && typo.wordSpacingDesktop != null) el.style.setProperty(prefix + 'word-spacing-d', typo.wordSpacingDesktop + wsu); |
| 71 |
if (typo.wordSpacingTablet !== '' && typo.wordSpacingTablet != null) el.style.setProperty(prefix + 'word-spacing-t', typo.wordSpacingTablet + wsu); |
| 72 |
if (typo.wordSpacingMobile !== '' && typo.wordSpacingMobile != null) el.style.setProperty(prefix + 'word-spacing-m', typo.wordSpacingMobile + wsu); |
| 73 |
} |
| 74 |
|
| 75 |
var wrap = document.createElement('div'); |
| 76 |
wrap.className = 'bkbg-cl-wrap'; |
| 77 |
wrap.style.background = o.sectionBg || '#f0fdf4'; |
| 78 |
typoCssVarsForEl(o.typoTitle, '--bkbg-cl-tt-', wrap); |
| 79 |
typoCssVarsForEl(o.typoItem, '--bkbg-cl-it-', wrap); |
| 80 |
root.appendChild(wrap); |
| 81 |
|
| 82 |
if (o.showTitle && o.title) { |
| 83 |
var h = document.createElement('h3'); |
| 84 |
h.className = 'bkbg-cl-title'; |
| 85 |
h.textContent = o.title; |
| 86 |
wrap.appendChild(h); |
| 87 |
} |
| 88 |
if (o.showSubtitle && o.subtitle) { |
| 89 |
var sub = document.createElement('p'); |
| 90 |
sub.className = 'bkbg-cl-subtitle'; |
| 91 |
sub.textContent = o.subtitle; |
| 92 |
wrap.appendChild(sub); |
| 93 |
} |
| 94 |
|
| 95 |
// Progress |
| 96 |
var progRow = null, progBar = null, progPct = null; |
| 97 |
if (o.showProgress) { |
| 98 |
var progWrap = document.createElement('div'); |
| 99 |
progRow = document.createElement('div'); |
| 100 |
progRow.className = 'bkbg-cl-prog-row'; |
| 101 |
var progLabel = document.createElement('span'); |
| 102 |
progLabel.textContent = 'Progress'; |
| 103 |
progPct = document.createElement('span'); |
| 104 |
progPct.className = 'bkbg-cl-prog-pct'; |
| 105 |
progRow.appendChild(progLabel); |
| 106 |
progRow.appendChild(progPct); |
| 107 |
var progTrack = document.createElement('div'); |
| 108 |
progTrack.className = 'bkbg-cl-prog-track'; |
| 109 |
progBar = document.createElement('div'); |
| 110 |
progBar.className = 'bkbg-cl-prog-bar'; |
| 111 |
progTrack.appendChild(progBar); |
| 112 |
progWrap.appendChild(progRow); |
| 113 |
progWrap.appendChild(progTrack); |
| 114 |
wrap.appendChild(progWrap); |
| 115 |
} |
| 116 |
|
| 117 |
// List |
| 118 |
var listEl = document.createElement('div'); |
| 119 |
listEl.className = 'bkbg-cl-list'; |
| 120 |
wrap.appendChild(listEl); |
| 121 |
|
| 122 |
// Congrats |
| 123 |
var congratsEl = document.createElement('div'); |
| 124 |
congratsEl.className = 'bkbg-cl-congrats'; |
| 125 |
congratsEl.style.display = 'none'; |
| 126 |
congratsEl.textContent = '🎉 All done! Great work!'; |
| 127 |
wrap.appendChild(congratsEl); |
| 128 |
|
| 129 |
// Add item row |
| 130 |
var addInput = null; |
| 131 |
if (o.showAddItem) { |
| 132 |
var addRow = document.createElement('div'); |
| 133 |
addRow.className = 'bkbg-cl-add-row'; |
| 134 |
addInput = document.createElement('input'); |
| 135 |
addInput.type = 'text'; |
| 136 |
addInput.className = 'bkbg-cl-add-input'; |
| 137 |
addInput.placeholder = 'Add a new item…'; |
| 138 |
var addBtn = document.createElement('button'); |
| 139 |
addBtn.className = 'bkbg-cl-add-btn'; |
| 140 |
addBtn.textContent = '+ Add'; |
| 141 |
addBtn.addEventListener('click', function () { |
| 142 |
var txt = addInput.value.trim(); |
| 143 |
if (!txt) return; |
| 144 |
var newItem = { id: Date.now().toString(36), text: txt, done: false, icon: EMOJI_POOL[items.length % EMOJI_POOL.length] }; |
| 145 |
items.push(newItem); |
| 146 |
extraItems.push(newItem); |
| 147 |
addInput.value = ''; |
| 148 |
renderList(); |
| 149 |
persist(); |
| 150 |
}); |
| 151 |
addInput.addEventListener('keydown', function (e) { if (e.key === 'Enter') addBtn.click(); }); |
| 152 |
addRow.appendChild(addInput); |
| 153 |
addRow.appendChild(addBtn); |
| 154 |
wrap.appendChild(addRow); |
| 155 |
} |
| 156 |
|
| 157 |
// ── Render ── |
| 158 |
function renderProgress() { |
| 159 |
if (!progBar) return; |
| 160 |
var total = items.length; |
| 161 |
var done = items.filter(function (i) { return i.done; }).length; |
| 162 |
var pct = total ? Math.round((done / total) * 100) : 0; |
| 163 |
progBar.style.width = pct + '%'; |
| 164 |
progPct.textContent = pct + '% (' + done + '/' + total + ')'; |
| 165 |
if (total > 0 && done === total) { |
| 166 |
congratsEl.style.display = 'block'; |
| 167 |
congratsEl.style.animation = 'none'; |
| 168 |
void congratsEl.offsetWidth; |
| 169 |
congratsEl.style.animation = 'cl-pop .4s ease'; |
| 170 |
} else { |
| 171 |
congratsEl.style.display = 'none'; |
| 172 |
} |
| 173 |
} |
| 174 |
|
| 175 |
function renderList() { |
| 176 |
listEl.innerHTML = ''; |
| 177 |
items.forEach(function (item, idx) { |
| 178 |
var row = document.createElement('div'); |
| 179 |
row.className = 'bkbg-cl-item' + (item.done ? ' is-done' : ''); |
| 180 |
|
| 181 |
// Checkbox |
| 182 |
var chk = document.createElement('button'); |
| 183 |
chk.className = 'bkbg-cl-check is-' + (o.checkStyle || 'circle') + (item.done ? ' is-checked' : ''); |
| 184 |
chk.setAttribute('aria-label', item.done ? 'Uncheck' : 'Check'); |
| 185 |
chk.textContent = item.done ? '✓' : ''; |
| 186 |
chk.addEventListener('click', function () { |
| 187 |
items[idx].done = !items[idx].done; |
| 188 |
persist(); |
| 189 |
renderList(); |
| 190 |
renderProgress(); |
| 191 |
}); |
| 192 |
|
| 193 |
var icon = document.createElement('span'); |
| 194 |
icon.className = 'bkbg-cl-icon'; |
| 195 |
icon.textContent = item.icon || '📌'; |
| 196 |
|
| 197 |
var txt = document.createElement('span'); |
| 198 |
txt.className = 'bkbg-cl-text'; |
| 199 |
txt.textContent = item.text; |
| 200 |
|
| 201 |
row.appendChild(chk); |
| 202 |
row.appendChild(icon); |
| 203 |
row.appendChild(txt); |
| 204 |
|
| 205 |
if (o.allowDelete) { |
| 206 |
var del = document.createElement('button'); |
| 207 |
del.className = 'bkbg-cl-del'; |
| 208 |
del.setAttribute('aria-label', 'Delete'); |
| 209 |
del.textContent = '×'; |
| 210 |
del.addEventListener('click', function () { |
| 211 |
items.splice(idx, 1); |
| 212 |
// Also remove from extraItems if it was added by user |
| 213 |
var ei = extraItems.findIndex(function (e) { return e.id === item.id; }); |
| 214 |
if (ei !== -1) extraItems.splice(ei, 1); |
| 215 |
persist(); |
| 216 |
renderList(); |
| 217 |
renderProgress(); |
| 218 |
}); |
| 219 |
row.appendChild(del); |
| 220 |
} |
| 221 |
|
| 222 |
listEl.appendChild(row); |
| 223 |
}); |
| 224 |
} |
| 225 |
|
| 226 |
renderList(); |
| 227 |
renderProgress(); |
| 228 |
} |
| 229 |
|
| 230 |
document.querySelectorAll('.bkbg-cl-app').forEach(buildBlock); |
| 231 |
})(); |
| 232 |
|