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

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

364 lines 17.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 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 _TypographyControl, _typoCssVars;
20 function getTypographyControl() { return _TypographyControl || (_TypographyControl = window.bkbgTypographyControl); }
21 function getTypoCssVars() { return _typoCssVars || (_typoCssVars = window.bkbgTypoCssVars); }
22
23 var ASPECT_OPTIONS = [
24 { label: 'Auto (natural)', value: 'auto' },
25 { label: '1:1 Square', value: '1-1' },
26 { label: '4:3', value: '4-3' },
27 { label: '3:2', value: '3-2' },
28 { label: '16:9', value: '16-9' },
29 { label: '21:9 Cinematic',value: '21-9' },
30 { label: '9:16 Portrait', value: '9-16' },
31 ];
32
33 var DIRECTION_OPTIONS = [
34 { label: 'Left → Right', value: 'left' },
35 { label: 'Right → Left', value: 'right' },
36 { label: 'Top → Bottom',value: 'top' },
37 { label: 'Bottom→ Top', value: 'bottom' },
38 ];
39
40 var STYLE_OPTIONS = [
41 { label: 'Sweep (curtain)', value: 'sweep' },
42 { label: 'Fade', value: 'fade' },
43 { label: 'Zoom In', value: 'zoom' },
44 { label: 'Blur to Clear', value: 'blur' },
45 ];
46
47 var TRIGGER_OPTIONS = [
48 { label: 'On Scroll (IntersectionObserver)', value: 'scroll' },
49 { label: 'On Hover', value: 'hover' },
50 { label: 'On Click', value: 'click' },
51 ];
52
53 function getAspectPadding(ratio) {
54 if (ratio === '1-1') return '100%';
55 if (ratio === '4-3') return '75%';
56 if (ratio === '3-2') return '66.67%';
57 if (ratio === '16-9') return '56.25%';
58 if (ratio === '21-9') return '42.86%';
59 if (ratio === '9-16') return '177.78%';
60 return null;
61 }
62
63 registerBlockType('blockenberg/image-reveal', {
64 title: __('Image Reveal', 'blockenberg'),
65 icon: 'visibility',
66 category: 'bkbg-effects',
67 description: __('Image with a curtain reveal effect triggered on hover or scroll.', 'blockenberg'),
68
69 edit: function (props) {
70 var a = props.attributes;
71 var set = props.setAttributes;
72 var blockProps = useBlockProps({ style: (function() {
73 var s = {}; var _tv = getTypoCssVars();
74 Object.assign(s, _tv(a.labelTypo, '--bkbg-ir-lb-'));
75 Object.assign(s, _tv(a.captionTypo, '--bkbg-ir-cp-'));
76 return s;
77 })() });
78
79 var padding = getAspectPadding(a.aspectRatio);
80 var containerStyle = {
81 position: 'relative',
82 borderRadius: a.borderRadius + 'px',
83 overflow: 'hidden',
84 maxWidth: a.maxWidth > 0 ? a.maxWidth + 'px' : '100%',
85 margin: '0 auto',
86 };
87 if (a.aspectRatio !== 'auto' && padding) {
88 containerStyle.paddingTop = padding;
89 containerStyle.height = '0';
90 } else {
91 containerStyle.minHeight = '200px';
92 }
93
94 var imgStyle = {
95 position: a.aspectRatio !== 'auto' ? 'absolute' : 'relative',
96 inset: '0',
97 width: '100%',
98 height: '100%',
99 objectFit: 'cover',
100 display: 'block',
101 };
102
103 var overlayStyle = {
104 position: 'absolute',
105 inset: '0',
106 background: a.overlayBg,
107 opacity: a.overlayOpacity / 100,
108 display: 'flex',
109 alignItems: 'center',
110 justifyContent: 'center',
111 borderRadius: a.borderRadius + 'px',
112 transition: 'all ' + a.animationDuration + 'ms ease',
113 pointerEvents: 'none',
114 };
115
116 return el(Fragment, null,
117 el(InspectorControls, null,
118 // Image Panel
119 el(PanelBody, { title: 'Image', initialOpen: true },
120 el(MediaUploadCheck, null,
121 el(MediaUpload, {
122 onSelect: function (media) {
123 set({ imageUrl: media.url, imageId: media.id, imageAlt: media.alt || '' });
124 },
125 allowedTypes: ['image'],
126 value: a.imageId,
127 render: function (ref) {
128 return el(Button, {
129 onClick: ref.open,
130 variant: a.imageUrl ? 'secondary' : 'primary',
131 __nextHasNoMarginBottom: true,
132 }, a.imageUrl ? __('Change Image', 'blockenberg') : __('Select Image', 'blockenberg'));
133 }
134 })
135 ),
136 a.imageUrl && el(Button, {
137 onClick: function () { set({ imageUrl: '', imageId: 0, imageAlt: '' }); },
138 isDestructive: true,
139 variant: 'link',
140 style: { marginTop: 8, display: 'block' },
141 __nextHasNoMarginBottom: true,
142 }, __('Remove Image', 'blockenberg')),
143 el('div', { style: { height: 12 } }),
144 el(TextControl, {
145 label: 'Alt Text',
146 value: a.imageAlt,
147 onChange: function (v) { set({ imageAlt: v }); },
148 __nextHasNoMarginBottom: true,
149 }),
150 el('div', { style: { height: 12 } }),
151 el(SelectControl, {
152 label: 'Aspect Ratio',
153 value: a.aspectRatio,
154 options: ASPECT_OPTIONS,
155 onChange: function (v) { set({ aspectRatio: v }); },
156 __nextHasNoMarginBottom: true,
157 }),
158 el('div', { style: { height: 12 } }),
159 el(RangeControl, {
160 label: 'Max Width (px, 0 = full)',
161 value: a.maxWidth,
162 min: 0, max: 1400, step: 20,
163 onChange: function (v) { set({ maxWidth: v }); },
164 __nextHasNoMarginBottom: true,
165 }),
166 el('div', { style: { height: 12 } }),
167 el(RangeControl, {
168 label: 'Border Radius (px)',
169 value: a.borderRadius,
170 min: 0, max: 48,
171 onChange: function (v) { set({ borderRadius: v }); },
172 __nextHasNoMarginBottom: true,
173 })
174 ),
175
176 // Reveal Panel
177 el(PanelBody, { title: 'Reveal Effect', initialOpen: true },
178 el(SelectControl, {
179 label: 'Trigger',
180 value: a.trigger,
181 options: TRIGGER_OPTIONS,
182 onChange: function (v) { set({ trigger: v }); },
183 __nextHasNoMarginBottom: true,
184 }),
185 el('div', { style: { height: 12 } }),
186 el(SelectControl, {
187 label: 'Reveal Style',
188 value: a.revealStyle,
189 options: STYLE_OPTIONS,
190 onChange: function (v) { set({ revealStyle: v }); },
191 __nextHasNoMarginBottom: true,
192 }),
193 a.revealStyle === 'sweep' && el(Fragment, null,
194 el('div', { style: { height: 12 } }),
195 el(SelectControl, {
196 label: 'Direction',
197 value: a.direction,
198 options: DIRECTION_OPTIONS,
199 onChange: function (v) { set({ direction: v }); },
200 __nextHasNoMarginBottom: true,
201 })
202 ),
203 el('div', { style: { height: 12 } }),
204 el(RangeControl, {
205 label: 'Animation Duration (ms)',
206 value: a.animationDuration,
207 min: 200, max: 2000, step: 50,
208 onChange: function (v) { set({ animationDuration: v }); },
209 __nextHasNoMarginBottom: true,
210 }),
211 el('div', { style: { height: 12 } }),
212 a.trigger === 'scroll' && el(Fragment, null,
213 el(RangeControl, {
214 label: 'Scroll threshold (% of element in view)',
215 value: a.scrollThreshold,
216 min: 5, max: 90,
217 onChange: function (v) { set({ scrollThreshold: v }); },
218 __nextHasNoMarginBottom: true,
219 }),
220 el('div', { style: { height: 12 } }),
221 el(ToggleControl, {
222 label: 'Reveal only once',
223 checked: a.revealOnce,
224 onChange: function (v) { set({ revealOnce: v }); },
225 __nextHasNoMarginBottom: true,
226 })
227 )
228 ),
229
230 // Overlay Panel
231 el(PanelBody, { title: 'Overlay & Labels', initialOpen: false },
232 el(RangeControl, {
233 label: 'Overlay Opacity (%)',
234 value: a.overlayOpacity,
235 min: 10, max: 100,
236 onChange: function (v) { set({ overlayOpacity: v }); },
237 __nextHasNoMarginBottom: true,
238 }),
239 el('div', { style: { height: 12 } }),
240 el(ToggleControl, {
241 label: 'Show Overlay Label',
242 checked: a.showOverlayLabel,
243 onChange: function (v) { set({ showOverlayLabel: v }); },
244 __nextHasNoMarginBottom: true,
245 }),
246 a.showOverlayLabel && el(TextControl, {
247 label: 'Overlay Label Text',
248 value: a.overlayLabel,
249 onChange: function (v) { set({ overlayLabel: v }); },
250 __nextHasNoMarginBottom: true,
251 }),
252 el('div', { style: { height: 12 } }),
253 el(ToggleControl, {
254 label: 'Show Revealed Label',
255 checked: a.showRevealedLabel,
256 onChange: function (v) { set({ showRevealedLabel: v }); },
257 __nextHasNoMarginBottom: true,
258 }),
259 a.showRevealedLabel && el(TextControl, {
260 label: 'Revealed Label Text',
261 value: a.revealedLabel,
262 onChange: function (v) { set({ revealedLabel: v }); },
263 __nextHasNoMarginBottom: true,
264 }),
265 el('div', { style: { height: 12 } }),
266 el('div', { style: { height: 12 } }),
267 el(ToggleControl, {
268 label: 'Show Caption',
269 checked: a.showCaption,
270 onChange: function (v) { set({ showCaption: v }); },
271 __nextHasNoMarginBottom: true,
272 }),
273 a.showCaption && el(TextControl, {
274 label: 'Caption',
275 value: a.caption,
276 onChange: function (v) { set({ caption: v }); },
277 __nextHasNoMarginBottom: true,
278 })
279 ),
280
281 // Colors Panel
282
283 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
284 getTypographyControl() && el(getTypographyControl(), { label: __('Label', 'blockenberg'), value: a.labelTypo, onChange: function (v) { set({ labelTypo: v }); } }),
285 getTypographyControl() && el(getTypographyControl(), { label: __('Caption', 'blockenberg'), value: a.captionTypo, onChange: function (v) { set({ captionTypo: v }); } })
286 ),
287 el(PanelColorSettings, {
288 title: 'Colors',
289 initialOpen: false,
290 colorSettings: [
291 { label: 'Overlay Color', value: a.overlayBg, onChange: function (v) { set({ overlayBg: v || '#1e1b4b' }); } },
292 { label: 'Label Text Color', value: a.overlayTextColor, onChange: function (v) { set({ overlayTextColor: v || '#ffffff' }); } },
293 { label: 'Caption Color', value: a.captionColor, onChange: function (v) { set({ captionColor: v || '#6b7280' }); } },
294 ]
295 })
296 ),
297
298 // Editor Preview
299 el('div', blockProps,
300 el('figure', { className: 'bkbg-ir-figure', style: { margin: '0', position: 'relative' } },
301 el('div', { className: 'bkbg-ir-container', style: containerStyle },
302 !a.imageUrl
303 ? el('div', {
304 style: {
305 position: 'absolute',
306 inset: '0',
307 background: '#f3f4f6',
308 display: 'flex',
309 alignItems: 'center',
310 justifyContent: 'center',
311 color: '#9ca3af',
312 fontSize: 14,
313 minHeight: 200,
314 }
315 }, '📷 Click "Select Image" in the sidebar')
316 : el('img', {
317 src: a.imageUrl,
318 alt: a.imageAlt,
319 style: imgStyle,
320 }),
321 // Overlay preview
322 el('div', { className: 'bkbg-ir-overlay', style: overlayStyle },
323 a.showOverlayLabel && a.overlayLabel && el('div', {
324 className: 'bkbg-ir-overlay-label',
325 style: {
326 color: a.overlayTextColor,
327 textAlign: 'center',
328 padding: '0 16px',
329 pointerEvents: 'none',
330 }
331 }, a.overlayLabel)
332 )
333 ),
334 a.showCaption && a.caption && el('figcaption', {
335 className: 'bkbg-ir-caption',
336 style: { color: a.captionColor, marginTop: 8, textAlign: 'center' }
337 }, a.caption)
338 ),
339 el('div', {
340 style: {
341 marginTop: 8,
342 background: '#fef3c7',
343 border: '1px solid #fbbf24',
344 borderRadius: 6,
345 padding: '6px 10px',
346 fontSize: 12,
347 color: '#92400e',
348 }
349 }, '👁 Trigger: ' + a.trigger + ' | Style: ' + a.revealStyle + (a.revealStyle === 'sweep' ? ' (' + a.direction + ')' : ''))
350 )
351 );
352 },
353
354 save: function (props) {
355 return el('div', useBlockProps.save(),
356 el('div', {
357 className: 'bkbg-ir-app',
358 'data-opts': JSON.stringify(props.attributes),
359 })
360 );
361 }
362 });
363 }() );
364