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

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

288 lines 23.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 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 buildWrapStyle(a) {
55 return Object.assign(
56 {
57 '--bkbg-gs-active-border': a.activeBorderColor,
58 '--bkbg-gs-active-bw': a.activeBorderWidth + 'px',
59 '--bkbg-gs-main-r': a.mainRadius + 'px',
60 '--bkbg-gs-thumb-r': a.thumbnailRadius + 'px',
61 '--bkbg-gs-thumb-gap': a.thumbnailGap + 'px',
62 '--bkbg-gs-caption-bg': a.captionBg || 'rgba(0,0,0,0.55)',
63 '--bkbg-gs-caption-color': a.captionColor,
64 '--bkbg-gs-arrow-bg': a.arrowBg || 'rgba(0,0,0,0.45)',
65 '--bkbg-gs-arrow-color': a.arrowColor,
66 '--bkbg-gs-counter-bg': a.counterBg || 'rgba(0,0,0,0.55)',
67 '--bkbg-gs-counter-color': a.counterColor || '#ffffff',
68 '--bkbg-gs-lightbox-bg': a.lightboxOverlayBg || 'rgba(0,0,0,0.9)',
69 paddingTop: a.paddingTop + 'px',
70 paddingBottom: a.paddingBottom + 'px',
71 backgroundColor: a.bgColor || undefined,
72 },
73 _tv()(a.typoCaption, '--bkbg-gs-cap-'),
74 _tv()(a.typoCounter, '--bkbg-gs-cnt-')
75 );
76 }
77
78 function renderColorControl(key, label, value, onChange, openKey, setOpenKey) {
79 var isOpen = openKey === key;
80 return el('div', { key: key, style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '4px 0', gap: '8px' } },
81 el('span', { style: { fontSize: '12px', color: '#1e1e1e', flex: 1, lineHeight: 1.4 } }, label),
82 el('div', { style: { position: 'relative', flexShrink: 0 } },
83 el('button', { type: 'button', title: value || 'default', onClick: function () { setOpenKey(isOpen ? null : key); },
84 style: { width: '28px', height: '28px', borderRadius: '4px', border: isOpen ? '2px solid #007cba' : '2px solid #ddd', cursor: 'pointer', padding: 0, background: value || '#cccccc' }
85 }),
86 isOpen && el(Popover, { position: 'bottom left', onClose: function () { setOpenKey(null); } },
87 el('div', { style: { padding: '8px' } },
88 el(ColorPicker, { color: value, enableAlpha: true, onChange: onChange })
89 )
90 )
91 )
92 );
93 }
94
95 function ratioPercent(r) {
96 var parts = r.split('/');
97 return (parseInt(parts[1], 10) / parseInt(parts[0], 10) * 100).toFixed(2) + '%';
98 }
99
100 registerBlockType('blockenberg/gallery-showcase', {
101 title: __('Gallery Showcase', 'blockenberg'),
102 icon: 'format-gallery',
103 category: 'bkbg-media',
104
105 edit: function (props) {
106 var a = props.attributes;
107 var setAttributes = props.setAttributes;
108
109 var openColorKeyState = useState(null);
110 var openColorKey = openColorKeyState[0];
111 var setOpenColorKey = openColorKeyState[1];
112
113 var activeState = useState(0);
114 var active = activeState[0];
115 var setActive = activeState[1];
116
117 var blockProps = useBlockProps({ style: buildWrapStyle(a) });
118
119 function cc(key, label, attrKey) {
120 return renderColorControl(key, label, a[attrKey], function (val) {
121 var upd = {}; upd[attrKey] = val; setAttributes(upd);
122 }, openColorKey, setOpenColorKey);
123 }
124
125 var safeActive = Math.min(active, Math.max(0, a.images.length - 1));
126 var currentImage = a.images[safeActive];
127
128 var isVertical = a.thumbnailPosition === 'left' || a.thumbnailPosition === 'right';
129 var thumbAfter = a.thumbnailPosition === 'bottom' || a.thumbnailPosition === 'right';
130
131 var mainBox = el('div', { className: 'bkbg-gs-main', style: { position: 'relative', borderRadius: a.mainRadius + 'px', overflow: 'hidden', boxShadow: a.mainShadow ? '0 16px 48px rgba(0,0,0,0.12)' : 'none', flex: 1, background: '#f3f4f6' } },
132 el('div', { style: { paddingBottom: ratioPercent(a.mainAspect) } }),
133 currentImage
134 ? el('img', { src: currentImage.imageUrl, alt: currentImage.title || '', style: { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: a.mainObjectFit, borderRadius: a.mainRadius + 'px' } })
135 : el('div', { style: { position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: '12px', color: '#9ca3af' } },
136 el('span', { style: { fontSize: '48px' } }, '🖼'),
137 el('p', { style: { margin: 0, fontSize: '14px' } }, __('No images yet — add images below in the sidebar.', 'blockenberg'))
138 ),
139 a.showCounter && a.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' } },
140 (safeActive + 1) + ' / ' + a.images.length
141 ),
142 a.showCaption && currentImage && currentImage.caption && a.captionPosition === 'overlay' && el('div', { className: 'bkbg-gs-caption bkbg-gs-caption--overlay', style: { position: 'absolute', bottom: 0, left: 0, right: 0, padding: '10px 14px', background: 'rgba(0,0,0,0.55)', color: '#fff' } }, currentImage.caption),
143 a.showArrows && a.images.length > 1 && el(Fragment, null,
144 el('button', { type: 'button', onClick: function () { setActive((safeActive - 1 + a.images.length) % a.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' } }, ''),
145 el('button', { type: 'button', onClick: function () { setActive((safeActive + 1) % a.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' } }, '')
146 )
147 );
148
149 var thumbStrip = a.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' } },
150 a.images.map(function (img, i) {
151 var isActive = i === safeActive;
152 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' } },
153 img.imageUrl && el('img', { src: img.imageUrl, alt: '', style: { width: '100%', height: '100%', objectFit: 'cover', display: 'block' } })
154 );
155 })
156 );
157
158 var flexDir = isVertical
159 ? (a.thumbnailPosition === 'left' ? 'row-reverse' : 'row')
160 : (a.thumbnailPosition === 'top' ? 'column-reverse' : 'column');
161
162 return el(Fragment, null,
163 el(InspectorControls, null,
164 el(PanelBody, { title: __('Display', 'blockenberg'), initialOpen: true },
165 el(SelectControl, { label: __('Thumbnail position', 'blockenberg'), value: a.thumbnailPosition, options: THUMB_POS_OPTIONS, onChange: function (v) { setAttributes({ thumbnailPosition: v }); } }),
166 el(SelectControl, { label: __('Main aspect ratio', 'blockenberg'), value: a.mainAspect, options: ASPECT_OPTIONS, onChange: function (v) { setAttributes({ mainAspect: v }); } }),
167 el(SelectControl, { label: __('Main fit', 'blockenberg'), value: a.mainObjectFit, options: FIT_OPTIONS, onChange: function (v) { setAttributes({ mainObjectFit: v }); } }),
168 el(RangeControl, { label: __('Main radius (px)', 'blockenberg'), value: a.mainRadius, min: 0, max: 40, onChange: function (v) { setAttributes({ mainRadius: v }); } }),
169 el(RangeControl, { label: __('Thumbnail radius (px)','blockenberg'),value: a.thumbnailRadius, min: 0, max: 20, onChange: function (v) { setAttributes({ thumbnailRadius: v }); } }),
170 el(RangeControl, { label: __('Thumbnail gap (px)', 'blockenberg'), value: a.thumbnailGap, min: 2, max: 24, onChange: function (v) { setAttributes({ thumbnailGap: v }); } }),
171 el(ToggleControl, { label: __('Show image shadow', 'blockenberg'), checked: a.mainShadow, onChange: function (v) { setAttributes({ mainShadow: v }); }, __nextHasNoMarginBottom: true }),
172 el(ToggleControl, { label: __('Show counter badge', 'blockenberg'), checked: a.showCounter, onChange: function (v) { setAttributes({ showCounter: v }); }, __nextHasNoMarginBottom: true })
173 ),
174 el(PanelBody, { title: __('Captions', 'blockenberg'), initialOpen: false },
175 el(ToggleControl, { label: __('Show captions', 'blockenberg'), checked: a.showCaption, onChange: function (v) { setAttributes({ showCaption: v }); }, __nextHasNoMarginBottom: true }),
176 a.showCaption && el(SelectControl, { label: __('Caption position', 'blockenberg'), value: a.captionPosition, options: CAPTION_POS_OPTIONS, onChange: function (v) { setAttributes({ captionPosition: v }); } }),
177 ),
178 el(PanelBody, { title: __('Navigation', 'blockenberg'), initialOpen: false },
179 el(ToggleControl, { label: __('Show arrows', 'blockenberg'), checked: a.showArrows, onChange: function (v) { setAttributes({ showArrows: v }); }, __nextHasNoMarginBottom: true }),
180 a.showArrows && el(SelectControl, { label: __('Arrow style', 'blockenberg'), value: a.arrowStyle, options: ARROW_STYLE_OPTIONS, onChange: function (v) { setAttributes({ arrowStyle: v }); } }),
181 el(SelectControl, { label: __('Transition type', 'blockenberg'), value: a.transitionType, options: TRANSITION_OPTIONS, onChange: function (v) { setAttributes({ transitionType: v }); } }),
182 el(RangeControl, { label: __('Transition duration (ms)', 'blockenberg'), value: a.transitionDuration, min: 100, max: 1000, step: 50, onChange: function (v) { setAttributes({ transitionDuration: v }); } })
183 ),
184 el(PanelBody, { title: __('Lightbox', 'blockenberg'), initialOpen: false },
185 el(ToggleControl, { label: __('Enable lightbox', 'blockenberg'), checked: a.enableLightbox, onChange: function (v) { setAttributes({ enableLightbox: v }); }, __nextHasNoMarginBottom: true }),
186 el(ToggleControl, { label: __('Keyboard navigation','blockenberg'), checked: a.enableKeyboard, onChange: function (v) { setAttributes({ enableKeyboard: v }); }, __nextHasNoMarginBottom: true }),
187 el(ToggleControl, { label: __('Show zoom button', 'blockenberg'), checked: a.showZoom, onChange: function (v) { setAttributes({ showZoom: v }); }, __nextHasNoMarginBottom: true })
188 ),
189
190 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
191 _tc()({ label: __('Caption', 'blockenberg'), value: a.typoCaption, onChange: function (v) { setAttributes({ typoCaption: v }); } }),
192 _tc()({ label: __('Counter', 'blockenberg'), value: a.typoCounter, onChange: function (v) { setAttributes({ typoCounter: v }); } })
193 ),
194 el(PanelBody, { title: __('Colors', 'blockenberg'), initialOpen: false },
195 cc('activeBorderColor', __('Active thumb border', 'blockenberg'), 'activeBorderColor'),
196 el(RangeControl, { label: __('Active border width (px)', 'blockenberg'), value: a.activeBorderWidth, min: 1, max: 6, onChange: function (v) { setAttributes({ activeBorderWidth: v }); } }),
197 cc('captionBg', __('Caption background', 'blockenberg'), 'captionBg'),
198 cc('captionColor', __('Caption text', 'blockenberg'), 'captionColor'),
199 cc('arrowBg', __('Arrow background', 'blockenberg'), 'arrowBg'),
200 cc('arrowColor', __('Arrow icon', 'blockenberg'), 'arrowColor'),
201 cc('counterBg', __('Counter background', 'blockenberg'), 'counterBg'),
202 cc('counterColor', __('Counter text', 'blockenberg'), 'counterColor'),
203 cc('lightboxOverlayBg', __('Lightbox overlay', 'blockenberg'), 'lightboxOverlayBg'),
204 cc('bgColor', __('Section background', 'blockenberg'), 'bgColor')
205 ),
206 el(PanelBody, { title: __('Spacing', 'blockenberg'), initialOpen: false },
207 el(RangeControl, { label: __('Padding top (px)', 'blockenberg'), value: a.paddingTop, min: 0, max: 200, onChange: function (v) { setAttributes({ paddingTop: v }); } }),
208 el(RangeControl, { label: __('Padding bottom (px)', 'blockenberg'), value: a.paddingBottom, min: 0, max: 200, onChange: function (v) { setAttributes({ paddingBottom: v }); } })
209 ),
210 el(PanelBody, { title: __('Images', 'blockenberg'), initialOpen: false },
211 el(MediaUploadCheck, null,
212 el(MediaUpload, {
213 onSelect: function (medias) {
214 var list = Array.isArray(medias) ? medias : [medias];
215 var newImages = list.map(function (m) { return { imageUrl: m.url, imageId: m.id, caption: m.caption || '', title: m.title || '' }; });
216 setAttributes({ images: a.images.concat(newImages) });
217 },
218 allowedTypes: ['image'], multiple: true, value: a.images.map(function (i) { return i.imageId; }),
219 render: function (p) { return el(Button, { onClick: p.open, variant: 'primary', style: { marginBottom: '12px' } }, __('+ Add Images', 'blockenberg')); }
220 })
221 ),
222 a.images.map(function (img, i) {
223 return el('div', { key: img.imageId || i, style: { display: 'flex', gap: '8px', alignItems: 'flex-start', marginBottom: '10px', padding: '8px', background: '#f9f9f9', borderRadius: '6px' } },
224 el('img', { src: img.imageUrl, style: { width: '56px', height: '40px', objectFit: 'cover', borderRadius: '4px', flexShrink: 0 } }),
225 el('div', { style: { flex: 1, minWidth: 0 } },
226 el(TextControl, { placeholder: __('Caption…', 'blockenberg'), value: img.caption, onChange: function (v) { setAttributes({ images: a.images.map(function (im, idx) { if (idx !== i) return im; return Object.assign({}, im, { caption: v }); }) }); } })
227 ),
228 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' }, '')
229 );
230 })
231 )
232 ),
233
234 el('div', blockProps,
235 el('div', { className: 'bkbg-gs-showcase bkbg-gs--thumb-' + a.thumbnailPosition, style: { display: 'flex', flexDirection: flexDir, gap: '14px', alignItems: isVertical ? 'flex-start' : 'stretch' } },
236 thumbAfter ? mainBox : thumbStrip,
237 thumbAfter ? thumbStrip : mainBox
238 ),
239 a.showCaption && currentImage && currentImage.caption && a.captionPosition === 'below' && el('p', { className: 'bkbg-gs-below-caption', style: { textAlign: 'center', color: a.captionColor, margin: '10px 0 0' } }, currentImage.caption)
240 )
241 );
242 },
243
244 save: function (props) {
245 var a = props.attributes;
246 var isVertical = a.thumbnailPosition === 'left' || a.thumbnailPosition === 'right';
247 var thumbAfter = a.thumbnailPosition === 'bottom' || a.thumbnailPosition === 'right';
248 var flexDir = isVertical
249 ? (a.thumbnailPosition === 'left' ? 'row-reverse' : 'row')
250 : (a.thumbnailPosition === 'top' ? 'column-reverse' : 'column');
251
252 var mainBox = el('div', { className: 'bkbg-gs-main', 'data-transition': a.transitionType, 'data-duration': a.transitionDuration, style: { position: 'relative', borderRadius: a.mainRadius + 'px', overflow: 'hidden', flex: 1 } },
253 el('div', { className: 'bkbg-gs-main-images' },
254 a.images.map(function (img, i) {
255 return el('div', { key: i, className: 'bkbg-gs-slide' + (i === 0 ? ' is-active' : ''), 'data-idx': i, style: { display: i === 0 ? undefined : 'none' } },
256 el('div', { style: { paddingBottom: (parseInt(a.mainAspect.split('/')[1], 10) / parseInt(a.mainAspect.split('/')[0], 10) * 100).toFixed(2) + '%' } }),
257 el('img', { src: img.imageUrl, alt: img.title || img.caption || '', loading: 'lazy', style: { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: a.mainObjectFit } })
258 );
259 })
260 ),
261 a.showCounter && a.images.length > 0 && el('div', { className: 'bkbg-gs-counter', 'aria-live': 'polite' }, '1 / ' + a.images.length),
262 a.showCaption && el('div', { className: 'bkbg-gs-caption bkbg-gs-caption--' + a.captionPosition, 'aria-live': 'polite' }, a.images[0] && a.images[0].caption),
263 a.showArrows && a.images.length > 1 && el(Fragment, null,
264 el('button', { type: 'button', className: 'bkbg-gs-arrow bkbg-gs-arrow--prev bkbg-gs-arrow--' + a.arrowStyle, 'aria-label': __('Previous', 'blockenberg') }, ''),
265 el('button', { type: 'button', className: 'bkbg-gs-arrow bkbg-gs-arrow--next bkbg-gs-arrow--' + a.arrowStyle, 'aria-label': __('Next', 'blockenberg') }, '')
266 ),
267 a.enableLightbox && el('button', { type: 'button', className: 'bkbg-gs-lightbox-trigger', 'aria-label': __('Open lightbox', 'blockenberg') }, '')
268 );
269
270 var thumbStrip = el('div', { className: 'bkbg-gs-thumbs', role: 'list', style: { display: 'flex', flexDirection: isVertical ? 'column' : 'row', gap: a.thumbnailGap + 'px' } },
271 a.images.map(function (img, i) {
272 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) },
273 el('img', { src: img.imageUrl, alt: img.title || '', loading: 'lazy', style: { width: '100%', height: '100%', objectFit: 'cover', display: 'block' } })
274 );
275 })
276 );
277
278 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 }),
279 el('div', { className: 'bkbg-gs-showcase bkbg-gs--thumb-' + a.thumbnailPosition, style: { display: 'flex', flexDirection: flexDir, gap: '14px', alignItems: isVertical ? 'flex-start' : 'stretch' } },
280 thumbAfter ? mainBox : thumbStrip,
281 thumbAfter ? thumbStrip : mainBox
282 ),
283 a.showCaption && a.captionPosition === 'below' && el('p', { className: 'bkbg-gs-below-caption', 'aria-live': 'polite' }, a.images[0] && a.images[0].caption)
284 );
285 }
286 });
287 }() );
288