PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.7
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.7
2.0.13 2.0.12 2.0.11 2.0.10 2.0.9 trunk 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8
blockenberg / blocks / split-bill-calculator / frontend.js

frontend.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.7, at blocks/split-bill-calculator/frontend.js

256 lines 10.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () {
2 'use strict';
3
4 var _typoKeys = [
5 ['family','font-family'],['weight','font-weight'],['style','font-style'],
6 ['decoration','text-decoration'],['transform','text-transform'],
7 ['sizeDesktop','font-size-d'],['sizeTablet','font-size-t'],['sizeMobile','font-size-m'],
8 ['lineHeightDesktop','line-height-d'],['lineHeightTablet','line-height-t'],['lineHeightMobile','line-height-m'],
9 ['letterSpacingDesktop','letter-spacing-d'],['letterSpacingTablet','letter-spacing-t'],['letterSpacingMobile','letter-spacing-m'],
10 ['wordSpacingDesktop','word-spacing-d'],['wordSpacingTablet','word-spacing-t'],['wordSpacingMobile','word-spacing-m']
11 ];
12 function typoCssVarsForEl(el, obj, prefix) {
13 if (!obj || typeof obj !== 'object') return;
14 _typoKeys.forEach(function (pair) {
15 var val = obj[pair[0]];
16 if (val !== undefined && val !== '') el.style.setProperty(prefix + pair[1], String(val));
17 });
18 if (obj.sizeUnit && obj.sizeUnit !== 'px') {
19 ['sizeDesktop','sizeTablet','sizeMobile'].forEach(function (k, i) {
20 var v = obj[k]; if (v !== undefined && v !== '') el.style.setProperty(prefix + ['font-size-d','font-size-t','font-size-m'][i], v + obj.sizeUnit);
21 });
22 }
23 }
24
25 function fmt(sym, n) {
26 return sym + n.toFixed(2);
27 }
28
29 function init(app) {
30 try {
31 var a = JSON.parse(app.getAttribute('data-opts'));
32 } catch(e) { return; }
33
34 var sym = a.currencySymbol || '$';
35 var accent = a.accentColor || '#6c3fb5';
36 var tipPresets = String(a.tipPresets || '10,15,20,25').split(',').map(function(s){ return parseInt(s.trim()) || 0; });
37
38 var billVal = parseFloat(a.defaultBill) || 100;
39 var tipVal = parseFloat(a.defaultTip) || 15;
40 var peopleVal = parseInt(a.defaultPeople) || 4;
41
42 // Build DOM
43 app.innerHTML = '';
44 var wrap = document.createElement('div');
45 wrap.className = 'bkbg-sbc-wrap';
46 wrap.style.maxWidth = (a.maxWidth || 500) + 'px';
47 wrap.style.borderRadius = (a.cardRadius || 16) + 'px';
48 wrap.style.background = a.cardBg || '#fff';
49 app.style.paddingTop = (a.paddingTop || 60) + 'px';
50 app.style.paddingBottom = (a.paddingBottom || 60) + 'px';
51 if (a.sectionBg) app.style.background = a.sectionBg;
52
53 typoCssVarsForEl(app, a.titleTypo, '--bksbc-tt-');
54 typoCssVarsForEl(app, a.subtitleTypo, '--bksbc-st-');
55 typoCssVarsForEl(app, a.resultTypo, '--bksbc-rs-');
56
57 app.appendChild(wrap);
58
59 // Header
60 if (a.showTitle || a.showSubtitle) {
61 var header = document.createElement('div');
62 header.className = 'bkbg-sbc-header';
63 if (a.showTitle && a.title) {
64 var title = document.createElement('div');
65 title.className = 'bkbg-sbc-title';
66 title.textContent = a.title;
67 title.style.color = a.titleColor || '';
68 header.appendChild(title);
69 }
70 if (a.showSubtitle && a.subtitle) {
71 var sub = document.createElement('div');
72 sub.className = 'bkbg-sbc-subtitle';
73 sub.textContent = a.subtitle;
74 sub.style.color = a.subtitleColor || '';
75 header.appendChild(sub);
76 }
77 wrap.appendChild(header);
78 }
79
80 var inputRadius = (a.inputRadius || 8) + 'px';
81 var inputBorder = '1.5px solid ' + (a.inputBorder || '#e5e7eb');
82
83 function makeField(labelText) {
84 var field = document.createElement('div');
85 field.className = 'bkbg-sbc-field';
86 var lbl = document.createElement('div');
87 lbl.className = 'bkbg-sbc-label';
88 lbl.style.color = a.labelColor || '#374151';
89 lbl.textContent = labelText;
90 field.appendChild(lbl);
91 return field;
92 }
93
94 function makeInput(type, val) {
95 var inp = document.createElement('input');
96 inp.type = type;
97 inp.className = 'bkbg-sbc-input';
98 inp.value = val;
99 inp.min = '0';
100 inp.style.borderRadius = inputRadius;
101 inp.style.border = inputBorder;
102 inp.style.fontSize = '16px';
103 return inp;
104 }
105
106 // Bill field
107 var billField = makeField('Bill Amount');
108 var billInput = makeInput('number', billVal);
109 billField.appendChild(billInput);
110 wrap.appendChild(billField);
111
112 // People field
113 var peopleField = makeField('Number of People');
114 var peopleInput = makeInput('number', peopleVal);
115 peopleInput.min = '1';
116 peopleField.appendChild(peopleInput);
117 wrap.appendChild(peopleField);
118
119 // Tip field
120 var tipField = makeField('Tip Percentage');
121
122 var customTipInput;
123 if (a.showTipPresets) {
124 var presetsRow = document.createElement('div');
125 presetsRow.className = 'bkbg-sbc-presets';
126
127 tipPresets.forEach(function(p) {
128 var btn = document.createElement('button');
129 btn.className = 'bkbg-sbc-preset-btn';
130 btn.textContent = p + '%';
131 btn.style.background = p === tipVal ? (a.presetActiveBg || accent) : (a.presetBg || '#f3f4f6');
132 btn.style.borderColor = p === tipVal ? (a.presetActiveBg || accent) : '#e5e7eb';
133 btn.style.color = p === tipVal ? (a.presetActiveColor || '#fff') : (a.presetColor || '#374151');
134 btn.addEventListener('click', function() {
135 tipVal = p;
136 if (customTipInput) customTipInput.value = p;
137 updatePresetButtons();
138 recalculate();
139 });
140 presetsRow.appendChild(btn);
141 });
142
143 customTipInput = document.createElement('input');
144 customTipInput.type = 'number';
145 customTipInput.className = 'bkbg-sbc-tip-custom';
146 customTipInput.value = tipVal;
147 customTipInput.min = '0';
148 customTipInput.max = '100';
149 customTipInput.style.borderRadius = inputRadius;
150 customTipInput.style.border = inputBorder;
151 customTipInput.addEventListener('input', function() {
152 tipVal = parseFloat(this.value) || 0;
153 updatePresetButtons();
154 recalculate();
155 });
156 presetsRow.appendChild(customTipInput);
157 tipField.appendChild(presetsRow);
158 } else {
159 var tipInputDirect = makeInput('number', tipVal);
160 tipInputDirect.max = '100';
161 tipInputDirect.addEventListener('input', function(){ tipVal = parseFloat(this.value)||0; recalculate(); });
162 tipField.appendChild(tipInputDirect);
163 }
164 wrap.appendChild(tipField);
165
166 function updatePresetButtons() {
167 var btns = presetsRow ? presetsRow.querySelectorAll('.bkbg-sbc-preset-btn') : [];
168 tipPresets.forEach(function(p, i) {
169 var active = p === tipVal;
170 btns[i].style.background = active ? (a.presetActiveBg || accent) : (a.presetBg || '#f3f4f6');
171 btns[i].style.borderColor = active ? (a.presetActiveBg || accent) : '#e5e7eb';
172 btns[i].style.color = active ? (a.presetActiveColor || '#fff'): (a.presetColor || '#374151');
173 });
174 }
175
176 // Result card
177 var resultCard = document.createElement('div');
178 resultCard.className = 'bkbg-sbc-result';
179 resultCard.style.background = a.resultBg || accent;
180 resultCard.style.borderRadius = inputRadius;
181 resultCard.style.marginBottom = a.showBreakdown ? '16px' : '0';
182
183 var resultLabel = document.createElement('div');
184 resultLabel.className = 'bkbg-sbc-result-label';
185 resultLabel.style.color = a.resultColor || '#fff';
186 resultLabel.textContent = 'Each person pays';
187
188 var resultAmount = document.createElement('div');
189 resultAmount.className = 'bkbg-sbc-result-amount';
190 resultAmount.style.color = a.resultColor || '#fff';
191
192 resultCard.appendChild(resultLabel);
193 resultCard.appendChild(resultAmount);
194 wrap.appendChild(resultCard);
195
196 // Breakdown
197 var breakdownEl = null;
198 var rows = {};
199 if (a.showBreakdown) {
200 breakdownEl = document.createElement('div');
201 breakdownEl.className = 'bkbg-sbc-breakdown';
202 breakdownEl.style.background = a.breakdownBg || '#f5f3ff';
203 breakdownEl.style.borderColor = a.breakdownBorder || '#ede9fe';
204 breakdownEl.style.borderRadius = inputRadius;
205
206 ['Bill Subtotal', 'Tip', 'Total', 'Split'].forEach(function(key) {
207 var row = document.createElement('div');
208 row.className = 'bkbg-sbc-breakdown-row';
209 row.style.borderBottomColor = a.breakdownBorder || '#ede9fe';
210 row.style.color = a.labelColor || '#374151';
211 var lbl = document.createElement('span');
212 var val = document.createElement('strong');
213 row.appendChild(lbl);
214 row.appendChild(val);
215 breakdownEl.appendChild(row);
216 rows[key] = { lbl: lbl, val: val };
217 });
218 wrap.appendChild(breakdownEl);
219 }
220
221 function recalculate() {
222 billVal = parseFloat(billInput.value) || 0;
223 peopleVal = Math.max(1, parseInt(peopleInput.value) || 1);
224
225 var tipAmt = billVal * tipVal / 100;
226 var total = billVal + tipAmt;
227 var perPerson = a.roundUp
228 ? Math.ceil(total / peopleVal * 100) / 100
229 : total / peopleVal;
230
231 resultAmount.textContent = fmt(sym, perPerson);
232
233 if (a.showBreakdown && rows) {
234 rows['Bill Subtotal'].lbl.textContent = 'Bill Subtotal';
235 rows['Bill Subtotal'].val.textContent = fmt(sym, billVal);
236 rows['Tip'].lbl.textContent = 'Tip (' + tipVal + '%)';
237 rows['Tip'].val.textContent = '+' + fmt(sym, tipAmt);
238 rows['Total'].lbl.textContent = 'Total';
239 rows['Total'].val.textContent = fmt(sym, total);
240 rows['Split'].lbl.textContent = 'Split ' + peopleVal + ' ways';
241 rows['Split'].val.textContent = fmt(sym, perPerson) + ' each';
242 }
243 }
244
245 billInput.addEventListener('input', recalculate);
246 peopleInput.addEventListener('input', recalculate);
247 if (!a.showTipPresets && typeof tipInputDirect !== 'undefined') {
248 // already attached above
249 }
250
251 recalculate();
252 }
253
254 document.querySelectorAll('.bkbg-sbc-app').forEach(init);
255 })();
256