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 / product-spec / index.js

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

330 lines 19.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 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 Button = wp.components.Button;
11 var TextControl = wp.components.TextControl;
12 var ToggleControl = wp.components.ToggleControl;
13 var RangeControl = wp.components.RangeControl;
14 var SelectControl = wp.components.SelectControl;
15 var CheckboxControl = wp.components.CheckboxControl;
16
17 var _tc; function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
18 var _tv; function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
19
20 // ── ES5 safe update helpers ──────────────────────────────────
21 function updateGroup(groups, gIdx, field, val) {
22 return groups.map(function (g, i) {
23 if (i !== gIdx) return g;
24 var u = {}; u[field] = val;
25 return Object.assign({}, g, u);
26 });
27 }
28
29 function updateSpec(groups, gIdx, sIdx, field, val) {
30 return groups.map(function (g, i) {
31 if (i !== gIdx) return g;
32 var newSpecs = g.specs.map(function (s, j) {
33 if (j !== sIdx) return s;
34 var u = {}; u[field] = val;
35 return Object.assign({}, s, u);
36 });
37 return Object.assign({}, g, { specs: newSpecs });
38 });
39 }
40
41 function addSpec(groups, gIdx) {
42 return groups.map(function (g, i) {
43 if (i !== gIdx) return g;
44 return Object.assign({}, g, { specs: g.specs.concat([{ key: 'Spec', value: 'Value', highlight: false }]) });
45 });
46 }
47
48 function removeSpec(groups, gIdx, sIdx) {
49 return groups.map(function (g, i) {
50 if (i !== gIdx) return g;
51 return Object.assign({}, g, { specs: g.specs.filter(function (_, j) { return j !== sIdx; }) });
52 });
53 }
54
55 registerBlockType('blockenberg/product-spec', {
56 title: __('Product Spec Sheet', 'blockenberg'),
57 icon: 'editor-table',
58 category: 'bkbg-marketing',
59 description: __('Structured product specification sheet with grouped categories and rich styling.', 'blockenberg'),
60
61 edit: function (props) {
62 var a = props.attributes;
63 var set = props.setAttributes;
64 var TC = getTypoControl();
65
66 var layoutOptions = [
67 { label: __('Table', 'blockenberg'), value: 'table' },
68 { label: __('Cards', 'blockenberg'), value: 'cards' },
69 { label: __('Compact (2 cols)', 'blockenberg'), value: 'compact' }
70 ];
71 var styleOptions = [
72 { label: __('Striped rows', 'blockenberg'), value: 'striped' },
73 { label: __('Bordered', 'blockenberg'), value: 'bordered' },
74 { label: __('Minimal', 'blockenberg'), value: 'minimal' }
75 ];
76
77 var inspector = el(InspectorControls, {},
78 // Header
79 el(PanelBody, { title: __('Product Header', 'blockenberg'), initialOpen: true },
80 el(ToggleControl, {
81 label: __('Show product header', 'blockenberg'), checked: a.showHeader, __nextHasNoMarginBottom: true,
82 onChange: function (v) { set({ showHeader: v }); }
83 }),
84 a.showHeader && el(TextControl, {
85 label: __('Product name', 'blockenberg'), value: a.productName, __nextHasNoMarginBottom: true,
86 onChange: function (v) { set({ productName: v }); }
87 }),
88 a.showHeader && el(TextControl, {
89 label: __('Tagline', 'blockenberg'), value: a.productTagline, __nextHasNoMarginBottom: true,
90 onChange: function (v) { set({ productTagline: v }); }
91 }),
92 ),
93
94 // Spec Groups
95 el(PanelBody, { title: __('Spec Groups', 'blockenberg'), initialOpen: false },
96 a.groups.map(function (group, gIdx) {
97 return el('div', { key: 'g-' + gIdx, style: { marginBottom: '16px', border: '1px solid #e2e8f0', borderRadius: '8px', overflow: 'hidden' } },
98 el('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '8px 12px', background: '#f8fafc' } },
99 el(TextControl, {
100 label: '',
101 value: group.title, __nextHasNoMarginBottom: true,
102 style: { fontWeight: 700, margin: 0 },
103 onChange: function (v) { set({ groups: updateGroup(a.groups, gIdx, 'title', v) }); }
104 }),
105 el('div', { style: { display: 'flex', gap: '4px' } },
106 el(Button, {
107 isSmall: true, icon: 'plus-alt2',
108 onClick: function () { set({ groups: addSpec(a.groups, gIdx) }); }
109 }),
110 el(Button, {
111 isSmall: true, isDestructive: true, icon: 'trash',
112 disabled: a.groups.length <= 1,
113 onClick: function () { set({ groups: a.groups.filter(function (_, i) { return i !== gIdx; }) }); }
114 })
115 )
116 ),
117 el('div', { style: { padding: '8px 12px', display: 'flex', flexDirection: 'column', gap: '6px' } },
118 group.specs.map(function (spec, sIdx) {
119 return el('div', { key: 's-' + sIdx, style: { display: 'flex', gap: '6px', alignItems: 'center' } },
120 el(TextControl, {
121 label: '', value: spec.key, __nextHasNoMarginBottom: true,
122 placeholder: __('Key', 'blockenberg'),
123 onChange: function (v) { set({ groups: updateSpec(a.groups, gIdx, sIdx, 'key', v) }); }
124 }),
125 el(TextControl, {
126 label: '', value: spec.value, __nextHasNoMarginBottom: true,
127 placeholder: __('Value', 'blockenberg'),
128 onChange: function (v) { set({ groups: updateSpec(a.groups, gIdx, sIdx, 'value', v) }); }
129 }),
130 el(Button, {
131 isSmall: true, isDestructive: true, icon: 'trash',
132 disabled: group.specs.length <= 1,
133 onClick: function () { set({ groups: removeSpec(a.groups, gIdx, sIdx) }); }
134 })
135 );
136 })
137 )
138 );
139 }),
140 el(Button, {
141 variant: 'secondary', isSmall: true, icon: 'plus-alt2',
142 onClick: function () {
143 set({ groups: a.groups.concat([{ title: 'New Group', specs: [{ key: 'Spec', value: 'Value', highlight: false }] }]) });
144 }
145 }, __('Add Group', 'blockenberg'))
146 ),
147
148 // Display
149 el(PanelBody, { title: __('Display', 'blockenberg'), initialOpen: false },
150 el(SelectControl, {
151 label: __('Layout', 'blockenberg'), value: a.layout, options: layoutOptions, __nextHasNoMarginBottom: true,
152 onChange: function (v) { set({ layout: v }); }
153 }),
154 el(SelectControl, {
155 label: __('Style', 'blockenberg'), value: a.style, options: styleOptions, __nextHasNoMarginBottom: true,
156 onChange: function (v) { set({ style: v }); }
157 }),
158 el(ToggleControl, {
159 label: __('Show group titles', 'blockenberg'), checked: a.showGroupTitles, __nextHasNoMarginBottom: true,
160 onChange: function (v) { set({ showGroupTitles: v }); }
161 }),
162 el(ToggleControl, {
163 label: __('Highlight starred rows', 'blockenberg'), checked: a.showHighlight, __nextHasNoMarginBottom: true,
164 onChange: function (v) { set({ showHighlight: v }); }
165 }),
166 el(RangeControl, {
167 label: __('Border radius', 'blockenberg'), value: a.borderRadius, min: 0, max: 24, __nextHasNoMarginBottom: true,
168 onChange: function (v) { set({ borderRadius: v }); }
169 })
170 ),
171
172 // Typography
173 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
174 TC && el(TC, { label: __('Product Header', 'blockenberg'), typo: a.headerTypo || {}, onChange: function (v) { set({ headerTypo: v }); } }),
175 TC && el(TC, { label: __('Group Title', 'blockenberg'), typo: a.groupTitleTypo || {}, onChange: function (v) { set({ groupTitleTypo: v }); } }),
176 TC && el(TC, { label: __('Row Text', 'blockenberg'), typo: a.rowTypo || {}, onChange: function (v) { set({ rowTypo: v }); } })
177 ),
178
179 // Colors
180 el(PanelBody, { title: __('Colors', 'blockenberg'), initialOpen: false },
181 el(PanelColorSettings, {
182 title: __('Table Colors', 'blockenberg'), initialOpen: false,
183 colorSettings: [
184 { value: a.bgColor, label: __('Background', 'blockenberg'), onChange: function (c) { set({ bgColor: c }); } },
185 { value: a.borderColor, label: __('Borders', 'blockenberg'), onChange: function (c) { set({ borderColor: c }); } },
186 { value: a.keyColor, label: __('Key text', 'blockenberg'), onChange: function (c) { set({ keyColor: c }); } },
187 { value: a.valueColor, label: __('Value text', 'blockenberg'), onChange: function (c) { set({ valueColor: c }); } },
188 { value: a.stripeBg, label: __('Stripe row bg', 'blockenberg'), onChange: function (c) { set({ stripeBg: c }); } },
189 { value: a.accentColor, label: __('Accent', 'blockenberg'), onChange: function (c) { set({ accentColor: c }); } }
190 ]
191 }),
192 el(PanelColorSettings, {
193 title: __('Header & Group Colors', 'blockenberg'), initialOpen: false,
194 colorSettings: [
195 { value: a.headerBg, label: __('Header background', 'blockenberg'), onChange: function (c) { set({ headerBg: c }); } },
196 { value: a.headerColor, label: __('Header text', 'blockenberg'), onChange: function (c) { set({ headerColor: c }); } },
197 { value: a.groupTitleBg, label: __('Group title bg', 'blockenberg'), onChange: function (c) { set({ groupTitleBg: c }); } },
198 { value: a.groupTitleColor, label: __('Group title text', 'blockenberg'), onChange: function (c) { set({ groupTitleColor: c }); } }
199 ]
200 }),
201 el(PanelColorSettings, {
202 title: __('Highlight Colors', 'blockenberg'), initialOpen: false,
203 colorSettings: [
204 { value: a.highlightBg, label: __('Highlight background', 'blockenberg'), onChange: function (c) { set({ highlightBg: c }); } },
205 { value: a.highlightColor, label: __('Highlight text', 'blockenberg'), onChange: function (c) { set({ highlightColor: c }); } }
206 ]
207 })
208 )
209 );
210
211 // ── Preview ──────────────────────────────────────────────
212 var isCards = a.layout === 'cards';
213 var isCompact = a.layout === 'compact';
214
215 var tablePreview = el('div', {
216 style: {
217 border: '1px solid ' + a.borderColor,
218 borderRadius: a.borderRadius + 'px',
219 overflow: 'hidden'
220 }
221 },
222 a.showHeader && el('div', {
223 style: {
224 background: a.headerBg,
225 padding: '20px 24px',
226 color: a.headerColor
227 }
228 },
229 el('div', { className: 'bkbg-ps-product-name', style: { color: a.headerColor } }, a.productName),
230 a.productTagline && el('div', { style: { fontSize: '14px', opacity: 0.75, marginTop: '4px' } }, a.productTagline)
231 ),
232
233 isCards
234 ? el('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '16px', padding: '16px' } },
235 a.groups.map(function (group, gIdx) {
236 return el('div', { key: 'gc-' + gIdx, style: { border: '1px solid ' + a.borderColor, borderRadius: '8px', overflow: 'hidden' } },
237 a.showGroupTitles && el('div', {
238 className: 'bkbg-ps-group-title',
239 style: { background: a.groupTitleBg, padding: '8px 14px', color: a.groupTitleColor, borderBottom: '1px solid ' + a.borderColor }
240 }, group.title),
241 el('div', { style: { padding: '0' } },
242 group.specs.map(function (spec, sIdx) {
243 var isHighlighted = a.showHighlight && spec.highlight;
244 var isStripe = a.style === 'striped' && sIdx % 2 === 1;
245 return el('div', {
246 key: 'sc-' + sIdx,
247 style: {
248 display: 'flex', justifyContent: 'space-between', alignItems: 'center',
249 padding: '8px 14px',
250 background: isHighlighted ? a.highlightBg : isStripe ? a.stripeBg : a.bgColor,
251 borderBottom: '1px solid ' + a.borderColor
252 }
253 },
254 el('span', { className: 'bkbg-ps-key', style: { color: isHighlighted ? a.highlightColor : a.keyColor } }, spec.key),
255 el('span', { className: 'bkbg-ps-value', style: { color: isHighlighted ? a.highlightColor : a.valueColor, textAlign: 'right', maxWidth: '55%' } }, spec.value)
256 );
257 })
258 )
259 );
260 })
261 )
262 : el('table', { style: { width: '100%', borderCollapse: 'collapse' } },
263 a.groups.map(function (group, gIdx) {
264 return el(Fragment, { key: 'gf-' + gIdx },
265 a.showGroupTitles && el('tr', {},
266 el('td', {
267 className: 'bkbg-ps-group-title',
268 colSpan: isCompact ? 4 : 2,
269 style: {
270 background: a.groupTitleBg, padding: '8px 16px',
271 color: a.groupTitleColor,
272 borderBottom: '1px solid ' + a.borderColor
273 }
274 }, group.title)
275 ),
276 group.specs.map(function (spec, sIdx) {
277 var isHighlighted = a.showHighlight && spec.highlight;
278 var isStripe = a.style === 'striped' && sIdx % 2 === 1;
279 return el('tr', { key: 'sr-' + sIdx },
280 el('td', {
281 className: 'bkbg-ps-key',
282 style: {
283 padding: '10px 16px',
284 color: isHighlighted ? a.highlightColor : a.keyColor,
285 background: isHighlighted ? a.highlightBg : isStripe ? a.stripeBg : a.bgColor,
286 borderBottom: '1px solid ' + a.borderColor
287 }
288 }, spec.key),
289 el('td', {
290 className: 'bkbg-ps-value',
291 style: {
292 padding: '10px 16px',
293 color: isHighlighted ? a.highlightColor : a.valueColor,
294 background: isHighlighted ? a.highlightBg : isStripe ? a.stripeBg : a.bgColor,
295 borderBottom: '1px solid ' + a.borderColor
296 }
297 }, spec.value)
298 );
299 })
300 );
301 })
302 )
303 );
304
305 var blockProps = useBlockProps((function () {
306 var fn = getTypoCssVars();
307 var s = {};
308 if (fn) {
309 Object.assign(s, fn(a.headerTypo || {}, '--bkbg-pspec-hd-'));
310 Object.assign(s, fn(a.groupTitleTypo || {}, '--bkbg-pspec-gt-'));
311 Object.assign(s, fn(a.rowTypo || {}, '--bkbg-pspec-rw-'));
312 }
313 return { className: 'bkbg-editor-wrap', 'data-block-label': 'Product Spec Sheet', style: s };
314 })());
315
316 return el('div', blockProps, inspector, tablePreview);
317 },
318
319 save: function (props) {
320 var useBlockProps = wp.blockEditor.useBlockProps;
321 return el('div', useBlockProps.save(),
322 el('div', {
323 className: 'bkbg-product-spec-app',
324 'data-opts': JSON.stringify(props.attributes)
325 })
326 );
327 }
328 });
329 }() );
330