PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.14
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.14
2.0.14 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 / venn-diagram / index.js

index.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.14, at blocks/venn-diagram/index.js

222 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, SelectControl, ToggleControl, TextControl, TextareaControl } = window.wp.components;
6 const { __ } = window.wp.i18n;
7
8 // Resolve the shared components lazily without defining globals for each block.
9 function _tc(label, key, attrs, setA) {
10 var Control = window.bkbgTypographyControl;
11 return Control ? el(Control, {
12 label: label,
13 value: attrs[key] || {},
14 onChange: function (value) { var update = {}; update[key] = value; setA(update); }
15 }) : null;
16 }
17 function _tvf(vars, key, attrs, prefix) {
18 var cssVars = window.bkbgTypoCssVars;
19 if (cssVars) Object.assign(vars, cssVars(attrs[key] || {}, prefix));
20 return vars;
21 }
22 function getTypoControl(label, key, attrs, setA) { return _tc(label, key, attrs, setA); }
23 function getTypoCssVars(attrs) {
24 var v = {};
25 _tvf(v, 'titleTypo', attrs, '--bkvenn-tt-');
26 _tvf(v, 'subtitleTypo', attrs, '--bkvenn-st-');
27 return v;
28 }
29
30 // ── SVG geometry ──────────────────────────────────────────────────
31 // Two-circle venn
32 function renderVenn2(a) {
33 const S = a.svgSize;
34 const r = S * 0.36;
35 const cy = S * 0.5;
36 const cx1 = S * 0.35, cx2 = S * 0.65;
37 const op = a.circleOpacity;
38 const fs = a.fontSize;
39
40 return el('svg', { viewBox: `0 0 ${S} ${S}`, width: '100%', style: { maxWidth: S + 'px', display: 'block', margin: '0 auto' } },
41 // Circles
42 el('circle', { cx: cx1, cy, r, fill: a.circleA.color, opacity: op }),
43 el('circle', { cx: cx2, cy, r, fill: a.circleB.color, opacity: op }),
44 // Outlines
45 el('circle', { cx: cx1, cy, r, fill: 'none', stroke: a.circleA.color, strokeWidth: 2, opacity: 0.6 }),
46 el('circle', { cx: cx2, cy, r, fill: 'none', stroke: a.circleB.color, strokeWidth: 2, opacity: 0.6 }),
47 // Labels
48 a.showCircleLabels && el('text', { x: cx1 - r * 0.35, y: cy, textAnchor: 'middle', fill: a.circleA.color, fontSize: a.labelSize, fontWeight: 700, fontFamily: 'inherit', dominantBaseline: 'middle' }, a.circleA.label),
49 a.showCircleLabels && el('text', { x: cx2 + r * 0.35, y: cy, textAnchor: 'middle', fill: a.circleB.color, fontSize: a.labelSize, fontWeight: 700, fontFamily: 'inherit', dominantBaseline: 'middle' }, a.circleB.label),
50 // Overlap text
51 a.showOverlapText && el('text', { x: S * 0.5, y: cy, textAnchor: 'middle', fill: a.textColor, fontSize: a.overlapSize, fontWeight: 700, fontFamily: 'inherit', dominantBaseline: 'middle' }, a.overlapAB),
52 );
53 }
54
55 // Three-circle venn
56 function renderVenn3(a) {
57 const S = a.svgSize;
58 const r = S * 0.34;
59 // Centres of 3 circles arranged in a triangle
60 const cpx = S / 2;
61 const cpy = S / 2 - S * 0.04;
62 const angle = [-Math.PI / 2, -Math.PI / 2 + (2 * Math.PI / 3), -Math.PI / 2 + (4 * Math.PI / 3)];
63 const dist = r * 0.6;
64 const cx = [cpx + dist * Math.cos(angle[0]), cpx + dist * Math.cos(angle[1]), cpx + dist * Math.cos(angle[2])];
65 const cy = [cpy + dist * Math.sin(angle[0]), cpy + dist * Math.sin(angle[1]), cpy + dist * Math.sin(angle[2])];
66 const op = a.circleOpacity;
67 const circles = [
68 { cx: cx[0], cy: cy[0], color: a.circleA.color, label: a.circleA.label },
69 { cx: cx[1], cy: cy[1], color: a.circleB.color, label: a.circleB.label },
70 { cx: cx[2], cy: cy[2], color: a.circleC.color, label: a.circleC.label },
71 ];
72 // Overlap label positions (midpoint between circle centres + slightly toward centre)
73 function midOut(i, j, pullFrac) {
74 const mx = (cx[i] + cx[j]) / 2;
75 const my = (cy[i] + cy[j]) / 2;
76 return { x: mx + (cpx - mx) * pullFrac, y: my + (cpy - my) * pullFrac };
77 }
78 const pAB = midOut(0, 1, 0.3);
79 const pBC = midOut(1, 2, 0.3);
80 const pAC = midOut(0, 2, 0.3);
81 const pABC = { x: cpx, y: cpy + r * 0.05 };
82
83 // Label positions (outside the overlapping area)
84 function labelPos(i) {
85 const fromCenter = r * 1.05;
86 return { x: cpx + fromCenter * Math.cos(angle[i]), y: cpy + fromCenter * Math.sin(angle[i]) };
87 }
88
89 return el('svg', { viewBox: `0 0 ${S} ${S}`, width: '100%', style: { maxWidth: S + 'px', display: 'block', margin: '0 auto' } },
90 // Filled circles
91 circles.map((c, i) => el('circle', { key: 'f' + i, cx: c.cx, cy: c.cy, r, fill: c.color, opacity: op })),
92 // Stroke outlines
93 circles.map((c, i) => el('circle', { key: 'o' + i, cx: c.cx, cy: c.cy, r, fill: 'none', stroke: c.color, strokeWidth: 2, opacity: 0.6 })),
94 // Circle main labels
95 a.showCircleLabels && circles.map((c, i) => {
96 const lp = labelPos(i);
97 return el('text', {
98 key: 'lbl' + i, x: lp.x, y: lp.y,
99 textAnchor: 'middle', dominantBaseline: 'middle',
100 fill: c.color, fontSize: a.labelSize, fontWeight: 700, fontFamily: 'inherit'
101 }, c.label);
102 }),
103 // Overlap labels
104 a.showOverlapText && [
105 el('text', { key: 'oAB', x: pAB.x, y: pAB.y, textAnchor: 'middle', dominantBaseline: 'middle', fill: a.textColor, fontSize: a.overlapSize, fontWeight: 600, fontFamily: 'inherit', opacity: 0.85 }, a.overlapAB),
106 el('text', { key: 'oBC', x: pBC.x, y: pBC.y, textAnchor: 'middle', dominantBaseline: 'middle', fill: a.textColor, fontSize: a.overlapSize, fontWeight: 600, fontFamily: 'inherit', opacity: 0.85 }, a.overlapBC),
107 el('text', { key: 'oAC', x: pAC.x, y: pAC.y, textAnchor: 'middle', dominantBaseline: 'middle', fill: a.textColor, fontSize: a.overlapSize, fontWeight: 600, fontFamily: 'inherit', opacity: 0.85 }, a.overlapAC),
108 el('text', { key: 'oABC', x: pABC.x, y: pABC.y, textAnchor: 'middle', dominantBaseline: 'middle', fill: a.textColor, fontSize: a.overlapSize + 1, fontWeight: 700, fontFamily: 'inherit' }, a.overlapABC),
109 ],
110 );
111 }
112
113 // ── Circle items legend ───────────────────────────────────────────
114 function renderItems(a) {
115 const cols = a.mode === 'three' ? [a.circleA, a.circleB, a.circleC] : [a.circleA, a.circleB];
116 return el('div', { className: 'bkbg-venn-items', style: { display: 'flex', gap: '24px', flexWrap: 'wrap', justifyContent: 'center', marginTop: '24px' } },
117 cols.map((c, i) =>
118 el('div', { key: i, style: { minWidth: '140px', flex: '1' } },
119 el('div', { style: { fontWeight: 700, fontSize: a.labelSize + 'px', color: c.color, marginBottom: '8px', display: 'flex', alignItems: 'center', gap: '6px' } },
120 el('span', { style: { display: 'inline-block', width: '10px', height: '10px', borderRadius: '50%', background: c.color } }),
121 c.label
122 ),
123 (c.items || []).map((item, j) =>
124 el('div', { key: j, style: { fontSize: a.fontSize + 'px', color: a.subtitleColor, marginBottom: '4px', paddingLeft: '16px' } }, '· ' + item)
125 )
126 )
127 )
128 );
129 }
130
131 function wrapStyle(a) {
132 return { background: a.bgColor, borderRadius: a.borderRadius + 'px', paddingTop: a.paddingTop + 'px', paddingBottom: a.paddingBottom + 'px', paddingLeft: '32px', paddingRight: '32px' };
133 }
134
135 // ── Circle editor panel ───────────────────────────────────────────
136 function circlePanel(label, circle, onChange, __, el, TextControl, TextareaControl, PanelBody) {
137 return el(PanelBody, { title: label, initialOpen: false },
138 el(TextControl, { label: __('Label'), value: circle.label, onChange: v => onChange({ ...circle, label: v }) }),
139 el('div', { style: { marginBottom: '8px' } },
140 el('label', { style: { display: 'block', marginBottom: '6px', fontSize: '11px', fontWeight: '600', textTransform: 'uppercase' } }, __('Circle Color')),
141 el('input', { type: 'color', value: circle.color, style: { width: '100%', height: '36px', border: '1px solid #ddd', borderRadius: '4px', cursor: 'pointer' }, onChange: e => onChange({ ...circle, color: e.target.value }) })
142 ),
143 el(TextareaControl, { label: __('Items (one per line)'), value: (circle.items || []).join('\n'), rows: 4, onChange: v => onChange({ ...circle, items: v.split('\n').filter(s => s.trim()) }) }),
144 );
145 }
146
147 registerBlockType('blockenberg/venn-diagram', {
148 edit: function ({ attributes: a, setAttributes }) {
149 const blockProps = useBlockProps((function () {
150 var s = wrapStyle(a);
151 Object.assign(s, getTypoCssVars(a));
152 return { style: s };
153 })());
154 const is3 = a.mode === 'three';
155 return el('div', blockProps,
156 el(InspectorControls, {},
157 el(PanelBody, { title: __('Content'), initialOpen: true },
158 el(TextControl, { label: __('Title'), value: a.title, onChange: v => setAttributes({ title: v }) }),
159 el(ToggleControl, { label: __('Show title'), checked: a.showTitle, onChange: v => setAttributes({ showTitle: v }) }),
160 el(TextControl, { label: __('Subtitle'), value: a.subtitle, onChange: v => setAttributes({ subtitle: v }) }),
161 el(ToggleControl, { label: __('Show subtitle'), checked: a.showSubtitle, onChange: v => setAttributes({ showSubtitle: v }) }),
162 el(SelectControl, { label: __('Mode'), value: a.mode, options: [{ value: 'two', label: 'Two circles' }, { value: 'three', label: 'Three circles' }], onChange: v => setAttributes({ mode: v }) }),
163 el(ToggleControl, { label: __('Show circle labels'), checked: a.showCircleLabels, onChange: v => setAttributes({ showCircleLabels: v }) }),
164 el(ToggleControl, { label: __('Show overlap text'), checked: a.showOverlapText, onChange: v => setAttributes({ showOverlapText: v }) }),
165 el(ToggleControl, { label: __('Show item lists'), checked: a.showCircleItems, onChange: v => setAttributes({ showCircleItems: v }) }),
166 ),
167 el(PanelBody, { title: __('Overlap Text'), initialOpen: false },
168 el(TextControl, { label: is3 ? __('A ∩ B') : __('Overlap label'), value: a.overlapAB, onChange: v => setAttributes({ overlapAB: v }) }),
169 is3 && el(TextControl, { label: __('B ∩ C'), value: a.overlapBC, onChange: v => setAttributes({ overlapBC: v }) }),
170 is3 && el(TextControl, { label: __('A ∩ C'), value: a.overlapAC, onChange: v => setAttributes({ overlapAC: v }) }),
171 is3 && el(TextControl, { label: __('A ∩ B ∩ C (centre)'), value: a.overlapABC, onChange: v => setAttributes({ overlapABC: v }) }),
172 ),
173 circlePanel(__('Circle A'), a.circleA, v => setAttributes({ circleA: v }), __, el, TextControl, TextareaControl, PanelBody),
174 circlePanel(__('Circle B'), a.circleB, v => setAttributes({ circleB: v }), __, el, TextControl, TextareaControl, PanelBody),
175 is3 && circlePanel(__('Circle C'), a.circleC, v => setAttributes({ circleC: v }), __, el, TextControl, TextareaControl, PanelBody),
176 el(PanelBody, { title: __('Appearance'), initialOpen: false },
177 el(RangeControl, { label: __('Diagram size'), value: a.svgSize, min: 240, max: 600, onChange: v => setAttributes({ svgSize: v }) }),
178 el(RangeControl, { label: __('Circle opacity'), value: Math.round(a.circleOpacity * 100), min: 5, max: 60, onChange: v => setAttributes({ circleOpacity: v / 100 }) }),
179 el(RangeControl, { label: __('Border radius'), value: a.borderRadius, min: 0, max: 32, onChange: v => setAttributes({ borderRadius: v }) }),
180 el(RangeControl, { label: __('Padding top'), value: a.paddingTop, min: 0, max: 120, onChange: v => setAttributes({ paddingTop: v }) }),
181 el(RangeControl, { label: __('Padding bottom'), value: a.paddingBottom, min: 0, max: 120, onChange: v => setAttributes({ paddingBottom: v }) }),
182 ),
183
184 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
185 getTypoControl(__('Title', 'blockenberg'), 'titleTypo', a, setAttributes),
186 getTypoControl(__('Subtitle', 'blockenberg'), 'subtitleTypo', a, setAttributes),
187 el(RangeControl, { label: __('Label font size'), value: a.labelSize, min: 10, max: 28, onChange: v => setAttributes({ labelSize: v }) }),
188 el(RangeControl, { label: __('Overlap font size'), value: a.overlapSize, min: 8, max: 22, onChange: v => setAttributes({ overlapSize: v }) }),
189 el(RangeControl, { label: __('Item font size'), value: a.fontSize, min: 10, max: 20, onChange: v => setAttributes({ fontSize: v }) })
190 ),
191 el(PanelColorSettings, {
192 title: __('Colors'), initialOpen: false,
193 colorSettings: [
194 { label: __('Background'), value: a.bgColor, onChange: v => setAttributes({ bgColor: v || '#ffffff' }) },
195 { label: __('Title'), value: a.titleColor, onChange: v => setAttributes({ titleColor: v || '#0f172a' }) },
196 { label: __('Subtitle'), value: a.subtitleColor, onChange: v => setAttributes({ subtitleColor: v || '#64748b' }) },
197 { label: __('Text / overlaps'), value: a.textColor, onChange: v => setAttributes({ textColor: v || '#1e293b' }) },
198 ]
199 }),
200 ),
201
202 a.showTitle && el('h3', { className: 'bkbg-venn-title', style: { color: a.titleColor, textAlign: 'center', margin: '0 0 8px' } }, a.title),
203 a.showSubtitle && el('p', { className: 'bkbg-venn-subtitle', style: { color: a.subtitleColor, textAlign: 'center', margin: '0 0 20px' } }, a.subtitle),
204 is3 ? renderVenn3(a) : renderVenn2(a),
205 a.showCircleItems && renderItems(a),
206 );
207 },
208
209 save: function ({ attributes: a }) {
210 const is3 = a.mode === 'three';
211 var _saveStyle = wrapStyle(a);
212 Object.assign(_saveStyle, getTypoCssVars(a));
213 return el('div', { className: 'bkbg-venn-wrap', style: _saveStyle },
214 a.showTitle && el('h3', { className: 'bkbg-venn-title', style: { color: a.titleColor, textAlign: 'center', margin: '0 0 8px' } }, a.title),
215 a.showSubtitle && el('p', { className: 'bkbg-venn-subtitle', style: { color: a.subtitleColor, textAlign: 'center', margin: '0 0 20px' } }, a.subtitle),
216 is3 ? renderVenn3(a) : renderVenn2(a),
217 a.showCircleItems && renderItems(a),
218 );
219 }
220 });
221 }() );
222