PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.12
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.12
2.0.14 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 / gallery-showcase / index.js

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

371 lines 26.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 MediaUpload = wp.blockEditor.MediaUpload;
9 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
10 var useBlockProps = wp.blockEditor.useBlockProps;
11 var PanelBody = wp.components.PanelBody;
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 var ColorPicker = wp.components.ColorPicker;
18 var Popover = wp.components.Popover;
19
20 var _gsTC, _gsTV;
21 function _tc() { return _gsTC || (_gsTC = window.bkbgTypographyControl); }
22 function _tv() { return _gsTV || (_gsTV = window.bkbgTypoCssVars); }
23
24 var THUMB_POS_OPTIONS = [
25 { label: __('Bottom', 'blockenberg'), value: 'bottom' },
26 { label: __('Top', 'blockenberg'), value: 'top' },
27 { label: __('Left', 'blockenberg'), value: 'left' },
28 { label: __('Right', 'blockenberg'), value: 'right' },
29 ];
30 var ASPECT_OPTIONS = [
31 { label: '4 / 3', value: '4/3' },
32 { label: '16 / 9', value: '16/9' },
33 { label: '1 / 1', value: '1/1' },
34 { label: '3 / 2', value: '3/2' },
35 ];
36 var FIT_OPTIONS = [
37 { label: __('Cover', 'blockenberg'), value: 'cover' },
38 { label: __('Contain', 'blockenberg'), value: 'contain' },
39 ];
40 var CAPTION_POS_OPTIONS = [
41 { label: __('Overlay', 'blockenberg'), value: 'overlay' },
42 { label: __('Below', 'blockenberg'), value: 'below' },
43 ];
44 var ARROW_STYLE_OPTIONS = [
45 { label: __('Circle', 'blockenberg'), value: 'circle' },
46 { label: __('Square', 'blockenberg'), value: 'square' },
47 { label: __('Minimal', 'blockenberg'), value: 'minimal' },
48 ];
49 var TRANSITION_OPTIONS = [
50 { label: __('Fade', 'blockenberg'), value: 'fade' },
51 { label: __('Slide', 'blockenberg'), value: 'slide' },
52 ];
53
54 function normalizeCaption(cap) {
55 if (!cap) return '';
56 var s = '';
57 if (typeof cap === 'string') s = cap;
58 else if (typeof cap === 'object') s = (cap.raw || cap.rendered || '').toString();
59 else s = String(cap);
60 // WP media captions may include simple HTML wrappers
61 return s.replace(/<[^>]+>/g, '').replace(/&nbsp;/g, ' ').trim();
62 }
63
64 // Match frontend.js: data-caption || alt (title is saved as alt when caption is empty).
65 function imageCaptionText(img) {
66 if (!img) return '';
67 return normalizeCaption(img.caption) || normalizeCaption(img.title) || normalizeCaption(img.alt) || '';
68 }
69
70 function isDefaultOverlayWhite(color) {
71 if (!color) return true;
72 var c = String(color).trim().toLowerCase();
73 return c === '#fff' || c === '#ffffff' || c === 'rgb(255, 255, 255)' || c === 'rgba(255, 255, 255, 1)';
74 }
75
76 function buildWrapStyle(a) {
77 var aspect = (a.mainAspect || '4/3').replace(/\s+/g, '');
78 var belowColor = (!a.captionColor || isDefaultOverlayWhite(a.captionColor))
79 ? '#6b7280'
80 : a.captionColor;
81 var typoCap = (_tv() || function () { return {}; })(a.typoCaption, '--bkbg-gs-cap-');
82 var typoCnt = (_tv() || function () { return {}; })(a.typoCounter, '--bkbg-gs-cnt-');
83 return Object.assign(
84 {
85 '--bkbg-gs-active-border': a.activeBorderColor,
86 '--bkbg-gs-active-bw': a.activeBorderWidth + 'px',
87 '--bkbg-gs-main-r': a.mainRadius + 'px',
88 '--bkbg-gs-thumb-r': a.thumbnailRadius + 'px',
89 '--bkbg-gs-thumb-gap': a.thumbnailGap + 'px',
90 '--bkbg-gs-aspect': aspect.replace('/', ' / '),
91 '--bkbg-gs-object-fit': a.mainObjectFit || 'cover',
92 '--bkbg-gs-main-shadow': a.mainShadow ? '0 16px 48px rgba(0,0,0,0.12)' : 'none',
93 '--bkbg-gs-caption-bg': a.captionBg || 'rgba(0,0,0,0.55)',
94 '--bkbg-gs-caption-color': a.captionColor || '#ffffff',
95 '--bkbg-gs-caption-below-color': belowColor,
96 '--bkbg-gs-arrow-bg': a.arrowBg || 'rgba(0,0,0,0.45)',
97 '--bkbg-gs-arrow-color': a.arrowColor,
98 '--bkbg-gs-counter-bg': a.counterBg || 'rgba(0,0,0,0.55)',
99 '--bkbg-gs-counter-color': a.counterColor || '#ffffff',
100 '--bkbg-gs-lightbox-bg': a.lightboxOverlayBg || a.lightboxBg || 'rgba(0,0,0,0.9)',
101 paddingTop: (a.paddingTop || 0) + 'px',
102 paddingBottom: (a.paddingBottom || 0) + 'px',
103 backgroundColor: a.bgColor || undefined,
104 },
105 typoCap,
106 typoCnt
107 );
108 }
109
110 function renderColorControl(key, label, value, onChange, openKey, setOpenKey) {
111 var isOpen = openKey === key;
112 return el('div', { key: key, style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '4px 0', gap: '8px' } },
113 el('span', { style: { fontSize: '12px', color: '#1e1e1e', flex: 1, lineHeight: 1.4 } }, label),
114 el('div', { style: { position: 'relative', flexShrink: 0 } },
115 el('button', { type: 'button', title: value || 'default', onClick: function () { setOpenKey(isOpen ? null : key); },
116 style: { width: '28px', height: '28px', borderRadius: '4px', border: isOpen ? '2px solid #007cba' : '2px solid #ddd', cursor: 'pointer', padding: 0, background: value || '#cccccc' }
117 }),
118 isOpen && el(Popover, { position: 'bottom left', onClose: function () { setOpenKey(null); } },
119 el('div', { style: { padding: '8px' } },
120 el(ColorPicker, { color: value, enableAlpha: true, onChange: onChange })
121 )
122 )
123 )
124 );
125 }
126
127 function ratioPercent(r) {
128 var parts = r.split('/');
129 return (parseInt(parts[1], 10) / parseInt(parts[0], 10) * 100).toFixed(2) + '%';
130 }
131
132 registerBlockType('blockenberg/gallery-showcase', {
133 title: __('Gallery Showcase', 'blockenberg'),
134 icon: 'format-gallery',
135 category: 'bkbg-media',
136
137 edit: function (props) {
138 var a = props.attributes;
139 var setAttributes = props.setAttributes;
140
141 var openColorKeyState = useState(null);
142 var openColorKey = openColorKeyState[0];
143 var setOpenColorKey = openColorKeyState[1];
144
145 var activeState = useState(0);
146 var active = activeState[0];
147 var setActive = activeState[1];
148
149 var blockProps = useBlockProps({ className: 'bkbg-gs-wrapper', style: buildWrapStyle(a) });
150
151 function cc(key, label, attrKey) {
152 return renderColorControl(key, label, a[attrKey], function (val) {
153 var upd = {}; upd[attrKey] = val; setAttributes(upd);
154 }, openColorKey, setOpenColorKey);
155 }
156
157 var images = Array.isArray(a.images) ? a.images : [];
158 var safeActive = Math.min(active, Math.max(0, images.length - 1));
159 var currentImage = images[safeActive];
160 // Same source as frontend (caption, else title/alt used as img alt).
161 var currentCaption = imageCaptionText(currentImage);
162 var showCap = a.showCaption !== false;
163 var capPos = a.captionPosition || 'overlay';
164 var belowCaptionColor = (!a.captionColor || isDefaultOverlayWhite(a.captionColor))
165 ? '#6b7280'
166 : a.captionColor;
167 var mainAspectCss = (a.mainAspect || '4/3').replace(/\s+/g, '').replace('/', ' / ');
168
169 var isVertical = a.thumbnailPosition === 'left' || a.thumbnailPosition === 'right';
170 var thumbAfter = a.thumbnailPosition === 'bottom' || a.thumbnailPosition === 'right';
171
172 var mainBox = el('div', { className: 'bkbg-gs-main', style: { position: 'relative', borderRadius: a.mainRadius + 'px', overflow: 'hidden', background: '#f3f4f6', aspectRatio: mainAspectCss, width: '100%' } },
173 currentImage
174 ? el('img', { src: currentImage.imageUrl, alt: currentImage.title || '', style: { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: a.mainObjectFit || 'cover', display: 'block', zIndex: 0 } })
175 : el('div', { style: { position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: '12px', color: '#9ca3af' } },
176 el('span', { style: { fontSize: '48px' } }, '🖼'),
177 el('p', { style: { margin: 0, fontSize: '14px' } }, __('No images yet — add images below in the sidebar.', 'blockenberg'))
178 ),
179 a.showCounter && images.length > 0 && el('div', { className: 'bkbg-gs-counter', style: { position: 'absolute', top: '12px', right: '12px', background: 'rgba(0,0,0,0.55)', color: '#fff', borderRadius: '99px', padding: '4px 12px', zIndex: 2 } },
180 (safeActive + 1) + ' / ' + images.length
181 ),
182 // Always render caption chrome in the editor when enabled (fallback matches frontend alt/title).
183 showCap && capPos === 'overlay' && el('div', {
184 className: 'bkbg-gs-caption bkbg-gs-caption--overlay',
185 style: {
186 position: 'absolute',
187 bottom: 0,
188 left: 0,
189 right: 0,
190 padding: '10px 14px',
191 background: a.captionBg || 'rgba(0,0,0,0.55)',
192 color: a.captionColor || '#ffffff',
193 fontSize: '14px',
194 lineHeight: '1.4',
195 zIndex: 5,
196 pointerEvents: 'none',
197 opacity: currentCaption ? 1 : 0.85
198 }
199 }, currentCaption || __('Add caption in Images panel…', 'blockenberg')),
200 a.showArrows && images.length > 1 && el(Fragment, null,
201 el('button', { type: 'button', onClick: function () { setActive((safeActive - 1 + images.length) % images.length); }, style: { position: 'absolute', top: '50%', left: '10px', transform: 'translateY(-50%)', background: 'rgba(0,0,0,0.45)', color: '#fff', border: 'none', borderRadius: a.arrowStyle === 'circle' ? '50%' : a.arrowStyle === 'square' ? '4px' : '0', width: '36px', height: '36px', cursor: 'pointer', fontSize: '18px', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 3 } }, '‹'),
202 el('button', { type: 'button', onClick: function () { setActive((safeActive + 1) % images.length); }, style: { position: 'absolute', top: '50%', right: '10px', transform: 'translateY(-50%)', background: 'rgba(0,0,0,0.45)', color: '#fff', border: 'none', borderRadius: a.arrowStyle === 'circle' ? '50%' : a.arrowStyle === 'square' ? '4px' : '0', width: '36px', height: '36px', cursor: 'pointer', fontSize: '18px', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 3 } }, '›')
203 )
204 );
205
206 var thumbStrip = images.length > 0 && el('div', { className: 'bkbg-gs-thumbs', style: { display: 'flex', flexDirection: isVertical ? 'column' : 'row', gap: a.thumbnailGap + 'px', overflowX: isVertical ? 'hidden' : 'auto', overflowY: isVertical ? 'auto' : 'hidden', flexShrink: 0, maxWidth: isVertical ? '120px' : 'none', padding: '4px 0' } },
207 images.map(function (img, i) {
208 var isActive = i === safeActive;
209 return el('button', { key: img.imageId || i, type: 'button', onClick: function () { setActive(i); }, style: { flexShrink: 0, width: isVertical ? '100%' : '80px', height: '60px', borderRadius: a.thumbnailRadius + 'px', overflow: 'hidden', border: isActive ? (a.activeBorderWidth + 'px solid ' + a.activeBorderColor) : '2px solid transparent', padding: 0, cursor: 'pointer', background: '#f3f4f6', opacity: isActive ? 1 : 0.7, transition: 'all 0.15s ease' } },
210 img.imageUrl && el('img', { src: img.imageUrl, alt: '', style: { width: '100%', height: '100%', objectFit: 'cover', display: 'block' } })
211 );
212 })
213 );
214
215 var flexDir = isVertical
216 ? (a.thumbnailPosition === 'left' ? 'row-reverse' : 'row')
217 : (a.thumbnailPosition === 'top' ? 'column-reverse' : 'column');
218
219 return el(Fragment, null,
220 el(InspectorControls, null,
221 el(PanelBody, { title: __('Display', 'blockenberg'), initialOpen: true },
222 el(SelectControl, { label: __('Thumbnail position', 'blockenberg'), value: a.thumbnailPosition, options: THUMB_POS_OPTIONS, onChange: function (v) { setAttributes({ thumbnailPosition: v }); } }),
223 el(SelectControl, { label: __('Main aspect ratio', 'blockenberg'), value: a.mainAspect, options: ASPECT_OPTIONS, onChange: function (v) { setAttributes({ mainAspect: v }); } }),
224 el(SelectControl, { label: __('Main fit', 'blockenberg'), value: a.mainObjectFit, options: FIT_OPTIONS, onChange: function (v) { setAttributes({ mainObjectFit: v }); } }),
225 el(RangeControl, { label: __('Main radius (px)', 'blockenberg'), value: a.mainRadius, min: 0, max: 40, onChange: function (v) { setAttributes({ mainRadius: v }); } }),
226 el(RangeControl, { label: __('Thumbnail radius (px)','blockenberg'),value: a.thumbnailRadius, min: 0, max: 20, onChange: function (v) { setAttributes({ thumbnailRadius: v }); } }),
227 el(RangeControl, { label: __('Thumbnail gap (px)', 'blockenberg'), value: a.thumbnailGap, min: 2, max: 24, onChange: function (v) { setAttributes({ thumbnailGap: v }); } }),
228 el(ToggleControl, { label: __('Show image shadow', 'blockenberg'), checked: a.mainShadow, onChange: function (v) { setAttributes({ mainShadow: v }); }, __nextHasNoMarginBottom: true }),
229 el(ToggleControl, { label: __('Show counter badge', 'blockenberg'), checked: a.showCounter, onChange: function (v) { setAttributes({ showCounter: v }); }, __nextHasNoMarginBottom: true })
230 ),
231 el(PanelBody, { title: __('Captions', 'blockenberg'), initialOpen: true },
232 el(ToggleControl, { label: __('Show captions', 'blockenberg'), checked: showCap, onChange: function (v) { setAttributes({ showCaption: v }); }, __nextHasNoMarginBottom: true }),
233 showCap && el(SelectControl, { label: __('Caption position', 'blockenberg'), value: capPos, options: CAPTION_POS_OPTIONS, onChange: function (v) { setAttributes({ captionPosition: v }); } })
234 ),
235 el(PanelBody, { title: __('Navigation', 'blockenberg'), initialOpen: false },
236 el(ToggleControl, { label: __('Show arrows', 'blockenberg'), checked: a.showArrows, onChange: function (v) { setAttributes({ showArrows: v }); }, __nextHasNoMarginBottom: true }),
237 a.showArrows && el(SelectControl, { label: __('Arrow style', 'blockenberg'), value: a.arrowStyle, options: ARROW_STYLE_OPTIONS, onChange: function (v) { setAttributes({ arrowStyle: v }); } }),
238 el(SelectControl, { label: __('Transition type', 'blockenberg'), value: a.transitionType, options: TRANSITION_OPTIONS, onChange: function (v) { setAttributes({ transitionType: v }); } }),
239 el(RangeControl, { label: __('Transition duration (ms)', 'blockenberg'), value: a.transitionDuration, min: 100, max: 1000, step: 50, onChange: function (v) { setAttributes({ transitionDuration: v }); } })
240 ),
241 el(PanelBody, { title: __('Lightbox', 'blockenberg'), initialOpen: false },
242 el(ToggleControl, { label: __('Enable lightbox', 'blockenberg'), checked: a.enableLightbox, onChange: function (v) { setAttributes({ enableLightbox: v }); }, __nextHasNoMarginBottom: true }),
243 el(ToggleControl, { label: __('Keyboard navigation','blockenberg'), checked: a.enableKeyboard, onChange: function (v) { setAttributes({ enableKeyboard: v }); }, __nextHasNoMarginBottom: true }),
244 el(ToggleControl, { label: __('Show zoom button', 'blockenberg'), checked: a.showZoom, onChange: function (v) { setAttributes({ showZoom: v }); }, __nextHasNoMarginBottom: true })
245 ),
246
247 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
248 _tc() && _tc()({ label: __('Caption', 'blockenberg'), value: a.typoCaption, onChange: function (v) { setAttributes({ typoCaption: v }); } }),
249 _tc() && _tc()({ label: __('Counter', 'blockenberg'), value: a.typoCounter, onChange: function (v) { setAttributes({ typoCounter: v }); } })
250 ),
251 el(PanelBody, { title: __('Colors', 'blockenberg'), initialOpen: false },
252 cc('activeBorderColor', __('Active thumb border', 'blockenberg'), 'activeBorderColor'),
253 el(RangeControl, { label: __('Active border width (px)', 'blockenberg'), value: a.activeBorderWidth, min: 1, max: 6, onChange: function (v) { setAttributes({ activeBorderWidth: v }); } }),
254 cc('captionBg', __('Caption background', 'blockenberg'), 'captionBg'),
255 cc('captionColor', __('Caption text', 'blockenberg'), 'captionColor'),
256 cc('arrowBg', __('Arrow background', 'blockenberg'), 'arrowBg'),
257 cc('arrowColor', __('Arrow icon', 'blockenberg'), 'arrowColor'),
258 cc('counterBg', __('Counter background', 'blockenberg'), 'counterBg'),
259 cc('counterColor', __('Counter text', 'blockenberg'), 'counterColor'),
260 cc('lightboxOverlayBg', __('Lightbox overlay', 'blockenberg'), 'lightboxOverlayBg'),
261 cc('bgColor', __('Section background', 'blockenberg'), 'bgColor')
262 ),
263 el(PanelBody, { title: __('Spacing', 'blockenberg'), initialOpen: false },
264 el(RangeControl, { label: __('Padding top (px)', 'blockenberg'), value: a.paddingTop, min: 0, max: 200, onChange: function (v) { setAttributes({ paddingTop: v }); } }),
265 el(RangeControl, { label: __('Padding bottom (px)', 'blockenberg'), value: a.paddingBottom, min: 0, max: 200, onChange: function (v) { setAttributes({ paddingBottom: v }); } })
266 ),
267 el(PanelBody, { title: __('Images', 'blockenberg'), initialOpen: false },
268 el(MediaUploadCheck, null,
269 el(MediaUpload, {
270 onSelect: function (medias) {
271 var list = Array.isArray(medias) ? medias : [medias];
272 var newImages = list.map(function (m) {
273 var title = (m.title && (typeof m.title === 'object' ? (m.title.raw || m.title.rendered || '') : m.title)) || '';
274 var cap = normalizeCaption(m.caption) || normalizeCaption(m.alt) || '';
275 return {
276 imageUrl: m.url,
277 imageId: m.id,
278 caption: cap,
279 title: title
280 };
281 });
282 setAttributes({ images: a.images.concat(newImages) });
283 },
284 allowedTypes: ['image'], multiple: true, value: a.images.map(function (i) { return i.imageId; }),
285 render: function (p) { return el(Button, { onClick: p.open, variant: 'primary', style: { marginBottom: '12px' } }, __('+ Add Images', 'blockenberg')); }
286 })
287 ),
288 a.images.map(function (img, i) {
289 return el('div', { key: img.imageId || i, style: { display: 'flex', gap: '8px', alignItems: 'flex-start', marginBottom: '10px', padding: '8px', background: '#f9f9f9', borderRadius: '6px' } },
290 el('img', { src: img.imageUrl, style: { width: '56px', height: '40px', objectFit: 'cover', borderRadius: '4px', flexShrink: 0 } }),
291 el('div', { style: { flex: 1, minWidth: 0 } },
292 el(TextControl, { placeholder: normalizeCaption(img.title) || __('Caption…', 'blockenberg'), value: normalizeCaption(img.caption), onChange: function (v) { setAttributes({ images: a.images.map(function (im, idx) { if (idx !== i) return im; return Object.assign({}, im, { caption: v }); }) }); } })
293 ),
294 el(Button, { onClick: function () { setAttributes({ images: a.images.filter(function (_, idx) { return idx !== i; }) }); if (safeActive >= i) setActive(Math.max(0, safeActive - 1)); }, isDestructive: true, variant: 'tertiary', size: 'compact' }, '✕')
295 );
296 })
297 )
298 ),
299
300 el('div', blockProps,
301 el('div', { className: 'bkbg-gs-showcase bkbg-gs--thumb-' + a.thumbnailPosition, style: { display: 'flex', flexDirection: flexDir, gap: '14px', alignItems: isVertical ? 'flex-start' : 'stretch' } },
302 thumbAfter ? mainBox : thumbStrip,
303 thumbAfter ? thumbStrip : mainBox
304 ),
305 showCap && capPos === 'below' && el('p', { className: 'bkbg-gs-below-caption', style: { textAlign: 'center', color: belowCaptionColor, margin: '10px 0 0', fontSize: '14px', lineHeight: '1.4', opacity: currentCaption ? 1 : 0.7 } }, currentCaption || __('Add caption in Images panel…', 'blockenberg'))
306 )
307 );
308 },
309
310 save: function (props) {
311 var a = props.attributes;
312 var isVertical = a.thumbnailPosition === 'left' || a.thumbnailPosition === 'right';
313 var thumbAfter = a.thumbnailPosition === 'bottom' || a.thumbnailPosition === 'right';
314 var flexDir = isVertical
315 ? (a.thumbnailPosition === 'left' ? 'row-reverse' : 'row')
316 : (a.thumbnailPosition === 'top' ? 'column-reverse' : 'column');
317 var showCap = a.showCaption !== false;
318 var capPos = a.captionPosition || 'overlay';
319 var firstCaption = a.images[0] ? imageCaptionText(a.images[0]) : '';
320
321 var mainBox = el('div', {
322 className: 'bkbg-gs-main',
323 'data-transition': a.transitionType || a.transition || 'fade',
324 'data-duration': a.transitionDuration || 320,
325 style: { position: 'relative', borderRadius: a.mainRadius + 'px', overflow: 'hidden' }
326 },
327 el('div', { className: 'bkbg-gs-main-images' },
328 a.images.map(function (img, i) {
329 var cap = imageCaptionText(img);
330 return el('div', {
331 key: i,
332 className: 'bkbg-gs-slide' + (i === 0 ? ' is-active' : ''),
333 'data-idx': i
334 },
335 el('img', {
336 src: img.imageUrl,
337 alt: img.title || cap || '',
338 loading: i === 0 ? 'eager' : 'lazy',
339 'data-caption': cap
340 })
341 );
342 })
343 ),
344 a.showCounter && a.images.length > 0 && el('div', { className: 'bkbg-gs-counter', 'aria-live': 'polite' }, '1 / ' + a.images.length),
345 showCap && capPos === 'overlay' && el('div', { className: 'bkbg-gs-caption bkbg-gs-caption--overlay', 'aria-live': 'polite' }, firstCaption),
346 a.showArrows && a.images.length > 1 && el(Fragment, null,
347 el('button', { type: 'button', className: 'bkbg-gs-arrow bkbg-gs-arrow--prev bkbg-gs-arrow--' + a.arrowStyle, 'aria-label': __('Previous', 'blockenberg') }, '‹'),
348 el('button', { type: 'button', className: 'bkbg-gs-arrow bkbg-gs-arrow--next bkbg-gs-arrow--' + a.arrowStyle, 'aria-label': __('Next', 'blockenberg') }, '›')
349 ),
350 a.enableLightbox && el('button', { type: 'button', className: 'bkbg-gs-lightbox-trigger', 'aria-label': __('Open lightbox', 'blockenberg') }, '⛶')
351 );
352
353 var thumbStrip = el('div', { className: 'bkbg-gs-thumbs', role: 'list', style: { display: 'flex', flexDirection: isVertical ? 'column' : 'row', gap: a.thumbnailGap + 'px' } },
354 a.images.map(function (img, i) {
355 return el('button', { key: i, type: 'button', role: 'listitem', className: 'bkbg-gs-thumb' + (i === 0 ? ' is-active' : ''), 'data-idx': i, 'aria-label': __('Image', 'blockenberg') + ' ' + (i + 1) },
356 el('img', { src: img.imageUrl, alt: img.title || '', loading: 'lazy', style: { width: '100%', height: '100%', objectFit: 'cover', display: 'block' } })
357 );
358 })
359 );
360
361 return el('div', wp.blockEditor.useBlockProps.save({ className: 'bkbg-gs-wrapper', style: buildWrapStyle(a), 'data-lightbox': a.enableLightbox ? '1' : undefined, 'data-keyboard': a.enableKeyboard ? '1' : undefined }),
362 el('div', { className: 'bkbg-gs-showcase bkbg-gs--thumb-' + a.thumbnailPosition, style: { display: 'flex', flexDirection: flexDir, gap: '14px', alignItems: isVertical ? 'flex-start' : 'stretch' } },
363 thumbAfter ? mainBox : thumbStrip,
364 thumbAfter ? thumbStrip : mainBox
365 ),
366 showCap && capPos === 'below' && el('p', { className: 'bkbg-gs-below-caption', 'aria-live': 'polite' }, firstCaption)
367 );
368 }
369 });
370 }() );
371