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

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

287 lines 19.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 registerBlockType = wp.blocks.registerBlockType;
4 var useBlockProps = wp.blockEditor.useBlockProps;
5 var InspectorControls = wp.blockEditor.InspectorControls;
6 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
7 var MediaUpload = wp.blockEditor.MediaUpload;
8 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
9 var PanelBody = wp.components.PanelBody;
10 var TextControl = wp.components.TextControl;
11 var TextareaControl = wp.components.TextareaControl;
12 var ToggleControl = wp.components.ToggleControl;
13 var SelectControl = wp.components.SelectControl;
14 var RangeControl = wp.components.RangeControl;
15 var Button = wp.components.Button;
16
17 var _tc, _tv;
18 function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
19 function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
20 var IP = function () { return window.bkbgIconPicker; };
21
22 function deepClone(obj) { return JSON.parse(JSON.stringify(obj)); }
23
24 // ── Item Editor: one top-level nav item ──
25 function ItemEditor(props) {
26 var item = props.item;
27 var idx = props.idx;
28 var setItem = props.setItem;
29 var removeItem = props.removeItem;
30 var attr = props.attr;
31
32 return el(PanelBody, {
33 title: item.label || ('Item ' + (idx + 1)),
34 initialOpen: false,
35 className: 'bkbg-mm-item-panel'
36 },
37 // Basic
38 el(TextControl, { label: 'Label', value: item.label, onChange: function(v){ setItem({ label: v }); }, __nextHasNoMarginBottom: true }),
39 el(TextControl, { label: 'URL', value: item.url, onChange: function(v){ setItem({ url: v }); }, __nextHasNoMarginBottom: true }),
40 el(ToggleControl, { label: 'Has mega dropdown', checked: item.hasMega, onChange: function(v){ setItem({ hasMega: v }); }, __nextHasNoMarginBottom: true }),
41
42 item.hasMega && el('div', { style: { marginTop: 12 } },
43
44 // Columns
45 el('p', { style: { fontWeight: 600, marginBottom: 8, fontSize: 12, textTransform: 'uppercase', color: '#6366f1' } }, 'Columns'),
46 (item.columns || []).map(function (col, ci) {
47 return el('div', { key: ci, style: { border: '1px solid #e2e8f0', borderRadius: 8, padding: 10, marginBottom: 10 } },
48 el(TextControl, { label: 'Column Heading', value: col.heading, onChange: function(v){ var cols = deepClone(item.columns); cols[ci].heading = v; setItem({ columns: cols }); }, __nextHasNoMarginBottom: true }),
49
50 // Links in column
51 (col.links || []).map(function (lnk, li) {
52 return el('div', { key: li, style: { background: '#f8fafc', borderRadius: 6, padding: '6px 8px', marginTop: 8 } },
53 attr.showIcons && el(IP().IconPickerControl, {
54 iconType: lnk.iconType || 'custom-char',
55 customChar: lnk.icon || '',
56 dashicon: lnk.iconDashicon || '',
57 imageUrl: lnk.iconImageUrl || '',
58 onChangeType: function (v) { var cols = deepClone(item.columns); cols[ci].links[li].iconType = v; setItem({ columns: cols }); },
59 onChangeChar: function (v) { var cols = deepClone(item.columns); cols[ci].links[li].icon = v; setItem({ columns: cols }); },
60 onChangeDashicon: function (v) { var cols = deepClone(item.columns); cols[ci].links[li].iconDashicon = v; setItem({ columns: cols }); },
61 onChangeImageUrl: function (v) { var cols = deepClone(item.columns); cols[ci].links[li].iconImageUrl = v; setItem({ columns: cols }); },
62 label: 'Icon'
63 }),
64 el(TextControl, { label: 'Link Label', value: lnk.label, onChange: function(v){ var cols = deepClone(item.columns); cols[ci].links[li].label = v; setItem({ columns: cols }); }, __nextHasNoMarginBottom: true }),
65 attr.showDescriptions && el(TextControl, { label: 'Description', value: lnk.desc, onChange: function(v){ var cols = deepClone(item.columns); cols[ci].links[li].desc = v; setItem({ columns: cols }); }, __nextHasNoMarginBottom: true }),
66 el(TextControl, { label: 'URL', value: lnk.url, onChange: function(v){ var cols = deepClone(item.columns); cols[ci].links[li].url = v; setItem({ columns: cols }); }, __nextHasNoMarginBottom: true }),
67 el(Button, {
68 variant: 'link', isDestructive: true, size: 'small',
69 onClick: function() { var cols = deepClone(item.columns); cols[ci].links.splice(li, 1); setItem({ columns: cols }); }
70 }, '✕ Remove link')
71 );
72 }),
73
74 el(Button, {
75 variant: 'secondary', size: 'small', style: { marginTop: 8 },
76 onClick: function() { var cols = deepClone(item.columns); cols[ci].links.push({ icon: '🔗', iconType: 'custom-char', iconDashicon: '', iconImageUrl: '', label: 'New Link', desc: '', url: '#' }); setItem({ columns: cols }); }
77 }, '+ Add Link'),
78 el(Button, {
79 variant: 'link', isDestructive: true, size: 'small', style: { marginLeft: 8 },
80 onClick: function() { var cols = deepClone(item.columns); cols.splice(ci, 1); setItem({ columns: cols }); }
81 }, '✕ Remove column')
82 );
83 }),
84
85 el(Button, {
86 variant: 'secondary', size: 'small', style: { marginBottom: 16 },
87 onClick: function() { var cols = deepClone(item.columns || []); cols.push({ heading: 'New Column', links: [{ icon: '🔗', iconType: 'custom-char', iconDashicon: '', iconImageUrl: '', label: 'Link', desc: '', url: '#' }] }); setItem({ columns: cols }); }
88 }, '+ Add Column'),
89
90 // Featured panel
91 el(ToggleControl, { label: 'Show featured panel', checked: item.showFeatured, onChange: function(v){ setItem({ showFeatured: v }); }, __nextHasNoMarginBottom: true }),
92 item.showFeatured && el('div', { style: { marginTop: 8, border: '1px solid #e2e8f0', borderRadius: 8, padding: 10 } },
93 el('p', { style: { fontWeight: 600, fontSize: 12, marginBottom: 8 } }, 'Featured Panel'),
94 el(MediaUploadCheck, {},
95 el(MediaUpload, {
96 onSelect: function(m) { setItem({ featuredImageUrl: m.url }); },
97 allowedTypes: ['image'],
98 value: item.featuredImageUrl,
99 render: function(obj) {
100 return el(Button, { onClick: obj.open, variant: 'secondary', size: 'small' }, item.featuredImageUrl ? 'Change Image' : 'Set Image');
101 }
102 })
103 ),
104 el(TextControl, { label: 'Title', value: item.featuredTitle, onChange: function(v){ setItem({ featuredTitle: v }); }, __nextHasNoMarginBottom: true }),
105 el(TextareaControl, { label: 'Description', value: item.featuredDesc, onChange: function(v){ setItem({ featuredDesc: v }); }, __nextHasNoMarginBottom: true }),
106 el(TextControl, { label: 'CTA Label', value: item.featuredCta, onChange: function(v){ setItem({ featuredCta: v }); }, __nextHasNoMarginBottom: true }),
107 el(TextControl, { label: 'CTA URL', value: item.featuredCtaUrl, onChange: function(v){ setItem({ featuredCtaUrl: v }); }, __nextHasNoMarginBottom: true })
108 )
109 ),
110
111 el(Button, { variant: 'link', isDestructive: true, size: 'small', style: { marginTop: 12 }, onClick: removeItem }, '✕ Remove this item')
112 );
113 }
114
115 registerBlockType('blockenberg/mega-menu', {
116 edit: function (props) {
117 var attr = props.attributes;
118 var setAttr = props.setAttributes;
119 var blockProps = (function(p){
120 var v = getTypoCssVars() || function () { return {}; };
121 p.style = Object.assign(p.style||{},
122 v(attr.navTypo,'--bkbg-mm-nv-'),
123 v(attr.colHeadingTypo,'--bkbg-mm-ch-'),
124 v(attr.linkTypo,'--bkbg-mm-lk-')
125 ); return p;
126 })(useBlockProps());
127
128 function updateItem(idx, patch) {
129 var items = deepClone(attr.items);
130 Object.assign(items[idx], patch);
131 setAttr({ items: items });
132 }
133
134 function removeItem(idx) {
135 var items = deepClone(attr.items);
136 items.splice(idx, 1);
137 setAttr({ items: items });
138 }
139
140 // Editor preview: render simplified nav bar
141 return el('div', blockProps,
142 el(InspectorControls, {},
143
144 // Nav Items
145 el(PanelBody, { title: 'Navigation Items', initialOpen: true },
146 attr.items.map(function (item, idx) {
147 return el(ItemEditor, {
148 key: idx, item: item, idx: idx, attr: attr,
149 setItem: function(patch) { updateItem(idx, patch); },
150 removeItem: function() { removeItem(idx); }
151 });
152 }),
153 el(Button, {
154 variant: 'primary', size: 'small', style: { marginTop: 8 },
155 onClick: function() {
156 var items = deepClone(attr.items);
157 items.push({ label: 'New Item', url: '#', hasMega: false, columns: [], showFeatured: false, featuredTitle: '', featuredDesc: '', featuredCta: '', featuredCtaUrl: '', featuredImageUrl: '' });
158 setAttr({ items: items });
159 }
160 }, '+ Add Item')
161 ),
162
163 // Logo & CTA
164 el(PanelBody, { title: 'Logo & CTA', initialOpen: false },
165 el('p', { style: { fontSize: 11, color: '#64748b', margin: '0 0 6px' } }, 'Logo image:'),
166 el(MediaUploadCheck, {},
167 el(MediaUpload, {
168 onSelect: function(m){ setAttr({ logoUrl: m.url }); },
169 allowedTypes: ['image'],
170 value: attr.logoUrl,
171 render: function(obj) {
172 return el('div', { style: { marginBottom: 12 } },
173 el(Button, { onClick: obj.open, variant: 'secondary', size: 'small' }, attr.logoUrl ? 'Change Logo' : 'Set Logo'),
174 attr.logoUrl && el(Button, { onClick: function(){ setAttr({ logoUrl: '' }); }, variant: 'link', isDestructive: true, size: 'small', style: { marginLeft: 8 } }, 'Remove')
175 );
176 }
177 })
178 ),
179 el(TextControl, { label: 'Logo Alt Text', value: attr.logoAlt, onChange: function(v){ setAttr({ logoAlt: v }); }, __nextHasNoMarginBottom: true }),
180 el(RangeControl, { label: 'Logo Width (px)', value: attr.logoWidth, min: 60, max: 300, onChange: function(v){ setAttr({ logoWidth: v }); }, __nextHasNoMarginBottom: true }),
181 el(ToggleControl, { label: 'Show CTA Button', checked: attr.showCta, onChange: function(v){ setAttr({ showCta: v }); }, __nextHasNoMarginBottom: true }),
182 attr.showCta && el(TextControl, { label: 'CTA Label', value: attr.ctaLabel, onChange: function(v){ setAttr({ ctaLabel: v }); }, __nextHasNoMarginBottom: true }),
183 attr.showCta && el(TextControl, { label: 'CTA URL', value: attr.ctaUrl, onChange: function(v){ setAttr({ ctaUrl: v }); }, __nextHasNoMarginBottom: true }),
184 attr.showCta && el(SelectControl, { label: 'CTA Style', value: attr.ctaStyle, options: [{ label: 'Filled', value: 'filled' }, { label: 'Outline', value: 'outline' }], onChange: function(v){ setAttr({ ctaStyle: v }); }, __nextHasNoMarginBottom: true })
185 ),
186
187 // Behaviour
188 el(PanelBody, { title: 'Menu Behaviour', initialOpen: false },
189 el(RangeControl, { label: 'Menu Bar Height (px)', value: attr.menuHeight, min: 48, max: 100, onChange: function(v){ setAttr({ menuHeight: v }); }, __nextHasNoMarginBottom: true }),
190 el(ToggleControl, { label: 'Sticky on scroll', checked: attr.sticky, onChange: function(v){ setAttr({ sticky: v }); }, __nextHasNoMarginBottom: true }),
191 el(ToggleControl, { label: 'Open panel on hover', checked: attr.openOnHover, onChange: function(v){ setAttr({ openOnHover: v }); }, __nextHasNoMarginBottom: true }),
192 el(ToggleControl, { label: 'Show link icons', checked: attr.showIcons, onChange: function(v){ setAttr({ showIcons: v }); }, __nextHasNoMarginBottom: true }),
193 el(ToggleControl, { label: 'Show link descriptions', checked: attr.showDescriptions, onChange: function(v){ setAttr({ showDescriptions: v }); }, __nextHasNoMarginBottom: true }),
194 el(RangeControl, { label: 'Panel Max Width (px)', value: attr.panelWidth, min: 400, max: 1200, step: 20, onChange: function(v){ setAttr({ panelWidth: v }); }, __nextHasNoMarginBottom: true }),
195 el(RangeControl, { label: 'Panel Radius (px)', value: attr.panelRadius, min: 0, max: 32, onChange: function(v){ setAttr({ panelRadius: v }); }, __nextHasNoMarginBottom: true })
196 ),
197
198 // Colors
199 el( PanelBody, { title: 'Typography', initialOpen: false },
200 getTypoControl() && el(getTypoControl(), { label: 'Nav Item', value: attr.navTypo || {}, onChange: function(v){ setAttr({ navTypo: v }); } }),
201 getTypoControl() && el(getTypoControl(), { label: 'Column Heading', value: attr.colHeadingTypo || {}, onChange: function(v){ setAttr({ colHeadingTypo: v }); } }),
202 getTypoControl() && el(getTypoControl(), { label: 'Link Label', value: attr.linkTypo || {}, onChange: function(v){ setAttr({ linkTypo: v }); } })
203 ),
204 el(PanelColorSettings, {
205 title: 'Colors',
206 initialOpen: false,
207 colorSettings: [
208 { label: 'Menu Bar Background', value: attr.menuBg, onChange: function(v){ setAttr({ menuBg: v || '#ffffff' }); } },
209 { label: 'Menu Text', value: attr.menuText, onChange: function(v){ setAttr({ menuText: v || '#0f172a' }); } },
210 { label: 'Menu Border', value: attr.menuBorder, onChange: function(v){ setAttr({ menuBorder: v || '#e2e8f0' }); } },
211 { label: 'Panel Background', value: attr.panelBg, onChange: function(v){ setAttr({ panelBg: v || '#ffffff' }); } },
212 { label: 'Panel Text', value: attr.panelText, onChange: function(v){ setAttr({ panelText: v || '#0f172a' }); } },
213 { label: 'Accent / Hover', value: attr.accentColor, onChange: function(v){ setAttr({ accentColor: v || '#6366f1' }); } },
214 { label: 'CTA Background', value: attr.ctaBg, onChange: function(v){ setAttr({ ctaBg: v || '#6366f1' }); } },
215 { label: 'CTA Text', value: attr.ctaColor, onChange: function(v){ setAttr({ ctaColor: v || '#ffffff' }); } },
216 { label: 'Featured Panel BG', value: attr.featuredBg, onChange: function(v){ setAttr({ featuredBg: v || '#ede9fe' }); } }
217 ]
218 })
219 ),
220
221 // Editor preview — simplified nav bar
222 el('div', {
223 style: {
224 background: attr.menuBg,
225 borderBottom: '1px solid ' + attr.menuBorder,
226 display: 'flex',
227 alignItems: 'center',
228 height: attr.menuHeight + 'px',
229 padding: '0 24px',
230 gap: 32,
231 borderRadius: 8
232 }
233 },
234 // Logo
235 attr.logoUrl
236 ? el('img', { src: attr.logoUrl, alt: attr.logoAlt, style: { height: Math.min(36, attr.menuHeight - 20) + 'px', objectFit: 'contain' } })
237 : el('div', { style: { fontWeight: 700, fontSize: 18, color: attr.menuText } }, attr.logoAlt || 'Logo'),
238
239 // Nav items
240 el('div', { style: { display: 'flex', gap: 24, flex: 1, alignItems: 'center' } },
241 attr.items.map(function (item, idx) {
242 return el('span', {
243 key: idx,
244 style: { color: attr.menuText, cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 4 }
245 }, item.label, item.hasMega && el('span', { style: { fontSize: 10, opacity: 0.6 } }, ''));
246 })
247 ),
248
249 // CTA
250 attr.showCta && el('div', {
251 style: {
252 background: attr.ctaStyle === 'filled' ? attr.ctaBg : 'transparent',
253 color: attr.ctaStyle === 'filled' ? attr.ctaColor : attr.ctaBg,
254 border: '2px solid ' + attr.ctaBg,
255 borderRadius: 8,
256 padding: '7px 18px',
257 fontWeight: 600,
258 cursor: 'pointer'
259 }
260 }, attr.ctaLabel),
261
262 // Mega panel preview hint
263 el('div', { style: { fontSize: 11, color: '#94a3b8', marginLeft: 8 } },
264 attr.items.filter(function(i){ return i.hasMega; }).length + ' mega panel(s)')
265 )
266 );
267 },
268
269 save: function (props) {
270 var attr = props.attributes;
271 var v = (typeof window.bkbgTypoCssVars === 'function') ? window.bkbgTypoCssVars : function () { return {}; };
272 var s = Object.assign({},
273 v(attr.navTypo,'--bkbg-mm-nv-'),
274 v(attr.colHeadingTypo,'--bkbg-mm-ch-'),
275 v(attr.linkTypo,'--bkbg-mm-lk-')
276 );
277 var blockProps = (function(p){p.style=Object.assign(p.style||{},s);return p;})(useBlockProps.save());
278 return el('div', blockProps,
279 el('div', {
280 className: 'bkbg-mm-app',
281 'data-opts': JSON.stringify(attr)
282 })
283 );
284 }
285 });
286 }() );
287