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 / comparison-matrix / index.js

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

297 lines 17.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 ( function () {
2 var el = wp.element.createElement;
3 var __ = wp.i18n.__;
4 var registerBlockType = wp.blocks.registerBlockType;
5 var useBlockProps = wp.blockEditor.useBlockProps;
6 var InspectorControls = wp.blockEditor.InspectorControls;
7 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
8 var PanelBody = wp.components.PanelBody;
9 var RangeControl = wp.components.RangeControl;
10 var ToggleControl = wp.components.ToggleControl;
11 var TextControl = wp.components.TextControl;
12 var Button = wp.components.Button;
13 var SelectControl = wp.components.SelectControl;
14
15 function getTypographyControl() { return (window.bkbgTypographyControl || function () { return null; }); }
16 function getTypoCssVars() { return (window.bkbgTypoCssVars || function () { return {}; }); }
17 function _tv(typo, prefix) { var fn = getTypoCssVars(); return fn(typo || {}, prefix); }
18 var IP = function () { return window.bkbgIconPicker; };
19
20 /* Render a cell value: 'true'/'false' → icon, otherwise text */
21 function CellValue(val, attr) {
22 if (val === 'true') {
23 var checkType = attr.checkIconType || 'custom-char';
24 if (checkType !== 'custom-char') {
25 return el('span', { className: 'bkbg-cm-check', style: { color: attr.checkColor, fontSize: 18 } },
26 IP().buildEditorIcon(checkType, attr.checkIcon, attr.checkIconDashicon, attr.checkIconImageUrl, attr.checkIconDashiconColor));
27 }
28 return el('span', { style: { color: attr.checkColor, fontWeight: 700, fontSize: 18 }}, attr.checkIcon || '');
29 }
30 if (val === 'false') {
31 var crossType = attr.crossIconType || 'custom-char';
32 if (crossType !== 'custom-char') {
33 return el('span', { className: 'bkbg-cm-cross', style: { color: attr.crossColor, fontSize: 18 } },
34 IP().buildEditorIcon(crossType, attr.crossIcon, attr.crossIconDashicon, attr.crossIconImageUrl, attr.crossIconDashiconColor));
35 }
36 return el('span', { style: { color: attr.crossColor, fontWeight: 700, fontSize: 18 }}, attr.crossIcon || '');
37 }
38 return el('span', { style: { fontWeight: 600 }}, val);
39 }
40
41 registerBlockType('blockenberg/comparison-matrix', {
42 title: __('Comparison Matrix'),
43 icon: 'grid-view',
44 category: 'bkbg-marketing',
45
46 edit: function (props) {
47 var attr = props.attributes;
48 var setAttr = props.setAttributes;
49 var typoVars = Object.assign({}, _tv(attr.typoHeader, '--bkcm-header-'), _tv(attr.typoBody, '--bkcm-body-'));
50 var bp = useBlockProps({ className: 'bkbg-cm-wrap', style: typoVars });
51
52 function updatePlan(idx, field, val) {
53 var plans = attr.plans.map(function (p, i) {
54 return i === idx ? Object.assign({}, p, { [field]: val }) : p;
55 });
56 setAttr({ plans: plans });
57 }
58 function addPlan() {
59 var n = attr.plans.length;
60 var newValues = attr.features.map(function (f) {
61 var newF = f.values.slice();
62 newF.push('true');
63 return newF;
64 });
65 setAttr({
66 plans: attr.plans.concat([{ name: 'Plan ' + (n + 1), subtitle: '', highlight: false, badge: '' }]),
67 features: attr.features.map(function (f, i) {
68 return Object.assign({}, f, { values: newValues[i] });
69 })
70 });
71 }
72 function removePlan(idx) {
73 if (attr.plans.length <= 2) return;
74 setAttr({
75 plans: attr.plans.filter(function (_, i) { return i !== idx; }),
76 features: attr.features.map(function (f) {
77 return Object.assign({}, f, { values: f.values.filter(function (_, i) { return i !== idx; }) });
78 })
79 });
80 }
81 function updateFeature(idx, field, val) {
82 var features = attr.features.map(function (f, i) {
83 return i === idx ? Object.assign({}, f, { [field]: val }) : f;
84 });
85 setAttr({ features: features });
86 }
87 function updateFeatureValue(fidx, pidx, val) {
88 var features = attr.features.map(function (f, i) {
89 if (i !== fidx) return f;
90 var values = f.values.slice();
91 values[pidx] = val;
92 return Object.assign({}, f, { values: values });
93 });
94 setAttr({ features: features });
95 }
96 function addFeature() {
97 setAttr({ features: attr.features.concat([{
98 label: 'New Feature', group: '',
99 values: attr.plans.map(function () { return 'true'; })
100 }]) });
101 }
102 function removeFeature(idx) {
103 setAttr({ features: attr.features.filter(function (_, i) { return i !== idx; }) });
104 }
105
106 var inspector = el(InspectorControls, {},
107 /* Plans */
108 el(PanelBody, { title: __('Plans / Columns'), initialOpen: true },
109 attr.plans.map(function (p, idx) {
110 return el(PanelBody, { key: idx, title: p.name || 'Plan ' + (idx + 1), initialOpen: false },
111 el(TextControl, { label: __('Name'), value: p.name || '',
112 onChange: function (v) { updatePlan(idx, 'name', v); },
113 __nextHasNoMarginBottom: true }),
114 el(TextControl, { label: __('Subtitle'), value: p.subtitle || '',
115 onChange: function (v) { updatePlan(idx, 'subtitle', v); },
116 __nextHasNoMarginBottom: true }),
117 el(TextControl, { label: __('Badge (optional)'), value: p.badge || '',
118 onChange: function (v) { updatePlan(idx, 'badge', v); },
119 __nextHasNoMarginBottom: true }),
120 el(ToggleControl, { label: __('Highlight (featured)'), checked: !!p.highlight,
121 onChange: function (v) { updatePlan(idx, 'highlight', v); },
122 __nextHasNoMarginBottom: true }),
123 attr.plans.length > 2 && el(Button, { isDestructive: true, variant: 'secondary',
124 onClick: function () { removePlan(idx); },
125 style: { marginTop: 8 }
126 }, __('Remove Plan'))
127 );
128 }),
129 el(Button, { variant: 'primary', onClick: addPlan,
130 style: { marginTop: 8, width: '100%' }
131 }, __('+ Add Plan'))
132 ),
133
134 /* Features */
135 el(PanelBody, { title: __('Features / Rows'), initialOpen: false },
136 attr.features.map(function (f, fidx) {
137 return el(PanelBody, { key: fidx, title: f.label || 'Feature ' + (fidx + 1), initialOpen: false },
138 el(TextControl, { label: __('Feature Label'), value: f.label || '',
139 onChange: function (v) { updateFeature(fidx, 'label', v); },
140 __nextHasNoMarginBottom: true }),
141 el(TextControl, { label: __('Group Header (optional)'), value: f.group || '',
142 onChange: function (v) { updateFeature(fidx, 'group', v); },
143 __nextHasNoMarginBottom: true }),
144 el('p', { style: { fontSize: 11, margin: '8px 0 4px', color: '#64748b' }},
145 __('Values per plan — use "true"/false" for check/cross, or any text:')),
146 attr.plans.map(function (p, pidx) {
147 return el(TextControl, { key: pidx,
148 label: p.name || 'Plan ' + (pidx + 1),
149 value: (f.values && f.values[pidx]) || 'true',
150 onChange: function (v) { updateFeatureValue(fidx, pidx, v); },
151 __nextHasNoMarginBottom: true });
152 }),
153 el(Button, { isDestructive: true, variant: 'secondary',
154 onClick: function () { removeFeature(fidx); },
155 style: { marginTop: 8 }
156 }, __('Remove Feature'))
157 );
158 }),
159 el(Button, { variant: 'primary', onClick: addFeature,
160 style: { marginTop: 8, width: '100%' }
161 }, __('+ Add Feature'))
162 ),
163
164 /* Appearance */
165 el(PanelBody, { title: __('Appearance'), initialOpen: false },
166 el(RangeControl, { label: __('Cell Padding (px)'), value: attr.cellPadding,
167 min: 8, max: 32,
168 onChange: function (v) { setAttr({ cellPadding: v }); },
169 __nextHasNoMarginBottom: true }),
170 el(RangeControl, { label: __('Header Radius (px)'), value: attr.headerRadius,
171 min: 0, max: 24,
172 onChange: function (v) { setAttr({ headerRadius: v }); },
173 __nextHasNoMarginBottom: true }),
174 el(RangeControl, { label: __('First Column Width (px)'), value: attr.firstColWidth,
175 min: 100, max: 400,
176 onChange: function (v) { setAttr({ firstColWidth: v }); },
177 __nextHasNoMarginBottom: true }),
178 el(ToggleControl, { label: __('Sticky Header Row'), checked: attr.stickyHeader,
179 onChange: function (v) { setAttr({ stickyHeader: v }); },
180 __nextHasNoMarginBottom: true }),
181 el('p', { style: { fontSize: 11, fontWeight: 600, marginTop: 12 } }, __('Check Icon')),
182 el(IP().IconPickerControl, IP().iconPickerProps(attr, setAttr, { charAttr: 'checkIcon', typeAttr: 'checkIconType', dashiconAttr: 'checkIconDashicon', imageUrlAttr: 'checkIconImageUrl', colorAttr: 'checkIconDashiconColor' })),
183 el('p', { style: { fontSize: 11, fontWeight: 600, marginTop: 12 } }, __('Cross Icon')),
184 el(IP().IconPickerControl, IP().iconPickerProps(attr, setAttr, { charAttr: 'crossIcon', typeAttr: 'crossIconType', dashiconAttr: 'crossIconDashicon', imageUrlAttr: 'crossIconImageUrl', colorAttr: 'crossIconDashiconColor' }))
185 ),
186
187
188 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
189 el(getTypographyControl(), { label: __('Header Typography', 'blockenberg'), value: attr.typoHeader, onChange: function (v) { setAttr({ typoHeader: v }); } }),
190 el(getTypographyControl(), { label: __('Body Typography', 'blockenberg'), value: attr.typoBody, onChange: function (v) { setAttr({ typoBody: v }); } })
191 ),
192 el(PanelColorSettings, {
193 title: __('Colors'), initialOpen: false,
194 colorSettings: [
195 { label: __('Header BG'), value: attr.headerBg,
196 onChange: function (v) { setAttr({ headerBg: v || '#f8fafc' }); } },
197 { label: __('Header Text'), value: attr.headerColor,
198 onChange: function (v) { setAttr({ headerColor: v || '#0f172a' }); } },
199 { label: __('Highlight BG'), value: attr.highlightBg,
200 onChange: function (v) { setAttr({ highlightBg: v || '#6366f1' }); } },
201 { label: __('Highlight Text'), value: attr.highlightColor,
202 onChange: function (v) { setAttr({ highlightColor: v || '#ffffff' }); } },
203 { label: __('Row Color'), value: attr.rowColor,
204 onChange: function (v) { setAttr({ rowColor: v || '#334155' }); } },
205 { label: __('Border'), value: attr.borderColor,
206 onChange: function (v) { setAttr({ borderColor: v || '#e2e8f0' }); } },
207 { label: __('Check'), value: attr.checkColor,
208 onChange: function (v) { setAttr({ checkColor: v || '#10b981' }); } },
209 { label: __('Cross'), value: attr.crossColor,
210 onChange: function (v) { setAttr({ crossColor: v || '#f43f5e' }); } }
211 ]
212 })
213 );
214
215 /* Editor preview table */
216 var colStyle = function (p) {
217 return {
218 background: p.highlight ? attr.highlightBg : attr.headerBg,
219 color: p.highlight ? attr.highlightColor : attr.headerColor,
220 padding: attr.cellPadding + 'px',
221 textAlign: 'center',
222 borderRadius: attr.headerRadius + 'px ' + attr.headerRadius + 'px 0 0',
223 border: '1px solid ' + attr.borderColor, borderBottom: 'none'
224 };
225 };
226
227 return el(wp.element.Fragment, {}, inspector,
228 el('div', bp,
229 el('div', { style: { overflowX: 'auto' }},
230 el('table', { className: 'bkbg-cm-table', style: {
231 width: '100%', borderCollapse: 'collapse'
232 }},
233 el('thead', {},
234 el('tr', {},
235 el('th', { className: 'bkbg-cm-th bkbg-cm-th-feature', style: {
236 width: attr.firstColWidth + 'px',
237 padding: attr.cellPadding + 'px',
238 border: '1px solid ' + attr.borderColor,
239 background: attr.headerBg
240 }}),
241 attr.plans.map(function (p, i) {
242 return el('th', { key: i, className: 'bkbg-cm-th' + (p.highlight ? ' bkbg-cm-highlight' : ''), style: colStyle(p) },
243 p.badge && el('div', { style: {
244 fontSize: 10, fontWeight: 700, marginBottom: 4,
245 background: attr.badgeBg || '#fef3c7',
246 color: attr.badgeColor || '#92400e',
247 padding: '2px 8px', borderRadius: 100,
248 display: 'inline-block'
249 }}, p.badge),
250 el('div', {}, p.name),
251 p.subtitle && el('div', { style: { fontSize: 11, opacity: 0.75, fontWeight: 400 }}, p.subtitle)
252 );
253 })
254 )
255 ),
256 el('tbody', {},
257 attr.features.map(function (f, ridx) {
258 return el('tr', { key: ridx, style: {
259 background: ridx % 2 === 0 ? attr.rowBg || '#ffffff' : attr.rowAltBg || '#f8fafc'
260 }},
261 el('td', { className: 'bkbg-cm-td bkbg-cm-td-feature', style: {
262 padding: attr.cellPadding + 'px',
263 border: '1px solid ' + attr.borderColor,
264 color: attr.headerColor,
265 width: attr.firstColWidth + 'px'
266 }}, f.label),
267 attr.plans.map(function (p, cidx) {
268 var val = f.values && f.values[cidx] != null ? f.values[cidx] : '';
269 return el('td', { key: cidx, className: 'bkbg-cm-td' + (p.highlight ? ' bkbg-cm-hl-col' : ''), style: {
270 padding: attr.cellPadding + 'px',
271 border: '1px solid ' + attr.borderColor,
272 color: attr.rowColor,
273 background: p.highlight ? attr.highlightBg + '18' : 'transparent'
274 }},
275 CellValue(val, attr)
276 );
277 })
278 );
279 })
280 )
281 )
282 )
283 )
284 );
285 },
286
287 save: function (props) {
288 var attr = props.attributes;
289 var sv = Object.assign({}, _tv(attr.typoHeader, '--bkcm-header-'), _tv(attr.typoBody, '--bkcm-body-'));
290 var bp = useBlockProps.save({ style: sv });
291 return el('div', bp,
292 el('div', { className: 'bkbg-cm-app', 'data-opts': JSON.stringify(attr) })
293 );
294 }
295 });
296 }() );
297