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

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

195 lines 12.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, ColorPicker, Popover } = window.wp.components;
6 const { __ } = window.wp.i18n;
7 const { useState } = window.wp.element;
8
9 var _tc, _tv;
10 function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
11 function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
12
13 // ── Renderer ──────────────────────────────────────────────────────────────
14 function renderLollipop( a ) {
15 const items = a.sortDesc
16 ? [ ...( a.items || [] ) ].sort( ( x, y ) => ( y.value || 0 ) - ( x.value || 0 ) )
17 : [ ...( a.items || [] ) ];
18
19 const maxVal = Math.max( ...items.map( it => it.value || 0 ), 1 );
20 const n = items.length;
21 const W = a.svgWidth;
22 const H = a.padTop * 2 + n * a.rowHeight;
23 const chartW = W - a.padLeft - a.padRight;
24 const DR = a.dotRadius;
25
26 const svgEls = [];
27 svgEls.push( el( 'rect', { key: 'bg', x: 0, y: 0, width: W, height: H, fill: a.bgColor || '#ffffff', rx: 8 } ) );
28
29 // Grid lines (vertical)
30 if ( a.showGridLines ) {
31 const ticks = 4;
32 for ( let i = 0; i <= ticks; i++ ) {
33 const x = a.padLeft + ( chartW * i / ticks );
34 svgEls.push( el( 'line', {
35 key: 'grid' + i,
36 x1: x, y1: a.padTop, x2: x, y2: H - a.padTop,
37 stroke: a.gridColor || '#f3f4f6', strokeWidth: 1,
38 } ) );
39 svgEls.push( el( 'text', {
40 key: 'tick' + i,
41 x, y: a.padTop - 8,
42 textAnchor: 'middle', fill: '#d1d5db',
43 fontSize: 10, fontFamily: 'inherit',
44 }, String( Math.round( maxVal * i / ticks ) ) ) );
45 }
46 }
47
48 items.forEach( ( item, i ) => {
49 const cy = a.padTop + i * a.rowHeight + a.rowHeight / 2;
50 const barX = a.padLeft;
51 const dotX = barX + ( ( item.value || 0 ) / maxVal ) * ( chartW - DR );
52 const color = item.color || '#4f46e5';
53
54 // Stem (from left edge to dot)
55 svgEls.push( el( 'line', {
56 key: 'stem' + i,
57 x1: barX, y1: cy, x2: dotX, y2: cy,
58 stroke: a.stemColor || '#e5e7eb', strokeWidth: a.stemThickness,
59 strokeLinecap: 'round',
60 } ) );
61
62 // Dot (filled)
63 svgEls.push( el( 'circle', { key: 'dot_bg' + i, cx: dotX, cy, r: DR, fill: color, opacity: 0.15 } ) );
64 svgEls.push( el( 'circle', { key: 'dot' + i, cx: dotX, cy, r: DR - 2, fill: color } ) );
65
66 // Value label inside dot
67 if ( a.showValues ) {
68 svgEls.push( el( 'text', {
69 key: 'val' + i,
70 x: dotX, y: cy,
71 textAnchor: 'middle', dominantBaseline: 'middle',
72 fill: '#ffffff', fontSize: a.valueFontSize, fontWeight: 700, fontFamily: 'inherit',
73 }, String( item.value || 0 ) ) );
74 }
75
76 // Row label (left)
77 svgEls.push( el( 'text', {
78 key: 'lbl' + i,
79 x: barX - 10, y: cy,
80 textAnchor: 'end', dominantBaseline: 'middle',
81 fill: a.labelColor || '#374151',
82 fontSize: a.labelFontSize, fontFamily: 'inherit',
83 }, item.label || '' ) );
84 } );
85
86 return el( 'svg', {
87 viewBox: `0 0 ${ W } ${ H }`, width: '100%',
88 style: { display: 'block', maxWidth: W + 'px', margin: '0 auto' }
89 }, ...svgEls );
90 }
91
92 // ── Helpers ───────────────────────────────────────────────────────────────
93 function updItem( setAttributes, items, idx, field, val ) {
94 setAttributes( { items: items.map( ( it, i ) => i === idx ? { ...it, [field]: val } : it ) } );
95 }
96
97 // ── Block ───────────────────────────────────────────────────────────────── /* ── colour-swatch + popover ── */
98 function BkbgColorSwatch(p) {
99 var st = useState(false), open = st[0], setOpen = st[1];
100 return el('div', { style:{ display:'flex', alignItems:'center', justifyContent:'space-between', padding:'4px 0', gap:'8px' } },
101 el('span', { style:{ fontSize:'12px', color:'#1e1e1e', flex:1, lineHeight:1.4 } }, p.label),
102 el('div', { style:{ position:'relative', flexShrink:0 } },
103 el('button', { type:'button', title: p.value||'none', onClick: function(){ setOpen(!open); },
104 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 } }),
105 open && el(Popover, { position:'bottom left', onClose: function(){ setOpen(false); } },
106 el('div', { style:{ padding:'8px' }, onMouseDown: function(e){ e.stopPropagation(); } },
107 el('div', { style:{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:'6px' } },
108 el('strong', { style:{ fontSize:'12px' } }, p.label),
109 el(Button, { icon:'no-alt', isSmall:true, onClick: function(){ setOpen(false); } })
110 ),
111 el(ColorPicker, { color: p.value, enableAlpha:true, onChange: p.onChange })
112 )
113 )
114 )
115 );
116 }
117 registerBlockType( 'blockenberg/lollipop-chart', {
118 edit: function ( props ) {
119 const { attributes: a, setAttributes } = props;
120 const blockProps = useBlockProps( (function () {
121 var _tvFn = getTypoCssVars();
122 var s = {};
123 if (_tvFn) Object.assign(s, _tvFn(a.titleTypo, '--bkbg-lp-tt-'));
124 return { className: 'bkbg-lollipop-wrap', style: s };
125 })() );
126
127 return el( 'div', blockProps,
128 el( InspectorControls, {},
129
130 el( PanelBody, { title: __( 'Chart Settings', 'blockenberg' ), initialOpen: true },
131 el( TextControl, { label: __( 'Title', 'blockenberg' ), value: a.title, onChange: v => setAttributes( { title: v } ) } ),
132 el( ToggleControl, { label: __( 'Show Title', 'blockenberg' ), checked: a.showTitle, onChange: v => setAttributes( { showTitle: v } ) } ),
133 el( ToggleControl, { label: __( 'Show Values', 'blockenberg' ), checked: a.showValues, onChange: v => setAttributes( { showValues: v } ) } ),
134 el( ToggleControl, { label: __( 'Show Grid Lines', 'blockenberg' ), checked: a.showGridLines, onChange: v => setAttributes( { showGridLines: v } ) } ),
135 el( ToggleControl, { label: __( 'Sort High → Low', 'blockenberg' ), checked: a.sortDesc, onChange: v => setAttributes( { sortDesc: v } ) } ),
136 ),
137
138 el( PanelBody, { title: __( 'Layout', 'blockenberg' ), initialOpen: false },
139 el( RangeControl, { label: __( 'Canvas Width', 'blockenberg' ), value: a.svgWidth, onChange: v => setAttributes( { svgWidth: v } ), min: 300, max: 1200, step: 20 } ),
140 el( RangeControl, { label: __( 'Row Height', 'blockenberg' ), value: a.rowHeight, onChange: v => setAttributes( { rowHeight: v } ), min: 24, max: 100 } ),
141 el( RangeControl, { label: __( 'Label Column Width', 'blockenberg' ), value: a.padLeft, onChange: v => setAttributes( { padLeft: v } ), min: 60, max: 300 } ),
142 el( RangeControl, { label: __( 'Dot Radius', 'blockenberg' ), value: a.dotRadius, onChange: v => setAttributes( { dotRadius: v } ), min: 8, max: 30 } ),
143 el( RangeControl, { label: __( 'Stem Thickness', 'blockenberg' ), value: a.stemThickness, onChange: v => setAttributes( { stemThickness: v } ), min: 1, max: 8 } ),
144 ),
145
146
147 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
148 el(getTypoControl(), { label: __('Title', 'blockenberg'), value: a.titleTypo, onChange: v => setAttributes({ titleTypo: v }) }),
149 el( RangeControl, { label: __( 'Label Font Size', 'blockenberg' ), value: a.labelFontSize, onChange: v => setAttributes( { labelFontSize: v } ), min: 9, max: 22 } ),
150 el( RangeControl, { label: __( 'Value Font Size', 'blockenberg' ), value: a.valueFontSize, onChange: v => setAttributes( { valueFontSize: v } ), min: 8, max: 18 } )
151 ),
152 el( PanelColorSettings, {
153 title: __( 'Colors', 'blockenberg' ), initialOpen: false,
154 colorSettings: [
155 { label: __( 'Background', 'blockenberg' ), value: a.bgColor, onChange: v => setAttributes( { bgColor: v || '#ffffff' } ) },
156 { label: __( 'Grid Lines', 'blockenberg' ), value: a.gridColor, onChange: v => setAttributes( { gridColor: v || '#f3f4f6' } ) },
157 { label: __( 'Stem Color', 'blockenberg' ), value: a.stemColor, onChange: v => setAttributes( { stemColor: v || '#e5e7eb' } ) },
158 { label: __( 'Title Color', 'blockenberg' ), value: a.titleColor, onChange: v => setAttributes( { titleColor: v || '#111827' } ) },
159 { label: __( 'Label Color', 'blockenberg' ), value: a.labelColor, onChange: v => setAttributes( { labelColor: v || '#374151' } ) },
160 ]
161 } ),
162
163 el( PanelBody, { title: __( 'Data', 'blockenberg' ), initialOpen: false },
164 el( Button, { variant: 'secondary', style: { marginBottom: 10 }, onClick: () => setAttributes( { items: [ ...a.items, { label: 'New Entry', value: 50, color: '#6b7280' } ] } ) }, __( '+ Add Item', 'blockenberg' ) ),
165 a.items.map( ( item, i ) =>
166 el( PanelBody, { key: i, title: ( item.label || `Item ${ i + 1 }` ) + `${ item.value }`, initialOpen: false },
167 el( TextControl, { label: __( 'Label', 'blockenberg' ), value: item.label, onChange: v => updItem( setAttributes, a.items, i, 'label', v ) } ),
168 el( RangeControl, { label: __( 'Value', 'blockenberg' ), value: item.value, onChange: v => updItem( setAttributes, a.items, i, 'value', v ), min: 0, max: 10000 } ),
169 el( BkbgColorSwatch, { label: __( 'Color', 'blockenberg' ), value: item.color, onChange: v => updItem( setAttributes, a.items, i, 'color', v ) } ),
170 el( Button, { isDestructive: true, isSmall: true, onClick: () => setAttributes( { items: a.items.filter( ( _, x ) => x !== i ) } ) }, __( 'Remove', 'blockenberg' ) ),
171 )
172 ),
173 ),
174 ),
175
176 a.showTitle && a.title && el( 'h3', { className: 'bkbg-lp-title', style: { color: a.titleColor } }, a.title ),
177 el( 'div', { className: 'bkbg-lp-svg' }, renderLollipop( a ) ),
178 );
179 },
180
181 save: function ( { attributes: a } ) {
182 const blockPropsSave = useBlockProps.save( (function () {
183 var _tvFn = window.bkbgTypoCssVars;
184 var s = {};
185 if (_tvFn) Object.assign(s, _tvFn(a.titleTypo, '--bkbg-lp-tt-'));
186 return { className: 'bkbg-lollipop-wrap', style: s };
187 })() );
188 return el( 'div', blockPropsSave,
189 a.showTitle && a.title ? el( 'h3', { className: 'bkbg-lp-title', style: { color: a.titleColor } }, a.title ) : null,
190 el( 'div', { className: 'bkbg-lp-svg' }, renderLollipop( a ) ),
191 );
192 },
193 } );
194 }() );
195