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 / sticky-sidebar / index.js

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

582 lines 28.0 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 MediaUpload = wp.blockEditor.MediaUpload;
9 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
10 var useBlockProps = wp.blockEditor.useBlockProps;
11 var RichText = wp.blockEditor.RichText;
12 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
13 var PanelBody = wp.components.PanelBody;
14 var ToggleControl = wp.components.ToggleControl;
15 var RangeControl = wp.components.RangeControl;
16 var SelectControl = wp.components.SelectControl;
17 var TextControl = wp.components.TextControl;
18 var Button = wp.components.Button;
19
20 var _tc, _tv;
21 function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
22 function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
23
24 var SIDEBAR_WIDTH_OPTIONS = [
25 { label: '25% sidebar / 75% content', value: '25' },
26 { label: '30% sidebar / 70% content', value: '30' },
27 { label: '33% sidebar / 67% content', value: '33' },
28 { label: '40% sidebar / 60% content', value: '40' },
29 ];
30
31 var SIDEBAR_STYLE_OPTIONS = [
32 { label: 'Card (bg + shadow)', value: 'card' },
33 { label: 'Border only', value: 'border' },
34 { label: 'Flat (no styling)', value: 'flat' },
35 ];
36
37 var HEADING_TAG_OPTIONS = [
38 { label: 'H2', value: 'h2' },
39 { label: 'H3', value: 'h3' },
40 { label: 'H4', value: 'h4' },
41 ];
42
43 var CTA_STYLE_OPTIONS = [
44 { label: 'Filled', value: 'filled' },
45 { label: 'Outline', value: 'outline' },
46 { label: 'Ghost', value: 'ghost' },
47 ];
48
49 var IMAGE_STYLE_OPTIONS = [
50 { label: 'Circle', value: 'circle' },
51 { label: 'Rounded', value: 'rounded' },
52 { label: 'Square', value: 'square' },
53 { label: 'Full width', value: 'full' },
54 ];
55
56 function getSidebarStyle(a) {
57 var s = {
58 padding: a.sidebarPadding + 'px',
59 borderRadius: a.borderRadius + 'px',
60 display: 'flex',
61 flexDirection: 'column',
62 gap: '16px',
63 };
64 if (a.sidebarStyle === 'card') {
65 s.background = a.sidebarBg;
66 s.boxShadow = '0 1px 12px rgba(0,0,0,0.08)';
67 s.border = '1px solid ' + a.sidebarBorderColor;
68 } else if (a.sidebarStyle === 'border') {
69 s.border = '1px solid ' + a.sidebarBorderColor;
70 }
71 return s;
72 }
73
74 function renderSidebarPreview(a, set) {
75 var imgBr = a.imageStyle === 'circle' ? '50%' : a.imageStyle === 'rounded' ? '10px' : '0';
76 var imgW = a.imageStyle === 'full' ? '100%' : a.imageSize + 'px';
77 var imgH = a.imageStyle === 'full' ? 'auto' : a.imageSize + 'px';
78
79 return el('div', { style: getSidebarStyle(a) },
80 // Image
81 a.showImage && a.imageUrl && el('div', { style: { textAlign: 'center' } },
82 el('img', {
83 src: a.imageUrl,
84 alt: a.imageAlt,
85 style: {
86 width: imgW,
87 height: imgH,
88 objectFit: 'cover',
89 borderRadius: imgBr,
90 display: 'block',
91 margin: '0 auto',
92 }
93 })
94 ),
95 // Heading
96 a.showHeading && a.heading && el(a.headingTag, {
97 className: 'bkbg-ss-heading',
98 style: { margin: 0, color: a.headingColor }
99 }, a.heading),
100 // Sub text
101 a.showSubText && a.subText && el('p', {
102 className: 'bkbg-ss-subtext',
103 style: { margin: 0, color: a.textColor }
104 }, a.subText),
105 // Rating
106 a.showRatingBox && el('div', {
107 style: {
108 background: 'rgba(0,0,0,0.04)',
109 borderRadius: 8,
110 padding: '10px 14px',
111 }
112 },
113 el('div', { style: { color: a.starColor, fontSize: 18, letterSpacing: 2 } },
114 '�
115 '.repeat(Math.round(a.ratingStars)) + ''.repeat(5 - Math.round(a.ratingStars))
116 ),
117 el('div', { style: { fontWeight: 700, color: a.headingColor, fontSize: 15, marginTop: 2 } }, a.ratingCount),
118 el('div', { style: { color: a.textColor, fontSize: 12, opacity: 0.7 } }, a.ratingLabel)
119 ),
120 // Bullets
121 a.showBullets && a.bullets.length > 0 && el('ul', {
122 style: { margin: 0, padding: 0, listStyle: 'none', display: 'flex', flexDirection: 'column', gap: 8 }
123 },
124 a.bullets.map(function (b, i) {
125 return el('li', {
126 key: i,
127 style: { display: 'flex', alignItems: 'flex-start', gap: 8, fontSize: 14, color: a.textColor }
128 },
129 el('span', { style: { color: a.bulletIconColor, fontWeight: 700, flexShrink: 0 } }, a.bulletIcon),
130 el('span', null, b.text)
131 );
132 })
133 ),
134 // Contact info
135 a.showContactInfo && el('div', { style: { display: 'flex', flexDirection: 'column', gap: 6, fontSize: 14 } },
136 a.contactPhone && el('div', { style: { color: a.textColor } }, '📞 ' + a.contactPhone),
137 a.contactEmail && el('div', { style: { color: a.linkColor } }, '✉️ ' + a.contactEmail)
138 ),
139 // CTA button
140 a.showCta && a.ctaLabel && (function () {
141 var btnStyle = {
142 display: 'block',
143 textAlign: 'center',
144 padding: '10px 20px',
145 borderRadius: 8,
146 fontWeight: 600,
147 fontSize: 15,
148 textDecoration: 'none',
149 cursor: 'pointer',
150 border: '2px solid ' + a.ctaBg,
151 };
152 if (a.ctaStyle === 'filled') {
153 btnStyle.background = a.ctaBg;
154 btnStyle.color = a.ctaColor;
155 } else if (a.ctaStyle === 'outline') {
156 btnStyle.background = 'transparent';
157 btnStyle.color = a.ctaBg;
158 } else {
159 btnStyle.background = 'transparent';
160 btnStyle.color = a.ctaBg;
161 btnStyle.border = 'none';
162 }
163 return el('a', { href: a.ctaUrl, style: btnStyle }, a.ctaLabel);
164 })()
165 );
166 }
167
168 registerBlockType('blockenberg/sticky-sidebar', {
169 title: __('Sticky Sidebar', 'blockenberg'),
170 icon: 'columns',
171 category: 'bkbg-layout',
172 description: __('Two-column layout with a rich sticky sidebar — perfect for articles, docs, and long-form content.', 'blockenberg'),
173
174 edit: function (props) {
175 var a = props.attributes;
176 var set = props.setAttributes;
177 var blockProps = useBlockProps((function() {
178 var _tvf = getTypoCssVars();
179 var s = { background: a.containerBg || 'transparent' };
180 if (_tvf) {
181 Object.assign(s, _tvf(a.headingTypo, '--bkssb-hd-'));
182 Object.assign(s, _tvf(a.subTextTypo, '--bkssb-st-'));
183 }
184 s['--bkssb-heading-color'] = a.headingColor || '#111827';
185 s['--bkssb-text-color'] = a.textColor || '#374151';
186 s['--bkssb-st-fw'] = a.subTextFontWeight || '400';
187 s['--bkssb-st-lh'] = a.subTextLineHeight || 1.5;
188 return { style: s };
189 })());
190
191 var sidebarFlex = parseInt(a.sidebarWidth) || 30;
192 var mainFlex = 100 - sidebarFlex;
193
194 var sidebarCol = el('div', {
195 style: {
196 flex: '0 0 ' + sidebarFlex + '%',
197 maxWidth: sidebarFlex + '%',
198 minWidth: '200px',
199 }
200 }, renderSidebarPreview(a, set));
201
202 var mainCol = el('div', {
203 style: {
204 flex: '1',
205 minWidth: 0,
206 background: a.mainBg || 'transparent',
207 }
208 },
209 el(RichText, {
210 tagName: 'div',
211 value: a.mainContent,
212 onChange: function (v) { set({ mainContent: v }); },
213 placeholder: __('Add your main content here...', 'blockenberg'),
214 style: { minHeight: 200, lineHeight: 1.7 },
215 multiline: 'p',
216 })
217 );
218
219 var cols = a.sidebarSide === 'left'
220 ? [sidebarCol, mainCol]
221 : [mainCol, sidebarCol];
222
223 function addBullet() {
224 set({ bullets: a.bullets.concat([{ text: 'New item' }]) });
225 }
226 function removeBullet(i) {
227 set({ bullets: a.bullets.filter(function (_, idx) { return idx !== i; }) });
228 }
229 function updateBullet(i, val) {
230 var updated = a.bullets.map(function (b, idx) {
231 if (idx !== i) return b;
232 var p = {}; p['text'] = val;
233 return Object.assign({}, b, p);
234 });
235 set({ bullets: updated });
236 }
237
238 return el(Fragment, null,
239 el(InspectorControls, null,
240 // Layout Panel
241 el(PanelBody, { title: 'Layout', initialOpen: true },
242 el(SelectControl, {
243 label: 'Sidebar Side',
244 value: a.sidebarSide,
245 options: [
246 { label: 'Left', value: 'left' },
247 { label: 'Right', value: 'right' },
248 ],
249 onChange: function (v) { set({ sidebarSide: v }); },
250 __nextHasNoMarginBottom: true,
251 }),
252 el('div', { style: { height: 12 } }),
253 el(SelectControl, {
254 label: 'Width Split',
255 value: a.sidebarWidth,
256 options: SIDEBAR_WIDTH_OPTIONS,
257 onChange: function (v) { set({ sidebarWidth: v }); },
258 __nextHasNoMarginBottom: true,
259 }),
260 el('div', { style: { height: 12 } }),
261 el(RangeControl, {
262 label: 'Sticky Top Offset (px)',
263 value: a.stickyTop,
264 min: 0, max: 200,
265 onChange: function (v) { set({ stickyTop: v }); },
266 __nextHasNoMarginBottom: true,
267 }),
268 el('div', { style: { height: 12 } }),
269 el(RangeControl, {
270 label: 'Column Gap (px)',
271 value: a.gap,
272 min: 8, max: 100,
273 onChange: function (v) { set({ gap: v }); },
274 __nextHasNoMarginBottom: true,
275 }),
276 el('div', { style: { height: 12 } }),
277 el(RangeControl, {
278 label: 'Container Padding (px)',
279 value: a.containerPadding,
280 min: 0, max: 80,
281 onChange: function (v) { set({ containerPadding: v }); },
282 __nextHasNoMarginBottom: true,
283 })
284 ),
285
286 // Sidebar Style Panel
287 el(PanelBody, { title: 'Sidebar Style', initialOpen: false },
288 el(SelectControl, {
289 label: 'Visual Style',
290 value: a.sidebarStyle,
291 options: SIDEBAR_STYLE_OPTIONS,
292 onChange: function (v) { set({ sidebarStyle: v }); },
293 __nextHasNoMarginBottom: true,
294 }),
295 el('div', { style: { height: 12 } }),
296 el(RangeControl, {
297 label: 'Sidebar Padding (px)',
298 value: a.sidebarPadding,
299 min: 0, max: 60,
300 onChange: function (v) { set({ sidebarPadding: v }); },
301 __nextHasNoMarginBottom: true,
302 }),
303 el('div', { style: { height: 12 } }),
304 el(RangeControl, {
305 label: 'Border Radius (px)',
306 value: a.borderRadius,
307 min: 0, max: 32,
308 onChange: function (v) { set({ borderRadius: v }); },
309 __nextHasNoMarginBottom: true,
310 })
311 ),
312
313 // Image Panel
314 el(PanelBody, { title: 'Sidebar Image', initialOpen: false },
315 el(ToggleControl, {
316 label: 'Show Image',
317 checked: a.showImage,
318 onChange: function (v) { set({ showImage: v }); },
319 __nextHasNoMarginBottom: true,
320 }),
321 a.showImage && el(Fragment, null,
322 el(MediaUploadCheck, null,
323 el(MediaUpload, {
324 onSelect: function (media) {
325 set({ imageUrl: media.url, imageId: media.id, imageAlt: media.alt || '' });
326 },
327 allowedTypes: ['image'],
328 value: a.imageId,
329 render: function (ref) {
330 return el(Button, {
331 onClick: ref.open,
332 variant: a.imageUrl ? 'secondary' : 'primary',
333 __nextHasNoMarginBottom: true,
334 }, a.imageUrl ? 'Change Image' : 'Select Image');
335 }
336 })
337 ),
338 el('div', { style: { height: 12 } }),
339 el(SelectControl, {
340 label: 'Image Style',
341 value: a.imageStyle,
342 options: IMAGE_STYLE_OPTIONS,
343 onChange: function (v) { set({ imageStyle: v }); },
344 __nextHasNoMarginBottom: true,
345 }),
346 a.imageStyle !== 'full' && el(Fragment, null,
347 el('div', { style: { height: 12 } }),
348 el(RangeControl, {
349 label: 'Image Size (px)',
350 value: a.imageSize,
351 min: 40, max: 200,
352 onChange: function (v) { set({ imageSize: v }); },
353 __nextHasNoMarginBottom: true,
354 })
355 )
356 )
357 ),
358
359 // Sidebar Content Panel
360 el(PanelBody, { title: 'Sidebar Content', initialOpen: false },
361 el(ToggleControl, {
362 label: 'Show Heading',
363 checked: a.showHeading,
364 onChange: function (v) { set({ showHeading: v }); },
365 __nextHasNoMarginBottom: true,
366 }),
367 a.showHeading && el(Fragment, null,
368 el(TextControl, {
369 label: 'Heading',
370 value: a.heading,
371 onChange: function (v) { set({ heading: v }); },
372 __nextHasNoMarginBottom: true,
373 }),
374 el('div', { style: { height: 8 } }),
375 el(SelectControl, {
376 label: 'Heading Tag',
377 value: a.headingTag,
378 options: HEADING_TAG_OPTIONS,
379 onChange: function (v) { set({ headingTag: v }); },
380 __nextHasNoMarginBottom: true,
381 })
382 ),
383 el('div', { style: { height: 12 } }),
384 el(ToggleControl, {
385 label: 'Show Sub-text',
386 checked: a.showSubText,
387 onChange: function (v) { set({ showSubText: v }); },
388 __nextHasNoMarginBottom: true,
389 }),
390 a.showSubText && el(TextControl, {
391 label: 'Sub-text',
392 value: a.subText,
393 onChange: function (v) { set({ subText: v }); },
394 __nextHasNoMarginBottom: true,
395 }),
396 el('div', { style: { height: 12 } }),
397 el(ToggleControl, {
398 label: 'Show Rating Box',
399 checked: a.showRatingBox,
400 onChange: function (v) { set({ showRatingBox: v }); },
401 __nextHasNoMarginBottom: true,
402 }),
403 a.showRatingBox && el(Fragment, null,
404 el(RangeControl, {
405 label: 'Rating Stars',
406 value: a.ratingStars,
407 min: 1, max: 5,
408 onChange: function (v) { set({ ratingStars: v }); },
409 __nextHasNoMarginBottom: true,
410 }),
411 el('div', { style: { height: 8 } }),
412 el(TextControl, {
413 label: 'Rating Score Text',
414 value: a.ratingCount,
415 onChange: function (v) { set({ ratingCount: v }); },
416 __nextHasNoMarginBottom: true,
417 }),
418 el('div', { style: { height: 8 } }),
419 el(TextControl, {
420 label: 'Rating Label',
421 value: a.ratingLabel,
422 onChange: function (v) { set({ ratingLabel: v }); },
423 __nextHasNoMarginBottom: true,
424 })
425 )
426 ),
427
428 // Bullets Panel
429 el(PanelBody, { title: 'Bullet Points', initialOpen: false },
430 el(ToggleControl, {
431 label: 'Show Bullets',
432 checked: a.showBullets,
433 onChange: function (v) { set({ showBullets: v }); },
434 __nextHasNoMarginBottom: true,
435 }),
436 a.showBullets && el(Fragment, null,
437 el(TextControl, {
438 label: 'Bullet Icon (emoji/symbol)',
439 value: a.bulletIcon,
440 onChange: function (v) { set({ bulletIcon: v }); },
441 __nextHasNoMarginBottom: true,
442 }),
443 el('div', { style: { height: 8 } }),
444 a.bullets.map(function (b, i) {
445 return el('div', { key: i, style: { display: 'flex', gap: 6, alignItems: 'center', marginBottom: 6 } },
446 el(TextControl, {
447 value: b.text,
448 onChange: function (v) { updateBullet(i, v); },
449 style: { margin: 0, flex: 1 },
450 __nextHasNoMarginBottom: true,
451 }),
452 el(Button, {
453 onClick: function () { removeBullet(i); },
454 isDestructive: true,
455 variant: 'tertiary',
456 __nextHasNoMarginBottom: true,
457 }, '')
458 );
459 }),
460 el(Button, {
461 onClick: addBullet,
462 variant: 'secondary',
463 style: { width: '100%', justifyContent: 'center', marginTop: 4 },
464 __nextHasNoMarginBottom: true,
465 }, '+ Add Bullet')
466 )
467 ),
468
469 // CTA Panel
470 el(PanelBody, { title: 'CTA Button', initialOpen: false },
471 el(ToggleControl, {
472 label: 'Show CTA',
473 checked: a.showCta,
474 onChange: function (v) { set({ showCta: v }); },
475 __nextHasNoMarginBottom: true,
476 }),
477 a.showCta && el(Fragment, null,
478 el(TextControl, {
479 label: 'Button Label',
480 value: a.ctaLabel,
481 onChange: function (v) { set({ ctaLabel: v }); },
482 __nextHasNoMarginBottom: true,
483 }),
484 el('div', { style: { height: 8 } }),
485 el(TextControl, {
486 label: 'Button URL',
487 value: a.ctaUrl,
488 onChange: function (v) { set({ ctaUrl: v }); },
489 __nextHasNoMarginBottom: true,
490 }),
491 el('div', { style: { height: 8 } }),
492 el(SelectControl, {
493 label: 'Button Style',
494 value: a.ctaStyle,
495 options: CTA_STYLE_OPTIONS,
496 onChange: function (v) { set({ ctaStyle: v }); },
497 __nextHasNoMarginBottom: true,
498 }),
499 el('div', { style: { height: 8 } }),
500 el(ToggleControl, {
501 label: 'Open in new tab',
502 checked: a.ctaNewTab,
503 onChange: function (v) { set({ ctaNewTab: v }); },
504 __nextHasNoMarginBottom: true,
505 })
506 )
507 ),
508
509 // Contact Panel
510 el(PanelBody, { title: 'Contact Info', initialOpen: false },
511 el(ToggleControl, {
512 label: 'Show Contact Info',
513 checked: a.showContactInfo,
514 onChange: function (v) { set({ showContactInfo: v }); },
515 __nextHasNoMarginBottom: true,
516 }),
517 a.showContactInfo && el(Fragment, null,
518 el(TextControl, {
519 label: 'Phone Number',
520 value: a.contactPhone,
521 onChange: function (v) { set({ contactPhone: v }); },
522 __nextHasNoMarginBottom: true,
523 }),
524 el('div', { style: { height: 8 } }),
525 el(TextControl, {
526 label: 'Email Address',
527 value: a.contactEmail,
528 onChange: function (v) { set({ contactEmail: v }); },
529 __nextHasNoMarginBottom: true,
530 })
531 )
532 ),
533
534 // Colors Panel
535 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
536 getTypoControl() && getTypoControl()({ label: __('Heading', 'blockenberg'), value: a.headingTypo, onChange: function (v) { set({ headingTypo: v }); } }),
537 getTypoControl() && getTypoControl()({ label: __('Sub-text', 'blockenberg'), value: a.subTextTypo, onChange: function (v) { set({ subTextTypo: v }); } })
538 ),
539 el(PanelColorSettings, {
540 title: 'Colors',
541 initialOpen: false,
542 colorSettings: [
543 { label: 'Container Background', value: a.containerBg, onChange: function (v) { set({ containerBg: v || '' }); } },
544 { label: 'Main Content Bg', value: a.mainBg, onChange: function (v) { set({ mainBg: v || '' }); } },
545 { label: 'Sidebar Background', value: a.sidebarBg, onChange: function (v) { set({ sidebarBg: v || '#f8fafc' }); } },
546 { label: 'Sidebar Border', value: a.sidebarBorderColor, onChange: function (v) { set({ sidebarBorderColor: v || '#e2e8f0' }); } },
547 { label: 'Heading Color', value: a.headingColor, onChange: function (v) { set({ headingColor: v || '#111827' }); } },
548 { label: 'Text Color', value: a.textColor, onChange: function (v) { set({ textColor: v || '#374151' }); } },
549 { label: 'Bullet Icon Color', value: a.bulletIconColor, onChange: function (v) { set({ bulletIconColor: v || '#6366f1' }); } },
550 { label: 'CTA Background', value: a.ctaBg, onChange: function (v) { set({ ctaBg: v || '#6366f1' }); } },
551 { label: 'CTA Text Color', value: a.ctaColor, onChange: function (v) { set({ ctaColor: v || '#ffffff' }); } },
552 { label: 'Star Color', value: a.starColor, onChange: function (v) { set({ starColor: v || '#f59e0b' }); } },
553 { label: 'Link Color', value: a.linkColor, onChange: function (v) { set({ linkColor: v || '#6366f1' }); } },
554 ]
555 })
556 ),
557
558 // Editor Preview
559 el('div', blockProps,
560 el('div', {
561 style: {
562 display: 'flex',
563 gap: a.gap + 'px',
564 padding: a.containerPadding + 'px',
565 alignItems: 'flex-start',
566 }
567 }, ...cols)
568 )
569 );
570 },
571
572 save: function (props) {
573 return el('div', useBlockProps.save(),
574 el('div', {
575 className: 'bkbg-ss-app',
576 'data-opts': JSON.stringify(props.attributes),
577 })
578 );
579 }
580 });
581 }() );
582