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

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

221 lines 13.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 ( function () {
2 var el = React.createElement;
3 var registerBlockType = wp.blocks.registerBlockType;
4 var useBlockProps = wp.blockEditor.useBlockProps;
5 var InspectorControls = wp.blockEditor.InspectorControls;
6 var PanelBody = wp.components.PanelBody;
7 var RangeControl = wp.components.RangeControl;
8 var SelectControl = wp.components.SelectControl;
9 var TextControl = wp.components.TextControl;
10 var ToggleControl = wp.components.ToggleControl;
11 var Button = wp.components.Button;
12 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
13
14 /* ── lazy typography helpers ── */
15 function _tc() { return window.bkbgTypographyControl || null; }
16 Object.defineProperty(window, '__bkSR_tvf', { get: function () { delete window.__bkSR_tvf; return (window.__bkSR_tvf = window.bkbgTypoCssVars || function () { return {}; }); } });
17 function getTypoCssVars(a) {
18 var o = {};
19 Object.assign(o, window.__bkSR_tvf(a.pctTypo || {}, '--bksr-pc-'));
20 Object.assign(o, window.__bkSR_tvf(a.labelTypo || {}, '--bksr-lb-'));
21 return o;
22 }
23
24 var LAYOUT_OPTS = [
25 { value: 'row', label: 'Row (individual rings)' },
26 { value: 'concentric', label: 'Concentric (nested rings)' }
27 ];
28 var EASING_OPTS = [
29 { value: 'ease-out', label: 'Ease Out' },
30 { value: 'ease-in', label: 'Ease In' },
31 { value: 'linear', label: 'Linear' }
32 ];
33 var LINECAP_OPTS = [
34 { value: 'round', label: 'Round' },
35 { value: 'butt', label: 'Flat' },
36 { value: 'square', label: 'Square' }
37 ];
38 var LABEL_POS_OPTS = [
39 { value: 'below', label: 'Below ring' },
40 { value: 'inside',label: 'Inside ring' }
41 ];
42
43 /** Draw a single ring preview as inline SVG */
44 function RingPreview(props) {
45 var skill = props.skill;
46 var a = props.attr;
47 var size = a.ringSize || 140;
48 var sw = a.strokeWidth || 10;
49 var r = (size - sw) / 2;
50 var circ = 2 * Math.PI * r;
51 var cx = size / 2, cy = size / 2;
52 var dashArray = circ;
53 var dashOffset = circ * (1 - (skill.pct || 0) / 100);
54
55 return el('div', { style: { textAlign:'center', display:'flex', flexDirection:'column', alignItems:'center' } },
56 el('svg', { width:size, height:size, viewBox:'0 0 '+size+' '+size, style:{display:'block'} },
57 /* Track */
58 el('circle', { cx:cx, cy:cy, r:r, fill:'none', stroke:a.trackColor||'#1f2937', strokeWidth:sw }),
59 /* Progress */
60 el('circle', {
61 cx:cx, cy:cy, r:r, fill:'none',
62 stroke: skill.color || '#7c3aed',
63 strokeWidth:sw,
64 strokeDasharray: dashArray,
65 strokeDashoffset: dashOffset,
66 strokeLinecap: a.lineCap||'round',
67 transform:'rotate(-90 '+cx+' '+cy+')'
68 }),
69 /* Center percentage */
70 a.showPercentage && a.labelPosition === 'inside' && el('text', {
71 x:cx, y:cy, dominantBaseline:'middle', textAnchor:'middle',
72 fill: a.pctColor||'#fff', fontSize:(a.pctSize||28), fontWeight:a.pctFontWeight||700
73 }, (skill.pct||0)+'%'),
74 /* Small skill name inside */
75 a.showLabel && a.labelPosition === 'inside' && el('text', {
76 x:cx, y:cy+(a.pctSize||28)*0.65, dominantBaseline:'middle', textAnchor:'middle',
77 fill:a.labelColor||'#fff', fontSize:a.labelSize||14, opacity:0.7
78 }, skill.name)
79 ),
80 /* Outside labels */
81 (a.showPercentage && a.labelPosition !== 'inside') && el('div', { className: 'bkbg-sr-pct-below', style: { color:a.pctColor||'#fff' } }, (skill.pct||0)+'%'),
82 (a.showLabel && a.labelPosition !== 'inside') && el('div', { className: 'bkbg-sr-name-below', style: { color:a.labelColor||'#fff' } }, skill.name)
83 );
84 }
85
86 /** Concentric SVG preview */
87 function ConcentricPreview(props) {
88 var a = props.attr;
89 var skills = a.skills || [];
90 var ringGap = a.ringGap || 14;
91 var sw = a.strokeWidth || 10;
92 var totalSize = (a.ringSize || 140) + (skills.length - 1) * (sw + ringGap);
93 var cx = totalSize / 2, cy = totalSize / 2;
94
95 var rings = skills.map(function(skill, i) {
96 var r = (totalSize / 2) - sw/2 - i*(sw + ringGap);
97 if (r <= 0) return null;
98 var circ = 2 * Math.PI * r;
99 return el('g', { key: i },
100 el('circle', {cx:cx,cy:cy,r:r,fill:'none',stroke:a.trackColor||'#1f2937',strokeWidth:sw}),
101 el('circle', {
102 cx:cx,cy:cy,r:r,fill:'none',
103 stroke:skill.color||'#7c3aed',strokeWidth:sw,
104 strokeDasharray:circ,strokeDashoffset:circ*(1-(skill.pct||0)/100),
105 strokeLinecap:a.lineCap||'round',transform:'rotate(-90 '+cx+' '+cy+')'
106 })
107 );
108 }).filter(Boolean);
109
110 return el('div', { style: { display:'flex', flexDirection:'column', alignItems:'center', gap:16 } },
111 el('svg', { width:totalSize, height:totalSize, viewBox:'0 0 '+totalSize+' '+totalSize, style:{display:'block',maxWidth:'100%'} },
112 rings,
113 a.centerLabel && el('text', {x:cx,y:cy,dominantBaseline:'middle',textAnchor:'middle',fill:a.centerLabelColor||'#fff',fontSize:a.centerLabelSize||18,fontWeight:700},a.centerLabel)
114 ),
115 /* Legend */
116 el('div', { style: { display:'flex', flexDirection:'column', gap:6, minWidth:160 } },
117 skills.map(function(s,i){
118 return el('div',{key:i,style:{display:'flex',alignItems:'center',gap:8}},
119 el('div',{style:{width:10,height:10,borderRadius:'50%',background:s.color||'#7c3aed',flexShrink:0}}),
120 el('span',{className:'bkbg-sr-legend-name',style:{color:a.labelColor||'#fff',flex:1}},s.name),
121 el('span',{className:'bkbg-sr-legend-pct',style:{color:s.color||'#7c3aed'}},(s.pct||0)+'%')
122 );
123 })
124 )
125 );
126 }
127
128 registerBlockType('blockenberg/skill-rings', {
129 title: 'Skill Rings',
130 icon: 'chart-pie',
131 category: 'bkbg-effects',
132
133 edit: function (props) {
134 var attr = props.attributes; var setAttr = props.setAttributes;
135
136 function updateSkill(i, key, val) {
137 var s = (attr.skills||[]).map(function(x){return Object.assign({},x);}); s[i][key]=val; setAttr({skills:s});
138 }
139 function addSkill() { setAttr({skills:(attr.skills||[]).concat([{name:'New Skill',pct:70,color:'#7c3aed'}])}); }
140 function removeSkill(i) { setAttr({skills:(attr.skills||[]).filter(function(_,j){return j!==i;})}); }
141
142 var skillPanels = (attr.skills||[]).map(function(s,i){
143 return el(PanelBody,{key:i,title:(i+1)+'. '+s.name,initialOpen:false},
144 el(TextControl,{__nextHasNoMarginBottom:true,label:'Skill Name',value:s.name,onChange:function(v){updateSkill(i,'name',v);}}),
145 el(RangeControl,{__nextHasNoMarginBottom:true,label:'Percentage',value:s.pct,min:0,max:100,onChange:function(v){updateSkill(i,'pct',v);}}),
146 el('div',{style:{marginBottom:8}},
147 el('label',{style:{display:'block',fontSize:12,marginBottom:4}},'Color'),
148 el('input',{type:'color',value:s.color||'#7c3aed',style:{width:40,height:30,cursor:'pointer',border:'1px solid #ccc',borderRadius:4},onChange:function(e){updateSkill(i,'color',e.target.value);}})
149 ),
150 el(Button,{variant:'tertiary',isDestructive:true,isSmall:true,onClick:function(){removeSkill(i);}},'Remove')
151 );
152 });
153
154 var preview = attr.layout === 'concentric'
155 ? el(ConcentricPreview, {attr:attr})
156 : el('div', { style: { display:'flex', flexWrap:'wrap', gap:32, justifyContent:'center' } },
157 (attr.skills||[]).map(function(s,i){ return el(RingPreview,{key:i,skill:s,attr:attr}); })
158 );
159
160 return el(React.Fragment, null,
161 el(InspectorControls, null,
162 el(PanelBody,{title:'Layout & Animation',initialOpen:true},
163 el(SelectControl,{__nextHasNoMarginBottom:true,label:'Layout',value:attr.layout,options:LAYOUT_OPTS,onChange:function(v){setAttr({layout:v});}}),
164 el(RangeControl,{__nextHasNoMarginBottom:true,label:attr.layout==='concentric'?'Outer Ring Size (px)':'Ring Size (px)',value:attr.ringSize,min:80,max:400,onChange:function(v){setAttr({ringSize:v});}}),
165 el(RangeControl,{__nextHasNoMarginBottom:true,label:'Stroke Width (px)',value:attr.strokeWidth,min:2,max:40,onChange:function(v){setAttr({strokeWidth:v});}}),
166 attr.layout==='concentric' && el(RangeControl,{__nextHasNoMarginBottom:true,label:'Gap Between Rings (px)',value:attr.ringGap,min:2,max:40,onChange:function(v){setAttr({ringGap:v});}}),
167 el(SelectControl,{__nextHasNoMarginBottom:true,label:'Line Cap',value:attr.lineCap,options:LINECAP_OPTS,onChange:function(v){setAttr({lineCap:v});}})
168 ),
169 el(PanelBody,{title:'Animation',initialOpen:false},
170 el(RangeControl,{__nextHasNoMarginBottom:true,label:'Duration (ms)',value:attr.animDuration,min:200,max:4000,step:100,onChange:function(v){setAttr({animDuration:v});}}),
171 el(SelectControl,{__nextHasNoMarginBottom:true,label:'Easing',value:attr.animEasing,options:EASING_OPTS,onChange:function(v){setAttr({animEasing:v});}}),
172 el(RangeControl,{__nextHasNoMarginBottom:true,label:'Stagger Delay (ms)',value:attr.animDelay,min:0,max:500,onChange:function(v){setAttr({animDelay:v});}})
173 ),
174 el(PanelBody,{title:'Labels',initialOpen:false},
175 el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show Percentage',checked:attr.showPercentage,onChange:function(v){setAttr({showPercentage:v});}}),
176 el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show Skill Name',checked:attr.showLabel,onChange:function(v){setAttr({showLabel:v});}}),
177 attr.layout === 'row' && el(SelectControl,{__nextHasNoMarginBottom:true,label:'Label Position',value:attr.labelPosition,options:LABEL_POS_OPTS,onChange:function(v){setAttr({labelPosition:v});}}),
178 el(RangeControl,{__nextHasNoMarginBottom:true,label:'Percentage Size (px)',value:attr.pctSize,min:12,max:60,onChange:function(v){setAttr({pctSize:v});}}),
179 attr.layout === 'concentric' && el(TextControl,{__nextHasNoMarginBottom:true,label:'Center Label',value:attr.centerLabel,onChange:function(v){setAttr({centerLabel:v});}})
180 ),
181 el(PanelBody,{title:'Spacing',initialOpen:false},
182 el(RangeControl,{__nextHasNoMarginBottom:true,label:'Padding Top (px)',value:attr.paddingTop,min:0,max:200,onChange:function(v){setAttr({paddingTop:v});}}),
183 el(RangeControl,{__nextHasNoMarginBottom:true,label:'Padding Bottom (px)',value:attr.paddingBottom,min:0,max:200,onChange:function(v){setAttr({paddingBottom:v});}})
184 ),
185
186 el( PanelBody, { title: 'Typography', initialOpen: false },
187 _tc() && el(_tc(), { label: 'Percentage', value: attr.pctTypo || {}, onChange: function (v) { setAttr({ pctTypo: v }); } }),
188 _tc() && el(_tc(), { label: 'Label', value: attr.labelTypo || {}, onChange: function (v) { setAttr({ labelTypo: v }); } })
189 ),
190 el(PanelColorSettings,{
191 title:'Colors',initialOpen:false,
192 colorSettings:[
193 {value:attr.bgColor, onChange:function(v){setAttr({bgColor: v||''});},label:'Background'},
194 {value:attr.trackColor, onChange:function(v){setAttr({trackColor: v||'#1f2937'});},label:'Track (empty arc)'},
195 {value:attr.pctColor, onChange:function(v){setAttr({pctColor: v||'#ffffff'});},label:'Percentage Text'},
196 {value:attr.labelColor, onChange:function(v){setAttr({labelColor: v||'#ffffff'});},label:'Label Text'},
197 attr.layout==='concentric' && {value:attr.centerLabelColor,onChange:function(v){setAttr({centerLabelColor:v||'#fff'});},label:'Center Label'}
198 ].filter(Boolean)
199 }),
200 el(PanelBody,{title:'Skills',initialOpen:false},
201 skillPanels,
202 el(Button,{variant:'secondary',isSmall:true,style:{marginTop:8},onClick:addSkill},'+ Add Skill')
203 )
204 ),
205 el('div', useBlockProps(),
206 el('div', {
207 style: Object.assign({ background:attr.bgColor||'transparent', padding:(attr.paddingTop||60)+'px clamp(20px,5vw,60px) '+(attr.paddingBottom||60)+'px' }, getTypoCssVars(attr))
208 }, preview)
209 )
210 );
211 },
212
213 save: function (props) {
214 var sv = getTypoCssVars(props.attributes);
215 return el('div', useBlockProps.save(),
216 el('div', { className:'bkbg-sr-app', 'data-opts':JSON.stringify(props.attributes), style: sv })
217 );
218 }
219 });
220 }() );
221