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

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

178 lines 12.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 ( function () {
2 const el = window.wp.element.createElement;
3 const { registerBlockType } = window.wp.blocks;
4 const { InspectorControls, useBlockProps, PanelColorSettings } = window.wp.blockEditor;
5 const { PanelBody, RangeControl, ToggleControl, TextControl, Button } = window.wp.components;
6 const { __ } = window.wp.i18n;
7
8 var _tc; function getTC() { return _tc || (_tc = window.bkbgTypographyControl); }
9 var _tv; function getTV() { return _tv || (_tv = window.bkbgTypoCssVars); }
10
11 function buildTypoVars( a ) {
12 var fn = getTV();
13 if ( !fn ) return {};
14 var v = {};
15 Object.assign( v, fn( a.titleTypo || {}, '--bksc-tt-' ) );
16 return v;
17 }
18
19 // ── Renderer ──────────────────────────────────────────────────────────────
20 function renderSlopeChart( a ) {
21 const items = a.items || [];
22 const W = a.svgWidth;
23 const H = a.svgHeight;
24 const PT = a.padTop;
25 const PL = a.padLeft;
26 const PR = a.padRight;
27 const PB = a.padBottom;
28 const DR = a.dotRadius;
29
30 const allVals = items.flatMap( it => [ it.a || 0, it.b || 0 ] );
31 const minVal = Math.min( ...allVals ) * 0.95;
32 const maxVal = Math.max( ...allVals ) * 1.05 || 1;
33 const range = maxVal - minVal || 1;
34 const chartH = H - PT - PB;
35
36 const xA = PL;
37 const xB = W - PR;
38
39 function yAt( v ) { return H - PB - ( ( v - minVal ) / range ) * chartH; }
40 function lineColor( diff ) {
41 if ( diff > 0.001 ) return a.posColor || '#10b981';
42 if ( diff < -0.001 ) return a.negColor || '#ef4444';
43 return a.neuColor || '#9ca3af';
44 }
45 function fmt( v ) { return String( parseFloat( v.toFixed( 1 ) ) ); }
46
47 const svgEls = [];
48 svgEls.push( el( 'rect', { key: 'bg', x: 0, y: 0, width: W, height: H, fill: a.bgColor || '#ffffff', rx: 8 } ) );
49
50 // Column header lines and labels
51 [ [ xA, a.labelA || '2020' ], [ xB, a.labelB || '2024' ] ].forEach( ( [ x, lbl ], ci ) => {
52 svgEls.push( el( 'line', { key: `col${ ci }`, x1: x, y1: PT - 6, x2: x, y2: H - PB, stroke: '#d1d5db', strokeWidth: 1 } ) );
53 svgEls.push( el( 'text', { key: `clt${ ci }`, x, y: PT - 14, textAnchor: 'middle', fill: a.textColor || '#374151', fontSize: a.labelFontSize, fontWeight: a.labelFontWeight, fontFamily: 'inherit' }, lbl ) );
54 } );
55
56 // Lines and dots
57 items.forEach( ( item, i ) => {
58 const va = item.a || 0;
59 const vb = item.b || 0;
60 const diff = vb - va;
61 const clr = lineColor( diff );
62 const ya = yAt( va );
63 const yb = yAt( vb );
64
65 svgEls.push( el( 'line', { key: `ln${ i }`, x1: xA, y1: ya, x2: xB, y2: yb, stroke: clr, strokeWidth: a.lineThickness, strokeLinecap: 'round', opacity: 0.85 } ) );
66 svgEls.push( el( 'circle', { key: `dA${ i }`, cx: xA, cy: ya, r: DR, fill: clr } ) );
67 svgEls.push( el( 'circle', { key: `dB${ i }`, cx: xB, cy: yb, r: DR, fill: clr } ) );
68
69 // Left labels & values
70 svgEls.push( el( 'text', { key: `lblA${ i }`, x: xA - DR - 6, y: ya, textAnchor: 'end', dominantBaseline: 'middle', fill: a.textColor, fontSize: a.labelFontSize, fontFamily: 'inherit' }, item.label || '' ) );
71 if ( a.showValues ) {
72 svgEls.push( el( 'text', { key: `valA${ i }`, x: xA + DR + 5, y: ya, dominantBaseline: 'middle', fill: clr, fontSize: a.valueFontSize, fontWeight: a.valueFontWeight, fontFamily: 'inherit' }, fmt( va ) ) );
73 }
74
75 // Right values & labels
76 if ( a.showValues ) {
77 svgEls.push( el( 'text', { key: `valB${ i }`, x: xB - DR - 5, y: yb, textAnchor: 'end', dominantBaseline: 'middle', fill: clr, fontSize: a.valueFontSize, fontWeight: a.valueFontWeight, fontFamily: 'inherit' }, fmt( vb ) ) );
78 }
79 svgEls.push( el( 'text', { key: `lblB${ i }`, x: xB + DR + 6, y: yb, dominantBaseline: 'middle', fill: a.textColor, fontSize: a.labelFontSize, fontFamily: 'inherit' },
80 a.showDiff ? `${ diff > 0 ? '+' : '' }${ fmt( diff ) }` : item.label || ''
81 ) );
82 } );
83
84 // Legend
85 [ ['▲ Increase', a.posColor], ['▼ Decrease', a.negColor], ['— No change', a.neuColor] ].forEach( ( [ lbl, clr ], li ) => {
86 svgEls.push( el( 'text', { key: `leg${ li }`, x: xA + li * 120, y: PT - 34, fill: clr, fontSize: 11, fontFamily: 'inherit' }, lbl ) );
87 } );
88
89 return el( 'svg', { viewBox: `0 0 ${ W } ${ H }`, width: '100%', style: { display: 'block', maxWidth: W + 'px', margin: '0 auto' } }, ...svgEls );
90 }
91
92 function updItem( setAttributes, items, idx, field, val ) {
93 setAttributes( { items: items.map( ( it, i ) => i === idx ? { ...it, [field]: val } : it ) } );
94 }
95
96 registerBlockType( 'blockenberg/slope-chart', {
97 edit: function ( props ) {
98 const { attributes: a, setAttributes } = props;
99 const blockProps = useBlockProps( { className: 'bkbg-slope-wrap', style: buildTypoVars( a ) } );
100
101 return el( 'div', blockProps,
102 el( InspectorControls, {},
103
104 el( PanelBody, { title: __( 'Chart Settings', 'blockenberg' ), initialOpen: true },
105 el( TextControl, { label: __( 'Title', 'blockenberg' ), value: a.title, onChange: v => setAttributes( { title: v } ) } ),
106 el( ToggleControl, { label: __( 'Show Title', 'blockenberg' ), checked: a.showTitle, onChange: v => setAttributes( { showTitle: v } ) } ),
107 el( ToggleControl, { label: __( 'Show Values', 'blockenberg' ), checked: a.showValues, onChange: v => setAttributes( { showValues: v } ) } ),
108 el( ToggleControl, { label: __( 'Right label = difference (Δ)', 'blockenberg' ), checked: a.showDiff, onChange: v => setAttributes( { showDiff: v } ) } ),
109 el( TextControl, { label: __( 'Left Column Label', 'blockenberg' ), value: a.labelA, onChange: v => setAttributes( { labelA: v } ) } ),
110 el( TextControl, { label: __( 'Right Column Label', 'blockenberg' ), value: a.labelB, onChange: v => setAttributes( { labelB: v } ) } ),
111 ),
112
113 el( PanelBody, { title: __( 'Layout', 'blockenberg' ), initialOpen: false },
114 el( RangeControl, { label: __( 'Canvas Width', 'blockenberg' ), value: a.svgWidth, onChange: v => setAttributes( { svgWidth: v } ), min: 280, max: 900, step: 10 } ),
115 el( RangeControl, { label: __( 'Canvas Height', 'blockenberg' ), value: a.svgHeight, onChange: v => setAttributes( { svgHeight: v } ), min: 150, max: 700 } ),
116 el( RangeControl, { label: __( 'Left/Right padding', 'blockenberg' ), value: a.padLeft, onChange: v => setAttributes( { padLeft: v, padRight: v } ), min: 60, max: 280 } ),
117 el( RangeControl, { label: __( 'Line Thickness', 'blockenberg' ), value: a.lineThickness, onChange: v => setAttributes( { lineThickness: v } ), min: 1, max: 10 } ),
118 el( RangeControl, { label: __( 'Dot Radius', 'blockenberg' ), value: a.dotRadius, onChange: v => setAttributes( { dotRadius: v } ), min: 2, max: 18 } ),
119 ),
120
121
122 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
123 el( getTC(), { label: __( 'Title', 'blockenberg' ), value: a.titleTypo || {}, onChange: function( v ) { setAttributes( { titleTypo: v } ); } } ),
124 el( RangeControl, { label: __( 'Label Font Size', 'blockenberg' ), value: a.labelFontSize, onChange: v => setAttributes( { labelFontSize: v } ), min: 9, max: 22 } ),
125 el( RangeControl, { label: __( 'Label Font Weight', 'blockenberg' ), value: a.labelFontWeight, onChange: v => setAttributes( { labelFontWeight: v } ), min: 300, max: 900, step: 100 } ),
126 el( RangeControl, { label: __( 'Value Font Size', 'blockenberg' ), value: a.valueFontSize, onChange: v => setAttributes( { valueFontSize: v } ), min: 7, max: 16 } ),
127 el( RangeControl, { label: __( 'Value Font Weight', 'blockenberg' ), value: a.valueFontWeight, onChange: v => setAttributes( { valueFontWeight: v } ), min: 300, max: 900, step: 100, __nextHasNoMarginBottom: true } )
128 ),
129 el( PanelColorSettings, {
130 title: __( 'Colors', 'blockenberg' ), initialOpen: false,
131 colorSettings: [
132 { label: __( 'Increase Color', 'blockenberg' ), value: a.posColor, onChange: v => setAttributes( { posColor: v || '#10b981' } ) },
133 { label: __( 'Decrease Color', 'blockenberg' ), value: a.negColor, onChange: v => setAttributes( { negColor: v || '#ef4444' } ) },
134 { label: __( 'Neutral Color', 'blockenberg' ), value: a.neuColor, onChange: v => setAttributes( { neuColor: v || '#9ca3af' } ) },
135 { label: __( 'Background', 'blockenberg' ), value: a.bgColor, onChange: v => setAttributes( { bgColor: v || '#ffffff' } ) },
136 { label: __( 'Title Color', 'blockenberg' ), value: a.titleColor, onChange: v => setAttributes( { titleColor: v || '#111827' } ) },
137 { label: __( 'Text Color', 'blockenberg' ), value: a.textColor, onChange: v => setAttributes( { textColor: v || '#374151' } ) },
138 ]
139 } ),
140
141 el( PanelBody, { title: __( 'Items', 'blockenberg' ), initialOpen: false },
142 el( Button, { variant: 'secondary', style: { marginBottom: 10 }, onClick: () => setAttributes( { items: [ ...a.items, { label: 'New Item', a: 10, b: 20 } ] } ) }, __( '+ Add Item', 'blockenberg' ) ),
143 a.items.map( ( item, i ) =>
144 el( PanelBody, { key: i, title: item.label || `Item ${ i + 1 }`, initialOpen: false },
145 el( TextControl, { label: __( 'Label', 'blockenberg' ), value: item.label, onChange: v => updItem( setAttributes, a.items, i, 'label', v ) } ),
146 el( RangeControl, { label: `${ a.labelA || 'Before' } value`, value: item.a, onChange: v => updItem( setAttributes, a.items, i, 'a', v ), min: -10000, max: 100000, step: 0.1 } ),
147 el( RangeControl, { label: `${ a.labelB || 'After' } value`, value: item.b, onChange: v => updItem( setAttributes, a.items, i, 'b', v ), min: -10000, max: 100000, step: 0.1 } ),
148 el( Button, { isDestructive: true, isSmall: true, onClick: () => setAttributes( { items: a.items.filter( ( _, x ) => x !== i ) } ) }, __( 'Remove', 'blockenberg' ) ),
149 )
150 ),
151 ),
152 ),
153
154 a.showTitle && a.title && el( 'h3', { className: 'bkbg-slope-title', style: { color: a.titleColor } }, a.title ),
155 el( 'div', { className: 'bkbg-slope-svg' }, renderSlopeChart( a ) ),
156 );
157 },
158
159 save: function ( { attributes: a } ) {
160 const blockProps = useBlockProps.save( { className: 'bkbg-slope-wrap', style: buildTypoVars( a ) } );
161 return el( 'div', blockProps,
162 a.showTitle && a.title ? el( 'h3', { className: 'bkbg-slope-title', style: { color: a.titleColor } }, a.title ) : null,
163 el( 'div', { className: 'bkbg-slope-svg' }, renderSlopeChart( a ) ),
164 );
165 },
166
167 deprecated: [ {
168 save: function ( { attributes: a } ) {
169 var bp = useBlockProps.save( { className: 'bkbg-slope-wrap' } );
170 return el( 'div', bp,
171 a.showTitle && a.title ? el( 'h3', { className: 'bkbg-slope-title', style: { color: a.titleColor } }, a.title ) : null,
172 el( 'div', { className: 'bkbg-slope-svg' }, renderSlopeChart( a ) ),
173 );
174 },
175 } ],
176 } );
177 }() );
178