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 / restaurant-menu / index.js

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

436 lines 32.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 useState = wp.element.useState;
5 var __ = wp.i18n.__;
6 var registerBlockType = wp.blocks.registerBlockType;
7 var InspectorControls = wp.blockEditor.InspectorControls;
8 var RichText = wp.blockEditor.RichText;
9 var useBlockProps = wp.blockEditor.useBlockProps;
10 var PanelBody = wp.components.PanelBody;
11 var Button = wp.components.Button;
12 var ToggleControl = wp.components.ToggleControl;
13 var RangeControl = wp.components.RangeControl;
14 var SelectControl = wp.components.SelectControl;
15 var TextControl = wp.components.TextControl;
16 var TextareaControl = wp.components.TextareaControl;
17 var ColorPicker = wp.components.ColorPicker;
18 var Popover = wp.components.Popover;
19
20 var _tc; function getTypoControl(){ return _tc || (_tc = (window.bkbgTypographyControl || function(){return null})); }
21 var _tv; function getTypoCssVars(){ return _tv || (_tv = (window.bkbgTypoCssVars || function(){return {}})); }
22 var IP = function () { return window.bkbgIconPicker; };
23
24 var LAYOUT_OPTIONS = [
25 { label: __('Category tabs', 'blockenberg'), value: 'tabs' },
26 { label: __('Sections (scroll)', 'blockenberg'), value: 'sections' },
27 ];
28 var CURRENCY_POS_OPTIONS = [
29 { label: __('Before price ($9)', 'blockenberg'), value: 'before' },
30 { label: __('After price (9$)', 'blockenberg'), value: 'after' },
31 ];
32 var ITEM_STYLE_OPTIONS = [
33 { label: __('List', 'blockenberg'), value: 'list' },
34 { label: __('Cards', 'blockenberg'), value: 'card' },
35 ];
36 var SEPARATOR_OPTIONS = [
37 { label: __('Line', 'blockenberg'), value: 'line' },
38 { label: __('Dots', 'blockenberg'), value: 'dots' },
39 { label: __('None', 'blockenberg'), value: 'none' },
40 ];
41 var BADGE_LABELS = { V: 'V', VG: 'VG', GF: 'GF', DF: 'DF', HOT: '🌶', NEW: 'NEW' };
42 var ALL_BADGES = ['V', 'VG', 'GF', 'DF', 'HOT', 'NEW'];
43
44 function makeId() { return 'rm' + Math.random().toString(36).substr(2, 5); }
45
46 function buildWrapStyle(a) {
47 return {
48 '--bkbg-rm-accent': a.accentColor,
49 '--bkbg-rm-active-tab-bg': a.activeTabBg,
50 '--bkbg-rm-active-tab-color': a.activeTabColor,
51 '--bkbg-rm-inactive-tab-bg': a.inactiveTabBg,
52 '--bkbg-rm-inactive-tab-color': a.inactiveTabColor,
53 '--bkbg-rm-item-name-color': a.itemNameColor,
54 '--bkbg-rm-desc-color': a.descColor,
55 '--bkbg-rm-price-color': a.priceColor,
56 '--bkbg-rm-card-bg': a.cardBg,
57 '--bkbg-rm-card-border': a.cardBorderColor,
58 '--bkbg-rm-separator-color': a.separatorColor,
59 '--bkbg-rm-cat-name-sz': a.categoryNameSize + 'px',
60 '--bkbg-rm-item-name-sz': a.itemNameSize + 'px',
61 '--bkbg-rm-desc-sz': a.descSize + 'px',
62 '--bkbg-rm-price-sz': a.priceSize + 'px',
63 '--bkbg-rm-badge-sz': a.badgeSize + 'px',
64 '--bkbg-rm-card-r': a.cardRadius + 'px',
65 '--bkbg-rm-card-pad': a.cardPadding + 'px',
66 '--bkbg-rm-badge-r': a.badgeRadius + 'px',
67 paddingTop: a.paddingTop + 'px',
68 paddingBottom: a.paddingBottom + 'px',
69 backgroundColor: a.bgColor || undefined,
70 };
71 }
72
73 function renderColorControl(key, label, value, onChange, openKey, setOpenKey) {
74 var isOpen = openKey === key;
75 return el('div', { key: key, style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '4px 0', gap: '8px' } },
76 el('span', { style: { fontSize: '12px', color: '#1e1e1e', flex: 1, lineHeight: 1.4 } }, label),
77 el('div', { style: { position: 'relative', flexShrink: 0 } },
78 el('button', { type: 'button', title: value || 'default', onClick: function () { setOpenKey(isOpen ? null : key); },
79 style: { width: '28px', height: '28px', borderRadius: '4px', border: isOpen ? '2px solid #007cba' : '2px solid #ddd', cursor: 'pointer', padding: 0, background: value || '#ccc' }
80 }),
81 isOpen && el(Popover, { position: 'bottom left', onClose: function () { setOpenKey(null); } },
82 el('div', { style: { padding: '8px' } },
83 el(ColorPicker, { color: value, enableAlpha: true, onChange: onChange })
84 )
85 )
86 )
87 );
88 }
89
90 /* ── Price badge ────────────────────────────────────────────────── */
91 var BADGE_COLORS = { V: '#16a34a', VG: '#059669', GF: '#ca8a04', DF: '#0ea5e9', HOT: '#ef4444', NEW: '#6c3fb5' };
92
93 function BadgeEl(props) {
94 var b = props.badge;
95 var a = props.a;
96 return el('span', { className: 'bkbg-rm-badge bkbg-rm-badge--' + b.toLowerCase(), style: { display: 'inline-block', padding: '2px 7px', borderRadius: a.badgeRadius + 'px', fontSize: a.badgeSize + 'px', fontWeight: 700, background: (BADGE_COLORS[b] || '#6c3fb5') + '1a', color: BADGE_COLORS[b] || '#6c3fb5', marginRight: '4px' } }, BADGE_LABELS[b] || b);
97 }
98
99 /* ── Menu item row ──────────────────────────────────────────────── */
100 function MenuItem(props) {
101 var item = props.item;
102 var a = props.a;
103 var isEditor = props.isEditor;
104 var onUpdate = props.onUpdate;
105 var priceStr = a.currencyPosition === 'before' ? (a.currency + item.price) : (item.price + a.currency);
106
107 var rowStyle = {
108 display: 'flex', alignItems: 'flex-start', gap: '12px',
109 padding: a.itemStyle === 'card' ? (a.cardPadding + 'px') : '14px 0',
110 background: a.itemStyle === 'card' ? a.cardBg : 'transparent',
111 borderRadius: a.itemStyle === 'card' ? (a.cardRadius + 'px') : '0',
112 border: a.itemStyle === 'card' ? ('1px solid ' + a.cardBorderColor) : 'none',
113 borderBottom: a.itemStyle === 'list' && a.separator === 'line' ? ('1px solid ' + a.separatorColor) : undefined,
114 };
115
116 return el('div', { className: 'bkbg-rm-item bkbg-rm-item--' + a.itemStyle, style: rowStyle },
117 el('div', { className: 'bkbg-rm-item-info', style: { flex: 1, minWidth: 0 } },
118 isEditor
119 ? el(RichText, { tagName: 'span', className: 'bkbg-rm-item-name', value: item.name, onChange: function (v) { onUpdate('name', v); }, placeholder: __('Item name', 'blockenberg'), style: { display: 'block', color: a.itemNameColor, marginBottom: a.showDescription ? '4px' : '0' } })
120 : el(RichText.Content, { tagName: 'span', className: 'bkbg-rm-item-name', value: item.name }),
121 a.showDescription && (isEditor
122 ? el(RichText, { tagName: 'p', className: 'bkbg-rm-item-desc', value: item.description, onChange: function (v) { onUpdate('description', v); }, placeholder: __('Description…', 'blockenberg'), style: { margin: 0, color: a.descColor } })
123 : el(RichText.Content, { tagName: 'p', className: 'bkbg-rm-item-desc', value: item.description })
124 ),
125 a.showBadges && item.badges && item.badges.length > 0 && el('div', { className: 'bkbg-rm-badges', style: { marginTop: '6px' } },
126 item.badges.map(function (b) { return el(BadgeEl, { key: b, badge: b, a: a }); })
127 )
128 ),
129 el('div', { className: 'bkbg-rm-item-price', style: { flexShrink: 0, color: a.priceColor, whiteSpace: 'nowrap' } }, priceStr)
130 );
131 }
132
133 /* ── Category section ───────────────────────────────────────────── */
134 function CategorySection(props) {
135 var cat = props.cat;
136 var a = props.a;
137 var isEditor = props.isEditor;
138 var onUpdate = props.onUpdate;
139
140 return el('div', { className: 'bkbg-rm-category-section', 'data-cat': cat.id },
141 el('div', { className: 'bkbg-rm-category-header', style: { display: 'flex', alignItems: 'center', gap: '10px', marginBottom: '20px' } },
142 a.showCategoryIcon && (cat.icon || cat.iconDashicon || cat.iconImageUrl) && el('span', { className: 'bkbg-rm-cat-icon', style: { fontSize: '28px' } }, IP().buildEditorIcon(cat.iconType || 'custom-char', cat.icon, cat.iconDashicon, cat.iconImageUrl, cat.iconDashiconColor)),
143 isEditor
144 ? el(RichText, { tagName: 'h3', className: 'bkbg-rm-cat-name', value: cat.name, onChange: function (v) { onUpdate('name', v); }, placeholder: __('Category name', 'blockenberg'), style: { color: a.itemNameColor, margin: 0 } })
145 : el(RichText.Content, { tagName: 'h3', className: 'bkbg-rm-cat-name', value: cat.name })
146 ),
147 el('div', { className: 'bkbg-rm-items', style: a.itemStyle === 'card' ? { display: 'grid', gridTemplateColumns: 'repeat(' + a.columns + ', 1fr)', gap: '12px' } : {} },
148 cat.items.map(function (item) {
149 return el(MenuItem, { key: item.id, item: item, a: a, isEditor: isEditor, onUpdate: function (key, val) {
150 if (!isEditor) return;
151 var newCat = Object.assign({}, cat, { items: cat.items.map(function (it) { if (it.id !== item.id) return it; var u = Object.assign({}, it); u[key] = val; return u; }) });
152 onUpdate(null, null, newCat);
153 } });
154 })
155 )
156 );
157 }
158
159 /* ── v1 save-only menu item (for deprecated) ──────────────────── */
160 function v1MenuItemSave(props) {
161 var item = props.item;
162 var a = props.a;
163 var priceStr = a.currencyPosition === 'before' ? (a.currency + item.price) : (item.price + a.currency);
164 var rowStyle = {
165 display: 'flex', alignItems: 'flex-start', gap: '12px',
166 padding: a.itemStyle === 'card' ? (a.cardPadding + 'px') : '14px 0',
167 background: a.itemStyle === 'card' ? a.cardBg : 'transparent',
168 borderRadius: a.itemStyle === 'card' ? (a.cardRadius + 'px') : '0',
169 border: a.itemStyle === 'card' ? ('1px solid ' + a.cardBorderColor) : 'none',
170 borderBottom: a.itemStyle === 'list' && a.separator === 'line' ? ('1px solid ' + a.separatorColor) : undefined,
171 };
172 return el('div', { className: 'bkbg-rm-item bkbg-rm-item--' + a.itemStyle, style: rowStyle },
173 el('div', { className: 'bkbg-rm-item-info', style: { flex: 1, minWidth: 0 } },
174 el(RichText.Content, { tagName: 'span', className: 'bkbg-rm-item-name', value: item.name }),
175 a.showDescription && el(RichText.Content, { tagName: 'p', className: 'bkbg-rm-item-desc', value: item.description }),
176 a.showBadges && item.badges && item.badges.length > 0 && el('div', { className: 'bkbg-rm-badges', style: { marginTop: '6px' } },
177 item.badges.map(function (b) { return el(BadgeEl, { key: b, badge: b, a: a }); })
178 )
179 ),
180 el('div', { className: 'bkbg-rm-item-price', style: { flexShrink: 0, fontSize: a.priceSize + 'px', fontWeight: 800, color: a.priceColor, whiteSpace: 'nowrap' } }, priceStr)
181 );
182 }
183
184 var v1Attributes = {
185 categories: { type: 'array', default: [] },
186 layout: { type: 'string', default: 'tabs' },
187 currency: { type: 'string', default: '$' },
188 currencyPosition:{ type: 'string', default: 'before' },
189 itemStyle: { type: 'string', default: 'list' },
190 columns: { type: 'integer', default: 1 },
191 showDescription: { type: 'boolean', default: true },
192 showBadges: { type: 'boolean', default: true },
193 showCategoryIcon:{ type: 'boolean', default: true },
194 separator: { type: 'string', default: 'line' },
195 badgeRadius: { type: 'integer', default: 4 },
196 cardRadius: { type: 'integer', default: 12 },
197 cardPadding: { type: 'integer', default: 18 },
198 categoryNameSize:{ type: 'integer', default: 13 },
199 itemNameSize: { type: 'integer', default: 16 },
200 descSize: { type: 'integer', default: 14 },
201 priceSize: { type: 'integer', default: 16 },
202 badgeSize: { type: 'integer', default: 11 },
203 accentColor: { type: 'string', default: '#6c3fb5' },
204 activeTabBg: { type: 'string', default: '#6c3fb5' },
205 activeTabColor: { type: 'string', default: '#ffffff' },
206 inactiveTabBg: { type: 'string', default: '#f3f4f6' },
207 inactiveTabColor:{ type: 'string', default: '#374151' },
208 itemNameColor: { type: 'string', default: '#111827' },
209 descColor: { type: 'string', default: '#6b7280' },
210 priceColor: { type: 'string', default: '#6c3fb5' },
211 cardBg: { type: 'string', default: '#ffffff' },
212 cardBorderColor: { type: 'string', default: '#e5e7eb' },
213 separatorColor: { type: 'string', default: '#f3f4f6' },
214 bgColor: { type: 'string', default: '' },
215 paddingTop: { type: 'integer', default: 64 },
216 paddingBottom: { type: 'integer', default: 64 }
217 };
218
219 registerBlockType('blockenberg/restaurant-menu', {
220 title: __('Restaurant Menu', 'blockenberg'),
221 icon: 'food',
222 category: 'bkbg-business',
223
224 deprecated: [{
225 attributes: v1Attributes,
226 save: function(props) {
227 var a = props.attributes;
228 return el('div', wp.blockEditor.useBlockProps.save({ className: 'bkbg-rm-wrapper bkbg-rm--' + a.layout + ' bkbg-rm-items--' + a.itemStyle, style: buildWrapStyle(a) }),
229 a.layout === 'tabs' && el('nav', { className: 'bkbg-rm-tab-bar', role: 'tablist' },
230 a.categories.map(function (cat, ci) {
231 return el('button', { key: cat.id, type: 'button', role: 'tab', className: 'bkbg-rm-tab-btn' + (ci === 0 ? ' is-active' : ''), 'data-cat': cat.id, 'aria-selected': ci === 0 ? 'true' : 'false' },
232 a.showCategoryIcon && el('span', { className: 'bkbg-rm-tab-icon', 'aria-hidden': 'true' }, cat.icon),
233 el('span', null, cat.name)
234 );
235 })
236 ),
237 el('div', { className: 'bkbg-rm-body' },
238 a.categories.map(function (cat, ci) {
239 return el('div', { key: cat.id, className: 'bkbg-rm-category-section' + (ci === 0 ? ' is-active' : ''), 'data-cat': cat.id, role: 'tabpanel', 'aria-hidden': ci === 0 ? 'false' : 'true' },
240 a.layout === 'sections' && el('div', { className: 'bkbg-rm-category-header' },
241 a.showCategoryIcon && el('span', { className: 'bkbg-rm-cat-icon', 'aria-hidden': 'true' }, cat.icon),
242 el(RichText.Content, { tagName: 'h3', className: 'bkbg-rm-cat-name', value: cat.name })
243 ),
244 el('div', { className: 'bkbg-rm-items' },
245 cat.items.map(function (item) {
246 return el(v1MenuItemSave, { key: item.id, item: item, a: a, isEditor: false });
247 })
248 )
249 );
250 })
251 )
252 );
253 }
254 }],
255
256 edit: function (props) {
257 var a = props.attributes;
258 var setAttributes = props.setAttributes;
259
260 var openColorKeyState = useState(null);
261 var openColorKey = openColorKeyState[0];
262 var setOpenColorKey = openColorKeyState[1];
263
264 var activeTabState = useState(0);
265 var activeTab = activeTabState[0];
266 var setActiveTab = activeTabState[1];
267
268 var blockProps = useBlockProps((function(){
269 var _tv = getTypoCssVars();
270 var s = buildWrapStyle(a);
271 Object.assign(s, _tv(a.catNameTypo || {}, '--bkrm-cn-'));
272 Object.assign(s, _tv(a.itemNameTypo || {}, '--bkrm-in-'));
273 Object.assign(s, _tv(a.descTypo || {}, '--bkrm-ds-'));
274 Object.assign(s, _tv(a.priceTypo || {}, '--bkrm-pr-'));
275 return { style: s };
276 })());
277
278 function cc(key, label, attrKey) {
279 return renderColorControl(key, label, a[attrKey], function (val) {
280 var upd = {}; upd[attrKey] = val; setAttributes(upd);
281 }, openColorKey, setOpenColorKey);
282 }
283
284 function updateCategory(id, key, val, newCatObj) {
285 setAttributes({ categories: a.categories.map(function (c) {
286 if (c.id !== id) return c;
287 if (newCatObj) return newCatObj;
288 var u = Object.assign({}, c); u[key] = val; return u;
289 }) });
290 }
291
292 function addItemToCategory(catId) {
293 setAttributes({ categories: a.categories.map(function (c) {
294 if (c.id !== catId) return c;
295 return Object.assign({}, c, { items: c.items.concat([{ id: makeId(), name: __('New Item', 'blockenberg'), description: __('Item description', 'blockenberg'), price: '0.00', badges: [] }]) });
296 }) });
297 }
298
299 var safeActive = Math.min(activeTab, a.categories.length - 1);
300 var currentCat = a.categories[safeActive];
301
302 /* Tab bar for tab layout */
303 var tabBar = a.layout === 'tabs' && el('div', { className: 'bkbg-rm-tab-bar', style: { display: 'flex', gap: '6px', flexWrap: 'wrap', marginBottom: '32px' } },
304 a.categories.map(function (cat, ci) {
305 var isActive = ci === safeActive;
306 return el('button', { key: cat.id, type: 'button', className: 'bkbg-rm-tab-btn' + (isActive ? ' is-active' : ''), onClick: function () { setActiveTab(ci); }, style: { display: 'flex', alignItems: 'center', gap: '7px', padding: '9px 18px', borderRadius: '99px', border: 'none', cursor: 'pointer', background: isActive ? a.activeTabBg : a.inactiveTabBg, color: isActive ? a.activeTabColor : a.inactiveTabColor, transition: 'all 0.2s' } },
307 a.showCategoryIcon && el('span', { className: 'bkbg-rm-tab-icon' }, IP().buildEditorIcon(cat.iconType || 'custom-char', cat.icon, cat.iconDashicon, cat.iconImageUrl, cat.iconDashiconColor)),
308 el('span', null, cat.name)
309 );
310 })
311 );
312
313 return el(Fragment, null,
314 el(InspectorControls, null,
315 el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: true },
316 el(SelectControl, { label: __('Display style', 'blockenberg'), value: a.layout, options: LAYOUT_OPTIONS, onChange: function (v) { setAttributes({ layout: v }); } }),
317 el(SelectControl, { label: __('Item style', 'blockenberg'), value: a.itemStyle, options: ITEM_STYLE_OPTIONS, onChange: function (v) { setAttributes({ itemStyle: v }); } }),
318 el(SelectControl, { label: __('Currency position', 'blockenberg'), value: a.currencyPosition, options: CURRENCY_POS_OPTIONS, onChange: function (v) { setAttributes({ currencyPosition: v }); } }),
319 el(TextControl, { label: __('Currency symbol', 'blockenberg'), value: a.currency, onChange: function (v) { setAttributes({ currency: v }); } }),
320 a.itemStyle === 'list' && el(SelectControl, { label: __('Item separator', 'blockenberg'), value: a.separator, options: SEPARATOR_OPTIONS, onChange: function (v) { setAttributes({ separator: v }); } }),
321 a.itemStyle === 'card' && el(RangeControl, { label: __('Card columns', 'blockenberg'), value: a.columns, min: 1, max: 3, onChange: function (v) { setAttributes({ columns: v }); } }),
322 el(RangeControl, { label: __('Card radius (px)', 'blockenberg'), value: a.cardRadius, min: 0, max: 24, onChange: function (v) { setAttributes({ cardRadius: v }); } }),
323 el(RangeControl, { label: __('Card padding (px)', 'blockenberg'), value: a.cardPadding, min: 8, max: 40, onChange: function (v) { setAttributes({ cardPadding: v }); } }),
324 el(RangeControl, { label: __('Badge radius (px)', 'blockenberg'), value: a.badgeRadius, min: 0, max: 12, onChange: function (v) { setAttributes({ badgeRadius: v }); } })
325 ),
326 el(PanelBody, { title: __('Display', 'blockenberg'), initialOpen: false },
327 el(ToggleControl, { label: __('Show category icons', 'blockenberg'), checked: a.showCategoryIcon, onChange: function (v) { setAttributes({ showCategoryIcon: v }); }, __nextHasNoMarginBottom: true }),
328 el(ToggleControl, { label: __('Show descriptions', 'blockenberg'), checked: a.showDescription, onChange: function (v) { setAttributes({ showDescription: v }); }, __nextHasNoMarginBottom: true }),
329 el(ToggleControl, { label: __('Show dietary badges', 'blockenberg'), checked: a.showBadges, onChange: function (v) { setAttributes({ showBadges: v }); }, __nextHasNoMarginBottom: true })
330 ),
331 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
332 (function(){ var TC = getTypoControl(); return TC ? el(TC, { label: __('Category Name', 'blockenberg'), value: a.catNameTypo, onChange: function(v){ setAttributes({catNameTypo:v}); } }) : null; })(),
333 (function(){ var TC = getTypoControl(); return TC ? el(TC, { label: __('Item Name', 'blockenberg'), value: a.itemNameTypo, onChange: function(v){ setAttributes({itemNameTypo:v}); } }) : null; })(),
334 (function(){ var TC = getTypoControl(); return TC ? el(TC, { label: __('Description', 'blockenberg'), value: a.descTypo, onChange: function(v){ setAttributes({descTypo:v}); } }) : null; })(),
335 (function(){ var TC = getTypoControl(); return TC ? el(TC, { label: __('Price', 'blockenberg'), value: a.priceTypo, onChange: function(v){ setAttributes({priceTypo:v}); } }) : null; })(),
336 el(RangeControl, { label: __('Badge (px)', 'blockenberg'), value: a.badgeSize, min: 9, max: 14, onChange: function (v) { setAttributes({ badgeSize: v }); } })
337 ),
338 el(PanelBody, { title: __('Colors', 'blockenberg'), initialOpen: false },
339 cc('accentColor', __('Accent', 'blockenberg'), 'accentColor'),
340 cc('activeTabBg', __('Active tab background', 'blockenberg'), 'activeTabBg'),
341 cc('activeTabColor', __('Active tab text', 'blockenberg'), 'activeTabColor'),
342 cc('inactiveTabBg', __('Inactive tab bg', 'blockenberg'), 'inactiveTabBg'),
343 cc('inactiveTabColor', __('Inactive tab text', 'blockenberg'), 'inactiveTabColor'),
344 cc('itemNameColor', __('Item name', 'blockenberg'), 'itemNameColor'),
345 cc('descColor', __('Description', 'blockenberg'), 'descColor'),
346 cc('priceColor', __('Price', 'blockenberg'), 'priceColor'),
347 cc('cardBg', __('Card background', 'blockenberg'), 'cardBg'),
348 cc('cardBorderColor', __('Card border', 'blockenberg'), 'cardBorderColor'),
349 cc('separatorColor', __('Separator line', 'blockenberg'), 'separatorColor'),
350 cc('bgColor', __('Section background', 'blockenberg'), 'bgColor')
351 ),
352 el(PanelBody, { title: __('Spacing', 'blockenberg'), initialOpen: false },
353 el(RangeControl, { label: __('Padding top (px)', 'blockenberg'), value: a.paddingTop, min: 0, max: 200, onChange: function (v) { setAttributes({ paddingTop: v }); } }),
354 el(RangeControl, { label: __('Padding bottom (px)', 'blockenberg'), value: a.paddingBottom, min: 0, max: 200, onChange: function (v) { setAttributes({ paddingBottom: v }); } })
355 ),
356 el(PanelBody, { title: __('Categories & Items', 'blockenberg'), initialOpen: false },
357 a.categories.map(function (cat, ci) {
358 return el(PanelBody, { key: cat.id, title: (cat.icon || '🍽') + ' ' + (cat.name || 'Category ' + (ci + 1)), initialOpen: false },
359 el(IP().IconPickerControl, IP().iconPickerProps(cat, function (key, val) { updateCategory(cat.id, key, val); }, { label: __('Icon', 'blockenberg'), typeAttr: 'iconType', dashiconAttr: 'iconDashicon', imageUrlAttr: 'iconImageUrl' })),
360 el(TextControl, { label: __('Category name', 'blockenberg'), value: cat.name, onChange: function (v) { updateCategory(cat.id, 'name', v); } }),
361 el('p', { style: { fontSize: '12px', fontWeight: 600, margin: '8px 0 4px' } }, __('Items', 'blockenberg')),
362 cat.items.map(function (item, ii) {
363 return el(PanelBody, { key: item.id, title: (item.name ? item.name.replace(/<[^>]+>/g,'').substring(0,24) : 'Item ' + (ii+1)), initialOpen: false },
364 el(TextControl, { label: __('Name', 'blockenberg'), value: item.name, onChange: function (v) { updateCategory(cat.id, null, null, Object.assign({}, cat, { items: cat.items.map(function (it) { if (it.id !== item.id) return it; return Object.assign({}, it, { name: v }); }) })); } }),
365 el(TextareaControl, { label: __('Description', 'blockenberg'), value: item.description, onChange: function (v) { updateCategory(cat.id, null, null, Object.assign({}, cat, { items: cat.items.map(function (it) { if (it.id !== item.id) return it; return Object.assign({}, it, { description: v }); }) })); }, rows: 2 }),
366 el(TextControl, { label: __('Price', 'blockenberg'), value: item.price, onChange: function (v) { updateCategory(cat.id, null, null, Object.assign({}, cat, { items: cat.items.map(function (it) { if (it.id !== item.id) return it; return Object.assign({}, it, { price: v }); }) })); } }),
367 el('p', { style: { fontSize: '12px', fontWeight: 600, margin: '6px 0 4px' } }, __('Dietary badges', 'blockenberg')),
368 el('div', { style: { display: 'flex', flexWrap: 'wrap', gap: '4px' } },
369 ALL_BADGES.map(function (b) {
370 var active = (item.badges || []).indexOf(b) !== -1;
371 return el(Button, { key: b, variant: active ? 'primary' : 'secondary', size: 'compact', onClick: function () {
372 var newBadges = active ? (item.badges || []).filter(function (x) { return x !== b; }) : (item.badges || []).concat([b]);
373 updateCategory(cat.id, null, null, Object.assign({}, cat, { items: cat.items.map(function (it) { if (it.id !== item.id) return it; return Object.assign({}, it, { badges: newBadges }); }) }));
374 } }, b);
375 })
376 ),
377 el(Button, { onClick: function () { updateCategory(cat.id, null, null, Object.assign({}, cat, { items: cat.items.filter(function (it) { return it.id !== item.id; }) })); }, isDestructive: true, variant: 'tertiary', size: 'compact', style: { marginTop: '6px' } }, __('Remove item', 'blockenberg'))
378 );
379 }),
380 el(Button, { variant: 'secondary', size: 'compact', onClick: function () { addItemToCategory(cat.id); }, style: { marginTop: '6px' } }, __('+ Add Item', 'blockenberg')),
381 el(Button, { onClick: function () { setAttributes({ categories: a.categories.filter(function (c) { return c.id !== cat.id; }) }); if (activeTab >= ci) setActiveTab(Math.max(0, activeTab - 1)); }, isDestructive: true, variant: 'tertiary', size: 'compact', style: { marginTop: '6px', display: 'block' } }, __('Remove category', 'blockenberg'))
382 );
383 }),
384 el(Button, { variant: 'primary', onClick: function () { setAttributes({ categories: a.categories.concat([{ id: makeId(), icon: '🍽', iconType: 'custom-char', iconDashicon: '', iconImageUrl: '', name: __('New Category', 'blockenberg'), items: [] }]) }); }, style: { marginTop: '8px' } }, __('+ Add Category', 'blockenberg'))
385 )
386 ),
387
388 el('div', blockProps,
389 tabBar,
390 a.layout === 'tabs' && currentCat && el(CategorySection, { cat: currentCat, a: a, isEditor: true,
391 onUpdate: function (key, val, newCatObj) { updateCategory(currentCat.id, key, val, newCatObj); }
392 }),
393 a.layout === 'sections' && a.categories.map(function (cat) {
394 return el(CategorySection, { key: cat.id, cat: cat, a: a, isEditor: true, onUpdate: function (key, val, newCatObj) { updateCategory(cat.id, key, val, newCatObj); } });
395 })
396 )
397 );
398 },
399
400 save: function (props) {
401 var a = props.attributes;
402 var _tv = getTypoCssVars();
403 var s = buildWrapStyle(a);
404 Object.assign(s, _tv(a.catNameTypo || {}, '--bkrm-cn-'));
405 Object.assign(s, _tv(a.itemNameTypo || {}, '--bkrm-in-'));
406 Object.assign(s, _tv(a.descTypo || {}, '--bkrm-ds-'));
407 Object.assign(s, _tv(a.priceTypo || {}, '--bkrm-pr-'));
408 return el('div', wp.blockEditor.useBlockProps.save({ className: 'bkbg-rm-wrapper bkbg-rm--' + a.layout + ' bkbg-rm-items--' + a.itemStyle, style: s }),
409 a.layout === 'tabs' && el('nav', { className: 'bkbg-rm-tab-bar', role: 'tablist' },
410 a.categories.map(function (cat, ci) {
411 return el('button', { key: cat.id, type: 'button', role: 'tab', className: 'bkbg-rm-tab-btn' + (ci === 0 ? ' is-active' : ''), 'data-cat': cat.id, 'aria-selected': ci === 0 ? 'true' : 'false' },
412 a.showCategoryIcon && el('span', { className: 'bkbg-rm-tab-icon', 'aria-hidden': 'true' }, IP().buildSaveIcon(cat.iconType || 'custom-char', cat.icon, cat.iconDashicon, cat.iconImageUrl, cat.iconDashiconColor)),
413 el('span', null, cat.name)
414 );
415 })
416 ),
417 el('div', { className: 'bkbg-rm-body' },
418 a.categories.map(function (cat, ci) {
419 return el('div', { key: cat.id, className: 'bkbg-rm-category-section' + (ci === 0 ? ' is-active' : ''), 'data-cat': cat.id, role: 'tabpanel', 'aria-hidden': ci === 0 ? 'false' : 'true' },
420 a.layout === 'sections' && el('div', { className: 'bkbg-rm-category-header' },
421 a.showCategoryIcon && el('span', { className: 'bkbg-rm-cat-icon', 'aria-hidden': 'true' }, IP().buildSaveIcon(cat.iconType || 'custom-char', cat.icon, cat.iconDashicon, cat.iconImageUrl, cat.iconDashiconColor)),
422 el(RichText.Content, { tagName: 'h3', className: 'bkbg-rm-cat-name', value: cat.name })
423 ),
424 el('div', { className: 'bkbg-rm-items' },
425 cat.items.map(function (item) {
426 return el(MenuItem, { key: item.id, item: item, a: a, isEditor: false });
427 })
428 )
429 );
430 })
431 )
432 );
433 }
434 });
435 }() );
436