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 / thumbnail-gallery / index.js

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

556 lines 27.4 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 PanelColorSettings = wp.blockEditor.PanelColorSettings;
12 var PanelBody = wp.components.PanelBody;
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 var Button = wp.components.Button;
18
19 var _tc; function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
20 var _tv; function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
21 var _tvf = function (typo, prefix) { var fn = getTypoCssVars(); return fn ? fn(typo, prefix) : {}; };
22
23 /* ─── helpers ─── */
24 function updateItem(arr, idx, field, val) {
25 return arr.map(function (item, i) {
26 if (i !== idx) return item;
27 var p = {}; p[field] = val;
28 return Object.assign({}, item, p);
29 });
30 }
31
32 function moveItem(arr, from, to) {
33 var a = arr.slice();
34 var item = a.splice(from, 1)[0];
35 a.splice(to, 0, item);
36 return a;
37 }
38
39 /* ─── ImageEditor ─── */
40 function ImageEditor(props) {
41 var images = props.images;
42 var onChange = props.onChange;
43 var activeIdx = props.activeIdx;
44 var setActiveIdx = props.setActiveIdx;
45
46 return el(Fragment, null,
47 el('div', { className: 'bkbg-tgal-img-list' },
48 images.map(function (img, idx) {
49 return el('div', {
50 key: idx,
51 className: 'bkbg-tgal-img-item' + (idx === activeIdx ? ' is-active' : ''),
52 style: { display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '6px', padding: '6px', border: '1px solid ' + (idx === activeIdx ? '#6366f1' : '#e5e7eb'), borderRadius: '6px', cursor: 'pointer' },
53 onClick: function () { setActiveIdx(idx); }
54 },
55 el('div', {
56 style: {
57 width: '48px', height: '36px', flexShrink: 0, borderRadius: '4px',
58 background: img.url ? 'url(' + img.url + ') center/cover' : '#e5e7eb'
59 }
60 }),
61 el('div', { style: { flex: 1, minWidth: 0 } },
62 el('div', { style: { fontWeight: '600', fontSize: '12px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, img.title || img.alt || 'Image ' + (idx + 1)),
63 el('div', { style: { fontSize: '11px', color: '#9ca3af', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, img.caption || '')
64 ),
65 el('div', { style: { display: 'flex', flexDirection: 'column', gap: '2px' } },
66 el(Button, {
67 icon: 'arrow-up-alt2', isSmall: true,
68 disabled: idx === 0,
69 onClick: function (e) { e.stopPropagation(); onChange(moveItem(images, idx, idx - 1)); setActiveIdx(idx - 1); }
70 }),
71 el(Button, {
72 icon: 'arrow-down-alt2', isSmall: true,
73 disabled: idx === images.length - 1,
74 onClick: function (e) { e.stopPropagation(); onChange(moveItem(images, idx, idx + 1)); setActiveIdx(idx + 1); }
75 }),
76 el(Button, {
77 icon: 'no-alt', isSmall: true, isDestructive: true,
78 onClick: function (e) { e.stopPropagation(); var a = images.slice(); a.splice(idx, 1); onChange(a); setActiveIdx(Math.max(0, idx - 1)); }
79 })
80 )
81 );
82 }),
83 /* add button */
84 el(MediaUploadCheck, null,
85 el(MediaUpload, {
86 onSelect: function (media) {
87 var newImgs = Array.isArray(media)
88 ? media.map(function (m) { return { url: m.url || '', id: m.id || 0, alt: m.alt || '', caption: m.caption || '', title: m.title || m.alt || '' }; })
89 : [{ url: media.url || '', id: media.id || 0, alt: media.alt || '', caption: media.caption || '', title: media.title || media.alt || '' }];
90 onChange(images.concat(newImgs));
91 },
92 allowedTypes: ['image'],
93 multiple: true,
94 render: function (ref) {
95 return el(Button, {
96 onClick: ref.open,
97 variant: 'secondary',
98 style: { marginTop: '8px', width: '100%', justifyContent: 'center' }
99 }, __('+ Add Images', 'blockenberg'));
100 }
101 })
102 )
103 ),
104
105 /* active image fields */
106 activeIdx < images.length && el('div', { style: { marginTop: '12px', padding: '10px', background: '#f8fafc', borderRadius: '6px' } },
107 el('div', { style: { fontWeight: '600', fontSize: '12px', marginBottom: '8px', color: '#374151' } }, __('Image ' + (activeIdx + 1) + ' Details', 'blockenberg')),
108 el(TextControl, {
109 label: __('Title', 'blockenberg'),
110 value: images[activeIdx].title || '',
111 onChange: function (v) { onChange(updateItem(images, activeIdx, 'title', v)); },
112 __nextHasNoMarginBottom: true
113 }),
114 el('div', { style: { marginTop: '8px' } },
115 el(TextControl, {
116 label: __('Caption', 'blockenberg'),
117 value: images[activeIdx].caption || '',
118 onChange: function (v) { onChange(updateItem(images, activeIdx, 'caption', v)); },
119 __nextHasNoMarginBottom: true
120 })
121 ),
122 el('div', { style: { marginTop: '8px' } },
123 el(TextControl, {
124 label: __('Alt Text', 'blockenberg'),
125 value: images[activeIdx].alt || '',
126 onChange: function (v) { onChange(updateItem(images, activeIdx, 'alt', v)); },
127 __nextHasNoMarginBottom: true
128 })
129 ),
130 el('div', { style: { marginTop: '8px' } },
131 el(MediaUploadCheck, null,
132 el(MediaUpload, {
133 onSelect: function (media) {
134 var newImg = Object.assign({}, images[activeIdx], {
135 url: media.url || '', id: media.id || 0,
136 alt: images[activeIdx].alt || media.alt || '',
137 caption: images[activeIdx].caption || media.caption || '',
138 title: images[activeIdx].title || media.title || ''
139 });
140 onChange(updateItem(images, activeIdx, 'url', newImg.url));
141 },
142 allowedTypes: ['image'],
143 render: function (ref) {
144 return el(Button, { onClick: ref.open, variant: 'secondary', isSmall: true }, __('Replace Image', 'blockenberg'));
145 }
146 })
147 )
148 )
149 )
150 );
151 }
152
153 /* ─── GalleryPreview ─── */
154 function GalleryPreview(props) {
155 var a = props.attributes;
156 var images = a.images;
157 var activeState = useState(0);
158 var activeIdx = activeState[0];
159 var setActiveIdx = activeState[1];
160
161 if (!images || images.length === 0) {
162 return el('div', {
163 style: { padding: '40px', textAlign: 'center', background: '#f8fafc', borderRadius: '12px', color: '#9ca3af' }
164 }, __('Add images to preview the gallery', 'blockenberg'));
165 }
166
167 var activeImg = images[Math.min(activeIdx, images.length - 1)];
168 var isVert = a.thumbPosition === 'left' || a.thumbPosition === 'right';
169
170 var mainStyle = {
171 position: 'relative',
172 background: a.mainBg || '#000',
173 borderRadius: a.mainBorderRadius + 'px',
174 overflow: 'hidden',
175 aspectRatio: a.mainAspect || '16/9',
176 flex: '1 1 auto',
177 minWidth: 0
178 };
179
180 var thumbStrip = el('div', {
181 style: {
182 display: 'flex',
183 flexDirection: isVert ? 'column' : 'row',
184 gap: a.thumbGap + 'px',
185 overflowX: isVert ? 'hidden' : 'auto',
186 overflowY: isVert ? 'auto' : 'hidden',
187 flexShrink: 0,
188 padding: '4px'
189 }
190 },
191 images.map(function (img, idx) {
192 var isActive = idx === activeIdx;
193 return el('div', {
194 key: idx,
195 onClick: function () { setActiveIdx(idx); },
196 style: {
197 flexShrink: 0,
198 width: isVert ? a.thumbSize + 'px' : a.thumbSize * 1.4 + 'px',
199 height: a.thumbSize + 'px',
200 borderRadius: a.thumbBorderRadius + 'px',
201 overflow: 'hidden',
202 cursor: 'pointer',
203 border: isActive ? (a.thumbActiveBorderWidth || 2) + 'px solid ' + a.thumbActiveBorder : (a.thumbActiveBorderWidth || 2) + 'px solid transparent',
204 boxSizing: 'border-box',
205 transition: 'border-color 0.15s',
206 background: img.url ? 'url(' + img.url + ') center/' + (a.mainObjectFit || 'cover') : a.thumbBg || '#e5e7eb'
207 }
208 });
209 })
210 );
211
212 var mainArea = el('div', { style: mainStyle },
213 activeImg.url && el('img', {
214 src: activeImg.url,
215 alt: activeImg.alt || '',
216 style: { width: '100%', height: '100%', objectFit: a.mainObjectFit || 'cover', display: 'block' }
217 }),
218 /* caption */
219 a.showCaptions && activeImg.caption && el('div', {
220 className: 'bkbg-tgal-caption',
221 style: {
222 position: 'absolute', bottom: 0, left: 0, right: 0,
223 background: a.captionBg || 'rgba(0,0,0,0.6)',
224 color: a.captionColor || '#fff',
225 padding: '8px 14px'
226 }
227 }, activeImg.caption),
228 /* counter */
229 a.showCounter && el('div', {
230 className: 'bkbg-tgal-counter',
231 style: {
232 position: 'absolute', top: '10px', right: '10px',
233 background: a.counterBg || 'rgba(0,0,0,0.55)',
234 color: a.counterColor || '#fff',
235 borderRadius: '20px',
236 padding: '2px 10px'
237 }
238 }, (activeIdx + 1) + ' / ' + images.length),
239 /* zoom icon */
240 a.showZoom && el('div', {
241 style: {
242 position: 'absolute', top: '10px', left: '10px',
243 background: a.zoomIconBg || 'rgba(0,0,0,0.45)',
244 color: a.zoomIconColor || '#fff',
245 borderRadius: '6px',
246 padding: '4px 7px',
247 fontSize: '14px',
248 cursor: 'zoom-in'
249 }
250 }, ''),
251 /* arrows */
252 a.showArrows && images.length > 1 && el(Fragment, null,
253 el('div', {
254 style: {
255 position: 'absolute', left: '10px', top: '50%', transform: 'translateY(-50%)',
256 background: a.arrowBg || 'rgba(0,0,0,0.45)', color: a.arrowColor || '#fff',
257 width: '32px', height: '32px', borderRadius: '50%',
258 display: 'flex', alignItems: 'center', justifyContent: 'center',
259 cursor: 'pointer', fontSize: '16px'
260 },
261 onClick: function () { setActiveIdx((activeIdx - 1 + images.length) % images.length); }
262 }, ''),
263 el('div', {
264 style: {
265 position: 'absolute', right: '10px', top: '50%', transform: 'translateY(-50%)',
266 background: a.arrowBg || 'rgba(0,0,0,0.45)', color: a.arrowColor || '#fff',
267 width: '32px', height: '32px', borderRadius: '50%',
268 display: 'flex', alignItems: 'center', justifyContent: 'center',
269 cursor: 'pointer', fontSize: '16px'
270 },
271 onClick: function () { setActiveIdx((activeIdx + 1) % images.length); }
272 }, '')
273 )
274 );
275
276 var containerStyle = {
277 display: 'flex',
278 flexDirection: isVert ? 'row' : (a.thumbPosition === 'top' ? 'column-reverse' : 'column'),
279 gap: '12px'
280 };
281
282 if (a.thumbPosition === 'left') {
283 containerStyle.flexDirection = 'row';
284 } else if (a.thumbPosition === 'right') {
285 containerStyle.flexDirection = 'row-reverse';
286 }
287
288 return el('div', { style: containerStyle },
289 a.thumbPosition === 'top' ? thumbStrip : null,
290 a.thumbPosition === 'left' ? thumbStrip : null,
291 mainArea,
292 a.thumbPosition === 'right' ? thumbStrip : null,
293 a.thumbPosition === 'bottom' || !a.thumbPosition ? thumbStrip : null
294 );
295 }
296
297 /* ─── register ─── */
298 registerBlockType('blockenberg/thumbnail-gallery', {
299 edit: function (props) {
300 var a = props.attributes;
301 var set = props.setAttributes;
302 var imgIdxState = useState(0);
303 var imgIdx = imgIdxState[0];
304 var setImgIdx = imgIdxState[1];
305
306 var blockProps = useBlockProps((function () {
307 var s = {
308 paddingTop: (a.paddingTop || 0) + 'px',
309 paddingBottom: (a.paddingBottom || 0) + 'px',
310 background: a.bgColor
311 };
312 var tv = Object.assign({}, _tvf(a.captionTypo, '--bktg-cp-'), _tvf(a.counterTypo, '--bktg-ct-'));
313 return { style: Object.assign(s, tv) };
314 })());
315
316 var inspector = el(InspectorControls, null,
317 /* Images */
318 el(PanelBody, { title: __('Images', 'blockenberg'), initialOpen: true },
319 el(ImageEditor, {
320 images: a.images,
321 onChange: function (v) { set({ images: v }); },
322 activeIdx: imgIdx,
323 setActiveIdx: setImgIdx
324 })
325 ),
326
327 /* Layout */
328 el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: false },
329 el(SelectControl, {
330 label: __('Thumbnail Position', 'blockenberg'),
331 value: a.thumbPosition,
332 options: [
333 { value: 'bottom', label: 'Bottom' },
334 { value: 'top', label: 'Top' },
335 { value: 'left', label: 'Left' },
336 { value: 'right', label: 'Right' }
337 ],
338 onChange: function (v) { set({ thumbPosition: v }); },
339 __nextHasNoMarginBottom: true
340 }),
341 el('div', { style: { marginTop: '8px' } },
342 el(SelectControl, {
343 label: __('Main Aspect Ratio', 'blockenberg'),
344 value: a.mainAspect,
345 options: [
346 { value: '16/9', label: '16:9 Widescreen' },
347 { value: '4/3', label: '4:3 Standard' },
348 { value: '3/2', label: '3:2 Photo' },
349 { value: '1/1', label: '1:1 Square' },
350 { value: '3/4', label: '3:4 Portrait' }
351 ],
352 onChange: function (v) { set({ mainAspect: v }); },
353 __nextHasNoMarginBottom: true
354 })
355 ),
356 el('div', { style: { marginTop: '8px' } },
357 el(SelectControl, {
358 label: __('Image Fit', 'blockenberg'),
359 value: a.mainObjectFit,
360 options: [
361 { value: 'cover', label: 'Cover' },
362 { value: 'contain', label: 'Contain' }
363 ],
364 onChange: function (v) { set({ mainObjectFit: v }); },
365 __nextHasNoMarginBottom: true
366 })
367 ),
368 el('div', { style: { marginTop: '8px' } },
369 el(SelectControl, {
370 label: __('Animation', 'blockenberg'),
371 value: a.animationType,
372 options: [
373 { value: 'fade', label: 'Fade' },
374 { value: 'slide', label: 'Slide' }
375 ],
376 onChange: function (v) { set({ animationType: v }); },
377 __nextHasNoMarginBottom: true
378 })
379 ),
380 el('div', { style: { marginTop: '12px' } },
381 el(RangeControl, {
382 label: __('Max Width (px)', 'blockenberg'),
383 value: a.maxWidth,
384 min: 400, max: 1600, step: 20,
385 onChange: function (v) { set({ maxWidth: v }); },
386 __nextHasNoMarginBottom: true
387 })
388 )
389 ),
390
391 /* Thumbnails */
392 el(PanelBody, { title: __('Thumbnails', 'blockenberg'), initialOpen: false },
393 el(RangeControl, {
394 label: __('Thumbnail Size (px)', 'blockenberg'),
395 value: a.thumbSize,
396 min: 40, max: 160, step: 4,
397 onChange: function (v) { set({ thumbSize: v }); },
398 __nextHasNoMarginBottom: true
399 }),
400 el('div', { style: { marginTop: '8px' } },
401 el(RangeControl, {
402 label: __('Thumbnail Gap (px)', 'blockenberg'),
403 value: a.thumbGap,
404 min: 0, max: 24, step: 2,
405 onChange: function (v) { set({ thumbGap: v }); },
406 __nextHasNoMarginBottom: true
407 })
408 ),
409 el('div', { style: { marginTop: '8px' } },
410 el(RangeControl, {
411 label: __('Thumbnail Border Radius', 'blockenberg'),
412 value: a.thumbBorderRadius,
413 min: 0, max: 20,
414 onChange: function (v) { set({ thumbBorderRadius: v }); },
415 __nextHasNoMarginBottom: true
416 })
417 ),
418 el('div', { style: { marginTop: '8px' } },
419 el(RangeControl, {
420 label: __('Active Border Width (px)', 'blockenberg'),
421 value: a.thumbActiveBorderWidth,
422 min: 1, max: 6,
423 onChange: function (v) { set({ thumbActiveBorderWidth: v }); },
424 __nextHasNoMarginBottom: true
425 })
426 )
427 ),
428
429 /* Options */
430 el(PanelBody, { title: __('Options', 'blockenberg'), initialOpen: false },
431 el(ToggleControl, {
432 label: __('Show Captions', 'blockenberg'),
433 checked: a.showCaptions,
434 onChange: function (v) { set({ showCaptions: v }); },
435 __nextHasNoMarginBottom: true
436 }),
437 el(ToggleControl, {
438 label: __('Show Counter', 'blockenberg'),
439 checked: a.showCounter,
440 onChange: function (v) { set({ showCounter: v }); },
441 __nextHasNoMarginBottom: true
442 }),
443 el(ToggleControl, {
444 label: __('Show Navigation Arrows', 'blockenberg'),
445 checked: a.showArrows,
446 onChange: function (v) { set({ showArrows: v }); },
447 __nextHasNoMarginBottom: true
448 }),
449 el(ToggleControl, {
450 label: __('Show Zoom Button', 'blockenberg'),
451 checked: a.showZoom,
452 onChange: function (v) { set({ showZoom: v }); },
453 __nextHasNoMarginBottom: true
454 }),
455 el(ToggleControl, {
456 label: __('Auto-Play', 'blockenberg'),
457 checked: a.autoPlay,
458 onChange: function (v) { set({ autoPlay: v }); },
459 __nextHasNoMarginBottom: true
460 }),
461 a.autoPlay && el('div', { style: { marginTop: '8px' } },
462 el(RangeControl, {
463 label: __('Auto-Play Delay (ms)', 'blockenberg'),
464 value: a.autoPlayDelay,
465 min: 1000, max: 10000, step: 500,
466 onChange: function (v) { set({ autoPlayDelay: v }); },
467 __nextHasNoMarginBottom: true
468 })
469 ),
470 el('div', { style: { marginTop: '8px' } },
471 el(RangeControl, {
472 label: __('Main Border Radius', 'blockenberg'),
473 value: a.mainBorderRadius,
474 min: 0, max: 32,
475 onChange: function (v) { set({ mainBorderRadius: v }); },
476 __nextHasNoMarginBottom: true
477 })
478 ),
479 el('div', { style: { marginTop: '8px' } },
480 el(RangeControl, {
481 label: __('Padding Top (px)', 'blockenberg'),
482 value: a.paddingTop,
483 min: 0, max: 120, step: 4,
484 onChange: function (v) { set({ paddingTop: v }); },
485 __nextHasNoMarginBottom: true
486 })
487 ),
488 el('div', { style: { marginTop: '8px' } },
489 el(RangeControl, {
490 label: __('Padding Bottom (px)', 'blockenberg'),
491 value: a.paddingBottom,
492 min: 0, max: 120, step: 4,
493 onChange: function (v) { set({ paddingBottom: v }); },
494 __nextHasNoMarginBottom: true
495 })
496 )
497 ),
498
499 /* Typography */
500 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
501 getTypoControl() && el(getTypoControl(), {
502 label: __('Caption Typography', 'blockenberg'),
503 value: a.captionTypo || {},
504 onChange: function (v) { set({ captionTypo: v }); }
505 }),
506 getTypoControl() && el(getTypoControl(), {
507 label: __('Counter Typography', 'blockenberg'),
508 value: a.counterTypo || {},
509 onChange: function (v) { set({ counterTypo: v }); }
510 })
511 ),
512
513 /* Colors */
514 el(PanelColorSettings, {
515 title: __('Colors', 'blockenberg'),
516 initialOpen: false,
517 colorSettings: [
518 { value: a.bgColor, onChange: function (v) { set({ bgColor: v }); }, label: __('Background', 'blockenberg') },
519 { value: a.mainBg, onChange: function (v) { set({ mainBg: v }); }, label: __('Main Image Background', 'blockenberg') },
520 { value: a.captionBg, onChange: function (v) { set({ captionBg: v }); }, label: __('Caption Background', 'blockenberg') },
521 { value: a.captionColor, onChange: function (v) { set({ captionColor: v }); }, label: __('Caption Text', 'blockenberg') },
522 { value: a.counterBg, onChange: function (v) { set({ counterBg: v }); }, label: __('Counter Background', 'blockenberg') },
523 { value: a.counterColor, onChange: function (v) { set({ counterColor: v }); }, label: __('Counter Text', 'blockenberg') },
524 { value: a.thumbActiveBorder, onChange: function (v) { set({ thumbActiveBorder: v }); }, label: __('Active Thumb Border', 'blockenberg') },
525 { value: a.thumbBg, onChange: function (v) { set({ thumbBg: v }); }, label: __('Thumbnail Background', 'blockenberg') },
526 { value: a.arrowBg, onChange: function (v) { set({ arrowBg: v }); }, label: __('Arrow Background', 'blockenberg') },
527 { value: a.arrowColor, onChange: function (v) { set({ arrowColor: v }); }, label: __('Arrow Icon', 'blockenberg') }
528 ]
529 })
530 );
531
532 var previewWrap = el('div', {
533 style: { maxWidth: a.maxWidth + 'px', margin: '0 auto' }
534 },
535 el(GalleryPreview, { attributes: a })
536 );
537
538 return el(Fragment, null,
539 inspector,
540 el('div', blockProps, previewWrap)
541 );
542 },
543
544 save: function (props) {
545 var a = props.attributes;
546 var bp = useBlockProps.save((function () {
547 var tv = Object.assign({}, _tvf(a.captionTypo, '--bktg-cp-'), _tvf(a.counterTypo, '--bktg-ct-'));
548 return { style: tv };
549 })());
550 return el('div', bp,
551 el('div', { className: 'bkbg-tgal-app', 'data-opts': JSON.stringify(a) })
552 );
553 }
554 });
555 }() );
556