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

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

375 lines 22.9 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 Fragment = wp.element.Fragment;
4 var __ = wp.i18n.__;
5 var registerBlockType = wp.blocks.registerBlockType;
6 var InspectorControls = wp.blockEditor.InspectorControls;
7 var useBlockProps = wp.blockEditor.useBlockProps;
8 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
9 var PanelBody = wp.components.PanelBody;
10 var ToggleControl = wp.components.ToggleControl;
11 var RangeControl = wp.components.RangeControl;
12 var TextControl = wp.components.TextControl;
13 var Button = wp.components.Button;
14
15 var _dmTC, _dmTV;
16 function _tc() { return _dmTC || (_dmTC = window.bkbgTypographyControl); }
17 function _tv(t, p) { return (_dmTV || (_dmTV = window.bkbgTypoCssVars)) ? _dmTV(t, p) : {}; }
18
19 /* ── Calculation helpers ─────────────────────────── */
20 function calcTotals(options, criteria) {
21 return options.map(function (opt) {
22 var total = 0;
23 criteria.forEach(function (crit, ci) {
24 total += (opt.scores[ci] || 0) * (crit.weight || 1);
25 });
26 return total;
27 });
28 }
29
30 function winnerIdx(totals) {
31 var maxVal = -1, maxIdx = -1;
32 totals.forEach(function (t, i) { if (t > maxVal) { maxVal = t; maxIdx = i; } });
33 return maxIdx;
34 }
35
36 /* ── ES5 update helpers ──────────────────────────── */
37 function updateOption(options, idx, field, val) {
38 return options.map(function (o, i) {
39 if (i !== idx) return o;
40 var u = {}; u[field] = val;
41 return Object.assign({}, o, u);
42 });
43 }
44
45 function updateScore(options, oIdx, cIdx, val) {
46 return options.map(function (o, i) {
47 if (i !== oIdx) return o;
48 var newScores = o.scores.map(function (s, j) { return j === cIdx ? val : s; });
49 return Object.assign({}, o, { scores: newScores });
50 });
51 }
52
53 function updateCriterion(criteria, idx, field, val) {
54 return criteria.map(function (c, i) {
55 if (i !== idx) return c;
56 var u = {}; u[field] = val;
57 return Object.assign({}, c, u);
58 });
59 }
60
61 function addOption(options, criteria) {
62 var scores = criteria.map(function () { return 5; });
63 return options.concat([{ name: 'New Option', scores: scores }]);
64 }
65
66 function removeOption(options, idx) {
67 return options.filter(function (_, i) { return i !== idx; });
68 }
69
70 function addCriterion(criteria, options) {
71 var newCriteria = criteria.concat([{ label: 'New Criterion', weight: 3 }]);
72 var newOptions = options.map(function (o) {
73 return Object.assign({}, o, { scores: o.scores.concat([5]) });
74 });
75 return { criteria: newCriteria, options: newOptions };
76 }
77
78 function removeCriterion(criteria, options, cIdx) {
79 var newCriteria = criteria.filter(function (_, i) { return i !== cIdx; });
80 var newOptions = options.map(function (o) {
81 return Object.assign({}, o, { scores: o.scores.filter(function (_, i) { return i !== cIdx; }) });
82 });
83 return { criteria: newCriteria, options: newOptions };
84 }
85
86 registerBlockType('blockenberg/decision-matrix', {
87 edit: function (props) {
88 var a = props.attributes;
89 var set = props.setAttributes;
90 var blockProps = useBlockProps({ className: 'bkbg-dm-editor',
91 style: Object.assign(
92 { '--bkbg-dm-ttl-fs': (a.titleFontSize || 24) + 'px',
93 '--bkbg-dm-sub-fs': (a.subtitleFontSize || 14) + 'px',
94 '--bkbg-dm-cel-fs': (a.fontSize || 14) + 'px' },
95 _tv(a.typoTitle || {}, '--bkbg-dm-ttl-'),
96 _tv(a.typoSubtitle || {}, '--bkbg-dm-sub-'),
97 _tv(a.typoCell || {}, '--bkbg-dm-cel-')
98 )
99 });
100
101 var totals = calcTotals(a.options, a.criteria);
102 var winner = a.showWinner ? winnerIdx(totals) : -1;
103 var maxTotal = Math.max.apply(null, totals);
104
105 function renderPreview() {
106 return el('div', {
107 className: 'bkbg-dm-block',
108 style: { background: a.bgColor || undefined, boxSizing: 'border-box' }
109 },
110 /* Header */
111 (a.showTitle || a.showSubtitle) && el('div', {
112 className: 'bkbg-dm-header',
113 style: { marginBottom: '20px' }
114 },
115 a.showTitle && el('h3', {
116 className: 'bkbg-dm-title',
117 style: { color: a.titleColor, margin: '0 0 6px' }
118 }, a.blockTitle),
119 a.showSubtitle && a.blockSubtitle && el('p', {
120 className: 'bkbg-dm-subtitle',
121 style: { color: a.subtitleColor, margin: 0 }
122 }, a.blockSubtitle)
123 ),
124 /* Winner banner */
125 a.showWinner && winner >= 0 && el('div', {
126 className: 'bkbg-dm-winner-banner',
127 style: {
128 background: a.winnerBg, border: '1px solid ' + a.winnerBorderColor,
129 borderRadius: a.borderRadius + 'px', padding: '12px 18px',
130 marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '10px'
131 }
132 },
133 el('span', { style: { fontSize: '20px' } }, '🏆'),
134 el('span', { style: { fontWeight: 700, color: a.winnerColor, fontSize: (a.winnerFontSize || 15) + 'px' } },
135 a.options[winner].name + ' wins with ' + totals[winner] + ' points'
136 )
137 ),
138 /* Table */
139 el('div', { style: { overflowX: 'auto' } },
140 el('table', {
141 className: 'bkbg-dm-table',
142 style: {
143 width: '100%', borderCollapse: 'collapse',
144 border: '1px solid ' + a.borderColor,
145 borderRadius: a.borderRadius + 'px',
146 overflow: 'hidden'
147 }
148 },
149 /* Header row */
150 el('thead', {},
151 el('tr', {},
152 el('th', {
153 style: {
154 background: a.headerBg, color: a.headerColor,
155 padding: '12px 16px', textAlign: 'left', fontWeight: 700,
156 border: '1px solid rgba(255,255,255,.1)'
157 }
158 }, 'Criterion'),
159 a.showWeights && el('th', {
160 style: {
161 background: a.headerBg, color: a.headerColor,
162 padding: '12px 10px', textAlign: 'center', fontWeight: 600,
163 fontSize: '12px', border: '1px solid rgba(255,255,255,.1)'
164 }
165 }, 'Weight'),
166 a.options.map(function (opt, oIdx) {
167 var isWin = oIdx === winner;
168 return el('th', {
169 key: oIdx,
170 style: {
171 background: isWin ? a.winnerBorderColor : a.headerBg,
172 color: a.headerColor, padding: '12px 16px',
173 textAlign: 'center', fontWeight: 700,
174 border: '1px solid rgba(255,255,255,.1)'
175 }
176 },
177 opt.name,
178 isWin && el('span', { style: { marginLeft: '6px', fontSize: '14px' } }, '🏆')
179 );
180 })
181 )
182 ),
183 /* Body rows */
184 el('tbody', {},
185 a.criteria.map(function (crit, ci) {
186 return el('tr', { key: ci },
187 el('td', {
188 style: {
189 background: a.criteriaLabelBg, color: a.criteriaLabelColor,
190 padding: '10px 16px', fontWeight: 600,
191 border: '1px solid ' + a.borderColor
192 }
193 }, crit.label),
194 a.showWeights && el('td', {
195 style: {
196 background: a.criteriaLabelBg, color: a.accentColor,
197 padding: '10px', textAlign: 'center', fontWeight: 700, fontSize: '13px',
198 border: '1px solid ' + a.borderColor
199 }
200 }, '×' + (crit.weight || 1)),
201 a.options.map(function (opt, oi) {
202 var raw = opt.scores[ci] || 0;
203 var weighted = raw * (crit.weight || 1);
204 var isWin = oi === winner;
205 return el('td', {
206 key: oi,
207 style: {
208 background: isWin ? a.winnerBg : a.cellBg,
209 color: a.cellColor, padding: '10px 16px',
210 textAlign: 'center',
211 border: '1px solid ' + a.borderColor
212 }
213 },
214 el('span', { style: { fontWeight: 600 } }, String(raw)),
215 a.showRawScores && el('span', {
216 style: { fontSize: '11px', color: a.accentColor, marginLeft: '4px' }
217 }, '(' + weighted + ')')
218 );
219 })
220 );
221 }),
222 /* Total row */
223 el('tr', { style: { background: a.totalRowBg } },
224 el('td', {
225 colSpan: a.showWeights ? 2 : 1,
226 style: {
227 padding: '12px 16px', fontWeight: 700, fontSize: '13px',
228 color: a.totalRowColor, textTransform: 'uppercase',
229 letterSpacing: '.05em', background: a.totalRowBg,
230 border: '1px solid ' + a.borderColor
231 }
232 }, '🏁 Weighted Total'),
233 totals.map(function (t, i) {
234 var isWin = i === winner;
235 return el('td', {
236 key: i,
237 style: {
238 padding: '12px 16px', textAlign: 'center', fontWeight: 800,
239 fontSize: '16px',
240 color: isWin ? a.winnerColor : a.totalRowColor,
241 background: isWin ? a.winnerBg : a.totalRowBg,
242 border: '1px solid ' + a.borderColor
243 }
244 }, String(t));
245 })
246 )
247 )
248 )
249 )
250 );
251 }
252
253 var inspector = el(InspectorControls, {},
254 /* Title */
255 el(PanelBody, { title: 'Title', initialOpen: true },
256 el(ToggleControl, { __nextHasNoMarginBottom: true, label: 'Show Title', checked: a.showTitle, onChange: function (v) { set({ showTitle: v }); } }),
257 a.showTitle && el(TextControl, { __nextHasNoMarginBottom: true, label: 'Title', value: a.blockTitle, onChange: function (v) { set({ blockTitle: v }); } }),
258 el(ToggleControl, { __nextHasNoMarginBottom: true, label: 'Show Subtitle', checked: a.showSubtitle, onChange: function (v) { set({ showSubtitle: v }); } }),
259 a.showSubtitle && el(TextControl, { __nextHasNoMarginBottom: true, label: 'Subtitle', value: a.blockSubtitle, onChange: function (v) { set({ blockSubtitle: v }); } })
260 ),
261 /* Options */
262 el(PanelBody, { title: 'Options (' + a.options.length + ')', initialOpen: true },
263 a.options.map(function (opt, oi) {
264 return el('div', {
265 key: oi,
266 style: { border: '1px solid #e2e8f0', borderRadius: 8, padding: 10, marginBottom: 8, background: '#f8fafc' }
267 },
268 el('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 } },
269 el('strong', { style: { fontSize: 12 } }, 'Option ' + (oi + 1)),
270 el(Button, { isSmall: true, isDestructive: true, onClick: function () { set({ options: removeOption(a.options, oi) }); } }, '')
271 ),
272 el(TextControl, { __nextHasNoMarginBottom: true, label: 'Name', value: opt.name, onChange: function (v) { set({ options: updateOption(a.options, oi, 'name', v) }); } }),
273 el('div', { style: { fontSize: 11, fontWeight: 600, marginBottom: 4, color: '#64748b' } }, 'Scores (1–10):'),
274 el('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 6 } },
275 a.criteria.map(function (crit, ci) {
276 return el('label', { key: ci, style: { display: 'flex', flexDirection: 'column', alignItems: 'center', fontSize: 10, gap: 2 } },
277 el('span', { style: { maxWidth: 60, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', color: '#475569' } }, crit.label),
278 el('input', {
279 type: 'number', min: 0, max: 10,
280 value: opt.scores[ci] !== undefined ? opt.scores[ci] : 5,
281 style: { width: 44, textAlign: 'center', padding: '3px 4px', border: '1px solid #ddd', borderRadius: 4, fontSize: 13 },
282 onChange: function (e) { set({ options: updateScore(a.options, oi, ci, parseInt(e.target.value) || 0) }); }
283 })
284 );
285 })
286 )
287 );
288 }),
289 el(Button, {
290 variant: 'secondary', style: { width: '100%', justifyContent: 'center', marginTop: 4 },
291 onClick: function () { set({ options: addOption(a.options, a.criteria) }); }
292 }, '+ Add Option')
293 ),
294 /* Criteria */
295 el(PanelBody, { title: 'Criteria (' + a.criteria.length + ')', initialOpen: true },
296 a.criteria.map(function (crit, ci) {
297 return el('div', {
298 key: ci,
299 style: { border: '1px solid #e2e8f0', borderRadius: 8, padding: 10, marginBottom: 8, background: '#f8fafc' }
300 },
301 el('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 } },
302 el('strong', { style: { fontSize: 12 } }, 'Criterion ' + (ci + 1)),
303 el(Button, {
304 isSmall: true, isDestructive: true,
305 onClick: function () {
306 var result = removeCriterion(a.criteria, a.options, ci);
307 set({ criteria: result.criteria, options: result.options });
308 }
309 }, '')
310 ),
311 el(TextControl, { __nextHasNoMarginBottom: true, label: 'Label', value: crit.label, onChange: function (v) { set({ criteria: updateCriterion(a.criteria, ci, 'label', v) }); } }),
312 el(RangeControl, { __nextHasNoMarginBottom: true, label: 'Weight (importance)', value: crit.weight || 1, min: 1, max: 10, onChange: function (v) { set({ criteria: updateCriterion(a.criteria, ci, 'weight', v) }); } })
313 );
314 }),
315 el(Button, {
316 variant: 'secondary', style: { width: '100%', justifyContent: 'center', marginTop: 4 },
317 onClick: function () {
318 var result = addCriterion(a.criteria, a.options);
319 set({ criteria: result.criteria, options: result.options });
320 }
321 }, '+ Add Criterion')
322 ),
323 /* Display */
324 el(PanelBody, { title: 'Display', initialOpen: false },
325 el(ToggleControl, { __nextHasNoMarginBottom: true, label: 'Show Weights Column', checked: a.showWeights, onChange: function (v) { set({ showWeights: v }); } }),
326 el(ToggleControl, { __nextHasNoMarginBottom: true, label: 'Show Weighted Score', checked: a.showRawScores, onChange: function (v) { set({ showRawScores: v }); } }),
327 el(ToggleControl, { __nextHasNoMarginBottom: true, label: 'Highlight Winner', checked: a.showWinner, onChange: function (v) { set({ showWinner: v }); } })
328 ),
329 /* Typography */
330 el(PanelBody, { title: 'Typography', initialOpen: false },
331 _tc() && _tc()({ label: __('Title typography', 'blockenberg'), value: a.typoTitle || {}, onChange: function (v) { set({ typoTitle: v }); } }),
332 _tc() && _tc()({ label: __('Subtitle typography', 'blockenberg'), value: a.typoSubtitle || {}, onChange: function (v) { set({ typoSubtitle: v }); } }),
333 _tc() && _tc()({ label: __('Table typography', 'blockenberg'), value: a.typoCell || {}, onChange: function (v) { set({ typoCell: v }); } }),
334 el(RangeControl, { __nextHasNoMarginBottom: true, label: 'Winner Label Font Size', value: a.winnerFontSize, min: 10, max: 24, onChange: function (v) { set({ winnerFontSize: v }); } })
335 ),
336 /* Appearance */
337 el(PanelBody, { title: 'Appearance', initialOpen: false },
338 el(RangeControl, { __nextHasNoMarginBottom: true, label: 'Border Radius', value: a.borderRadius, min: 0, max: 20, onChange: function (v) { set({ borderRadius: v }); } })
339 ),
340 /* Colors */
341 el(PanelColorSettings, {
342 title: 'Colors', initialOpen: false,
343 colorSettings: [
344 { label: 'Block Background', value: a.bgColor, onChange: function (v) { set({ bgColor: v || '' }); } },
345 { label: 'Header Background', value: a.headerBg, onChange: function (v) { set({ headerBg: v || '#1e293b' }); } },
346 { label: 'Header Text', value: a.headerColor, onChange: function (v) { set({ headerColor: v || '#ffffff' }); } },
347 { label: 'Criteria Label Bg', value: a.criteriaLabelBg, onChange: function (v) { set({ criteriaLabelBg: v || '#f1f5f9' }); } },
348 { label: 'Criteria Label Color', value: a.criteriaLabelColor, onChange: function (v) { set({ criteriaLabelColor: v || '#475569' }); } },
349 { label: 'Cell Background', value: a.cellBg, onChange: function (v) { set({ cellBg: v || '#ffffff' }); } },
350 { label: 'Cell Color', value: a.cellColor, onChange: function (v) { set({ cellColor: v || '#374151' }); } },
351 { label: 'Border Color', value: a.borderColor, onChange: function (v) { set({ borderColor: v || '#e2e8f0' }); } },
352 { label: 'Winner Background', value: a.winnerBg, onChange: function (v) { set({ winnerBg: v || '#eff6ff' }); } },
353 { label: 'Winner Color', value: a.winnerColor, onChange: function (v) { set({ winnerColor: v || '#1d4ed8' }); } },
354 { label: 'Winner Border', value: a.winnerBorderColor, onChange: function (v) { set({ winnerBorderColor: v || '#3b82f6' }); } },
355 { label: 'Total Row Bg', value: a.totalRowBg, onChange: function (v) { set({ totalRowBg: v || '#f8fafc' }); } },
356 { label: 'Accent Color', value: a.accentColor, onChange: function (v) { set({ accentColor: v || '#3b82f6' }); } }
357 ]
358 })
359 );
360
361 return el(Fragment, {}, inspector, el('div', blockProps, renderPreview()));
362 },
363
364 save: function (props) {
365 var useBlockProps = wp.blockEditor.useBlockProps;
366 return el('div', useBlockProps.save(),
367 el('div', {
368 className: 'bkbg-decision-matrix-app',
369 'data-opts': JSON.stringify(props.attributes)
370 })
371 );
372 }
373 });
374 }() );
375