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 / price-list / index.js

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

498 lines 27.5 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 useState = wp.element.useState;
6 var registerBlockType = wp.blocks.registerBlockType;
7 var InspectorControls = wp.blockEditor.InspectorControls;
8 var useBlockProps = wp.blockEditor.useBlockProps;
9 var MediaUpload = wp.blockEditor.MediaUpload;
10 var PanelBody = wp.components.PanelBody;
11 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
12 var Button = wp.components.Button;
13 var ToggleControl = wp.components.ToggleControl;
14 var RangeControl = wp.components.RangeControl;
15 var SelectControl = wp.components.SelectControl;
16 var TextControl = wp.components.TextControl;
17
18 var _tc; function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
19 var _tv; function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
20
21 function uid() {
22 return Math.random().toString(36).slice(2, 8);
23 }
24
25 function defaultItem() {
26 return { id: uid(), name: 'New Item', description: '', price: '$0.00', badge: '', imageUrl: '', imageId: 0 };
27 }
28
29 function defaultSection() {
30 return { id: uid(), title: 'New Section', items: [defaultItem()] };
31 }
32
33 registerBlockType('blockenberg/price-list', {
34 title: __('Price List', 'blockenberg'),
35 description: __('Restaurant menu / service pricing with category sections.', 'blockenberg'),
36 category: 'bkbg-marketing',
37 icon: el('svg', { viewBox: '0 0 24 24', xmlns: 'http://www.w3.org/2000/svg' },
38 el('path', { d: 'M4 6h16v2H4V6zm0 5h16v2H4v-2zm0 5h16v2H4v-2z' })
39 ),
40 attributes: {
41 sections: { type: 'array', default: [] },
42 layout: { type: 'string', default: 'list' },
43 columns: { type: 'number', default: 2 },
44 showImage: { type: 'boolean', default: false },
45 showBadge: { type: 'boolean', default: true },
46 imageSize: { type: 'number', default: 72 },
47 imageRadius: { type: 'number', default: 8 },
48 sectionSpacing: { type: 'number', default: 40 },
49 itemSpacing: { type: 'number', default: 16 },
50 dividerEnabled: { type: 'boolean', default: true },
51 sectionTypo: { type: 'object', default: {} },
52 nameTypo: { type: 'object', default: {} },
53 descTypo: { type: 'object', default: {} },
54 priceTypo: { type: 'object', default: {} },
55 sectionColor: { type: 'string', default: '#111827' },
56 nameColor: { type: 'string', default: '#111827' },
57 descColor: { type: 'string', default: '#6b7280' },
58 priceColor: { type: 'string', default: '#2563eb' },
59 dividerColor: { type: 'string', default: '#f3f4f6' },
60 badgeBg: { type: 'string', default: '#fef3c7' },
61 badgeColor: { type: 'string', default: '#92400e' },
62 bgColor: { type: 'string', default: '' }
63 },
64
65 edit: function (props) {
66 var a = props.attributes;
67 var setAttr = props.setAttributes;
68
69 /* default sections on first render */
70 var sections = a.sections.length ? a.sections : [];
71
72 /* ── helpers ── */
73 function updateSection(si, field, val) {
74 var next = sections.map(function (s, i) {
75 return i === si ? Object.assign({}, s, { [field]: val }) : s;
76 });
77 setAttr({ sections: next });
78 }
79 function addSection() {
80 setAttr({ sections: sections.concat(defaultSection()) });
81 }
82 function removeSection(si) {
83 setAttr({ sections: sections.filter(function (_, i) { return i !== si; }) });
84 }
85 function moveSection(si, dir) {
86 var arr = sections.slice();
87 var target = si + dir;
88 if (target < 0 || target >= arr.length) return;
89 var tmp = arr[si]; arr[si] = arr[target]; arr[target] = tmp;
90 setAttr({ sections: arr });
91 }
92 function updateItem(si, ii, field, val) {
93 var next = sections.map(function (s, i) {
94 if (i !== si) return s;
95 return Object.assign({}, s, {
96 items: s.items.map(function (it, j) {
97 return j === ii ? Object.assign({}, it, { [field]: val }) : it;
98 })
99 });
100 });
101 setAttr({ sections: next });
102 }
103 function addItem(si) {
104 var next = sections.map(function (s, i) {
105 return i === si ? Object.assign({}, s, { items: s.items.concat(defaultItem()) }) : s;
106 });
107 setAttr({ sections: next });
108 }
109 function removeItem(si, ii) {
110 var next = sections.map(function (s, i) {
111 return i === si ? Object.assign({}, s, { items: s.items.filter(function (_, j) { return j !== ii; }) }) : s;
112 });
113 setAttr({ sections: next });
114 }
115
116 /* --- open/close state for sections --- */
117 var _open = useState({});
118 var openMap = _open[0]; var setOpenMap = _open[1];
119 function toggleOpen(id) {
120 setOpenMap(function (prev) {
121 var n = Object.assign({}, prev);
122 n[id] = !n[id];
123 return n;
124 });
125 }
126
127 var blockProps = useBlockProps((function () {
128 var _tvFn = getTypoCssVars();
129 var s = {
130 '--bkbg-pl-sec-gap': a.sectionSpacing + 'px',
131 '--bkbg-pl-item-gap': a.itemSpacing + 'px',
132 '--bkbg-pl-img-size': a.imageSize + 'px',
133 '--bkbg-pl-img-radius': a.imageRadius + 'px',
134 '--bkbg-pl-sec-color': a.sectionColor,
135 '--bkbg-pl-name-color': a.nameColor,
136 '--bkbg-pl-desc-color': a.descColor,
137 '--bkbg-pl-price-color': a.priceColor,
138 '--bkbg-pl-divider': a.dividerColor,
139 '--bkbg-pl-badge-bg': a.badgeBg,
140 '--bkbg-pl-badge-color': a.badgeColor,
141 '--bkbg-pl-cols': a.columns,
142 background: a.bgColor || undefined
143 };
144 if (_tvFn) {
145 Object.assign(s, _tvFn(a.sectionTypo, '--bkbg-pl-sec-'));
146 Object.assign(s, _tvFn(a.nameTypo, '--bkbg-pl-nm-'));
147 Object.assign(s, _tvFn(a.descTypo, '--bkbg-pl-ds-'));
148 Object.assign(s, _tvFn(a.priceTypo, '--bkbg-pl-pr-'));
149 }
150 return { style: s };
151 })());
152
153 var inspector = el(InspectorControls, null,
154
155 el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: true },
156 el(SelectControl, {
157 label: __('List style', 'blockenberg'),
158 value: a.layout,
159 options: [
160 { label: 'Vertical list', value: 'list' },
161 { label: 'Two-column grid', value: 'grid' },
162 { label: 'Horizontal card', value: 'horizontal' }
163 ],
164 onChange: function (v) { setAttr({ layout: v }); }
165 }),
166 a.layout === 'grid' && el(RangeControl, {
167 label: __('Columns', 'blockenberg'),
168 value: a.columns, min: 2, max: 4,
169 onChange: function (v) { setAttr({ columns: v }); }
170 }),
171 el(RangeControl, {
172 label: __('Section spacing (px)', 'blockenberg'),
173 value: a.sectionSpacing, min: 16, max: 80,
174 onChange: function (v) { setAttr({ sectionSpacing: v }); }
175 }),
176 el(RangeControl, {
177 label: __('Item spacing (px)', 'blockenberg'),
178 value: a.itemSpacing, min: 8, max: 48,
179 onChange: function (v) { setAttr({ itemSpacing: v }); }
180 }),
181 el(ToggleControl, {
182 label: __('Show dividers between items', 'blockenberg'),
183 checked: a.dividerEnabled,
184 onChange: function (v) { setAttr({ dividerEnabled: v }); },
185 __nextHasNoMarginBottom: true
186 }),
187 el(ToggleControl, {
188 label: __('Show badges', 'blockenberg'),
189 checked: a.showBadge,
190 onChange: function (v) { setAttr({ showBadge: v }); },
191 __nextHasNoMarginBottom: true
192 }),
193 el(ToggleControl, {
194 label: __('Show images', 'blockenberg'),
195 checked: a.showImage,
196 onChange: function (v) { setAttr({ showImage: v }); },
197 __nextHasNoMarginBottom: true
198 }),
199 a.showImage && el(Fragment, null,
200 el(RangeControl, {
201 label: __('Image size (px)', 'blockenberg'),
202 value: a.imageSize, min: 40, max: 200,
203 onChange: function (v) { setAttr({ imageSize: v }); }
204 }),
205 el(RangeControl, {
206 label: __('Image radius (px)', 'blockenberg'),
207 value: a.imageRadius, min: 0, max: 50,
208 onChange: function (v) { setAttr({ imageRadius: v }); }
209 })
210 )
211 ),
212
213 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
214 (function () {
215 var TC = getTypoControl();
216 if (!TC) return el('p', null, 'Loading…');
217 return el(Fragment, null,
218 el(TC, { label: __('Section Title', 'blockenberg'), value: a.sectionTypo, onChange: function (v) { setAttr({ sectionTypo: v }); } }),
219 el(TC, { label: __('Item Name', 'blockenberg'), value: a.nameTypo, onChange: function (v) { setAttr({ nameTypo: v }); } }),
220 el(TC, { label: __('Description', 'blockenberg'), value: a.descTypo, onChange: function (v) { setAttr({ descTypo: v }); } }),
221 el(TC, { label: __('Price', 'blockenberg'), value: a.priceTypo, onChange: function (v) { setAttr({ priceTypo: v }); } })
222 );
223 })()
224 ),
225
226 el(PanelBody, { title: __('Colors', 'blockenberg'), initialOpen: false },
227 el(PanelColorSettings, {
228 title: __('Colors', 'blockenberg'),
229 initialOpen: false,
230 colorSettings: [
231 { label: __('Background', 'blockenberg'), value: a.bgColor, onChange: function(v){ setAttr({ bgColor: v||'' }); } },
232 { label: __('Section title', 'blockenberg'), value: a.sectionColor, onChange: function(v){ setAttr({ sectionColor: v||'#111827' }); } },
233 { label: __('Item name', 'blockenberg'), value: a.nameColor, onChange: function(v){ setAttr({ nameColor: v||'#111827' }); } },
234 { label: __('Description', 'blockenberg'), value: a.descColor, onChange: function(v){ setAttr({ descColor: v||'#6b7280' }); } },
235 { label: __('Price', 'blockenberg'), value: a.priceColor, onChange: function(v){ setAttr({ priceColor: v||'#2563eb' }); } },
236 { label: __('Divider', 'blockenberg'), value: a.dividerColor, onChange: function(v){ setAttr({ dividerColor: v||'#f3f4f6' }); } },
237 { label: __('Badge background', 'blockenberg'), value: a.badgeBg, onChange: function(v){ setAttr({ badgeBg: v||'#fef3c7' }); } },
238 { label: __('Badge text', 'blockenberg'), value: a.badgeColor, onChange: function(v){ setAttr({ badgeColor: v||'#92400e' }); } }
239 ]
240 })
241 )
242 );
243
244 /* ── Editor: sections preview ── */
245 var editorSections = sections.map(function (sec, si) {
246 var isOpen = openMap[sec.id] !== false; /* default open */
247 return el('div', { className: 'bkbg-pl-section', key: sec.id },
248
249 /* section header */
250 el('div', { className: 'bkbg-pl-sec-hd' },
251 el('h3', { className: 'bkbg-pl-sec-title', style: { flex: 1 } }, sec.title),
252 el('div', { className: 'bkbg-pl-sec-actions' },
253 si > 0 && el(Button, {
254 variant: 'tertiary', isSmall: true,
255 onClick: function () { moveSection(si, -1); }
256 }, ''),
257 si < sections.length - 1 && el(Button, {
258 variant: 'tertiary', isSmall: true,
259 onClick: function () { moveSection(si, 1); }
260 }, ''),
261 el(Button, {
262 variant: 'tertiary', isSmall: true,
263 onClick: function () { toggleOpen(sec.id); }
264 }, isOpen ? '' : ''),
265 el(Button, {
266 variant: 'tertiary', isSmall: true, isDestructive: true,
267 onClick: function () { removeSection(si); }
268 }, '')
269 )
270 ),
271
272 isOpen && el(Fragment, null,
273 el(TextControl, {
274 label: __('Section Title', 'blockenberg'),
275 value: sec.title,
276 onChange: function (v) { updateSection(si, 'title', v); }
277 }),
278
279 /* items */
280 el('div', { className: 'bkbg-pl-items-' + a.layout },
281 sec.items.map(function (item, ii) {
282 return el('div', { className: 'bkbg-pl-item bkbg-pl-item--editor', key: item.id },
283 el('div', { className: 'bkbg-pl-item-row' },
284
285 /* image */
286 a.showImage && el(MediaUpload, {
287 onSelect: function (m) { updateItem(si, ii, 'imageUrl', m.url); updateItem(si, ii, 'imageId', m.id); },
288 type: 'image', value: item.imageId,
289 render: function (p) {
290 return item.imageUrl
291 ? el('img', { src: item.imageUrl, className: 'bkbg-pl-item-img', onClick: p.open, style: { cursor: 'pointer' } })
292 : el(Button, { variant: 'secondary', isSmall: true, onClick: p.open }, '+IMG');
293 }
294 }),
295
296 el('div', { style: { flex: 1 } },
297 el(TextControl, {
298 label: __('Name', 'blockenberg'),
299 value: item.name,
300 onChange: function (v) { updateItem(si, ii, 'name', v); }
301 }),
302 el(TextControl, {
303 label: __('Description', 'blockenberg'),
304 value: item.description,
305 onChange: function (v) { updateItem(si, ii, 'description', v); }
306 }),
307 el('div', { style: { display: 'flex', gap: 8 } },
308 el(TextControl, {
309 label: __('Price', 'blockenberg'),
310 value: item.price,
311 onChange: function (v) { updateItem(si, ii, 'price', v); },
312 style: { flex: 1 }
313 }),
314 a.showBadge && el(TextControl, {
315 label: __('Badge', 'blockenberg'),
316 value: item.badge,
317 onChange: function (v) { updateItem(si, ii, 'badge', v); },
318 style: { flex: 1 }
319 })
320 )
321 ),
322
323 el(Button, {
324 variant: 'tertiary', isSmall: true, isDestructive: true,
325 onClick: function () { removeItem(si, ii); }
326 }, '')
327 )
328 );
329 })
330 ),
331
332 el(Button, {
333 variant: 'secondary', isSmall: true,
334 onClick: function () { addItem(si); },
335 style: { marginTop: 8 }
336 }, __('+ Add Item', 'blockenberg'))
337 )
338 );
339 });
340
341 return el(Fragment, null,
342 inspector,
343 el('div', blockProps,
344 el('div', { className: 'bkbg-pl-wrap' },
345 editorSections,
346 el(Button, {
347 variant: 'primary', isSmall: true,
348 onClick: addSection,
349 style: { marginTop: 16 }
350 }, __('+ Add Section', 'blockenberg'))
351 )
352 )
353 );
354 },
355
356 deprecated: [{
357 attributes: {
358 sections: { type: 'array', default: [] },
359 layout: { type: 'string', default: 'list' },
360 columns: { type: 'number', default: 2 },
361 showImage: { type: 'boolean', default: false },
362 showBadge: { type: 'boolean', default: true },
363 imageSize: { type: 'number', default: 72 },
364 imageRadius: { type: 'number', default: 8 },
365 sectionSpacing: { type: 'number', default: 40 },
366 itemSpacing: { type: 'number', default: 16 },
367 dividerEnabled: { type: 'boolean', default: true },
368 sectionFontSize: { type: 'number', default: 20 },
369 sectionFontWeight:{ type: 'number', default: 700 },
370 nameFontSize: { type: 'number', default: 16 },
371 nameFontWeight: { type: 'number', default: 600 },
372 descFontSize: { type: 'number', default: 14 },
373 priceFontSize: { type: 'number', default: 16 },
374 priceFontWeight: { type: 'number', default: 700 },
375 sectionColor: { type: 'string', default: '#111827' },
376 nameColor: { type: 'string', default: '#111827' },
377 descColor: { type: 'string', default: '#6b7280' },
378 priceColor: { type: 'string', default: '#2563eb' },
379 dividerColor: { type: 'string', default: '#f3f4f6' },
380 badgeBg: { type: 'string', default: '#fef3c7' },
381 badgeColor: { type: 'string', default: '#92400e' },
382 bgColor: { type: 'string', default: '' }
383 },
384 save: function (props) {
385 var a = props.attributes;
386 var blockProps = wp.blockEditor.useBlockProps.save({
387 style: {
388 '--bkbg-pl-sec-gap': a.sectionSpacing + 'px',
389 '--bkbg-pl-item-gap': a.itemSpacing + 'px',
390 '--bkbg-pl-img-size': a.imageSize + 'px',
391 '--bkbg-pl-img-radius': a.imageRadius + 'px',
392 '--bkbg-pl-sec-size': a.sectionFontSize + 'px',
393 '--bkbg-pl-sec-weight': a.sectionFontWeight,
394 '--bkbg-pl-name-size': a.nameFontSize + 'px',
395 '--bkbg-pl-name-weight': a.nameFontWeight,
396 '--bkbg-pl-desc-size': a.descFontSize + 'px',
397 '--bkbg-pl-price-size': a.priceFontSize + 'px',
398 '--bkbg-pl-price-weight':a.priceFontWeight,
399 '--bkbg-pl-sec-color': a.sectionColor,
400 '--bkbg-pl-name-color': a.nameColor,
401 '--bkbg-pl-desc-color': a.descColor,
402 '--bkbg-pl-price-color': a.priceColor,
403 '--bkbg-pl-divider': a.dividerColor,
404 '--bkbg-pl-badge-bg': a.badgeBg,
405 '--bkbg-pl-badge-color': a.badgeColor,
406 '--bkbg-pl-cols': a.columns,
407 background: a.bgColor || undefined
408 }
409 });
410 return el('div', blockProps,
411 el('div', { className: 'bkbg-pl-wrap' },
412 a.sections.map(function (sec) {
413 return el('div', { className: 'bkbg-pl-section', key: sec.id },
414 el('h3', { className: 'bkbg-pl-sec-title' }, sec.title),
415 el('div', { className: 'bkbg-pl-items-' + a.layout + (a.dividerEnabled ? ' bkbg-pl-dividers' : '') },
416 sec.items.map(function (item) {
417 return el('div', { className: 'bkbg-pl-item', key: item.id },
418 a.showImage && item.imageUrl && el('img', {
419 className: 'bkbg-pl-item-img',
420 src: item.imageUrl, alt: item.name, loading: 'lazy'
421 }),
422 el('div', { className: 'bkbg-pl-item-body' },
423 el('div', { className: 'bkbg-pl-item-top' },
424 el('span', { className: 'bkbg-pl-item-name' }, item.name),
425 el('span', { className: 'bkbg-pl-item-dots' }),
426 el('span', { className: 'bkbg-pl-item-price' }, item.price)
427 ),
428 item.description && el('p', { className: 'bkbg-pl-item-desc' }, item.description),
429 a.showBadge && item.badge && el('span', { className: 'bkbg-pl-badge' }, item.badge)
430 )
431 );
432 })
433 )
434 );
435 })
436 )
437 );
438 }
439 }],
440
441 save: function (props) {
442 var a = props.attributes;
443 var _tvFn = getTypoCssVars();
444 var s = {
445 '--bkbg-pl-sec-gap': a.sectionSpacing + 'px',
446 '--bkbg-pl-item-gap': a.itemSpacing + 'px',
447 '--bkbg-pl-img-size': a.imageSize + 'px',
448 '--bkbg-pl-img-radius': a.imageRadius + 'px',
449 '--bkbg-pl-sec-color': a.sectionColor,
450 '--bkbg-pl-name-color': a.nameColor,
451 '--bkbg-pl-desc-color': a.descColor,
452 '--bkbg-pl-price-color': a.priceColor,
453 '--bkbg-pl-divider': a.dividerColor,
454 '--bkbg-pl-badge-bg': a.badgeBg,
455 '--bkbg-pl-badge-color': a.badgeColor,
456 '--bkbg-pl-cols': a.columns,
457 background: a.bgColor || undefined
458 };
459 if (_tvFn) {
460 Object.assign(s, _tvFn(a.sectionTypo, '--bkbg-pl-sec-'));
461 Object.assign(s, _tvFn(a.nameTypo, '--bkbg-pl-nm-'));
462 Object.assign(s, _tvFn(a.descTypo, '--bkbg-pl-ds-'));
463 Object.assign(s, _tvFn(a.priceTypo, '--bkbg-pl-pr-'));
464 }
465 var blockProps = wp.blockEditor.useBlockProps.save({ style: s });
466
467 return el('div', blockProps,
468 el('div', { className: 'bkbg-pl-wrap' },
469 a.sections.map(function (sec) {
470 return el('div', { className: 'bkbg-pl-section', key: sec.id },
471 el('h3', { className: 'bkbg-pl-sec-title' }, sec.title),
472 el('div', { className: 'bkbg-pl-items-' + a.layout + (a.dividerEnabled ? ' bkbg-pl-dividers' : '') },
473 sec.items.map(function (item) {
474 return el('div', { className: 'bkbg-pl-item', key: item.id },
475 a.showImage && item.imageUrl && el('img', {
476 className: 'bkbg-pl-item-img',
477 src: item.imageUrl, alt: item.name, loading: 'lazy'
478 }),
479 el('div', { className: 'bkbg-pl-item-body' },
480 el('div', { className: 'bkbg-pl-item-top' },
481 el('span', { className: 'bkbg-pl-item-name' }, item.name),
482 el('span', { className: 'bkbg-pl-item-dots' }),
483 el('span', { className: 'bkbg-pl-item-price' }, item.price)
484 ),
485 item.description && el('p', { className: 'bkbg-pl-item-desc' }, item.description),
486 a.showBadge && item.badge && el('span', { className: 'bkbg-pl-badge' }, item.badge)
487 )
488 );
489 })
490 )
491 );
492 })
493 )
494 );
495 }
496 });
497 }() );
498