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 / index.js

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

179 lines 14.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 ( function () {
2 var el = wp.element.createElement;
3 var useState = wp.element.useState;
4 var Fragment = wp.element.Fragment;
5 var registerBlockType = wp.blocks.registerBlockType;
6 var __ = wp.i18n.__;
7 var InspectorControls = wp.blockEditor.InspectorControls;
8 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
9 var useBlockProps = wp.blockEditor.useBlockProps;
10 var PanelBody = wp.components.PanelBody;
11 var RangeControl = wp.components.RangeControl;
12 var TextControl = wp.components.TextControl;
13 var ToggleControl = wp.components.ToggleControl;
14 var Button = wp.components.Button;
15
16 var _tc; function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
17 var _tv; function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
18
19 function fmt(sym, n) { return sym + n.toFixed(2); }
20
21 function SplitBillPreview(props) {
22 var a = props.attributes;
23 var sa = props.setAttributes;
24 var sym = a.currencySymbol || '$';
25 var accent = a.accentColor || '#6c3fb5';
26
27 var _bill = useState(String(a.defaultBill || 100)); var bill = _bill[0]; var setBill = _bill[1];
28 var _tip = useState(a.defaultTip || 15); var tip = _tip[0]; var setTip = _tip[1];
29 var _people = useState(String(a.defaultPeople || 4)); var people = _people[0]; var setPeople = _people[1];
30
31 var billN = parseFloat(bill) || 0;
32 var tipN = parseFloat(tip) || 0;
33 var peopleN = Math.max(1, parseInt(people) || 1);
34 var tipAmt = billN * tipN / 100;
35 var total = billN + tipAmt;
36 var perPerson = a.roundUp ? Math.ceil(total / peopleN * 100) / 100 : total / peopleN;
37
38 var tipPresets = String(a.tipPresets || '10,15,20,25').split(',').map(function(s){ return parseInt(s.trim()) || 0; });
39
40 function inputRow(lbl, val, setVal, hint, type) {
41 return el('div', { style:{ marginBottom:'16px' }},
42 el('label', { style:{ display:'flex',justifyContent:'space-between',fontSize:'13px',fontWeight:600,color:a.labelColor,marginBottom:'5px' }},
43 el('span', null, lbl),
44 hint && el('span', { style:{ fontWeight:400,color:'#9ca3af',fontSize:'12px' }}, hint)
45 ),
46 el('input', { type: type||'number', value: val, min:0,
47 style:{ width:'100%',padding:'11px 14px',borderRadius:(a.inputRadius||8)+'px',border:'1.5px solid '+(a.inputBorder||'#e5e7eb'),fontSize:'16px',outline:'none',boxSizing:'border-box',fontFamily:'inherit' },
48 onChange: function(e){ setVal(e.target.value); }
49 })
50 );
51 }
52
53 return el('div', { style:{ paddingTop:a.paddingTop+'px',paddingBottom:a.paddingBottom+'px',background:a.sectionBg||undefined }},
54 el('div', { style:{ background:a.cardBg,borderRadius:(a.cardRadius||16)+'px',padding:'36px 32px',maxWidth:(a.maxWidth||500)+'px',margin:'0 auto',boxShadow:'0 4px 24px rgba(0,0,0,0.09)' }},
55
56 (a.showTitle||a.showSubtitle) && el('div', { style:{marginBottom:'22px'}},
57 a.showTitle && el('div', { className: 'bkbg-sbc-title', style:{color:a.titleColor,marginBottom:'6px'} }, a.title),
58 a.showSubtitle && el('div', { className: 'bkbg-sbc-subtitle', style:{color:a.subtitleColor} }, a.subtitle)
59 ),
60
61 inputRow('Bill Amount', bill, setBill, sym),
62 inputRow('Number of People', people, setPeople, 'persons', 'number'),
63
64 // Tip %
65 el('div', { style:{marginBottom:'16px'}},
66 el('label', { style:{display:'block',fontSize:'13px',fontWeight:600,color:a.labelColor,marginBottom:'5px' }}, 'Tip Percentage'),
67 a.showTipPresets && el('div', { style:{display:'flex',gap:'8px',flexWrap:'wrap',marginBottom:'10px'}},
68 tipPresets.map(function(p) {
69 var active = tip === p;
70 return el('button', { key:p, onClick:function(){ setTip(p); }, style:{ flex:1, padding:'8px 6px', borderRadius:'8px', border:'2px solid '+(active?accent:'#e5e7eb'), background:active?(a.presetActiveBg||accent):(a.presetBg||'#f3f4f6'), color:active?(a.presetActiveColor||'#fff'):(a.presetColor||'#374151'), fontWeight:700, fontSize:'14px', cursor:'pointer', fontFamily:'inherit', minWidth:'48px' }}, p+'%');
71 }),
72 el('input', { type:'number', value:tip, min:0, max:100,
73 style:{width:'72px',padding:'8px 10px',borderRadius:'8px',border:'1.5px solid '+(a.inputBorder||'#e5e7eb'),fontSize:'15px',outline:'none',fontFamily:'inherit'},
74 onChange: function(e){ setTip(parseFloat(e.target.value)||0); }
75 })
76 ),
77 !a.showTipPresets && el('input', { type:'number', value:tip, min:0, max:100,
78 style:{width:'100%',padding:'11px 14px',borderRadius:(a.inputRadius||8)+'px',border:'1.5px solid '+(a.inputBorder||'#e5e7eb'),fontSize:'16px',outline:'none',boxSizing:'border-box',fontFamily:'inherit'},
79 onChange: function(e){ setTip(parseFloat(e.target.value)||0); }
80 })
81 ),
82
83 // Per-person result
84 el('div', { style:{background:a.resultBg||accent,borderRadius:(a.inputRadius||8)+'px',padding:'24px',textAlign:'center',marginBottom:a.showBreakdown?'16px':'0' }},
85 el('div', { style:{fontSize:'13px',fontWeight:600,color:a.resultColor||'#fff',opacity:.8,marginBottom:'6px'} }, 'Each person pays'),
86 el('div', { className: 'bkbg-sbc-result-amount', style:{color:a.resultColor||'#fff'} }, sym + perPerson.toFixed(2))
87 ),
88
89 // Breakdown
90 a.showBreakdown && el('div', { style:{background:a.breakdownBg||'#f5f3ff',border:'1.5px solid '+(a.breakdownBorder||'#ede9fe'),borderRadius:(a.inputRadius||8)+'px',padding:'16px 20px'} },
91 [
92 ['Bill Subtotal', fmt(sym, billN)],
93 ['Tip (' + tipN + '%)', '+' + fmt(sym, tipAmt)],
94 ['Total', fmt(sym, total)],
95 ['Split ' + peopleN + ' ways', fmt(sym, perPerson) + ' each']
96 ].map(function(row) {
97 return el('div', { key:row[0], style:{display:'flex',justifyContent:'space-between',padding:'5px 0',fontSize:'14px',color:a.labelColor,borderBottom:'1px solid '+(a.breakdownBorder||'#ede9fe')} },
98 el('span', null, row[0]),
99 el('span', { style:{fontWeight:600} }, row[1])
100 );
101 })
102 )
103 )
104 );
105 }
106
107 registerBlockType('blockenberg/split-bill-calculator', {
108 edit: function(props) {
109 var a = props.attributes; var set = props.setAttributes;
110 var blockProps = useBlockProps((function () {
111 var s = {};
112 var tv = getTypoCssVars();
113 if (tv) {
114 Object.assign(s, tv(a.titleTypo, '--bksbc-tt-'));
115 Object.assign(s, tv(a.subtitleTypo, '--bksbc-st-'));
116 Object.assign(s, tv(a.resultTypo, '--bksbc-rs-'));
117 }
118 return { style: s };
119 })());
120 var colorSettings = [
121 { value: a.accentColor, onChange: function(v){ set({accentColor:v}); }, label: 'Accent Color' },
122 { value: a.cardBg, onChange: function(v){ set({cardBg:v}); }, label: 'Card Background' },
123 { value: a.resultBg, onChange: function(v){ set({resultBg:v}); }, label: 'Result Background' },
124 { value: a.resultColor, onChange: function(v){ set({resultColor:v}); }, label: 'Result Text' },
125 { value: a.presetBg, onChange: function(v){ set({presetBg:v}); }, label: 'Tip Preset Background' },
126 { value: a.presetColor, onChange: function(v){ set({presetColor:v}); }, label: 'Tip Preset Text' },
127 { value: a.presetActiveBg, onChange: function(v){ set({presetActiveBg:v}); }, label: 'Active Preset Background' },
128 { value: a.presetActiveColor, onChange: function(v){ set({presetActiveColor:v}); }, label: 'Active Preset Text' },
129 { value: a.breakdownBg, onChange: function(v){ set({breakdownBg:v}); }, label: 'Breakdown Background' },
130 { value: a.breakdownBorder, onChange: function(v){ set({breakdownBorder:v}); }, label: 'Breakdown Border' },
131 { value: a.labelColor, onChange: function(v){ set({labelColor:v}); }, label: 'Label Color' },
132 { value: a.inputBorder, onChange: function(v){ set({inputBorder:v}); }, label: 'Input Border' },
133 { value: a.titleColor, onChange: function(v){ set({titleColor:v}); }, label: 'Title Color' },
134 { value: a.subtitleColor, onChange: function(v){ set({subtitleColor:v}); }, label: 'Subtitle Color' },
135 { value: a.sectionBg, onChange: function(v){ set({sectionBg:v}); }, label: 'Section Background' }
136 ];
137 return el(Fragment, null,
138 el(InspectorControls, null,
139 el(PanelBody, { title: 'Header', initialOpen: false },
140 el(ToggleControl, { __nextHasNoMarginBottom: true, label: 'Show Title', checked: a.showTitle, onChange: function(v){ set({showTitle:v}); } }),
141 el(ToggleControl, { __nextHasNoMarginBottom: true, label: 'Show Subtitle', checked: a.showSubtitle, onChange: function(v){ set({showSubtitle:v}); } }),
142 el(TextControl, { label: 'Title', value: a.title, onChange: function(v){ set({title:v}); } }),
143 el(TextControl, { label: 'Subtitle', value: a.subtitle, onChange: function(v){ set({subtitle:v}); } })
144 ),
145 el(PanelBody, { title: 'Calculator Settings', initialOpen: true },
146 el(TextControl, { label: 'Currency Symbol', value: a.currencySymbol, onChange: function(v){ set({currencySymbol:v}); } }),
147 el(TextControl, { label: 'Default Bill Amount', value: String(a.defaultBill), onChange: function(v){ set({defaultBill:parseFloat(v)||0}); } }),
148 el(TextControl, { label: 'Default Tip (%)', value: String(a.defaultTip), onChange: function(v){ set({defaultTip:parseFloat(v)||0}); } }),
149 el(TextControl, { label: 'Default People', value: String(a.defaultPeople),onChange: function(v){ set({defaultPeople:parseInt(v)||1}); } }),
150 el(ToggleControl, { __nextHasNoMarginBottom: true, label: 'Show Tip Presets', checked: a.showTipPresets, onChange: function(v){ set({showTipPresets:v}); } }),
151 a.showTipPresets && el(TextControl, { label: 'Tip Presets (comma-separated %)', value: a.tipPresets, onChange: function(v){ set({tipPresets:v}); } }),
152 el(ToggleControl, { __nextHasNoMarginBottom: true, label: 'Show Breakdown', checked: a.showBreakdown, onChange: function(v){ set({showBreakdown:v}); } }),
153 el(ToggleControl, { __nextHasNoMarginBottom: true, label: 'Round Up Per Person', checked: a.roundUp, onChange: function(v){ set({roundUp:v}); } })
154 ),
155
156 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
157 getTypoControl() ? el(getTypoControl(), { label: __('Title Typography', 'blockenberg'), value: a.titleTypo || {}, onChange: function (v) { set({ titleTypo: v }); } }) : null,
158 getTypoControl() ? el(getTypoControl(), { label: __('Subtitle Typography', 'blockenberg'), value: a.subtitleTypo || {}, onChange: function (v) { set({ subtitleTypo: v }); } }) : null,
159 getTypoControl() ? el(getTypoControl(), { label: __('Result Typography', 'blockenberg'), value: a.resultTypo || {}, onChange: function (v) { set({ resultTypo: v }); } }) : null
160 ),
161 el(PanelColorSettings, { title: 'Colors', initialOpen: false, colorSettings: colorSettings }),
162 el(PanelBody, { title: 'Sizing & Layout', initialOpen: false },
163 el(RangeControl, { label: 'Card Border Radius', value: a.cardRadius, min: 0, max: 40, step: 1, onChange: function(v){ set({cardRadius:v}); } }),
164 el(RangeControl, { label: 'Input Border Radius',value: a.inputRadius, min: 0, max: 24, step: 1, onChange: function(v){ set({inputRadius:v}); } }),
165 el(RangeControl, { label: 'Max Width (px)', value: a.maxWidth, min: 300,max: 900, step: 10, onChange: function(v){ set({maxWidth:v}); } }),
166 el(RangeControl, { label: 'Padding Top (px)', value: a.paddingTop, min: 0, max: 160, step: 4, onChange: function(v){ set({paddingTop:v}); } }),
167 el(RangeControl, { label: 'Padding Bottom (px)',value: a.paddingBottom, min: 0, max: 160, step: 4, onChange: function(v){ set({paddingBottom:v}); } })
168 )
169 ),
170 el('div', blockProps, el(SplitBillPreview, { attributes: a, setAttributes: set }))
171 );
172 },
173 save: function(props) {
174 var a = props.attributes;
175 return el('div', wp.blockEditor.useBlockProps.save(), el('div', { className: 'bkbg-sbc-app', 'data-opts': JSON.stringify({ titleTypo: a.titleTypo, subtitleTypo: a.subtitleTypo, resultTypo: a.resultTypo, title: a.title, subtitle: a.subtitle, showTitle: a.showTitle, showSubtitle: a.showSubtitle, currencySymbol: a.currencySymbol, defaultBill: a.defaultBill, defaultTip: a.defaultTip, defaultPeople: a.defaultPeople, showTipPresets: a.showTipPresets, tipPresets: a.tipPresets, showBreakdown: a.showBreakdown, roundUp: a.roundUp, accentColor: a.accentColor, cardBg: a.cardBg, resultBg: a.resultBg, resultColor: a.resultColor, presetBg: a.presetBg, presetColor: a.presetColor, presetActiveBg: a.presetActiveBg, presetActiveColor: a.presetActiveColor, breakdownBg: a.breakdownBg, breakdownBorder: a.breakdownBorder, labelColor: a.labelColor, inputBorder: a.inputBorder, titleColor: a.titleColor, subtitleColor: a.subtitleColor, sectionBg: a.sectionBg, cardRadius: a.cardRadius, inputRadius: a.inputRadius, maxWidth: a.maxWidth, paddingTop: a.paddingTop, paddingBottom: a.paddingBottom }) }));
176 }
177 });
178 }() );
179