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

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

311 lines 16.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 ( function () {
2 var el = window.wp.element.createElement;
3 var Fragment = window.wp.element.Fragment;
4 var useState = window.wp.element.useState;
5 var useEffect = window.wp.element.useEffect;
6 var useRef = window.wp.element.useRef;
7 var registerBlockType = window.wp.blocks.registerBlockType;
8 var InspectorControls = window.wp.blockEditor.InspectorControls;
9 var useBlockProps = window.wp.blockEditor.useBlockProps;
10 var PanelColorSettings = window.wp.blockEditor.PanelColorSettings;
11 var PanelBody = window.wp.components.PanelBody;
12 var RangeControl = window.wp.components.RangeControl;
13 var SelectControl = window.wp.components.SelectControl;
14 var TextControl = window.wp.components.TextControl;
15 var TextareaControl= window.wp.components.TextareaControl;
16 var ToggleControl = window.wp.components.ToggleControl;
17 var Button = window.wp.components.Button;
18 var __ = window.wp.i18n.__;
19
20 var _tc, _tv;
21 function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
22 function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
23
24 var CDN = 'https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.umd.min.js';
25
26 function loadChart(cb) {
27 if (window.Chart) { cb(window.Chart); return; }
28 var s = document.createElement('script');
29 s.src = CDN;
30 s.onload = function () { cb(window.Chart); };
31 document.head.appendChild(s);
32 }
33
34 function parseLines(text) {
35 if (!text) return [];
36 return text.trim().split('\n').map(function (l) { return parseFloat(l.trim()); }).filter(function (n) { return !isNaN(n); });
37 }
38
39 function parseLabels(text) {
40 if (!text) return [];
41 return text.trim().split('\n').map(function (l) { return l.trim(); }).filter(Boolean);
42 }
43
44 function hexToRgba(hex, alpha) {
45 hex = (hex || '#6c3fb5').replace('#', '');
46 if (hex.length === 3) hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
47 var r = parseInt(hex.substr(0,2),16), g = parseInt(hex.substr(2,2),16), b = parseInt(hex.substr(4,2),16);
48 return 'rgba(' + r + ',' + g + ',' + b + ',' + alpha + ')';
49 }
50
51 function makeId() { return 'mc' + Math.random().toString(36).substr(2, 5); }
52
53 /* ── Dataset row editor ─────────────────────────────────────────── */
54 function DatasetEditor(props) {
55 var a = props.a;
56 var setAttributes = props.set;
57 var datasets = [];
58 try { datasets = JSON.parse(a.datasetsJson || '[]'); } catch (e) {}
59
60 function update(id, key, val) {
61 var next = datasets.map(function (d) {
62 if (d.id !== id) return d;
63 var u = Object.assign({}, d); u[key] = val; return u;
64 });
65 setAttributes({ datasetsJson: JSON.stringify(next) });
66 }
67 function remove(id) {
68 setAttributes({ datasetsJson: JSON.stringify(datasets.filter(function (d) { return d.id !== id; })) });
69 }
70 function add() {
71 var d = { id: makeId(), label: 'Series ' + (datasets.length + 1), type: 'bar', color: '#6c3fb5', yAxis: 'y', data: '10\n20\n30\n40\n50\n60\n70\n80\n90\n100\n110\n120' };
72 setAttributes({ datasetsJson: JSON.stringify(datasets.concat([d])) });
73 }
74
75 return el('div', null,
76 datasets.map(function (ds, idx) {
77 return el('div', { key: ds.id, style: { border: '1px solid #e0e0e0', borderRadius: '8px', padding: '10px', marginBottom: '8px' } },
78 el('div', { style: { display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '8px' } },
79 el('div', { style: { width: '12px', height: '12px', borderRadius: '50%', background: ds.color, flexShrink: 0 } }),
80 el('span', { style: { fontWeight: 700, fontSize: '12px', flex: 1 } }, ds.label),
81 el('span', { style: { fontSize: '10px', color: '#888', background: ds.type === 'bar' ? '#dbeafe' : '#d1fae5', padding: '2px 6px', borderRadius: '99px' } }, ds.type)
82 ),
83 el(TextControl, { label: __('Label', 'blockenberg'), value: ds.label, onChange: function (v) { update(ds.id, 'label', v); } }),
84 el(SelectControl, { label: __('Chart type', 'blockenberg'), value: ds.type, options: [{ label: 'Bar', value: 'bar' }, { label: 'Line', value: 'line' }], onChange: function (v) { update(ds.id, 'type', v); } }),
85 a.dualAxis && el(SelectControl, { label: __('Y-axis', 'blockenberg'), value: ds.yAxis, options: [{ label: 'Left (Y)', value: 'y' }, { label: 'Right (Y1)', value: 'y1' }], onChange: function (v) { update(ds.id, 'yAxis', v); } }),
86 el('div', { style: { display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '8px' } },
87 el('span', { style: { fontSize: '12px', fontWeight: 600 } }, __('Color:', 'blockenberg')),
88 el('input', { type: 'color', value: ds.color, onChange: function (e) { update(ds.id, 'color', e.target.value); }, style: { width: '32px', height: '28px', padding: 0, border: 'none', cursor: 'pointer', background: 'none' } })
89 ),
90 el(TextareaControl, { label: __('Values (one per line)', 'blockenberg'), value: ds.data, rows: 6, onChange: function (v) { update(ds.id, 'data', v); } }),
91 el(Button, { isDestructive: true, variant: 'tertiary', size: 'compact', onClick: function () { remove(ds.id); } }, __('Remove series', 'blockenberg'))
92 );
93 }),
94 el(Button, { variant: 'secondary', onClick: add, style: { width: '100%', justifyContent: 'center' } }, '+ ' + __('Add Series', 'blockenberg'))
95 );
96 }
97
98 /* ── Live chart preview ─────────────────────────────────────────── */
99 function MixedPreview(props) {
100 var a = props.a;
101 var canvasRef = useRef(null);
102 var chartRef = useRef(null);
103
104 useEffect(function () {
105 var canvas = canvasRef.current;
106 if (!canvas) return;
107
108 loadChart(function (ChartJS) {
109 if (chartRef.current) { try { chartRef.current.destroy(); } catch(e){} }
110
111 var datasets = [];
112 try { datasets = JSON.parse(a.datasetsJson || '[]'); } catch (e) {}
113 var labels = parseLabels(a.labels);
114
115 var chartDatasets = datasets.map(function (ds) {
116 var vals = parseLines(ds.data);
117 var alpha = (a.fillAlpha || 85) / 100;
118 var base = {
119 label: ds.label,
120 data: vals,
121 backgroundColor: hexToRgba(ds.color, alpha),
122 borderColor: ds.color,
123 borderWidth: 2,
124 yAxisID: ds.yAxis || 'y',
125 };
126 if (ds.type === 'bar') {
127 base.borderRadius = a.barRadius || 4;
128 base.type = 'bar';
129 } else {
130 base.type = 'line';
131 base.pointRadius = a.dotSize || 4;
132 base.pointHoverRadius = (a.dotSize || 4) + 2;
133 base.borderWidth = a.lineWidth || 2;
134 base.fill = false;
135 base.tension = 0.35;
136 }
137 return base;
138 });
139
140 var scales = {
141 x: { grid: { display: !!a.showGrid } },
142 y: { position: 'left', title: { display: !!a.yLabel, text: a.yLabel || '' }, grid: { display: !!a.showGrid } },
143 };
144 if (a.dualAxis) {
145 scales.y1 = { position: 'right', title: { display: !!a.y1Label, text: a.y1Label || '' }, grid: { drawOnChartArea: false } };
146 }
147
148 chartRef.current = new ChartJS(canvas, {
149 type: 'bar',
150 data: { labels: labels, datasets: chartDatasets },
151 options: {
152 responsive: true,
153 animation: { duration: a.animate ? 800 : 0 },
154 plugins: {
155 legend: { display: !!a.showLegend, position: a.legendPos || 'bottom' },
156 },
157 scales: scales,
158 }
159 });
160 });
161
162 return function () { if (chartRef.current) { try { chartRef.current.destroy(); } catch(e){} chartRef.current = null; } };
163 }, [a.datasetsJson, a.labels, a.showGrid, a.showLegend, a.legendPos, a.animate, a.dualAxis, a.yLabel, a.y1Label, a.barRadius, a.lineWidth, a.dotSize, a.fillAlpha]);
164
165 return el('div', { style: { position: 'relative', width: '100%' } },
166 el('canvas', { ref: canvasRef, height: a.chartHeight || 400 })
167 );
168 }
169
170 registerBlockType('blockenberg/mixed-chart', {
171 title: __('Mixed Chart', 'blockenberg'),
172 icon: 'chart-bar',
173 category: 'bkbg-charts',
174
175 deprecated: [{
176 save: function (props) {
177 var a = props.attributes;
178 return el(window.wp.blockEditor.useBlockProps.save({
179 className: 'bkbg-mc-wrapper',
180 style: {
181 background: a.bgColor || undefined,
182 paddingTop: a.paddingTop + 'px',
183 paddingBottom: a.paddingBottom + 'px',
184 }
185 }),
186 el('div', {
187 className: 'bkbg-mc-card',
188 'data-chart': JSON.stringify({
189 title: a.chartTitle, subtitle: a.chartSubtitle,
190 showTitle: a.showTitle, showSubtitle: a.showSubtitle,
191 labels: a.labels, datasetsJson: a.datasetsJson,
192 showGrid: a.showGrid, showLegend: a.showLegend, legendPos: a.legendPos,
193 animate: a.animate, dualAxis: a.dualAxis,
194 yLabel: a.yLabel, y1Label: a.y1Label,
195 barRadius: a.barRadius, lineWidth: a.lineWidth, dotSize: a.dotSize, fillAlpha: a.fillAlpha,
196 }),
197 'data-height': a.chartHeight,
198 style: { borderRadius: a.borderRadius + 'px' }
199 },
200 el('canvas', { className: 'bkbg-mc-canvas' })
201 )
202 );
203 }
204 }],
205
206 edit: function (props) {
207 var a = props.attributes;
208 var set = props.setAttributes;
209
210 var blockProps = useBlockProps((function () {
211 var _tvf = getTypoCssVars();
212 var s = {
213 background: a.bgColor || undefined,
214 borderRadius: a.borderRadius + 'px',
215 paddingTop: a.paddingTop + 'px',
216 paddingBottom: a.paddingBottom + 'px',
217 };
218 Object.assign(s, _tvf(a.titleTypo, '--bkbg-mxc-tt-'));
219 Object.assign(s, _tvf(a.subtitleTypo, '--bkbg-mxc-st-'));
220 return { style: s };
221 })());
222
223 return el(Fragment, null,
224 el(InspectorControls, null,
225 el(PanelBody, { title: __('Datasets', 'blockenberg'), initialOpen: true },
226 el(DatasetEditor, { a: a, set: set })
227 ),
228 el(PanelBody, { title: __('Labels (X-axis)', 'blockenberg'), initialOpen: false },
229 el(TextareaControl, { label: __('Labels (one per line)', 'blockenberg'), value: a.labels, rows: 8, onChange: function (v) { set({ labels: v }); } })
230 ),
231 el(PanelBody, { title: __('Chart Options', 'blockenberg'), initialOpen: false },
232 el(ToggleControl, { label: __('Show title', 'blockenberg'), checked: a.showTitle, onChange: function (v) { set({ showTitle: v }); }, __nextHasNoMarginBottom: true }),
233 el(ToggleControl, { label: __('Show subtitle', 'blockenberg'), checked: a.showSubtitle, onChange: function (v) { set({ showSubtitle: v }); }, __nextHasNoMarginBottom: true }),
234 a.showTitle && el(TextControl, { label: __('Title', 'blockenberg'), value: a.chartTitle, onChange: function (v) { set({ chartTitle: v }); } }),
235 a.showSubtitle && el(TextControl, { label: __('Subtitle', 'blockenberg'), value: a.chartSubtitle, onChange: function (v) { set({ chartSubtitle: v }); } }),
236 el(RangeControl, { label: __('Chart height (px)', 'blockenberg'), value: a.chartHeight, min: 200, max: 800, onChange: function (v) { set({ chartHeight: v }); } }),
237 el(RangeControl, { label: __('Bar corner radius', 'blockenberg'), value: a.barRadius, min: 0, max: 20, onChange: function (v) { set({ barRadius: v }); } }),
238 el(RangeControl, { label: __('Line width (px)', 'blockenberg'), value: a.lineWidth, min: 1, max: 6, onChange: function (v) { set({ lineWidth: v }); } }),
239 el(RangeControl, { label: __('Dot size (px)', 'blockenberg'), value: a.dotSize, min: 0, max: 12, onChange: function (v) { set({ dotSize: v }); } }),
240 el(RangeControl, { label: __('Fill opacity (%)', 'blockenberg'), value: a.fillAlpha, min: 0, max: 100, onChange: function (v) { set({ fillAlpha: v }); } }),
241 el(ToggleControl, { label: __('Show grid lines', 'blockenberg'), checked: a.showGrid, onChange: function (v) { set({ showGrid: v }); }, __nextHasNoMarginBottom: true }),
242 el(ToggleControl, { label: __('Show legend', 'blockenberg'), checked: a.showLegend, onChange: function (v) { set({ showLegend: v }); }, __nextHasNoMarginBottom: true }),
243 a.showLegend && el(SelectControl, { label: __('Legend position', 'blockenberg'), value: a.legendPos, options: [{ label: 'Bottom', value: 'bottom' }, { label: 'Top', value: 'top' }, { label: 'Left', value: 'left' }, { label: 'Right', value: 'right' }], onChange: function (v) { set({ legendPos: v }); } }),
244 el(ToggleControl, { label: __('Entrance animation', 'blockenberg'), checked: a.animate, onChange: function (v) { set({ animate: v }); }, __nextHasNoMarginBottom: true })
245 ),
246 el(PanelBody, { title: __('Axes', 'blockenberg'), initialOpen: false },
247 el(ToggleControl, { label: __('Dual Y-axis', 'blockenberg'), checked: a.dualAxis, onChange: function (v) { set({ dualAxis: v }); }, __nextHasNoMarginBottom: true }),
248 el(TextControl, { label: __('Left Y-axis label', 'blockenberg'), value: a.yLabel, onChange: function (v) { set({ yLabel: v }); } }),
249 a.dualAxis && el(TextControl, { label: __('Right Y-axis label', 'blockenberg'), value: a.y1Label, onChange: function (v) { set({ y1Label: v }); } })
250 ),
251 el(PanelBody, { title: __('Card Style', 'blockenberg'), initialOpen: false },
252 el(RangeControl, { label: __('Border radius (px)', 'blockenberg'), value: a.borderRadius, min: 0, max: 40, onChange: function (v) { set({ borderRadius: v }); } }),
253 el(PanelColorSettings, { title: __('Background', 'blockenberg'), initialOpen: false, colorSettings: [{ value: a.bgColor, onChange: function (v) { set({ bgColor: v || '' }); }, label: __('Card background', 'blockenberg') }] })
254 ),
255 el(PanelBody, { title: __('Spacing', 'blockenberg'), initialOpen: false },
256 el(RangeControl, { label: __('Padding top (px)', 'blockenberg'), value: a.paddingTop, min: 0, max: 200, onChange: function (v) { set({ paddingTop: v }); } }),
257 el(RangeControl, { label: __('Padding bottom (px)', 'blockenberg'), value: a.paddingBottom, min: 0, max: 200, onChange: function (v) { set({ paddingBottom: v }); } })
258 ),
259 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
260 el( getTypoControl(), { label: __( 'Title', 'blockenberg' ), value: a.titleTypo, onChange: function (v) { set({ titleTypo: v }); } }),
261 el( getTypoControl(), { label: __( 'Subtitle', 'blockenberg' ), value: a.subtitleTypo, onChange: function (v) { set({ subtitleTypo: v }); } })
262 )
263 ),
264
265 el('div', blockProps,
266 el('div', { className: 'bkbg-mc-card', style: { background: '#fff', borderRadius: a.borderRadius + 'px', padding: '24px', boxShadow: '0 2px 16px rgba(0,0,0,0.07)' } },
267 (a.showTitle || a.showSubtitle) && el('div', { style: { marginBottom: '16px' } },
268 a.showTitle && el('h3', { className: 'bkbg-mc-title', style: { margin: 0, color: '#111827' } }, a.chartTitle),
269 a.showSubtitle && el('p', { className: 'bkbg-mc-subtitle', style: { margin: '4px 0 0', color: '#6b7280' } }, a.chartSubtitle)
270 ),
271 el(MixedPreview, { a: a })
272 )
273 )
274 );
275 },
276
277 save: function (props) {
278 var a = props.attributes;
279 var blockProps = window.wp.blockEditor.useBlockProps.save((function () {
280 var _tvf = getTypoCssVars();
281 var s = {
282 background: a.bgColor || undefined,
283 paddingTop: a.paddingTop + 'px',
284 paddingBottom: a.paddingBottom + 'px',
285 };
286 Object.assign(s, _tvf(a.titleTypo, '--bkbg-mxc-tt-'));
287 Object.assign(s, _tvf(a.subtitleTypo, '--bkbg-mxc-st-'));
288 return { className: 'bkbg-mc-wrapper', style: s };
289 })());
290 return el('div', blockProps,
291 el('div', {
292 className: 'bkbg-mc-card',
293 'data-chart': JSON.stringify({
294 title: a.chartTitle, subtitle: a.chartSubtitle,
295 showTitle: a.showTitle, showSubtitle: a.showSubtitle,
296 labels: a.labels, datasetsJson: a.datasetsJson,
297 showGrid: a.showGrid, showLegend: a.showLegend, legendPos: a.legendPos,
298 animate: a.animate, dualAxis: a.dualAxis,
299 yLabel: a.yLabel, y1Label: a.y1Label,
300 barRadius: a.barRadius, lineWidth: a.lineWidth, dotSize: a.dotSize, fillAlpha: a.fillAlpha,
301 }),
302 'data-height': a.chartHeight,
303 style: { borderRadius: a.borderRadius + 'px' }
304 },
305 el('canvas', { className: 'bkbg-mc-canvas' })
306 )
307 );
308 }
309 });
310 }() );
311