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

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

445 lines 22.3 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 __ = wp.i18n.__;
4 var useState = wp.element.useState;
5 var registerBlockType = wp.blocks.registerBlockType;
6 var InspectorControls = wp.blockEditor.InspectorControls;
7 var useBlockProps = wp.blockEditor.useBlockProps;
8 var MediaUpload = wp.blockEditor.MediaUpload;
9 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
10 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
11 var PanelBody = wp.components.PanelBody;
12 var ToggleControl = wp.components.ToggleControl;
13 var RangeControl = wp.components.RangeControl;
14 var SelectControl = wp.components.SelectControl;
15 var TextControl = wp.components.TextControl;
16 var Button = wp.components.Button;
17 var _TypographyControl, _typoCssVars;
18 function getTypographyControl() { return _TypographyControl || (_TypographyControl = window.bkbgTypographyControl); }
19 function getTypoCssVars() { return _typoCssVars || (_typoCssVars = window.bkbgTypoCssVars); }
20 /* ── Shape definitions ─────────────────────────────────────────────────── */
21 var SHAPES = {
22 circle: 'circle(50% at 50% 50%)',
23 hexagon: 'polygon(50% 0%, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%)',
24 blob: 'path("M180,32C230,0,290,20,310,70s10,110-30,150s-100,70-160,60s-110-50-100-110S130,64,180,32z")',
25 star: 'polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%)',
26 diamond: 'polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)',
27 cross: 'polygon(33% 0%, 67% 0%, 67% 33%, 100% 33%, 100% 67%, 67% 67%, 67% 100%, 33% 100%, 33% 67%, 0% 67%, 0% 33%, 33% 33%)',
28 heart: 'path("M256,460C256,460,32,338,32,180a96,96,0,0,1,181-45,96,96,0,0,1,181,45C394,338,256,460,256,460Z")',
29 pentagon: 'polygon(50% 0%, 100% 38%, 82% 100%, 18% 100%, 0% 38%)',
30 triangle: 'polygon(50% 0%, 0% 100%, 100% 100%)',
31 squircle: 'path("M180,20C240,0,340,0,360,100s0,200-50,240s-180,60-230,20S0,200,0,140S120,40,180,20Z")',
32 };
33
34 var SHAPE_OPTIONS = Object.keys(SHAPES).map(function (k) {
35 return { label: k.charAt(0).toUpperCase() + k.slice(1), value: k };
36 });
37
38 var PLACEHOLDER = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="240" height="240"><rect width="240" height="240" fill="%23e5e7eb"/><text x="50%25" y="50%25" font-family="sans-serif" font-size="14" fill="%239ca3af" text-anchor="middle" dy=".3em">Choose Image</text></svg>';
39
40 /* ── Build clip-path value (blob/heart/squircle use path(), others polygon) */
41 function getClip(shape) {
42 return SHAPES[shape] || SHAPES.circle;
43 }
44
45 /* ── Shared render ─────────────────────────────────────────────────────── */
46 function MaskPreview(props) {
47 var a = props.a;
48 var isEdit = props.isEdit;
49
50 var size = a.size;
51 var clip = getClip(a.shape);
52 var shadow = a.showShadow
53 ? '0 20px 40px rgba(' + hexRgb(a.shadowColor || '#000') + ',' + (a.shadowIntensity / 100) + ')'
54 : 'none';
55 var transform = a.rotation ? 'rotate(' + a.rotation + 'deg)' : '';
56 var transition = isEdit ? 'none' : ('transform ' + a.transitionDuration + 'ms ease');
57
58 var wrapStyle = {
59 display: 'inline-block',
60 position: 'relative',
61 textAlign: 'center',
62 };
63
64 var outerStyle = {
65 display: 'inline-block',
66 position: 'relative',
67 width: size + 'px',
68 height: size + 'px',
69 transform: transform,
70 };
71
72 /* Border ring — rendered as a same-shape div BEHIND the image */
73 var borderEl = null;
74 if (a.showBorder) {
75 var bOff = a.borderOffset || 8;
76 var bW = a.borderWidth || 4;
77 var bSize = size + bOff * 2 + bW * 2;
78 borderEl = el('div', {
79 className: 'bkim-border',
80 style: {
81 position: 'absolute',
82 top: -(bOff + bW) + 'px',
83 left: -(bOff + bW) + 'px',
84 width: bSize + 'px',
85 height: bSize + 'px',
86 background: a.borderColor || '#6c3fb5',
87 clipPath: clip,
88 WebkitClipPath: clip,
89 zIndex: 0,
90 transition: transition,
91 }
92 });
93 }
94
95 var imgStyle = {
96 display: 'block',
97 width: size + 'px',
98 height: size + 'px',
99 objectFit: 'cover',
100 objectPosition: a.objectPosition || 'center center',
101 clipPath: clip,
102 WebkitClipPath: clip,
103 filter: shadow !== 'none' ? 'drop-shadow(0 15px 25px rgba(' + hexRgb(a.shadowColor || '#000') + ',' + (a.shadowIntensity / 100) + '))' : 'none',
104 transition: transition,
105 position: 'relative',
106 zIndex: 1,
107 };
108
109 var img = el('img', {
110 src: a.imageUrl || PLACEHOLDER,
111 alt: a.imageAlt || '',
112 style: imgStyle,
113 className: 'bkim-img',
114 });
115
116 var caption = (a.showCaption && a.caption) ? el('p', {
117 className: 'bkim-caption',
118 style: {
119 marginTop: '12px',
120 fontSize: (a.captionSize || 14) + 'px',
121 color: a.captionColor || '#374151',
122 textAlign: a.captionAlign || 'center',
123 fontStyle: 'italic',
124 lineHeight: '1.4',
125 }
126 }, a.caption) : null;
127
128 var innerEl = el('div', { style: outerStyle }, borderEl, img);
129
130 if (a.linkUrl && !isEdit) {
131 innerEl = el('a', {
132 href: a.linkUrl,
133 target: a.linkNewTab ? '_blank' : undefined,
134 rel: a.linkNewTab ? 'noopener noreferrer' : undefined,
135 style: { display: 'inline-block' }
136 }, innerEl);
137 }
138
139 return el('figure', { style: wrapStyle, className: 'bkim-figure' }, innerEl, caption);
140 }
141
142 function hexRgb(hex) {
143 if (!hex || hex.charAt(0) !== '#') return '0,0,0';
144 return parseInt(hex.slice(1, 3), 16) + ',' + parseInt(hex.slice(3, 5), 16) + ',' + parseInt(hex.slice(5, 7), 16);
145 }
146
147 /* ── Register ──────────────────────────────────────────────────────────── */
148 registerBlockType('blockenberg/image-mask', {
149 edit: function (props) {
150 var a = props.attributes;
151 var set = props.setAttributes;
152
153 var blockProps = useBlockProps({
154 className: 'bkim-wrap',
155 style: (function () {
156 var s = { textAlign: a.align || 'center' };
157 var fn = getTypoCssVars();
158 if (fn) { Object.assign(s, fn(a.titleTypo, '--bkim-tt-'), fn(a.subtitleTypo, '--bkim-st-'), fn(a.captionTypo, '--bkim-cp-')); }
159 return s;
160 })()
161 });
162
163 return el(wp.element.Fragment, null,
164
165 el(InspectorControls, null,
166
167 /* Image */
168 el(PanelBody, { title: __('Image', 'blockenberg'), initialOpen: true },
169 el(MediaUploadCheck, null,
170 el(MediaUpload, {
171 onSelect: function (media) { set({ imageUrl: media.url, imageId: media.id, imageAlt: media.alt || '' }); },
172 allowedTypes: ['image'],
173 value: a.imageId,
174 render: function (ref) {
175 return el(Button, {
176 onClick: ref.open,
177 variant: a.imageUrl ? 'secondary' : 'primary',
178 style: { width: '100%', justifyContent: 'center', marginBottom: '8px' }
179 }, a.imageUrl ? __('Replace Image', 'blockenberg') : __('Choose Image', 'blockenberg'));
180 }
181 })
182 ),
183 a.imageUrl ? el(Button, {
184 variant: 'tertiary', isDestructive: true,
185 onClick: function () { set({ imageUrl: '', imageId: 0, imageAlt: '' }); }
186 }, __('Remove Image', 'blockenberg')) : null,
187 el(TextControl, {
188 label: __('Alt Text', 'blockenberg'),
189 value: a.imageAlt,
190 onChange: function (v) { set({ imageAlt: v }); }
191 }),
192 el(SelectControl, {
193 label: __('Image Focus Point', 'blockenberg'),
194 value: a.objectPosition,
195 options: [
196 { label: __('Center', 'blockenberg'), value: 'center center' },
197 { label: __('Top', 'blockenberg'), value: 'center top' },
198 { label: __('Bottom', 'blockenberg'), value: 'center bottom' },
199 { label: __('Left', 'blockenberg'), value: 'left center' },
200 { label: __('Right', 'blockenberg'), value: 'right center' },
201 { label: __('Top Left', 'blockenberg'), value: 'left top' },
202 { label: __('Top Right', 'blockenberg'), value: 'right top' },
203 { label: __('Bottom Left', 'blockenberg'), value: 'left bottom' },
204 { label: __('Bottom Right', 'blockenberg'), value: 'right bottom' },
205 ],
206 onChange: function (v) { set({ objectPosition: v }); }
207 })
208 ),
209
210 /* Shape */
211 el(PanelBody, { title: __('Shape', 'blockenberg'), initialOpen: true },
212 el(SelectControl, {
213 label: __('Mask Shape', 'blockenberg'),
214 value: a.shape,
215 options: SHAPE_OPTIONS,
216 onChange: function (v) { set({ shape: v }); }
217 }),
218 el(RangeControl, {
219 label: __('Size (px)', 'blockenberg'),
220 value: a.size,
221 min: 80, max: 800,
222 onChange: function (v) { set({ size: v }); }
223 }),
224 el(RangeControl, {
225 label: __('Rotation (°)', 'blockenberg'),
226 value: a.rotation,
227 min: -180, max: 180,
228 onChange: function (v) { set({ rotation: v }); }
229 })
230 ),
231
232 /* Border */
233 el(PanelBody, { title: __('Shape Border', 'blockenberg'), initialOpen: false },
234 el(ToggleControl, {
235 label: __('Show Border Ring', 'blockenberg'),
236 checked: a.showBorder,
237 onChange: function (v) { set({ showBorder: v }); },
238 __nextHasNoMarginBottom: true
239 }),
240 a.showBorder ? el(RangeControl, {
241 label: __('Border Width (px)', 'blockenberg'),
242 value: a.borderWidth,
243 min: 1, max: 20,
244 onChange: function (v) { set({ borderWidth: v }); }
245 }) : null,
246 a.showBorder ? el(RangeControl, {
247 label: __('Border Offset (px)', 'blockenberg'),
248 value: a.borderOffset,
249 min: 0, max: 40,
250 onChange: function (v) { set({ borderOffset: v }); }
251 }) : null
252 ),
253
254 /* Shadow & Hover */
255 el(PanelBody, { title: __('Shadow & Hover', 'blockenberg'), initialOpen: false },
256 el(ToggleControl, {
257 label: __('Drop Shadow', 'blockenberg'),
258 checked: a.showShadow,
259 onChange: function (v) { set({ showShadow: v }); },
260 __nextHasNoMarginBottom: true
261 }),
262 a.showShadow ? el(RangeControl, {
263 label: __('Shadow Intensity (%)', 'blockenberg'),
264 value: a.shadowIntensity,
265 min: 5, max: 70,
266 onChange: function (v) { set({ shadowIntensity: v }); }
267 }) : null,
268 el(SelectControl, {
269 label: __('Hover Effect', 'blockenberg'),
270 value: a.hoverEffect,
271 options: [
272 { label: __('Zoom In', 'blockenberg'), value: 'zoom' },
273 { label: __('Zoom Out', 'blockenberg'), value: 'zoom-out'},
274 { label: __('Rotate', 'blockenberg'), value: 'rotate' },
275 { label: __('None', 'blockenberg'), value: 'none' },
276 ],
277 onChange: function (v) { set({ hoverEffect: v }); }
278 }),
279 a.hoverEffect === 'zoom' || a.hoverEffect === 'zoom-out' ? el(RangeControl, {
280 label: __('Hover Scale (%)', 'blockenberg'),
281 value: a.hoverScale,
282 min: 80, max: 150,
283 onChange: function (v) { set({ hoverScale: v }); }
284 }) : null,
285 el(RangeControl, {
286 label: __('Transition (ms)', 'blockenberg'),
287 value: a.transitionDuration,
288 min: 0, max: 1000, step: 50,
289 onChange: function (v) { set({ transitionDuration: v }); }
290 })
291 ),
292
293 /* Caption */
294 el(PanelBody, { title: __('Caption', 'blockenberg'), initialOpen: false },
295 el(ToggleControl, {
296 label: __('Show Caption', 'blockenberg'),
297 checked: a.showCaption,
298 onChange: function (v) { set({ showCaption: v }); },
299 __nextHasNoMarginBottom: true
300 }),
301 a.showCaption ? el(TextControl, {
302 label: __('Caption Text', 'blockenberg'),
303 value: a.caption,
304 onChange: function (v) { set({ caption: v }); }
305 }) : null,
306 a.showCaption ? el(SelectControl, {
307 label: __('Caption Align', 'blockenberg'),
308 value: a.captionAlign,
309 options: [
310 { label: __('Left', 'blockenberg'), value: 'left' },
311 { label: __('Center', 'blockenberg'), value: 'center' },
312 { label: __('Right', 'blockenberg'), value: 'right' },
313 ],
314 onChange: function (v) { set({ captionAlign: v }); }
315 }) : null,
316 a.showCaption ? el(RangeControl, {
317 label: __('Caption Size (px)', 'blockenberg'),
318 value: a.captionSize,
319 min: 10, max: 24,
320 onChange: function (v) { set({ captionSize: v }); }
321 }) : null
322 ),
323
324 /* Link */
325 el(PanelBody, { title: __('Link', 'blockenberg'), initialOpen: false },
326 el(TextControl, {
327 label: __('Link URL', 'blockenberg'),
328 type: 'url',
329 value: a.linkUrl,
330 onChange: function (v) { set({ linkUrl: v }); }
331 }),
332 el(ToggleControl, {
333 label: __('Open in New Tab', 'blockenberg'),
334 checked: a.linkNewTab,
335 onChange: function (v) { set({ linkNewTab: v }); },
336 __nextHasNoMarginBottom: true
337 })
338 ),
339
340 /* Colors */
341 el(PanelColorSettings, {
342 title: __('Colors', 'blockenberg'),
343 initialOpen: false,
344 colorSettings: [
345 { value: a.borderColor, onChange: function (v) { set({ borderColor: v || '#6c3fb5' }); }, label: __('Border Ring Color', 'blockenberg') },
346 { value: a.shadowColor, onChange: function (v) { set({ shadowColor: v || '#000000' }); }, label: __('Shadow Color', 'blockenberg') },
347 { value: a.captionColor, onChange: function (v) { set({ captionColor: v || '#374151' }); }, label: __('Caption Color', 'blockenberg') },
348 ]
349 }),
350 el(PanelBody, { title: 'Typography', initialOpen: false },
351 getTypographyControl() && el(getTypographyControl(), { label: __('Title', 'blockenberg'), value: a.titleTypo, onChange: function (v) { set({ titleTypo: v }); } }),
352 getTypographyControl() && el(getTypographyControl(), { label: __('Subtitle', 'blockenberg'), value: a.subtitleTypo, onChange: function (v) { set({ subtitleTypo: v }); } }),
353 getTypographyControl() && el(getTypographyControl(), { label: __('Caption', 'blockenberg'), value: a.captionTypo, onChange: function (v) { set({ captionTypo: v }); } })
354 ),
355
356 ),
357
358 /* Canvas */
359 el('div', blockProps,
360 el(MaskPreview, { a: a, isEdit: true })
361 )
362 );
363 },
364
365 save: function (props) {
366 var a = props.attributes;
367 var blockProps = wp.blockEditor.useBlockProps.save({
368 className: 'bkim-wrap',
369 style: (function () {
370 var s = {};
371 var fn = getTypoCssVars();
372 if (fn) { Object.assign(s, fn(a.titleTypo, '--bkim-tt-'), fn(a.subtitleTypo, '--bkim-st-'), fn(a.captionTypo, '--bkim-cp-')); }
373 return s;
374 })()
375 });
376 var clip = getClip(a.shape);
377
378 var imgStyle = [
379 'width:' + a.size + 'px',
380 'height:' + a.size + 'px',
381 'object-fit:cover',
382 'object-position:' + (a.objectPosition || 'center center'),
383 'clip-path:' + clip,
384 '-webkit-clip-path:' + clip,
385 'display:block',
386 'position:relative',
387 'z-index:1',
388 a.showShadow ? 'filter:drop-shadow(0 15px 25px rgba(' + hexRgb(a.shadowColor || '#000') + ',' + (a.shadowIntensity / 100) + '))' : '',
389 'transition:transform ' + a.transitionDuration + 'ms ease',
390 ].filter(Boolean).join(';');
391
392 var outerStyle = 'display:inline-block;position:relative;width:' + a.size + 'px;height:' + a.size + 'px;' + (a.rotation ? 'transform:rotate(' + a.rotation + 'deg);' : '');
393
394 var borderEl = null;
395 if (a.showBorder) {
396 var bOff = a.borderOffset || 8;
397 var bW = a.borderWidth || 4;
398 var bSize = a.size + bOff * 2 + bW * 2;
399 var bStyle = 'position:absolute;top:' + -(bOff + bW) + 'px;left:' + -(bOff + bW) + 'px;width:' + bSize + 'px;height:' + bSize + 'px;background:' + (a.borderColor || '#6c3fb5') + ';clip-path:' + clip + ';-webkit-clip-path:' + clip + ';z-index:0;';
400 borderEl = el('div', { className: 'bkim-border', style: bStyle });
401 }
402
403 var imgEl = el('img', {
404 src: a.imageUrl || '',
405 alt: a.imageAlt || '',
406 className: 'bkim-img',
407 style: imgStyle,
408 'data-hover': a.hoverEffect,
409 'data-scale': a.hoverScale,
410 });
411
412 var caption = (a.showCaption && a.caption) ? el('p', {
413 className: 'bkim-caption',
414 style: 'margin-top:12px;color:' + (a.captionColor || '#374151') + ';text-align:' + (a.captionAlign || 'center') + ';font-style:italic;',
415 }, a.caption) : null;
416
417 var inner = el('div', { style: outerStyle }, borderEl, imgEl);
418
419 if (a.linkUrl) {
420 inner = el('a', {
421 href: a.linkUrl,
422 target: a.linkNewTab ? '_blank' : undefined,
423 rel: a.linkNewTab ? 'noopener noreferrer' : undefined,
424 style: 'display:inline-block;'
425 }, inner);
426 }
427
428 return el('div', blockProps,
429 el('figure', { className: 'bkim-figure', style: 'display:inline-block;text-align:' + (a.captionAlign || 'center') + ';' },
430 inner, caption
431 )
432 );
433 }
434 });
435
436 function hexRgb(hex) {
437 if (!hex || hex.charAt(0) !== '#') return '0,0,0';
438 return parseInt(hex.slice(1, 3), 16) + ',' + parseInt(hex.slice(3, 5), 16) + ',' + parseInt(hex.slice(5, 7), 16);
439 }
440
441 function getClip(shape) {
442 return SHAPES[shape] || SHAPES.circle;
443 }
444 }() );
445