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 / image-slider / index.js

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

303 lines 20.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 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 useBlockProps = wp.blockEditor.useBlockProps;
9 var MediaUpload = wp.blockEditor.MediaUpload;
10 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
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 _TypographyControl, _typoCssVars;
21 function getTypographyControl() { return _TypographyControl || (_TypographyControl = window.bkbgTypographyControl); }
22 function getTypoCssVars() { return _typoCssVars || (_typoCssVars = window.bkbgTypoCssVars); }
23
24 function renderColorControl(key, label, value, onChange, openKey, setOpenKey) {
25 var isOpen = openKey === key;
26 return el('div', { key: key, style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '4px 0', gap: '8px' } },
27 el('span', { style: { fontSize: '12px', color: '#1e1e1e', flex: 1 } }, label),
28 el('div', { style: { position: 'relative', flexShrink: 0 } },
29 el('button', {
30 type: 'button', title: value || 'transparent',
31 onClick: function () { setOpenKey(isOpen ? null : key); },
32 style: { width: '28px', height: '28px', borderRadius: '4px', border: isOpen ? '2px solid #007cba' : '2px solid #ddd', cursor: 'pointer', padding: 0, background: value || '#cccccc' }
33 }),
34 isOpen && el(Popover, { position: 'bottom left', onClose: function () { setOpenKey(null); } },
35 el('div', { style: { padding: '8px' } }, el(ColorPicker, { color: value, enableAlpha: true, onChange: onChange }))
36 )
37 )
38 );
39 }
40
41 function buildWrapStyle(a) {
42 var s = {
43 '--bkbg-is-height' : a.height + 'px',
44 '--bkbg-is-mob-h' : a.mobileHeight + 'px',
45 '--bkbg-is-radius' : a.borderRadius + 'px',
46 '--bkbg-is-accent' : a.accentColor,
47 '--bkbg-is-cap-bg' : a.captionBg,
48 '--bkbg-is-cap-color': a.captionColor,
49 '--bkbg-is-title-sz' : a.titleSize + 'px',
50 '--bkbg-is-sub-sz' : a.subtitleSize + 'px'
51 };
52 var _tv = getTypoCssVars();
53 Object.assign(s, _tv(a.titleTypo, '--bkbg-is-tt-'));
54 Object.assign(s, _tv(a.subtitleTypo, '--bkbg-is-st-'));
55 return s;
56 }
57
58 registerBlockType('blockenberg/image-slider', {
59 title: __('Image Slider', 'blockenberg'),
60 icon: 'images-alt2',
61 category: 'bkbg-media',
62 description: __('Responsive image carousel with captions, autoplay and touch support.', 'blockenberg'),
63
64 edit: function (props) {
65 var a = props.attributes;
66 var setAttributes = props.setAttributes;
67
68 var openColorKeyState = useState(null);
69 var openColorKey = openColorKeyState[0];
70 var setOpenColorKey = openColorKeyState[1];
71
72 var activeSlideState = useState(0);
73 var activeSlide = activeSlideState[0];
74 var setActiveSlide = activeSlideState[1];
75
76 var slides = a.slides || [];
77
78 function updateSlide(idx, key, val) {
79 var updated = slides.map(function (s, i) {
80 if (i !== idx) return s;
81 var n = Object.assign({}, s); n[key] = val; return n;
82 });
83 setAttributes({ slides: updated });
84 }
85
86 function addSlide() {
87 setAttributes({ slides: slides.concat([{ imageUrl: '', imageId: 0, alt: '', title: 'New Slide', subtitle: '', buttonText: '', buttonUrl: '', buttonTarget: false, overlayColor: '' }]) });
88 setActiveSlide(slides.length);
89 }
90
91 function removeSlide(idx) {
92 setAttributes({ slides: slides.filter(function (_, i) { return i !== idx; }) });
93 setActiveSlide(Math.max(0, idx - 1));
94 }
95
96 function cc(key, label, val, attrKey) {
97 return renderColorControl(key, label, val, function (v) {
98 var upd = {}; upd[attrKey] = v; setAttributes(upd);
99 }, openColorKey, setOpenColorKey);
100 }
101
102 var blockProps = useBlockProps({ className: 'bkbg-is-wrapper', style: buildWrapStyle(a) });
103 var curSlide = slides[activeSlide] || {};
104
105 var inspector = el(InspectorControls, {},
106 el(PanelBody, { title: __('Slides', 'blockenberg'), initialOpen: true },
107 el('div', { style: { display: 'flex', flexDirection: 'column', gap: '4px', marginBottom: '12px' } },
108 slides.map(function (s, idx) {
109 var isSel = idx === activeSlide;
110 return el('div', {
111 key: idx,
112 style: { display: 'flex', alignItems: 'center', gap: '6px', background: isSel ? '#e8f0fe' : '#f5f5f5', border: isSel ? '1px solid #4285f4' : '1px solid #ddd', borderRadius: '6px', padding: '6px 8px', cursor: 'pointer' },
113 onClick: function () { setActiveSlide(idx); }
114 },
115 s.imageUrl && el('img', { src: s.imageUrl, style: { width: '36px', height: '28px', objectFit: 'cover', borderRadius: '3px', flexShrink: 0 } }),
116 !s.imageUrl && el('span', { style: { width: '36px', height: '28px', background: '#ddd', borderRadius: '3px', flexShrink: 0, display: 'block' } }),
117 el('span', { style: { flex: 1, fontSize: '12px', fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, s.title || __('Slide', 'blockenberg')),
118 el('button', { type: 'button', onClick: function (e) { e.stopPropagation(); removeSlide(idx); }, style: { background: 'none', border: 'none', cursor: 'pointer', fontSize: '16px', color: '#cc1818', padding: '0 2px' } }, '×')
119 );
120 })
121 ),
122 el(Button, { variant: 'secondary', onClick: addSlide, style: { width: '100%', justifyContent: 'center', marginBottom: '16px' } },
123 '+ ' + __('Add Slide', 'blockenberg')
124 ),
125 slides.length > 0 && el('div', { style: { borderTop: '1px solid #e0e0e0', paddingTop: '12px' } },
126 el('strong', { style: { fontSize: '12px', display: 'block', marginBottom: '8px' } }, __('Edit Slide', 'blockenberg') + ' #' + (activeSlide + 1)),
127 el(MediaUploadCheck, {},
128 el(MediaUpload, {
129 onSelect: function (media) { setAttributes({ slides: slides.map(function (s, i) { return i !== activeSlide ? s : Object.assign({}, s, { imageUrl: media.url, imageId: media.id, alt: media.alt || '' }); }) }); },
130 allowedTypes: ['image'],
131 value: curSlide.imageId || 0,
132 render: function (obj) {
133 return el(Button, {
134 onClick: obj.open, variant: curSlide.imageUrl ? 'secondary' : 'primary',
135 style: { width: '100%', justifyContent: 'center', marginBottom: '8px' }
136 }, curSlide.imageUrl ? __('Replace Image', 'blockenberg') : __('Set Image', 'blockenberg'));
137 }
138 })
139 ),
140 curSlide.imageUrl && el(Button, {
141 variant: 'tertiary', isDestructive: true,
142 onClick: function () { setAttributes({ slides: slides.map(function (s, i) { return i !== activeSlide ? s : Object.assign({}, s, { imageUrl: '', imageId: 0 }); }) }); },
143 style: { marginBottom: '8px', fontSize: '12px' }
144 }, __('Remove Image', 'blockenberg')),
145 el(TextControl, { label: __('Title', 'blockenberg'), value: curSlide.title || '', onChange: function (v) { updateSlide(activeSlide, 'title', v); } }),
146 el(TextControl, { label: __('Subtitle', 'blockenberg'), value: curSlide.subtitle || '', onChange: function (v) { updateSlide(activeSlide, 'subtitle', v); } }),
147 el(TextControl, { label: __('Button Label (optional)', 'blockenberg'), value: curSlide.buttonText || '', onChange: function (v) { updateSlide(activeSlide, 'buttonText', v); } }),
148 curSlide.buttonText && el(Fragment, {},
149 el(TextControl, { label: __('Button URL', 'blockenberg'), value: curSlide.buttonUrl || '', onChange: function (v) { updateSlide(activeSlide, 'buttonUrl', v); } }),
150 el(ToggleControl, { label: __('Open in new tab', 'blockenberg'), checked: !!curSlide.buttonTarget, onChange: function (v) { updateSlide(activeSlide, 'buttonTarget', v); } })
151 )
152 )
153 ),
154
155 el(PanelBody, { title: __('Autoplay & Behaviour', 'blockenberg'), initialOpen: false },
156 el(ToggleControl, { label: __('Autoplay', 'blockenberg'), checked: a.autoplay, onChange: function (v) { setAttributes({ autoplay: v }); } }),
157 a.autoplay && el(RangeControl, { label: __('Speed (ms)', 'blockenberg'), value: a.autoplaySpeed, min: 1000, max: 10000, step: 500, onChange: function (v) { setAttributes({ autoplaySpeed: v }); } }),
158 a.autoplay && el(ToggleControl, { label: __('Pause on Hover', 'blockenberg'), checked: a.pauseOnHover, onChange: function (v) { setAttributes({ pauseOnHover: v }); } }),
159 el(ToggleControl, { label: __('Loop', 'blockenberg'), checked: a.loop, onChange: function (v) { setAttributes({ loop: v }); } }),
160 el(SelectControl, {
161 label: __('Transition', 'blockenberg'), value: a.transition,
162 options: [{ label: __('Slide', 'blockenberg'), value: 'slide' }, { label: __('Fade', 'blockenberg'), value: 'fade' }],
163 onChange: function (v) { setAttributes({ transition: v }); }
164 })
165 ),
166
167 el(PanelBody, { title: __('Navigation', 'blockenberg'), initialOpen: false },
168 el(ToggleControl, { label: __('Show Arrows', 'blockenberg'), checked: a.showArrows, onChange: function (v) { setAttributes({ showArrows: v }); } }),
169 a.showArrows && el(SelectControl, {
170 label: __('Arrow Style', 'blockenberg'), value: a.arrowStyle,
171 options: [{ label: __('Rounded', 'blockenberg'), value: 'rounded' }, { label: __('Circle', 'blockenberg'), value: 'circle' }, { label: __('Square', 'blockenberg'), value: 'square' }],
172 onChange: function (v) { setAttributes({ arrowStyle: v }); }
173 }),
174 el(ToggleControl, { label: __('Show Dots', 'blockenberg'), checked: a.showDots, onChange: function (v) { setAttributes({ showDots: v }); } })
175 ),
176
177 el(PanelBody, { title: __('Dimensions', 'blockenberg'), initialOpen: false },
178 el(RangeControl, { label: __('Height Desktop (px)', 'blockenberg'), value: a.height, min: 200, max: 900, onChange: function (v) { setAttributes({ height: v }); } }),
179 el(RangeControl, { label: __('Height Mobile (px)', 'blockenberg'), value: a.mobileHeight, min: 100, max: 600, onChange: function (v) { setAttributes({ mobileHeight: v }); } }),
180 el(RangeControl, { label: __('Border Radius (px)', 'blockenberg'), value: a.borderRadius, min: 0, max: 40, onChange: function (v) { setAttributes({ borderRadius: v }); } }),
181 el(SelectControl, {
182 label: __('Image Fit', 'blockenberg'), value: a.objectFit,
183 options: [{ label: 'Cover', value: 'cover' }, { label: 'Contain', value: 'contain' }, { label: 'Fill', value: 'fill' }],
184 onChange: function (v) { setAttributes({ objectFit: v }); }
185 })
186 ),
187
188 el(PanelBody, { title: __('Caption & Button', 'blockenberg'), initialOpen: false },
189 el(SelectControl, {
190 label: __('Caption Position', 'blockenberg'), value: a.captionPosition,
191 options: [
192 { label: __('Bottom Left', 'blockenberg'), value: 'bottom-left' },
193 { label: __('Bottom Center', 'blockenberg'), value: 'bottom-center' },
194 { label: __('Center', 'blockenberg'), value: 'center' },
195 { label: __('None', 'blockenberg'), value: 'none' }
196 ],
197 onChange: function (v) { setAttributes({ captionPosition: v }); }
198 }),
199 el(SelectControl, {
200 label: __('Button Style', 'blockenberg'), value: a.btnStyle,
201 options: [{ label: __('Primary (solid)', 'blockenberg'), value: 'primary' }, { label: __('Outline', 'blockenberg'), value: 'outline' }, { label: __('Ghost (text)', 'blockenberg'), value: 'ghost' }],
202 onChange: function (v) { setAttributes({ btnStyle: v }); }
203 }),
204 cc('accentColor', __('Accent / Button Color', 'blockenberg'), a.accentColor, 'accentColor'),
205 cc('captionBg', __('Caption Background', 'blockenberg'), a.captionBg, 'captionBg'),
206 cc('captionColor', __('Caption Text Color', 'blockenberg'), a.captionColor, 'captionColor')
207 ),
208 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
209 getTypographyControl() && el(getTypographyControl(), { label: __('Title', 'blockenberg'), value: a.titleTypo, onChange: function (v) { setAttributes({ titleTypo: v }); } }),
210 getTypographyControl() && el(getTypographyControl(), { label: __('Subtitle', 'blockenberg'), value: a.subtitleTypo, onChange: function (v) { setAttributes({ subtitleTypo: v }); } })
211 )
212 );
213
214 /* Preview */
215 var s = slides[activeSlide] || {};
216 return el(Fragment, {},
217 inspector,
218 el('div', blockProps,
219 el('div', { className: 'bkbg-is-slider bkbg-is-trans-' + a.transition },
220 el('div', { className: 'bkbg-is-slide bkbg-is-slide-active' },
221 s.imageUrl
222 ? el('img', { src: s.imageUrl, alt: s.alt || '', className: 'bkbg-is-img', style: { objectFit: a.objectFit } })
223 : el('div', { className: 'bkbg-is-placeholder' }, el('span', {}, __('Set a slide image in the panel →', 'blockenberg'))),
224 a.captionPosition !== 'none' && (s.title || s.subtitle) && el('div', { className: 'bkbg-is-caption bkbg-is-cap-' + a.captionPosition },
225 s.title && el('h2', { className: 'bkbg-is-title' }, s.title),
226 s.subtitle && el('p', { className: 'bkbg-is-subtitle' }, s.subtitle),
227 s.buttonText && el('a', { className: 'bkbg-is-btn bkbg-is-btn-' + a.btnStyle, href: s.buttonUrl || '#', style: { pointerEvents: 'none' } }, s.buttonText)
228 )
229 )
230 ),
231 a.showArrows && el(Fragment, {},
232 el('button', { className: 'bkbg-is-arrow bkbg-is-arrow-prev bkbg-is-arrow-' + a.arrowStyle, 'aria-label': __('Previous', 'blockenberg'), style: { pointerEvents: 'none' } }, ''),
233 el('button', { className: 'bkbg-is-arrow bkbg-is-arrow-next bkbg-is-arrow-' + a.arrowStyle, 'aria-label': __('Next', 'blockenberg'), style: { pointerEvents: 'none' } }, '')
234 ),
235 a.showDots && el('div', { className: 'bkbg-is-dots' },
236 slides.map(function (_, idx) {
237 return el('button', { key: idx, className: 'bkbg-is-dot' + (idx === activeSlide ? ' bkbg-is-dot-active' : ''), style: { pointerEvents: 'none' } });
238 })
239 ),
240 slides.length > 1 && el('div', { style: { position: 'absolute', top: '8px', right: '8px', background: 'rgba(0,0,0,0.5)', color: '#fff', fontSize: '11px', padding: '3px 7px', borderRadius: '4px' } },
241 (activeSlide + 1) + ' / ' + slides.length
242 )
243 )
244 );
245 },
246
247 save: function (props) {
248 var a = props.attributes;
249 var slides = a.slides || [];
250 var el = wp.element.createElement;
251 var _tv = getTypoCssVars();
252 var saveStyle = {
253 '--bkbg-is-height' : a.height + 'px',
254 '--bkbg-is-mob-h' : a.mobileHeight + 'px',
255 '--bkbg-is-radius' : a.borderRadius + 'px',
256 '--bkbg-is-accent' : a.accentColor,
257 '--bkbg-is-cap-bg' : a.captionBg,
258 '--bkbg-is-cap-color': a.captionColor,
259 '--bkbg-is-title-sz' : a.titleSize + 'px',
260 '--bkbg-is-sub-sz' : a.subtitleSize + 'px'
261 };
262 Object.assign(saveStyle, _tv(a.titleTypo, '--bkbg-is-tt-'));
263 Object.assign(saveStyle, _tv(a.subtitleTypo, '--bkbg-is-st-'));
264 var saveProps = wp.blockEditor.useBlockProps.save({
265 className: 'bkbg-is-wrapper',
266 style: saveStyle,
267 'data-autoplay' : a.autoplay ? '1' : '0',
268 'data-speed' : a.autoplaySpeed,
269 'data-loop' : a.loop ? '1' : '0',
270 'data-pause-hover' : a.pauseOnHover ? '1' : '0',
271 'data-transition' : a.transition
272 });
273
274 return el('div', saveProps,
275 el('div', { className: 'bkbg-is-slider bkbg-is-trans-' + a.transition },
276 slides.map(function (s, idx) {
277 return el('div', { key: idx, className: 'bkbg-is-slide' + (idx === 0 ? ' bkbg-is-slide-active' : '') },
278 s.imageUrl && el('img', { src: s.imageUrl, alt: s.alt || '', className: 'bkbg-is-img', style: { objectFit: a.objectFit }, loading: idx === 0 ? 'eager' : 'lazy' }),
279 a.captionPosition !== 'none' && (s.title || s.subtitle) && el('div', { className: 'bkbg-is-caption bkbg-is-cap-' + a.captionPosition },
280 s.title && el('h2', { className: 'bkbg-is-title' }, s.title),
281 s.subtitle && el('p', { className: 'bkbg-is-subtitle' }, s.subtitle),
282 s.buttonText && el('a', {
283 className: 'bkbg-is-btn bkbg-is-btn-' + a.btnStyle,
284 href: s.buttonUrl || '#',
285 target: s.buttonTarget ? '_blank' : undefined,
286 rel: s.buttonTarget ? 'noopener noreferrer' : undefined
287 }, s.buttonText)
288 )
289 );
290 })
291 ),
292 a.showArrows && el('button', { className: 'bkbg-is-arrow bkbg-is-arrow-prev bkbg-is-arrow-' + a.arrowStyle, 'aria-label': 'Previous slide' }, ''),
293 a.showArrows && el('button', { className: 'bkbg-is-arrow bkbg-is-arrow-next bkbg-is-arrow-' + a.arrowStyle, 'aria-label': 'Next slide' }, ''),
294 a.showDots && el('div', { className: 'bkbg-is-dots' },
295 slides.map(function (_, idx) {
296 return el('button', { key: idx, className: 'bkbg-is-dot' + (idx === 0 ? ' bkbg-is-dot-active' : ''), 'aria-label': 'Go to slide ' + (idx + 1) });
297 })
298 )
299 );
300 }
301 });
302 }() );
303