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

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

250 lines 11.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () {
2 var SVG_NS = 'http://www.w3.org/2000/svg';
3
4 var _typoKeys = {
5 family:'font-family', weight:'font-weight', style:'font-style',
6 transform:'text-transform', decoration:'text-decoration',
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 var _typoUnits = { size:'sizeUnit', lineHeight:'lineHeightUnit', letterSpacing:'letterSpacingUnit', wordSpacing:'wordSpacingUnit' };
13 var _typoUnitDefaults = { size:'px', lineHeight:'', letterSpacing:'px', wordSpacing:'px' };
14 function typoCssVarsForEl(el, obj, prefix) {
15 if (!obj || typeof obj !== 'object') return;
16 Object.keys(_typoKeys).forEach(function (k) {
17 var v = obj[k]; if (v === undefined || v === '') return;
18 var prop = _typoKeys[k];
19 var base = k.replace(/Desktop|Tablet|Mobile/, '');
20 var uKey = _typoUnits[base];
21 if (uKey && typeof v === 'number') v = v + (obj[uKey] || _typoUnitDefaults[base] || '');
22 el.style.setProperty('--' + prefix + prop, v);
23 });
24 }
25
26 function svgEl(tag, attrs) {
27 var el = document.createElementNS(SVG_NS, tag);
28 Object.keys(attrs).forEach(function(k) { el.setAttribute(k, attrs[k]); });
29 return el;
30 }
31
32 function easeFn(name) {
33 return { 'ease-out':'cubic-bezier(0,0,0.25,1)', 'ease-in':'cubic-bezier(0.75,0,1,1)', 'linear':'linear' }[name] || 'cubic-bezier(0,0,0.25,1)';
34 }
35
36 function initSkillRings(appEl) {
37 var opts;
38 try { opts = JSON.parse(appEl.getAttribute('data-opts') || '{}'); } catch(e){ opts={}; }
39
40 var a = {
41 skills: opts.skills || [],
42 layout: opts.layout || 'row',
43 ringSize: opts.ringSize || 140,
44 ringGap: opts.ringGap || 14,
45 strokeWidth: opts.strokeWidth || 10,
46 trackColor: opts.trackColor || '#1f2937',
47 animDuration: opts.animDuration || 1400,
48 animEasing: opts.animEasing || 'ease-out',
49 animDelay: opts.animDelay || 120,
50 showPercentage: opts.showPercentage !== false,
51 showLabel: opts.showLabel !== false,
52 labelPosition: opts.labelPosition || 'below',
53 lineCap: opts.lineCap || 'round',
54 bgColor: opts.bgColor || '',
55 labelColor: opts.labelColor || '#ffffff',
56 pctColor: opts.pctColor || '#ffffff',
57 pctSize: opts.pctSize || 28,
58 labelSize: opts.labelSize || 14,
59 paddingTop: opts.paddingTop || 60,
60 paddingBottom: opts.paddingBottom || 60,
61 centerLabel: opts.centerLabel || '',
62 centerLabelColor:opts.centerLabelColor||'#ffffff'
63 };
64
65 if (!a.skills.length) { appEl.remove(); return; }
66
67 var wrap = document.createElement('div');
68 wrap.className = 'bkbg-sr-wrap';
69 var cs = wrap.style;
70 cs.setProperty('--bkbg-sr-bg', a.bgColor || 'transparent');
71 cs.setProperty('--bkbg-sr-pt', a.paddingTop + 'px');
72 cs.setProperty('--bkbg-sr-pb', a.paddingBottom + 'px');
73 cs.setProperty('--bkbg-sr-dur', a.animDuration + 'ms');
74 cs.setProperty('--bkbg-sr-easing', easeFn(a.animEasing));
75 cs.setProperty('--bkbg-sr-pct-size', a.pctSize + 'px');
76 cs.setProperty('--bkbg-sr-label-size', a.labelSize + 'px');
77 cs.setProperty('--bkbg-sr-pct-color', a.pctColor);
78 cs.setProperty('--bkbg-sr-label-color', a.labelColor);
79
80 /* Apply typography CSS vars */
81 typoCssVarsForEl(wrap, opts.pctTypo, 'bksr-pc-');
82 typoCssVarsForEl(wrap, opts.labelTypo, 'bksr-lb-');
83
84 var progressEls = [];
85
86 if (a.layout === 'row') {
87 var row = document.createElement('div');
88 row.className = 'bkbg-sr-row';
89
90 a.skills.forEach(function(skill, i) {
91 var size = a.ringSize;
92 var sw = a.strokeWidth;
93 var r = (size - sw) / 2;
94 var circ = 2 * Math.PI * r;
95 var cx = size / 2, cy = size / 2;
96 var targetOffset = circ * (1 - (skill.pct || 0) / 100);
97
98 var item = document.createElement('div');
99 item.className = 'bkbg-sr-item';
100
101 var ringWrap = document.createElement('div');
102 ringWrap.className = 'bkbg-sr-ring-wrap';
103
104 var svg = svgEl('svg', { width:size, height:size, viewBox:'0 0 '+size+' '+size });
105
106 /* Track */
107 svg.appendChild(svgEl('circle', { cx:cx, cy:cy, r:r, fill:'none', stroke:a.trackColor, 'stroke-width':sw }));
108
109 /* Progress */
110 var prog = svgEl('circle', {
111 cx:cx, cy:cy, r:r, fill:'none',
112 stroke: skill.color || '#7c3aed',
113 'stroke-width': sw,
114 'stroke-dasharray': circ,
115 'stroke-dashoffset': circ, /* starts at 0% */
116 'stroke-linecap': a.lineCap,
117 class:'bkbg-sr-progress',
118 transform:'rotate(-90 '+cx+' '+cy+')'
119 });
120 prog.style.transitionDelay = (i * a.animDelay) + 'ms';
121 svg.appendChild(prog);
122
123 progressEls.push({ el: prog, target: targetOffset });
124
125 /* Center label (inside) */
126 if (a.labelPosition === 'inside') {
127 var center = document.createElement('div');
128 center.className = 'bkbg-sr-center';
129 if (a.showPercentage) {
130 var pctSpan = document.createElement('span');
131 pctSpan.className = 'bkbg-sr-pct';
132 pctSpan.textContent = (skill.pct||0) + '%';
133 center.appendChild(pctSpan);
134 }
135 if (a.showLabel) {
136 var nameSpan = document.createElement('span');
137 nameSpan.className = 'bkbg-sr-name-inside';
138 nameSpan.textContent = skill.name;
139 center.appendChild(nameSpan);
140 }
141 ringWrap.appendChild(center);
142 }
143
144 ringWrap.appendChild(svg);
145 item.appendChild(ringWrap);
146
147 /* Below label */
148 if (a.labelPosition !== 'inside') {
149 var below = document.createElement('div');
150 below.className = 'bkbg-sr-label-below';
151 if (a.showPercentage) {
152 var pctD = document.createElement('div');
153 pctD.className = 'bkbg-sr-pct-below';
154 pctD.style.color = skill.color || a.pctColor;
155 pctD.textContent = (skill.pct||0) + '%';
156 below.appendChild(pctD);
157 }
158 if (a.showLabel) {
159 var nameD = document.createElement('div');
160 nameD.className = 'bkbg-sr-name-below';
161 nameD.textContent = skill.name;
162 below.appendChild(nameD);
163 }
164 item.appendChild(below);
165 }
166
167 row.appendChild(item);
168 });
169
170 wrap.appendChild(row);
171
172 } else {
173 /* Concentric layout */
174 var sw2 = a.strokeWidth;
175 var gap = a.ringGap;
176 var n = a.skills.length;
177 var totalSize = a.ringSize + (n > 1 ? (n-1)*(sw2 + gap) : 0);
178 var cx2 = totalSize/2, cy2 = totalSize/2;
179
180 var conc = document.createElement('div');
181 conc.className = 'bkbg-sr-concentric';
182
183 var svg2 = svgEl('svg', { width:totalSize, height:totalSize, viewBox:'0 0 '+totalSize+' '+totalSize, style:'max-width:100%' });
184
185 a.skills.forEach(function(skill, i) {
186 var r2 = (totalSize/2) - sw2/2 - i*(sw2+gap);
187 if (r2 <= 0) return;
188 var circ2 = 2*Math.PI*r2;
189 var targetOffset2 = circ2*(1-(skill.pct||0)/100);
190
191 svg2.appendChild(svgEl('circle',{cx:cx2,cy:cy2,r:r2,fill:'none',stroke:a.trackColor,'stroke-width':sw2}));
192 var prog2 = svgEl('circle',{
193 cx:cx2,cy:cy2,r:r2,fill:'none',
194 stroke:skill.color||'#7c3aed','stroke-width':sw2,
195 'stroke-dasharray':circ2,'stroke-dashoffset':circ2,
196 'stroke-linecap':a.lineCap,class:'bkbg-sr-progress',
197 transform:'rotate(-90 '+cx2+' '+cy2+')'
198 });
199 prog2.style.transitionDelay = (i*a.animDelay)+'ms';
200 svg2.appendChild(prog2);
201 progressEls.push({el:prog2, target:targetOffset2});
202 });
203
204 /* Center label */
205 if (a.centerLabel) {
206 var txt = svgEl('text',{x:cx2,y:cy2,'dominant-baseline':'middle','text-anchor':'middle',fill:a.centerLabelColor,'font-size':20,'font-weight':700});
207 txt.textContent = a.centerLabel;
208 svg2.appendChild(txt);
209 }
210
211 conc.appendChild(svg2);
212
213 /* Legend */
214 var legend = document.createElement('div');
215 legend.className = 'bkbg-sr-legend';
216 a.skills.forEach(function(skill){
217 var li = document.createElement('div'); li.className='bkbg-sr-legend-item';
218 var dot = document.createElement('div'); dot.className='bkbg-sr-legend-dot'; dot.style.background=skill.color||'#7c3aed';
219 var nm = document.createElement('span'); nm.className='bkbg-sr-legend-name'; nm.textContent=skill.name;
220 var pc = document.createElement('span'); pc.className='bkbg-sr-legend-pct'; pc.style.color=skill.color||'#7c3aed'; pc.textContent=(skill.pct||0)+'%';
221 li.appendChild(dot); li.appendChild(nm); li.appendChild(pc);
222 legend.appendChild(li);
223 });
224 conc.appendChild(legend);
225 wrap.appendChild(conc);
226 }
227
228 appEl.replaceWith(wrap);
229
230 /* Animate when in view */
231 function triggerAnimation() {
232 progressEls.forEach(function(p) { p.el.setAttribute('stroke-dashoffset', p.target); });
233 }
234
235 if ('IntersectionObserver' in window) {
236 var obs = new IntersectionObserver(function(entries) {
237 if (entries[0].isIntersecting) {
238 triggerAnimation();
239 obs.disconnect();
240 }
241 }, { threshold: 0.2 });
242 obs.observe(wrap);
243 } else {
244 triggerAnimation();
245 }
246 }
247
248 document.querySelectorAll('.bkbg-sr-app').forEach(initSkillRings);
249 })();
250