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

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

495 lines 22.0 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 Fragment = wp.element.Fragment;
4 var registerBlockType = wp.blocks.registerBlockType;
5 var __ = wp.i18n.__;
6 var InspectorControls = wp.blockEditor.InspectorControls;
7 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
8 var useBlockProps = wp.blockEditor.useBlockProps;
9 var PanelBody = wp.components.PanelBody;
10 var RangeControl = wp.components.RangeControl;
11 var TextControl = wp.components.TextControl;
12 var ToggleControl = wp.components.ToggleControl;
13 var Button = wp.components.Button;
14 var ColorPicker = wp.components.ColorPicker;
15 var Popover = wp.components.Popover;
16 var useState = wp.element.useState;
17
18 var _gcTC, _gcTV;
19 function _tc() { return _gcTC || (_gcTC = window.bkbgTypographyControl); }
20 function _tv() { return _gcTV || (_gcTV = window.bkbgTypoCssVars); }
21
22 // --- SVG helpers ---
23
24 // angle=0 → left (min), angle=180 → right (max), going through top
25 function polarToCartesian(cx, cy, r, angleDeg) {
26 var rad = (angleDeg * Math.PI) / 180;
27 return {
28 x: cx - r * Math.cos(rad),
29 y: cy - r * Math.sin(rad)
30 };
31 }
32
33 // Compute layout metrics for the semicircle gauge
34 function gaugeMetrics(size, strokeWidth) {
35 var W = size;
36 var H = Math.round(size / 2 + strokeWidth + 8);
37 var cx = W / 2;
38 var cy = H - Math.round(strokeWidth / 2) - 4;
39 var r = cx - strokeWidth * 1.5 - 2;
40 var perimeter = Math.PI * r;
41 return { W: W, H: H, cx: cx, cy: cy, r: r, perimeter: perimeter };
42 }
43
44 // SVG arc path from left through top to right (counterclockwise in SVG)
45 function arcPath(cx, cy, r) {
46 return (
47 'M ' + (cx - r) + ' ' + cy +
48 ' A ' + r + ' ' + r + ' 0 1 0 ' + (cx + r) + ' ' + cy
49 );
50 }
51
52 // Find the zone colour for a given 0-100 percentage
53 function zoneColor(zones, pct) {
54 var color = '#6c3fb5';
55 for (var i = 0; i < zones.length; i++) {
56 if (pct <= zones[i].to) { color = zones[i].color; break; }
57 color = zones[i].color; // last zone wins if over max
58 }
59 return color;
60 }
61
62 // --- Editor SVG preview (shows static, fully-drawn arc at current value) ---
63 function GaugePreview(props) {
64 var a = props.attrs;
65 var sz = a.size || 260;
66 var sw = a.strokeWidth || 18;
67 var m = gaugeMetrics(sz, sw);
68 var p = arcPath(m.cx, m.cy, m.r);
69 var min = a.minValue || 0;
70 var max = a.maxValue || 100;
71 var f = Math.min(1, Math.max(0, (a.value - min) / (max - min)));
72 var pct = f * 100;
73 var zones = a.colorZones || [];
74 var fillColor = zoneColor(zones, pct);
75 var lnfs = (a.typoLabel && a.typoLabel.sizeDesktop) || a.labelFontSize || 14;
76
77 var children = [];
78
79 // Track
80 children.push(el('path', {
81 key: 'track', d: p, fill: 'none',
82 stroke: a.trackColor || '#e5e7eb',
83 strokeWidth: sw, strokeLinecap: 'round'
84 }));
85
86 // Zone arcs
87 zones.forEach(function (zone, zi) {
88 var zLen = (zone.to - zone.from) / 100 * m.perimeter;
89 var zOff = -(zone.from / 100) * m.perimeter;
90 children.push(el('path', {
91 key: 'z' + zi, d: p, fill: 'none', stroke: zone.color,
92 strokeWidth: sw, strokeLinecap: 'butt',
93 strokeDasharray: zLen + ' ' + m.perimeter,
94 strokeDashoffset: zOff
95 }));
96 });
97
98 // Ticks
99 if (a.showTicks) {
100 var tc = a.tickCount || 10;
101 for (var t = 0; t <= tc; t++) {
102 var pt1 = polarToCartesian(m.cx, m.cy, m.r - sw / 2 - 2, t / tc * 180);
103 var pt2 = polarToCartesian(m.cx, m.cy, m.r + sw / 2 + 2, t / tc * 180);
104 children.push(el('line', {
105 key: 'tk' + t,
106 x1: pt1.x, y1: pt1.y, x2: pt2.x, y2: pt2.y,
107 stroke: '#9ca3af', strokeWidth: 1.5
108 }));
109 }
110 }
111
112 // Fill arc (editor: fully drawn to current value)
113 children.push(el('path', {
114 key: 'fill', d: p, fill: 'none', stroke: fillColor,
115 strokeWidth: sw, strokeLinecap: 'round',
116 strokeDasharray: m.perimeter + ' ' + m.perimeter,
117 strokeDashoffset: (1 - f) * m.perimeter
118 }));
119
120 // Needle
121 if (a.showNeedle) {
122 var nTip = polarToCartesian(m.cx, m.cy, m.r - sw - 4, f * 180);
123 children.push(el('line', {
124 key: 'ndle',
125 x1: m.cx, y1: m.cy, x2: nTip.x, y2: nTip.y,
126 stroke: a.needleColor || '#374151',
127 strokeWidth: 3, strokeLinecap: 'round'
128 }));
129 children.push(el('circle', {
130 key: 'nhub', cx: m.cx, cy: m.cy,
131 r: sw / 2, fill: a.needleColor || '#374151'
132 }));
133 }
134
135 // Value
136 if (a.showValue !== false) {
137 children.push(el('text', {
138 key: 'val',
139 className: 'bkgc-value-text',
140 x: m.cx,
141 y: m.cy - (a.showNeedle ? sw + 2 : 0),
142 textAnchor: 'middle',
143 dominantBaseline: 'auto',
144 fill: a.valueColor || '#111827'
145 }, a.value + (a.unit || '')));
146 }
147
148 // Label
149 if (a.showLabel !== false && a.label) {
150 children.push(el('text', {
151 key: 'lbl',
152 className: 'bkgc-label-text',
153 x: m.cx, y: m.cy + lnfs + 2,
154 textAnchor: 'middle', dominantBaseline: 'hanging',
155 fill: a.labelColor || '#6b7280'
156 }, a.label));
157 }
158
159 return el('svg', {
160 viewBox: '0 0 ' + m.W + ' ' + m.H,
161 width: m.W, height: m.H,
162 style: { display: 'block', overflow: 'visible', margin: '0 auto' }
163 }, children);
164 }
165
166 // --- Register ---
167 /* ── colour-swatch + popover ── */
168 function BkbgColorSwatch(p) {
169 var st = useState(false), open = st[0], setOpen = st[1];
170 return el('div', { style:{ display:'flex', alignItems:'center', justifyContent:'space-between', padding:'4px 0', gap:'8px' } },
171 el('span', { style:{ fontSize:'12px', color:'#1e1e1e', flex:1, lineHeight:1.4 } }, p.label),
172 el('div', { style:{ position:'relative', flexShrink:0 } },
173 el('button', { type:'button', title: p.value||'none', onClick: function(){ setOpen(!open); },
174 style:{ width:'28px', height:'28px', borderRadius:'4px', border: open ? '2px solid #007cba' : '2px solid #ddd', cursor:'pointer', padding:0, display:'block', background: p.value||'#ffffff', flexShrink:0 } }),
175 open && el(Popover, { position:'bottom left', onClose: function(){ setOpen(false); } },
176 el('div', { style:{ padding:'8px' }, onMouseDown: function(e){ e.stopPropagation(); } },
177 el('div', { style:{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:'6px' } },
178 el('strong', { style:{ fontSize:'12px' } }, p.label),
179 el(Button, { icon:'no-alt', isSmall:true, onClick: function(){ setOpen(false); } })
180 ),
181 el(ColorPicker, { color: p.value, enableAlpha:true, onChange: p.onChange })
182 )
183 )
184 )
185 );
186 }
187
188 registerBlockType('blockenberg/gauge-chart', {
189
190 edit: function (props) {
191 var attrs = props.attributes;
192 var setAttr = props.setAttributes;
193 var zones = attrs.colorZones || [];
194
195 function updateZone(idx, key, val) {
196 var newZones = zones.map(function (z, i) {
197 if (i !== idx) return z;
198 var updated = {};
199 Object.keys(z).forEach(function (k) { updated[k] = z[k]; });
200 updated[key] = val;
201 return updated;
202 });
203 setAttr({ colorZones: newZones });
204 }
205
206 function addZone() {
207 setAttr({ colorZones: zones.concat([{ from: 0, to: 100, color: '#6c3fb5' }]) });
208 }
209
210 function removeZone(idx) {
211 setAttr({ colorZones: zones.filter(function (_, i) { return i !== idx; }) });
212 }
213
214 var wrapStyle = Object.assign({},
215 attrs.bgColor ? { background: attrs.bgColor } : {},
216 _tv()(attrs.typoValue, '--bkgc-vl-'),
217 _tv()(attrs.typoLabel, '--bkgc-lb-')
218 );
219 var blockProps = useBlockProps({ className: 'bkgc-wrap', style: wrapStyle });
220
221 return el(Fragment, null,
222 el(InspectorControls, null,
223
224 // ── Value ──
225 el(PanelBody, { title: __('Value', 'blockenberg'), initialOpen: true },
226 el(RangeControl, {
227 label: __('Value', 'blockenberg'),
228 value: attrs.value, min: attrs.minValue, max: attrs.maxValue,
229 onChange: function (v) { setAttr({ value: v }); }
230 }),
231 el(TextControl, {
232 label: __('Label', 'blockenberg'),
233 value: attrs.label,
234 onChange: function (v) { setAttr({ label: v }); }
235 }),
236 el(TextControl, {
237 label: __('Unit', 'blockenberg'),
238 value: attrs.unit,
239 onChange: function (v) { setAttr({ unit: v }); }
240 }),
241 el(RangeControl, {
242 label: __('Min Value', 'blockenberg'),
243 value: attrs.minValue, min: -9999, max: attrs.value - 1,
244 onChange: function (v) { setAttr({ minValue: v }); }
245 }),
246 el(RangeControl, {
247 label: __('Max Value', 'blockenberg'),
248 value: attrs.maxValue, min: attrs.value + 1, max: 99999,
249 onChange: function (v) { setAttr({ maxValue: v }); }
250 })
251 ),
252
253 // ── Color Zones ──
254
255 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
256 _tc()({ label: __('Value', 'blockenberg'), value: attrs.typoValue, onChange: function (v) { setAttr({ typoValue: v }); } }),
257 _tc()({ label: __('Label', 'blockenberg'), value: attrs.typoLabel, onChange: function (v) { setAttr({ typoLabel: v }); } })
258 ),
259 el(PanelBody, { title: __('Color Zones', 'blockenberg'), initialOpen: false },
260 zones.map(function (zone, idx) {
261 return el(PanelBody, {
262 key: 'zone-' + idx,
263 title: __('Zone', 'blockenberg') + ' ' + (idx + 1),
264 initialOpen: idx === 0
265 },
266 el(RangeControl, {
267 label: __('From (%)', 'blockenberg'),
268 value: zone.from, min: 0, max: 99,
269 onChange: function (v) { updateZone(idx, 'from', v); }
270 }),
271 el(RangeControl, {
272 label: __('To (%)', 'blockenberg'),
273 value: zone.to, min: 1, max: 100,
274 onChange: function (v) { updateZone(idx, 'to', v); }
275 }),
276 el(BkbgColorSwatch, {
277 label: __('Color', 'blockenberg'),
278 value: zone.color,
279 onChange: function (v) { updateZone(idx, 'color', v); }
280 }),
281 zones.length > 1 && el(Button, {
282 variant: 'secondary',
283 isDestructive: true,
284 onClick: function () { removeZone(idx); }
285 }, __('Remove Zone', 'blockenberg'))
286 );
287 }),
288 el(Button, {
289 variant: 'secondary',
290 style: { marginTop: '8px' },
291 onClick: addZone
292 }, __('+ Add Zone', 'blockenberg'))
293 ),
294
295 // ── Visual ──
296 el(PanelBody, { title: __('Visual', 'blockenberg'), initialOpen: false },
297 el(RangeControl, {
298 label: __('Size (px)', 'blockenberg'),
299 value: attrs.size, min: 100, max: 600,
300 onChange: function (v) { setAttr({ size: v }); }
301 }),
302 el(RangeControl, {
303 label: __('Stroke Width', 'blockenberg'),
304 value: attrs.strokeWidth, min: 4, max: 50,
305 onChange: function (v) { setAttr({ strokeWidth: v }); }
306 }),
307 el(ToggleControl, {
308 label: __('Show Needle', 'blockenberg'),
309 checked: attrs.showNeedle,
310 onChange: function (v) { setAttr({ showNeedle: v }); },
311 __nextHasNoMarginBottom: true
312 }),
313 el(ToggleControl, {
314 label: __('Show Value', 'blockenberg'),
315 checked: attrs.showValue,
316 onChange: function (v) { setAttr({ showValue: v }); },
317 __nextHasNoMarginBottom: true
318 }),
319 el(ToggleControl, {
320 label: __('Show Label', 'blockenberg'),
321 checked: attrs.showLabel,
322 onChange: function (v) { setAttr({ showLabel: v }); },
323 __nextHasNoMarginBottom: true
324 }),
325 el(ToggleControl, {
326 label: __('Show Tick Marks', 'blockenberg'),
327 checked: attrs.showTicks,
328 onChange: function (v) { setAttr({ showTicks: v }); },
329 __nextHasNoMarginBottom: true
330 }),
331 attrs.showTicks && el(RangeControl, {
332 label: __('Tick Count', 'blockenberg'),
333 value: attrs.tickCount, min: 2, max: 20,
334 onChange: function (v) { setAttr({ tickCount: v }); }
335 }),
336 ),
337
338 // ── Animation ──
339 el(PanelBody, { title: __('Animation', 'blockenberg'), initialOpen: false },
340 el(RangeControl, {
341 label: __('Animate Duration (ms)', 'blockenberg'),
342 value: attrs.animateDuration, min: 200, max: 5000, step: 100,
343 onChange: function (v) { setAttr({ animateDuration: v }); }
344 })
345 ),
346
347 // ── Colors ──
348 el(PanelColorSettings, {
349 title: __('Colors', 'blockenberg'),
350 initialOpen: false,
351 colorSettings: [
352 { label: __('Track Color', 'blockenberg'), value: attrs.trackColor, onChange: function (v) { setAttr({ trackColor: v || '#e5e7eb' }); } },
353 { label: __('Value Color', 'blockenberg'), value: attrs.valueColor, onChange: function (v) { setAttr({ valueColor: v || '#111827' }); } },
354 { label: __('Label Color', 'blockenberg'), value: attrs.labelColor, onChange: function (v) { setAttr({ labelColor: v || '#6b7280' }); } },
355 { label: __('Needle Color', 'blockenberg'), value: attrs.needleColor, onChange: function (v) { setAttr({ needleColor: v || '#374151' }); } },
356 { label: __('Background', 'blockenberg'), value: attrs.bgColor, onChange: function (v) { setAttr({ bgColor: v || '' }); } }
357 ]
358 })
359 ),
360
361 el('div', blockProps,
362 el(GaugePreview, { attrs: attrs })
363 )
364 );
365 },
366
367 save: function (props) {
368 var a = props.attributes;
369 var sw = a.strokeWidth;
370 var m = gaugeMetrics(a.size, sw);
371 var p = arcPath(m.cx, m.cy, m.r);
372 var min = a.minValue;
373 var max = a.maxValue;
374 var zones = a.colorZones || [];
375 var valPct = Math.min(100, Math.max(0, (a.value - min) / (max - min) * 100));
376 var fillColor = zoneColor(zones, valPct);
377 var lnfs = (a.typoLabel && a.typoLabel.sizeDesktop) || a.labelFontSize || 14;
378
379 var wrapStyle = Object.assign({},
380 a.bgColor ? { background: a.bgColor } : {},
381 _tv()(a.typoValue, '--bkgc-vl-'),
382 _tv()(a.typoLabel, '--bkgc-lb-')
383 );
384
385 var blockProps = wp.blockEditor.useBlockProps.save({
386 className: 'bkgc-wrap',
387 style: wrapStyle,
388 'data-value': a.value,
389 'data-min': min,
390 'data-max': max,
391 'data-duration': a.animateDuration,
392 'data-perimeter': m.perimeter,
393 'data-unit': a.unit,
394 'data-show-needle': a.showNeedle ? '1' : '0',
395 'data-r': m.r,
396 'data-cx': m.cx,
397 'data-cy': m.cy,
398 'data-needle-color': a.needleColor || '#374151',
399 'data-fill-color': fillColor
400 });
401
402 var svgChildren = [];
403
404 // Track
405 svgChildren.push(el('path', {
406 key: 'track', d: p, fill: 'none',
407 stroke: a.trackColor || '#e5e7eb',
408 strokeWidth: sw, strokeLinecap: 'round'
409 }));
410
411 // Zone arcs
412 zones.forEach(function (zone, zi) {
413 var zLen = (zone.to - zone.from) / 100 * m.perimeter;
414 var zOff = -(zone.from / 100) * m.perimeter;
415 svgChildren.push(el('path', {
416 key: 'z' + zi, d: p, fill: 'none', stroke: zone.color,
417 strokeWidth: sw, strokeLinecap: 'butt',
418 strokeDasharray: zLen + ' ' + m.perimeter,
419 strokeDashoffset: zOff
420 }));
421 });
422
423 // Ticks
424 if (a.showTicks) {
425 for (var t = 0; t <= a.tickCount; t++) {
426 var pt1 = polarToCartesian(m.cx, m.cy, m.r - sw / 2 - 2, t / a.tickCount * 180);
427 var pt2 = polarToCartesian(m.cx, m.cy, m.r + sw / 2 + 2, t / a.tickCount * 180);
428 svgChildren.push(el('line', {
429 key: 'tk' + t,
430 x1: pt1.x, y1: pt1.y, x2: pt2.x, y2: pt2.y,
431 stroke: '#9ca3af', strokeWidth: 1.5
432 }));
433 }
434 }
435
436 // Fill arc — starts empty (dashoffset = perimeter), JS animates it
437 svgChildren.push(el('path', {
438 key: 'fill',
439 className: 'bkgc-fill',
440 d: p, fill: 'none', stroke: fillColor,
441 strokeWidth: sw, strokeLinecap: 'round',
442 strokeDasharray: m.perimeter + ' ' + m.perimeter,
443 strokeDashoffset: m.perimeter
444 }));
445
446 // Needle pivot at min position (angle=0 = left)
447 if (a.showNeedle) {
448 var nBase = polarToCartesian(m.cx, m.cy, m.r - sw - 4, 0);
449 svgChildren.push(el('line', {
450 key: 'ndle', className: 'bkgc-needle',
451 x1: m.cx, y1: m.cy, x2: nBase.x, y2: nBase.y,
452 stroke: a.needleColor || '#374151',
453 strokeWidth: 3, strokeLinecap: 'round'
454 }));
455 svgChildren.push(el('circle', {
456 key: 'nhub', cx: m.cx, cy: m.cy,
457 r: sw / 2, fill: a.needleColor || '#374151'
458 }));
459 }
460
461 // Value text — starts at 0, JS counts up
462 if (a.showValue) {
463 svgChildren.push(el('text', {
464 key: 'val',
465 className: 'bkgc-value-text',
466 x: m.cx,
467 y: m.cy - (a.showNeedle ? sw + 2 : 0),
468 textAnchor: 'middle',
469 dominantBaseline: 'auto',
470 fill: a.valueColor || '#111827'
471 }, '0' + (a.unit || '')));
472 }
473
474 // Label
475 if (a.showLabel && a.label) {
476 svgChildren.push(el('text', {
477 key: 'lbl',
478 className: 'bkgc-label-text',
479 x: m.cx, y: m.cy + lnfs + 2,
480 textAnchor: 'middle', dominantBaseline: 'hanging',
481 fill: a.labelColor || '#6b7280'
482 }, a.label));
483 }
484
485 return el('div', blockProps,
486 el('svg', {
487 viewBox: '0 0 ' + m.W + ' ' + m.H,
488 width: m.W, height: m.H,
489 style: { display: 'block', margin: '0 auto', overflow: 'visible' }
490 }, svgChildren)
491 );
492 }
493 });
494 }() );
495