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

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

224 lines 15.4 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 SelectControl = wp.components.SelectControl;
13 var TextControl = wp.components.TextControl;
14 var ToggleControl = wp.components.ToggleControl;
15 var Button = wp.components.Button;
16
17 var DEFAULT_SUBJECTS = [
18 {id:1, name:'Math', score:'90', max:'100', weight:'30'},
19 {id:2, name:'English', score:'85', max:'100', weight:'30'},
20 {id:3, name:'Science', score:'78', max:'100', weight:'20'},
21 {id:4, name:'History', score:'92', max:'100', weight:'20'}
22 ];
23
24 var GRADE_SCALE = [
25 {letter:'A+',min:97,color:'gradeAColor',dflt:'#059669'},
26 {letter:'A', min:93,color:'gradeAColor',dflt:'#10b981'},
27 {letter:'A−',min:90,color:'gradeAColor',dflt:'#34d399'},
28 {letter:'B+',min:87,color:'gradeBColor',dflt:'#2563eb'},
29 {letter:'B', min:83,color:'gradeBColor',dflt:'#3b82f6'},
30 {letter:'B−',min:80,color:'gradeBColor',dflt:'#60a5fa'},
31 {letter:'C+',min:77,color:'gradeCColor',dflt:'#d97706'},
32 {letter:'C', min:73,color:'gradeCColor',dflt:'#f59e0b'},
33 {letter:'C−',min:70,color:'gradeCColor',dflt:'#fbbf24'},
34 {letter:'D+',min:67,color:'gradeDColor',dflt:'#dc2626'},
35 {letter:'D', min:63,color:'gradeDColor',dflt:'#ef4444'},
36 {letter:'D−',min:60,color:'gradeDColor',dflt:'#f87171'},
37 {letter:'F', min:0, color:'gradeFColor',dflt:'#6b7280'}
38 ];
39
40 function getLetter(pct, a) {
41 for (var i=0; i<GRADE_SCALE.length; i++) {
42 if (pct >= GRADE_SCALE[i].min) {
43 return {letter: GRADE_SCALE[i].letter, color: a[GRADE_SCALE[i].color] || GRADE_SCALE[i].dflt};
44 }
45 }
46 return {letter:'F', color: a.gradeFColor||'#6b7280'};
47 }
48
49 function calcWeighted(subjects) {
50 var totalWeight = 0, weightedSum = 0;
51 subjects.forEach(function(s) {
52 var sc = parseFloat(s.score)||0, mx = parseFloat(s.max)||100, wt = parseFloat(s.weight)||0;
53 var pct = mx > 0 ? (sc/mx)*100 : 0;
54 weightedSum += pct * wt;
55 totalWeight += wt;
56 });
57 return totalWeight > 0 ? weightedSum / totalWeight : 0;
58 }
59
60 function calcSimple(subjects) {
61 if (!subjects.length) return 0;
62 var sum = 0, total = 0;
63 subjects.forEach(function(s){
64 var sc=parseFloat(s.score)||0, mx=parseFloat(s.max)||100;
65 sum+=sc; total+=mx;
66 });
67 return total > 0 ? (sum/total)*100 : 0;
68 }
69
70 var nextId = 10;
71
72 /* ── Typography lazy getters ── */
73 function _tc() { return window.bkbgTypographyControl || null; }
74 function _tv() { return window.bkbgTypoCssVars || function () { return {}; }; }
75
76 function GCPreview(props) {
77 var a = props.attributes;
78 var accent = a.accentColor || '#6c3fb5';
79 var _subs = useState(DEFAULT_SUBJECTS.map(function(s){return Object.assign({},s);}));
80 var subs = _subs[0]; var setSubs = _subs[1];
81
82 var pct = a.gradeMode === 'simple' ? calcSimple(subs) : calcWeighted(subs);
83 var gInfo = getLetter(pct, a);
84
85 var inpStyle = {padding:'6px 8px',border:'1.5px solid '+(a.inputBorder||'#e5e7eb'),borderRadius:(a.inputRadius||8)+'px',fontSize:'14px',fontFamily:'inherit',outline:'none',width:'100%',boxSizing:'border-box'};
86 var lblStyle = {fontSize:'12px',fontWeight:600,color:a.labelColor||'#374151',display:'block',marginBottom:'3px'};
87
88 function updateSub(id, field, val) {
89 setSubs(subs.map(function(s){ return s.id===id ? Object.assign({},s,{[field]:val}) : s; }));
90 }
91
92 var wrapS = Object.assign({paddingTop:(a.paddingTop||60)+'px',paddingBottom:(a.paddingBottom||60)+'px',background:a.sectionBg||undefined},_tv()(a.typoTitle,'--bkbg-grc-tt-'),_tv()(a.typoResult,'--bkbg-grc-rs-'),_tv()(a.typoLetter,'--bkbg-grc-lt-'));
93 return el('div',{style:wrapS},
94 el('div',{style:{background:a.cardBg||'#fff',borderRadius:(a.cardRadius||16)+'px',padding:'32px',maxWidth:(a.maxWidth||640)+'px',margin:'0 auto',boxShadow:'0 4px 24px rgba(0,0,0,.09)'}},
95
96 (a.showTitle||a.showSubtitle) && el('div',{style:{marginBottom:'22px'}},
97 a.showTitle && el('div',{className:'bkbg-grc-title',style:{color:a.titleColor,marginBottom:'6px'}},a.title),
98 a.showSubtitle && el('div',{style:{fontSize:'15px',color:a.subtitleColor,opacity:.75}},a.subtitle)
99 ),
100
101 // Result card
102 el('div',{style:{background:a.resultBg||accent,borderRadius:'12px',padding:'20px',textAlign:'center',color:a.resultColor||'#fff',marginBottom:'20px',display:'flex',alignItems:'center',justifyContent:'center',gap:'24px'}},
103 el('div',null,
104 el('div',{className:'bkbg-grc-result-pct'},pct.toFixed(1)+'%'),
105 el('div',{style:{fontSize:'14px',opacity:.8,marginTop:'4px'}},a.gradeMode==='simple'?'Simple Average':'Weighted Average')
106 ),
107 a.showLetterGrade && el(Fragment,null,
108 el('div',{style:{width:'1px',height:'60px',background:'rgba(255,255,255,.25)'}}),
109 el('div',{style:{textAlign:'center'}},
110 el('div',{className:'bkbg-grc-result-letter',style:{background:'rgba(255,255,255,.15)',borderRadius:'12px',padding:'0 16px'}},gInfo.letter),
111 el('div',{style:{fontSize:'13px',opacity:.8,marginTop:'4px'}},'Letter Grade')
112 )
113 )
114 ),
115
116 // Subjects table header
117 a.showBreakdown && el('div',null,
118 el('div',{style:{display:'grid',gridTemplateColumns:'2fr 1fr 1fr 1fr 80px',gap:'6px',padding:'6px 10px',marginBottom:'4px'}},
119 el('div',{style:lblStyle},'Subject'),
120 el('div',{style:lblStyle},'Score'),
121 el('div',{style:lblStyle},'Max'),
122 el('div',{style:{...lblStyle}},'Weight %'),
123 el('div',{style:lblStyle},'')
124 ),
125
126 subs.map(function(s) {
127 var spct = parseFloat(s.max)>0 ? (parseFloat(s.score)||0)/(parseFloat(s.max)||100)*100 : 0;
128 var sg = getLetter(spct, a);
129 return el('div',{key:s.id,style:{display:'grid',gridTemplateColumns:'2fr 1fr 1fr 1fr 80px',gap:'6px',alignItems:'center',background:a.rowBg||'#f9fafb',border:'1px solid '+(a.rowBorder||'#e5e7eb'),borderRadius:'8px',padding:'8px 10px',marginBottom:'6px'}},
130 el('input',{type:'text',value:s.name,placeholder:'Subject',style:inpStyle,onChange:function(e){updateSub(s.id,'name',e.target.value);}}),
131 el('input',{type:'number',value:s.score,min:0,style:inpStyle,onChange:function(e){updateSub(s.id,'score',e.target.value);}}),
132 el('input',{type:'number',value:s.max,min:1,style:inpStyle,onChange:function(e){updateSub(s.id,'max',e.target.value);}}),
133 el('input',{type:'number',value:s.weight,min:0,max:100,style:inpStyle,onChange:function(e){updateSub(s.id,'weight',e.target.value);}}),
134 el('div',{style:{display:'flex',gap:'6px',alignItems:'center'}},
135 el('span',{style:{fontWeight:700,fontSize:'13px',color:sg.color}},sg.letter),
136 el('button',{onClick:function(){setSubs(subs.filter(function(x){return x.id!==s.id;}));},style:{border:'none',background:'#fee2e2',color:'#ef4444',borderRadius:'6px',cursor:'pointer',padding:'3px 8px',fontWeight:700,fontSize:'13px',fontFamily:'inherit'}},'×')
137 )
138 );
139 }),
140
141 el('button',{onClick:function(){nextId++;setSubs(subs.concat({id:nextId,name:'',score:'',max:'100',weight:'0'}));},style:{marginTop:'8px',padding:'8px 16px',background:accent,color:'#fff',border:'none',borderRadius:'8px',fontWeight:700,cursor:'pointer',fontSize:'14px',fontFamily:'inherit'}}, '+ Add Subject')
142 ),
143
144 // Grade scale
145 a.showGradeScale && el('div',{style:{marginTop:'20px',background:a.scaleBg||'#f3f4f6',border:'1px solid '+(a.scaleBorder||'#e5e7eb'),borderRadius:'10px',padding:'14px 16px'}},
146 el('div',{style:{fontSize:'12px',fontWeight:700,color:a.labelColor||'#374151',textTransform:'uppercase',letterSpacing:'.05em',marginBottom:'10px'}},'Grade Scale'),
147 el('div',{style:{display:'flex',flexWrap:'wrap',gap:'6px'}},
148 GRADE_SCALE.map(function(gs){
149 return el('div',{key:gs.letter,style:{background:'#fff',border:'2px solid '+(a[gs.color]||gs.dflt),borderRadius:'6px',padding:'4px 10px',textAlign:'center',minWidth:'50px'}},
150 el('div',{style:{fontWeight:800,fontSize:'14px',color:a[gs.color]||gs.dflt}},gs.letter),
151 el('div',{style:{fontSize:'11px',color:'#6b7280'}},gs.min+'%+')
152 );
153 })
154 )
155 )
156 )
157 );
158 }
159
160 registerBlockType('blockenberg/grade-calculator', {
161 edit: function(props) {
162 var a = props.attributes; var set = props.setAttributes;
163 var blockProps = useBlockProps();
164 var colorSettings = [
165 {value:a.accentColor, onChange:function(v){set({accentColor:v});}, label:'Accent Color'},
166 {value:a.cardBg, onChange:function(v){set({cardBg:v});}, label:'Card Background'},
167 {value:a.resultBg, onChange:function(v){set({resultBg:v});}, label:'Result Card Background'},
168 {value:a.resultColor, onChange:function(v){set({resultColor:v});}, label:'Result Card Text'},
169 {value:a.gradeAColor, onChange:function(v){set({gradeAColor:v});}, label:'Grade A Color'},
170 {value:a.gradeBColor, onChange:function(v){set({gradeBColor:v});}, label:'Grade B Color'},
171 {value:a.gradeCColor, onChange:function(v){set({gradeCColor:v});}, label:'Grade C Color'},
172 {value:a.gradeDColor, onChange:function(v){set({gradeDColor:v});}, label:'Grade D Color'},
173 {value:a.gradeFColor, onChange:function(v){set({gradeFColor:v});}, label:'Grade F Color'},
174 {value:a.rowBg, onChange:function(v){set({rowBg:v});}, label:'Table Row Background'},
175 {value:a.rowBorder, onChange:function(v){set({rowBorder:v});}, label:'Table Row Border'},
176 {value:a.scaleBg, onChange:function(v){set({scaleBg:v});}, label:'Scale Background'},
177 {value:a.scaleBorder, onChange:function(v){set({scaleBorder:v});}, label:'Scale Border'},
178 {value:a.inputBorder, onChange:function(v){set({inputBorder:v});}, label:'Input Border'},
179 {value:a.labelColor, onChange:function(v){set({labelColor:v});}, label:'Label Color'},
180 {value:a.titleColor, onChange:function(v){set({titleColor:v});}, label:'Title Color'},
181 {value:a.subtitleColor,onChange:function(v){set({subtitleColor:v});}, label:'Subtitle Color'},
182 {value:a.sectionBg, onChange:function(v){set({sectionBg:v});}, label:'Section Background'}
183 ];
184 return el(Fragment, null,
185 el(InspectorControls, null,
186 el(PanelBody,{title:'Header',initialOpen:false},
187 el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show Title', checked:a.showTitle, onChange:function(v){set({showTitle:v});}}),
188 el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show Subtitle',checked:a.showSubtitle,onChange:function(v){set({showSubtitle:v});}}),
189 el(TextControl,{label:'Title', value:a.title, onChange:function(v){set({title:v});}}),
190 el(TextControl,{label:'Subtitle',value:a.subtitle,onChange:function(v){set({subtitle:v});}})
191 ),
192 el(PanelBody,{title:'Calculator Settings',initialOpen:true},
193 el(SelectControl,{label:'Grade Mode',value:a.gradeMode,options:[{label:'Weighted Average',value:'weighted'},{label:'Simple Average',value:'simple'}],onChange:function(v){set({gradeMode:v});}}),
194 el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show Letter Grade', checked:a.showLetterGrade,onChange:function(v){set({showLetterGrade:v});}}),
195 el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show Grade Scale', checked:a.showGradeScale, onChange:function(v){set({showGradeScale:v});}}),
196 el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show Subject Breakdown',checked:a.showBreakdown,onChange:function(v){set({showBreakdown:v});}})
197 ),
198
199 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
200 _tc() && el(_tc(), { label:'Title', value:a.typoTitle, onChange:function(v){ set({typoTitle:v}); } }),
201 _tc() && el(_tc(), { label:'Result %', value:a.typoResult, onChange:function(v){ set({typoResult:v}); } }),
202 _tc() && el(_tc(), { label:'Letter Grade', value:a.typoLetter, onChange:function(v){ set({typoLetter:v}); } })
203 ),
204 el(PanelColorSettings,{title:'Colors',initialOpen:false,colorSettings:colorSettings}),
205 el(PanelBody,{title:'Sizing & Layout',initialOpen:false},
206 el(RangeControl,{label:'Letter Grade Size', value:a.letterSize, min:24,max:80,step:2, onChange:function(v){set({letterSize:v});}}),
207 el(RangeControl,{label:'Card Border Radius',value:a.cardRadius, min:0, max:40,step:1, onChange:function(v){set({cardRadius:v});}}),
208 el(RangeControl,{label:'Input Border Radius',value:a.inputRadius,min:0,max:20,step:1, onChange:function(v){set({inputRadius:v});}}),
209 el(RangeControl,{label:'Max Width (px)', value:a.maxWidth, min:360,max:1100,step:10,onChange:function(v){set({maxWidth:v});}}),
210 el(RangeControl,{label:'Padding Top', value:a.paddingTop, min:0, max:160,step:4, onChange:function(v){set({paddingTop:v});}}),
211 el(RangeControl,{label:'Padding Bottom', value:a.paddingBottom,min:0,max:160,step:4,onChange:function(v){set({paddingBottom:v});}})
212 )
213 ),
214 el('div', blockProps, el(GCPreview, {attributes:a}))
215 );
216 },
217 save: function(props) {
218 var a = props.attributes;
219 return el('div', wp.blockEditor.useBlockProps.save(),
220 el('div',{className:'bkbg-grc-app','data-opts':JSON.stringify(a)}));
221 }
222 });
223 }() );
224