| 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 SelectControl = wp.components.SelectControl; |
| 13 |
var TextControl = wp.components.TextControl; |
| 14 |
var ToggleControl = wp.components.ToggleControl; |
| 15 |
|
| 16 |
var _tc, _tv; |
| 17 |
function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); } |
| 18 |
function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); } |
| 19 |
|
| 20 |
var BTN_LAYOUT = [ |
| 21 |
['MC','MR','MS','M+','M−'], |
| 22 |
['2nd','π','e','C','⌫'], |
| 23 |
['x²','xʸ','√x','ʸ√x','1/x'], |
| 24 |
['sin','cos','tan','log','ln'], |
| 25 |
['sin⁻¹','cos⁻¹','tan⁻¹','10ˣ','eˣ'], |
| 26 |
['(',')', '%', '÷', ''], |
| 27 |
['7', '8', '9', '×', ''], |
| 28 |
['4', '5', '6', '−', ''], |
| 29 |
['1', '2', '3', '+', ''], |
| 30 |
['±', '0', '.', '=', ''] |
| 31 |
]; |
| 32 |
|
| 33 |
function ScientificCalcPreview(props) { |
| 34 |
var a = props.attributes; |
| 35 |
var accent = a.accentColor || '#6c3fb5'; |
| 36 |
|
| 37 |
var _disp = useState('0'); |
| 38 |
var display = _disp[0]; var setDisplay = _disp[1]; |
| 39 |
|
| 40 |
var _expr = useState(''); |
| 41 |
var expr = _expr[0]; var setExpr = _expr[1]; |
| 42 |
|
| 43 |
var _hist = useState([]); |
| 44 |
var history = _hist[0]; var setHistory = _hist[1]; |
| 45 |
|
| 46 |
var _mem = useState(0); |
| 47 |
var memory = _mem[0]; var setMemory = _mem[1]; |
| 48 |
|
| 49 |
var _ang = useState(a.angleMode || 'deg'); |
| 50 |
var angleMode = _ang[0]; var setAngleMode = _ang[1]; |
| 51 |
|
| 52 |
var _shift = useState(false); |
| 53 |
var shift = _shift[0]; var setShift = _shift[1]; |
| 54 |
|
| 55 |
function toRad(x) { return angleMode === 'deg' ? x * Math.PI / 180 : x; } |
| 56 |
|
| 57 |
function evaluate(e) { |
| 58 |
try { |
| 59 |
var safe = e |
| 60 |
.replace(/×/g,'*').replace(/÷/g,'/') |
| 61 |
.replace(/−/g,'-').replace(/π/g,'Math.PI') |
| 62 |
.replace(/\be\b/g,'Math.E'); |
| 63 |
var r = Function('"use strict"; return (' + safe + ')')(); |
| 64 |
return isFinite(r) ? parseFloat(r.toPrecision(a.precision||10)).toString() : 'Error'; |
| 65 |
} catch(err) { return 'Error'; } |
| 66 |
} |
| 67 |
|
| 68 |
function handleBtn(lbl) { |
| 69 |
if (lbl === '') return; |
| 70 |
if (lbl === 'C') { setDisplay('0'); setExpr(''); return; } |
| 71 |
if (lbl === '⌫') { setDisplay(function(d){ var s = d.slice(0,-1); return s===''||s==='-'?'0':s; }); return; } |
| 72 |
if (lbl === '±') { setDisplay(function(d){ return d.startsWith('-')?d.slice(1):'-'+d; }); return; } |
| 73 |
if (lbl === '=') { |
| 74 |
var full = expr + display; |
| 75 |
var res = evaluate(full); |
| 76 |
if (a.showHistory) setHistory(function(h){ return [{expr:full+'='+res}].concat(h).slice(0,a.historyMax||10); }); |
| 77 |
setExpr(''); setDisplay(res); return; |
| 78 |
} |
| 79 |
if (lbl === 'MC') { setMemory(0); return; } |
| 80 |
if (lbl === 'MR') { setDisplay(memory.toString()); return; } |
| 81 |
if (lbl === 'MS') { setMemory(parseFloat(display)||0); return; } |
| 82 |
if (lbl === 'M+') { setMemory(function(m){ return m + (parseFloat(display)||0); }); return; } |
| 83 |
if (lbl === 'M−') { setMemory(function(m){ return m - (parseFloat(display)||0); }); return; } |
| 84 |
if (lbl === '2nd') { setShift(!shift); return; } |
| 85 |
var num = parseFloat(display); |
| 86 |
var unary = { |
| 87 |
'x²': function(v){ return (v*v).toString(); }, |
| 88 |
'√x': function(v){ return Math.sqrt(v).toString(); }, |
| 89 |
'1/x': function(v){ return (1/v).toString(); }, |
| 90 |
'sin': function(v){ return Math.sin(toRad(v)).toPrecision(a.precision||10)*1+''; }, |
| 91 |
'cos': function(v){ return Math.cos(toRad(v)).toPrecision(a.precision||10)*1+''; }, |
| 92 |
'tan': function(v){ return Math.tan(toRad(v)).toPrecision(a.precision||10)*1+''; }, |
| 93 |
'sin⁻¹': function(v){ return (angleMode==='deg'?Math.asin(v)*180/Math.PI:Math.asin(v)).toPrecision(a.precision||10)*1+''; }, |
| 94 |
'cos⁻¹': function(v){ return (angleMode==='deg'?Math.acos(v)*180/Math.PI:Math.acos(v)).toPrecision(a.precision||10)*1+''; }, |
| 95 |
'tan⁻¹': function(v){ return (angleMode==='deg'?Math.atan(v)*180/Math.PI:Math.atan(v)).toPrecision(a.precision||10)*1+''; }, |
| 96 |
'log': function(v){ return Math.log10(v).toPrecision(a.precision||10)*1+''; }, |
| 97 |
'ln': function(v){ return Math.log(v).toPrecision(a.precision||10)*1+''; }, |
| 98 |
'10ˣ': function(v){ return Math.pow(10,v).toString(); }, |
| 99 |
'eˣ': function(v){ return Math.exp(v).toPrecision(a.precision||10)*1+''; }, |
| 100 |
'%': function(v){ return (v/100).toString(); } |
| 101 |
}; |
| 102 |
if (unary[lbl]) { setDisplay(unary[lbl](num)); return; } |
| 103 |
var ops = ['+','−','×','÷','xʸ','ʸ√x']; |
| 104 |
if (ops.includes(lbl)) { |
| 105 |
setExpr(expr + display + lbl); |
| 106 |
setDisplay('0'); |
| 107 |
return; |
| 108 |
} |
| 109 |
if (lbl === 'π') { setDisplay(Math.PI.toPrecision(a.precision||10)*1+''); return; } |
| 110 |
if (lbl === 'e') { setDisplay(Math.E.toPrecision(a.precision||10)*1+''); return; } |
| 111 |
if (lbl === '(' || lbl === ')') { setExpr(expr+lbl); return; } |
| 112 |
setDisplay(function(d){ return d==='0'||d==='Error'?lbl:(d+lbl); }); |
| 113 |
} |
| 114 |
|
| 115 |
function getBtnStyle(lbl) { |
| 116 |
var base = {padding:'0',border:'none',cursor:'pointer',fontWeight:600,fontSize:(a.btnSize||15)+'px',fontFamily:'inherit',borderRadius:(a.btnRadius||10)+'px',transition:'filter .15s, transform .1s',minHeight:'44px'}; |
| 117 |
if (lbl === '=') return Object.assign({},base,{background:a.btnEqBg||accent,color:a.btnEqColor||'#fff'}); |
| 118 |
if (['C','⌫'].includes(lbl)) return Object.assign({},base,{background:a.btnClearBg||'#7f1d1d',color:a.btnClearColor||'#fecaca'}); |
| 119 |
if (['MC','MR','MS','M+','M−'].includes(lbl))return Object.assign({},base,{background:a.btnFnBg||'#1e2a4a',color:a.memColor||'#86efac'}); |
| 120 |
if (['+','−','×','÷','=','%','xʸ','ʸ√x','(',')',].includes(lbl)) return Object.assign({},base,{background:a.btnOpBg||'#3d2d6a',color:a.btnOpColor||'#c4b5fd'}); |
| 121 |
if (['0','1','2','3','4','5','6','7','8','9','.','±'].includes(lbl)) return Object.assign({},base,{background:a.btnNumBg||'#2d2d4a',color:a.btnNumColor||'#fff'}); |
| 122 |
return Object.assign({},base,{background:a.btnFnBg||'#1e2a4a',color:a.btnFnColor||'#93c5fd'}); |
| 123 |
} |
| 124 |
|
| 125 |
var wrapStyle = {paddingTop:(a.paddingTop||60)+'px',paddingBottom:(a.paddingBottom||60)+'px',background:a.sectionBg||undefined}; |
| 126 |
var cardStyle = {background:a.cardBg||'#1a1a2e',borderRadius:(a.cardRadius||20)+'px',padding:'24px',maxWidth:(a.maxWidth||420)+'px',margin:'0 auto',boxShadow:'0 8px 40px rgba(0,0,0,.45)'}; |
| 127 |
|
| 128 |
return el('div',{style:wrapStyle}, |
| 129 |
(a.showTitle||a.showSubtitle) && el('div',{style:{textAlign:'center',marginBottom:'20px'}}, |
| 130 |
a.showTitle && el('div',{className:'bkbg-sc-title',style:{color:a.titleColor,marginBottom:'6px'}},a.title), |
| 131 |
a.showSubtitle && el('div',{className:'bkbg-sc-subtitle',style:{color:a.subtitleColor}},a.subtitle) |
| 132 |
), |
| 133 |
el('div',{style:cardStyle}, |
| 134 |
// Angle mode + memory indicator |
| 135 |
el('div',{style:{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:'8px'}}, |
| 136 |
el('div',{style:{display:'flex',gap:'4px'}}, |
| 137 |
['deg','rad'].map(function(m){ |
| 138 |
return el('button',{key:m,onClick:function(){setAngleMode(m);},style:{padding:'3px 10px',border:'none',borderRadius:'6px',cursor:'pointer',fontWeight:700,fontSize:'12px',fontFamily:'inherit',background:angleMode===m?(a.accentColor||accent):'rgba(255,255,255,.1)',color:angleMode===m?'#fff':'rgba(255,255,255,.5)'}},m.toUpperCase()); |
| 139 |
}) |
| 140 |
), |
| 141 |
a.showMemory && el('div',{style:{fontSize:'12px',color:a.memColor||'#86efac',fontWeight:700}}, memory !== 0 ? 'M: '+memory : '') |
| 142 |
), |
| 143 |
// Expression bar |
| 144 |
el('div',{style:{textAlign:'right',fontSize:'13px',color:'rgba(255,255,255,.4)',minHeight:'18px',marginBottom:'2px',fontFamily:'ui-monospace,monospace',overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}},expr||'\u00a0'), |
| 145 |
// Display |
| 146 |
el('div',{style:{background:a.displayBg||'#0d0d1a',borderRadius:'10px',padding:'12px 16px',textAlign:'right',fontSize:(a.displaySize||36)+'px',fontWeight:700,color:a.displayColor||'#fff',fontFamily:'ui-monospace,monospace',overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap',marginBottom:'14px',minHeight:'64px',display:'flex',alignItems:'center',justifyContent:'flex-end'}},display), |
| 147 |
// Button grid |
| 148 |
el('div',{style:{display:'grid',gridTemplateColumns:'repeat(5,1fr)',gap:'6px'}}, |
| 149 |
BTN_LAYOUT.flat().map(function(lbl,i){ |
| 150 |
if (lbl==='') return el('div',{key:i}); |
| 151 |
return el('button',{key:i,onClick:function(){handleBtn(lbl);},style:getBtnStyle(lbl)},lbl); |
| 152 |
}) |
| 153 |
), |
| 154 |
// History |
| 155 |
a.showHistory && history.length > 0 && el('div',{style:{marginTop:'16px',background:a.historyBg||'#111128',borderRadius:'8px',padding:'10px 12px',maxHeight:'140px',overflowY:'auto'}}, |
| 156 |
el('div',{style:{fontSize:'11px',fontWeight:700,color:'rgba(255,255,255,.3)',textTransform:'uppercase',letterSpacing:'.05em',marginBottom:'6px'}},'History'), |
| 157 |
history.map(function(h,i){ |
| 158 |
return el('div',{key:i,style:{fontSize:'13px',color:a.historyColor||'#9ca3af',fontFamily:'ui-monospace,monospace',padding:'2px 0',borderBottom:'1px solid rgba(255,255,255,.05)'}},h.expr); |
| 159 |
}) |
| 160 |
) |
| 161 |
) |
| 162 |
); |
| 163 |
} |
| 164 |
|
| 165 |
registerBlockType('blockenberg/scientific-calculator', { |
| 166 |
edit: function(props) { |
| 167 |
var a = props.attributes; var set = props.setAttributes; |
| 168 |
var blockProps = useBlockProps((function(){ |
| 169 |
var _tv = getTypoCssVars(); |
| 170 |
var s = {}; |
| 171 |
if(_tv) Object.assign(s, _tv(a.titleTypo||{}, '--bksc-tt-')); |
| 172 |
if(_tv) Object.assign(s, _tv(a.subtitleTypo||{}, '--bksc-st-')); |
| 173 |
return { style: s }; |
| 174 |
})()); |
| 175 |
var colorSettings = [ |
| 176 |
{value:a.accentColor, onChange:function(v){set({accentColor:v});}, label:'Accent / Equals Button'}, |
| 177 |
{value:a.cardBg, onChange:function(v){set({cardBg:v});}, label:'Calculator Body'}, |
| 178 |
{value:a.displayBg, onChange:function(v){set({displayBg:v});}, label:'Display Background'}, |
| 179 |
{value:a.displayColor, onChange:function(v){set({displayColor:v});}, label:'Display Text'}, |
| 180 |
{value:a.btnNumBg, onChange:function(v){set({btnNumBg:v});}, label:'Number Button Background'}, |
| 181 |
{value:a.btnNumColor, onChange:function(v){set({btnNumColor:v});}, label:'Number Button Text'}, |
| 182 |
{value:a.btnOpBg, onChange:function(v){set({btnOpBg:v});}, label:'Operator Button Background'}, |
| 183 |
{value:a.btnOpColor, onChange:function(v){set({btnOpColor:v});}, label:'Operator Button Text'}, |
| 184 |
{value:a.btnFnBg, onChange:function(v){set({btnFnBg:v});}, label:'Function Button Background'}, |
| 185 |
{value:a.btnFnColor, onChange:function(v){set({btnFnColor:v});}, label:'Function Button Text'}, |
| 186 |
{value:a.btnEqBg, onChange:function(v){set({btnEqBg:v});}, label:'Equals Button Background'}, |
| 187 |
{value:a.btnEqColor, onChange:function(v){set({btnEqColor:v});}, label:'Equals Button Text'}, |
| 188 |
{value:a.btnClearBg, onChange:function(v){set({btnClearBg:v});}, label:'Clear Button Background'}, |
| 189 |
{value:a.btnClearColor,onChange:function(v){set({btnClearColor:v});}, label:'Clear Button Text'}, |
| 190 |
{value:a.historyBg, onChange:function(v){set({historyBg:v});}, label:'History Background'}, |
| 191 |
{value:a.historyColor, onChange:function(v){set({historyColor:v});}, label:'History Text'}, |
| 192 |
{value:a.memColor, onChange:function(v){set({memColor:v});}, label:'Memory Indicator'}, |
| 193 |
{value:a.titleColor, onChange:function(v){set({titleColor:v});}, label:'Title Color'}, |
| 194 |
{value:a.subtitleColor,onChange:function(v){set({subtitleColor:v});}, label:'Subtitle Color'}, |
| 195 |
{value:a.sectionBg, onChange:function(v){set({sectionBg:v});}, label:'Section Background'} |
| 196 |
]; |
| 197 |
return el(Fragment,null, |
| 198 |
el(InspectorControls,null, |
| 199 |
el(PanelBody,{title:'Header',initialOpen:false}, |
| 200 |
el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show Title', checked:a.showTitle, onChange:function(v){set({showTitle:v});}}), |
| 201 |
el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show Subtitle',checked:a.showSubtitle,onChange:function(v){set({showSubtitle:v});}}), |
| 202 |
el(TextControl,{label:'Title', value:a.title, onChange:function(v){set({title:v});}}), |
| 203 |
el(TextControl,{label:'Subtitle',value:a.subtitle,onChange:function(v){set({subtitle:v});}}) |
| 204 |
), |
| 205 |
el(PanelBody,{title:'Calculator Settings',initialOpen:true}, |
| 206 |
el(SelectControl,{label:'Default Angle Mode',value:a.angleMode,options:[{label:'Degrees',value:'deg'},{label:'Radians',value:'rad'}],onChange:function(v){set({angleMode:v});}}), |
| 207 |
el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show Memory Buttons',checked:a.showMemory, onChange:function(v){set({showMemory:v});}}), |
| 208 |
el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show History', checked:a.showHistory, onChange:function(v){set({showHistory:v});}}), |
| 209 |
el(RangeControl,{label:'History Max Entries',value:a.historyMax,min:3,max:20,step:1,onChange:function(v){set({historyMax:v});}}), |
| 210 |
el(RangeControl,{label:'Calculation Precision',value:a.precision,min:4,max:14,step:1,onChange:function(v){set({precision:v});}}) |
| 211 |
), |
| 212 |
|
| 213 |
el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false }, |
| 214 |
getTypoControl() && el(getTypoControl(), { label: __('Title Typography','blockenberg'), value: a.titleTypo||{}, onChange: function(v){ set({titleTypo:v}); } }), |
| 215 |
getTypoControl() && el(getTypoControl(), { label: __('Subtitle Typography','blockenberg'), value: a.subtitleTypo||{}, onChange: function(v){ set({subtitleTypo:v}); } }), |
| 216 |
el(RangeControl,{label:'Display Font Size',value:a.displaySize, min:18,max:64,step:2, onChange:function(v){set({displaySize:v});}}), |
| 217 |
el(RangeControl,{label:'Button Font Size', value:a.btnSize, min:11,max:22,step:1, onChange:function(v){set({btnSize:v});}}) |
| 218 |
), |
| 219 |
el(PanelColorSettings,{title:'Colors',initialOpen:false,colorSettings:colorSettings}), |
| 220 |
el(PanelBody,{title:'Sizing & Layout',initialOpen:false}, |
| 221 |
el(RangeControl,{label:'Card Border Radius',value:a.cardRadius, min:0, max:40,step:1, onChange:function(v){set({cardRadius:v});}}), |
| 222 |
el(RangeControl,{label:'Button Radius', value:a.btnRadius, min:0, max:20,step:1, onChange:function(v){set({btnRadius:v});}}), |
| 223 |
el(RangeControl,{label:'Max Width (px)', value:a.maxWidth, min:320,max:640,step:10,onChange:function(v){set({maxWidth:v});}}), |
| 224 |
el(RangeControl,{label:'Padding Top', value:a.paddingTop, min:0, max:160,step:4, onChange:function(v){set({paddingTop:v});}}), |
| 225 |
el(RangeControl,{label:'Padding Bottom', value:a.paddingBottom,min:0,max:160,step:4, onChange:function(v){set({paddingBottom:v});}}) |
| 226 |
) |
| 227 |
), |
| 228 |
el('div',blockProps, el(ScientificCalcPreview,{attributes:a})) |
| 229 |
); |
| 230 |
}, |
| 231 |
save: function(props){ |
| 232 |
var a = props.attributes; |
| 233 |
return el('div',wp.blockEditor.useBlockProps.save(), |
| 234 |
el('div',{className:'bkbg-sc-app','data-opts':JSON.stringify(a)})); |
| 235 |
} |
| 236 |
}); |
| 237 |
}() ); |
| 238 |
|