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

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

217 lines 15.5 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, _tv;
9 function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
10 function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
11
12 // ── Renderer ──────────────────────────────────────────────────────────────
13 function renderMarimekko( a ) {
14 const cols = a.columns || [];
15 const colors = a.segmentColors || [ '#6366f1','#10b981','#f59e0b','#ef4444','#8b5cf6','#ec4899' ];
16 const W = a.svgWidth;
17 const H = a.svgHeight;
18 const PT = a.padTop;
19 const PL = a.padLeft;
20 const PR = a.padRight;
21 const PB = a.padBottom;
22 const CG = a.colGap;
23 const chartW = W - PL - PR;
24 const chartH = H - PT - PB;
25
26 if ( ! cols.length ) return el( 'svg', { viewBox: `0 0 ${ W } ${ H }`, width: '100%' } );
27
28 // Total size (normalise columns so they fill 100%)
29 const totalSize = cols.reduce( ( s, c ) => s + ( c.size || 0 ), 0 ) || 1;
30
31 // Collect all unique segment labels in order
32 const segLabels = [];
33 cols.forEach( c => {
34 ( c.segments || [] ).forEach( seg => {
35 if ( ! segLabels.includes( seg.label ) ) segLabels.push( seg.label );
36 } );
37 } );
38
39 const svgEls = [];
40 svgEls.push( el( 'rect', { key: 'bg', x: 0, y: 0, width: W, height: H, fill: a.bgColor || '#ffffff', rx: 8 } ) );
41
42 // Y axis grid (0–100%)
43 if ( a.showYAxis ) {
44 for ( let t = 0; t <= 5; t++ ) {
45 const gy = PT + ( t / 5 ) * chartH;
46 svgEls.push( el( 'line', { key: `gl${ t }`, x1: PL, y1: gy, x2: W - PR, y2: gy, stroke: a.gridColor || '#f3f4f6', strokeWidth: 1 } ) );
47 svgEls.push( el( 'text', { key: `gy${ t }`, x: PL - 6, y: gy, textAnchor: 'end', dominantBaseline: 'middle', fill: a.textColor, fontSize: a.labelFontSize - 1, fontFamily: 'inherit' }, `${ 100 - t * 20 }%` ) );
48 }
49 // Y axis line
50 svgEls.push( el( 'line', { key: 'yax', x1: PL, y1: PT, x2: PL, y2: PT + chartH, stroke: a.textColor, strokeWidth: 1, strokeOpacity: 0.3 } ) );
51 }
52
53 // Render columns
54 let xCursor = 0;
55 cols.forEach( ( col, ci ) => {
56 const colW = ( ( col.size || 0 ) / totalSize ) * chartW - CG;
57 const colX = PL + ( xCursor / totalSize ) * chartW;
58 const segs = col.segments || [];
59 const segTotal = segs.reduce( ( s, sg ) => s + ( sg.value || 0 ), 0 ) || 1;
60
61 let yCursor = 0;
62 segs.forEach( ( seg, si ) => {
63 const segPct = ( seg.value || 0 ) / segTotal;
64 const segH = segPct * chartH;
65 const segY = PT + yCursor;
66 const colorIdx = segLabels.indexOf( seg.label );
67 const clr = colors[ colorIdx % colors.length ] || '#6366f1';
68
69 svgEls.push( el( 'rect', { key: `seg${ ci }${ si }`, x: colX, y: segY, width: Math.max( 1, colW ), height: segH, fill: clr, stroke: '#fff', strokeWidth: 1 } ) );
70
71 // Segment value label
72 if ( a.showValues && segH > 18 && colW > 24 ) {
73 svgEls.push( el( 'text', { key: `sv${ ci }${ si }`, x: colX + colW / 2, y: segY + segH / 2, textAnchor: 'middle', dominantBaseline: 'middle', fill: '#ffffff', fontSize: a.valueFontSize, fontFamily: 'inherit', fontWeight: 600 }, `${ Math.round( seg.value ) }%` ) );
74 }
75
76 yCursor += segH;
77 } );
78
79 // Column label below
80 if ( a.showColLabels ) {
81 const lx = colX + colW / 2;
82 const ly1 = H - PB + 14;
83 const ly2 = H - PB + 26;
84 svgEls.push( el( 'text', { key: `clbl${ ci }`, x: lx, y: ly1, textAnchor: 'middle', fill: a.textColor, fontSize: a.labelFontSize, fontFamily: 'inherit', fontWeight: a.labelFontWeight || 600 }, col.label ) );
85 svgEls.push( el( 'text', { key: `csz${ ci }`, x: lx, y: ly2, textAnchor: 'middle', fill: a.textColor, fontSize: a.labelFontSize - 1, fontFamily: 'inherit', fillOpacity: 0.7 }, `${ col.size || 0 }%` ) );
86 }
87
88 xCursor += col.size || 0;
89 } );
90
91 // X baseline
92 svgEls.push( el( 'line', { key: 'xax', x1: PL, y1: PT + chartH, x2: W - PR, y2: PT + chartH, stroke: a.textColor, strokeWidth: 1, strokeOpacity: 0.3 } ) );
93
94 // Legend
95 if ( a.showLegend ) {
96 const legY = H - 12;
97 const legStep = Math.min( 140, chartW / segLabels.length );
98 segLabels.forEach( ( lbl, li ) => {
99 const lx = PL + li * legStep;
100 svgEls.push( el( 'rect', { key: `lgn${ li }`, x: lx, y: legY - 7, width: 14, height: 10, fill: colors[ li % colors.length ], rx: 2 } ) );
101 svgEls.push( el( 'text', { key: `lgt${ li }`, x: lx + 18, y: legY, dominantBaseline: 'middle', fill: a.textColor, fontSize: a.labelFontSize - 1, fontFamily: 'inherit' }, lbl ) );
102 } );
103 }
104
105 return el( 'svg', { viewBox: `0 0 ${ W } ${ H }`, width: '100%', style: { display: 'block', maxWidth: W + 'px', margin: '0 auto' } }, ...svgEls );
106 }
107
108 function updCol( setAttributes, columns, ci, field, val ) {
109 setAttributes( { columns: columns.map( ( c, i ) => i === ci ? { ...c, [field]: val } : c ) } );
110 }
111 function updSeg( setAttributes, columns, ci, si, field, val ) {
112 setAttributes( { columns: columns.map( ( c, i ) => i !== ci ? c : {
113 ...c,
114 segments: ( c.segments || [] ).map( ( sg, j ) => j === si ? { ...sg, [field]: val } : sg ),
115 } ) } );
116 }
117
118 registerBlockType( 'blockenberg/marimekko-chart', {
119 edit: function ( props ) {
120 const { attributes: a, setAttributes } = props;
121 const blockProps = useBlockProps( (function () {
122 var _tvFn = getTypoCssVars();
123 var s = {};
124 if (_tvFn) Object.assign(s, _tvFn(a.titleTypo, '--bkbg-mk-tt-'));
125 return { className: 'bkbg-marimekko-wrap', style: s };
126 })() );
127
128 return el( 'div', blockProps,
129 el( InspectorControls, {},
130
131 el( PanelBody, { title: __( 'Chart Settings', 'blockenberg' ), initialOpen: true },
132 el( TextControl, { label: __( 'Title', 'blockenberg' ), value: a.title, onChange: v => setAttributes( { title: v } ) } ),
133 el( ToggleControl, { label: __( 'Show Title', 'blockenberg' ), checked: a.showTitle, onChange: v => setAttributes( { showTitle: v } ) } ),
134 el( ToggleControl, { label: __( 'Show Values', 'blockenberg' ), checked: a.showValues, onChange: v => setAttributes( { showValues: v } ) } ),
135 el( ToggleControl, { label: __( 'Show Legend', 'blockenberg' ), checked: a.showLegend, onChange: v => setAttributes( { showLegend: v } ) } ),
136 el( ToggleControl, { label: __( 'Show Column Labels', 'blockenberg' ),checked: a.showColLabels,onChange: v => setAttributes( { showColLabels: v } ) } ),
137 el( ToggleControl, { label: __( 'Show Y Axis', 'blockenberg' ), checked: a.showYAxis, onChange: v => setAttributes( { showYAxis: v } ) } ),
138 ),
139
140 el( PanelBody, { title: __( 'Layout', 'blockenberg' ), initialOpen: false },
141 el( RangeControl, { label: __( 'Canvas Width', 'blockenberg' ), value: a.svgWidth, onChange: v => setAttributes( { svgWidth: v } ), min: 300, max: 1200, step: 20 } ),
142 el( RangeControl, { label: __( 'Canvas Height', 'blockenberg' ), value: a.svgHeight, onChange: v => setAttributes( { svgHeight: v } ), min: 200, max: 800 } ),
143 el( RangeControl, { label: __( 'Column Gap', 'blockenberg' ), value: a.colGap, onChange: v => setAttributes( { colGap: v } ), min: 0, max: 20 } ),
144 el( RangeControl, { label: __( 'Left Padding', 'blockenberg' ), value: a.padLeft, onChange: v => setAttributes( { padLeft: v } ), min: 20, max: 120 } ),
145 el( RangeControl, { label: __( 'Bottom Padding', 'blockenberg' ),value: a.padBottom, onChange: v => setAttributes( { padBottom: v } ), min: 30, max: 120 } ),
146 ),
147
148 el( PanelColorSettings, {
149 title: __( 'Colors', 'blockenberg' ), initialOpen: false,
150 colorSettings: [
151 { label: __( 'Background', 'blockenberg' ), value: a.bgColor, onChange: v => setAttributes( { bgColor: v || '#ffffff' } ) },
152 { label: __( 'Grid', 'blockenberg' ), value: a.gridColor, onChange: v => setAttributes( { gridColor: v || '#f3f4f6' } ) },
153 { label: __( 'Title', 'blockenberg' ), value: a.titleColor, onChange: v => setAttributes( { titleColor: v || '#111827' } ) },
154 { label: __( 'Text/Axes', 'blockenberg' ), value: a.textColor, onChange: v => setAttributes( { textColor: v || '#374151' } ) },
155 ]
156 } ),
157
158
159 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
160 (function () { var C = getTypoControl(); return C ? el(C, { label: 'Title', value: a.titleTypo, onChange: v => setAttributes( { titleTypo: v } ) }) : null; })(),
161 el( RangeControl, { label: __( 'Label Font Size', 'blockenberg' ), value: a.labelFontSize, onChange: v => setAttributes( { labelFontSize: v } ), min: 8, max: 20 } ),
162 el( RangeControl, { label: __( 'Label Font Weight', 'blockenberg' ), value: a.labelFontWeight || 600, onChange: v => setAttributes( { labelFontWeight: v } ), min: 300, max: 900, step: 100 } ),
163 el( RangeControl, { label: __( 'Value Font Size', 'blockenberg' ), value: a.valueFontSize, onChange: v => setAttributes( { valueFontSize: v } ), min: 7, max: 16 } )
164 ),
165 el( PanelBody, { title: __( 'Segment Colors', 'blockenberg' ), initialOpen: false },
166 el( 'p', { style: { fontSize: 12, color: '#6b7280', marginBottom: 8 } }, __( 'Colors are applied in order to segments across all columns.', 'blockenberg' ) ),
167 ( a.segmentColors || [] ).map( ( clr, ci ) =>
168 el( 'div', { key: ci, style: { display: 'flex', gap: 8, alignItems: 'center', marginBottom: 6 } },
169 el( 'input', { type: 'color', value: clr, style: { width: 40, height: 28, border: 'none', borderRadius: 4, cursor: 'pointer' }, onChange: e => setAttributes( { segmentColors: a.segmentColors.map( ( c, j ) => j === ci ? e.target.value : c ) } ) } ),
170 el( TextControl, { value: clr, onChange: v => setAttributes( { segmentColors: a.segmentColors.map( ( c, j ) => j === ci ? v : c ) } ), style: { flex: 1 } } ),
171 el( Button, { isDestructive: true, isSmall: true, onClick: () => setAttributes( { segmentColors: a.segmentColors.filter( ( _, j ) => j !== ci ) } ) }, '' ),
172 )
173 ),
174 el( Button, { variant: 'secondary', style: { marginTop: 6 }, onClick: () => setAttributes( { segmentColors: [ ...( a.segmentColors || [] ), '#94a3b8' ] } ) }, __( '+ Add Color', 'blockenberg' ) ),
175 ),
176
177 el( PanelBody, { title: __( 'Columns', 'blockenberg' ), initialOpen: false },
178 el( Button, { variant: 'secondary', style: { marginBottom: 10 }, onClick: () => setAttributes( { columns: [ ...a.columns, { label: 'New Region', size: 10, segments: [ { label: 'Segment A', value: 60 }, { label: 'Segment B', value: 40 } ] } ] } ) }, __( '+ Add Column', 'blockenberg' ) ),
179 a.columns.map( ( col, ci ) =>
180 el( PanelBody, { key: ci, title: `${ col.label } (${ col.size }%)`, initialOpen: false },
181 el( TextControl, { label: __( 'Column Label', 'blockenberg' ), value: col.label, onChange: v => updCol( setAttributes, a.columns, ci, 'label', v ) } ),
182 el( RangeControl, { label: __( 'Column Width % (relative size)', 'blockenberg' ), value: col.size, onChange: v => updCol( setAttributes, a.columns, ci, 'size', v ), min: 1, max: 100 } ),
183 el( 'strong', { style: { display: 'block', marginTop: 8, marginBottom: 4, fontSize: 12 } }, __( 'Segments', 'blockenberg' ) ),
184 el( Button, { variant: 'secondary', isSmall: true, style: { marginBottom: 8 }, onClick: () => updCol( setAttributes, a.columns, ci, 'segments', [ ...( col.segments || [] ), { label: 'New Segment', value: 20 } ] ) }, __( '+ Add Segment', 'blockenberg' ) ),
185 ( col.segments || [] ).map( ( seg, si ) =>
186 el( 'div', { key: si, style: { display: 'flex', gap: 6, alignItems: 'flex-end', marginBottom: 4 } },
187 el( TextControl, { label: si === 0 ? __( 'Label', 'blockenberg' ) : undefined, value: seg.label, onChange: v => updSeg( setAttributes, a.columns, ci, si, 'label', v ), style: { flex: 2 } } ),
188 el( TextControl, { label: si === 0 ? __( 'Value', 'blockenberg' ) : undefined, value: String( seg.value ), type: 'number', onChange: v => updSeg( setAttributes, a.columns, ci, si, 'value', parseFloat( v ) || 0 ), style: { flex: 1 } } ),
189 el( Button, { isDestructive: true, isSmall: true, onClick: () => updCol( setAttributes, a.columns, ci, 'segments', ( col.segments || [] ).filter( ( _, j ) => j !== si ) ) }, '' ),
190 )
191 ),
192 el( Button, { isDestructive: true, isSmall: true, style: { marginTop: 8 }, onClick: () => setAttributes( { columns: a.columns.filter( ( _, x ) => x !== ci ) } ) }, __( 'Remove Column', 'blockenberg' ) ),
193 )
194 ),
195 ),
196 ),
197
198 a.showTitle && a.title && el( 'h3', { className: 'bkbg-marimekko-title', style: { color: a.titleColor } }, a.title ),
199 el( 'div', { className: 'bkbg-marimekko-svg' }, renderMarimekko( a ) ),
200 );
201 },
202
203 save: function ( { attributes: a } ) {
204 const _tvFn = getTypoCssVars();
205 const blockProps = useBlockProps.save( (function () {
206 var s = {};
207 if (_tvFn) Object.assign(s, _tvFn(a.titleTypo, '--bkbg-mk-tt-'));
208 return { className: 'bkbg-marimekko-wrap', style: s };
209 })() );
210 return el( 'div', blockProps,
211 a.showTitle && a.title ? el( 'h3', { className: 'bkbg-marimekko-title', style: { color: a.titleColor } }, a.title ) : null,
212 el( 'div', { className: 'bkbg-marimekko-svg' }, renderMarimekko( a ) ),
213 );
214 },
215 } );
216 }() );
217