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

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

513 lines 23.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 registerBlockType = wp.blocks.registerBlockType;
5 var InspectorControls = wp.blockEditor.InspectorControls;
6 var useBlockProps = wp.blockEditor.useBlockProps;
7 var MediaUpload = wp.blockEditor.MediaUpload;
8 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
9 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
10 var PanelBody = wp.components.PanelBody;
11 var ToggleControl = wp.components.ToggleControl;
12 var RangeControl = wp.components.RangeControl;
13 var SelectControl = wp.components.SelectControl;
14 var TextControl = wp.components.TextControl;
15 var Button = wp.components.Button;
16
17 /* Frame color presets */
18 var FRAME_COLORS = {
19 'silver': '#d4d4d8',
20 'space-gray': '#374151',
21 'gold': '#d4a853',
22 'white': '#f9fafb',
23 'black': '#111827',
24 'custom': null,
25 };
26
27 function getFrameColor(a) {
28 return a.frameColor === 'custom' ? a.frameColorCustom : (FRAME_COLORS[a.frameColor] || '#d4d4d8');
29 }
30
31 /* Aspect ratios */
32 var ASPECTS = { phone: 9 / 19.5, tablet: 4 / 3, laptop: 16 / 10, browser: 16 / 10 };
33
34 var PLACEHOLDER = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="300" height="200"><rect width="300" height="200" fill="%23e5e7eb"/><text x="50%25" y="50%25" font-family="sans-serif" font-size="14" fill="%239ca3af" text-anchor="middle" dy=".3em">Screenshot</text></svg>';
35
36 /* ── Device preview (shared edit + save) ───────────────────────────────── */
37 function DevicePreview(props) {
38 var a = props.a;
39 var imgUrl = a.imageUrl || PLACEHOLDER;
40 var frameColor = getFrameColor(a);
41 var aspect = ASPECTS[a.deviceType] || (9 / 19.5);
42 var sw = a.maxWidth;
43 var sh = Math.round(sw / aspect);
44 var fs = a.frameStroke;
45 var or = a.outerRadius;
46 var sr = a.screenRadius;
47 var dur = a.transitionDuration || 400;
48
49 /* Shadow */
50 var shadowVal = a.shadow
51 ? '0 ' + Math.round(sw * 0.06) + 'px ' + Math.round(sw * 0.14) + 'px rgba(0,0,0,' + (a.shadowIntensity / 100) + ')'
52 : 'none';
53
54 /* Perspective tilt */
55 var transform3d = '';
56 if (a.tiltX !== 0 || a.tiltY !== 0) {
57 transform3d = 'perspective(1200px) rotateX(' + a.tiltY + 'deg) rotateY(' + a.tiltX + 'deg)';
58 }
59
60 var scale = (a.scale || 100) / 100;
61
62 var outerStyle = {
63 display: 'inline-block',
64 transform: transform3d + (scale !== 1 ? ' scale(' + scale + ')' : ''),
65 transformOrigin: 'center top',
66 };
67
68 /* ── PHONE / TABLET ── */
69 if (a.deviceType === 'phone' || a.deviceType === 'tablet') {
70 var deviceStyle = {
71 position: 'relative',
72 width: sw + 'px',
73 boxSizing: 'border-box',
74 background: frameColor,
75 borderRadius: or + 'px',
76 padding: fs + 'px',
77 boxShadow: shadowVal,
78 };
79
80 var screenStyle = {
81 position: 'relative',
82 width: '100%',
83 paddingBottom: (100 / aspect) + '%',
84 borderRadius: sr + 'px',
85 overflow: 'hidden',
86 background: '#000',
87 };
88
89 var imgStyle = {
90 position: 'absolute',
91 inset: 0,
92 width: '100%',
93 height: '100%',
94 objectFit: 'cover',
95 display: 'block',
96 };
97
98 /* Notch */
99 var notchEl = (a.showNotch && a.deviceType === 'phone') ? el('div', {
100 className: 'bkdv-notch',
101 style: {
102 position: 'absolute',
103 top: '0',
104 left: '50%',
105 transform: 'translateX(-50%)',
106 width: '28%',
107 height: '20px',
108 background: frameColor,
109 borderBottomLeftRadius: '12px',
110 borderBottomRightRadius: '12px',
111 zIndex: 3,
112 }
113 }) : null;
114
115 /* Home indicator */
116 var homeEl = (a.showHomeIndicator && a.deviceType === 'phone') ? el('div', {
117 style: {
118 position: 'absolute',
119 bottom: Math.round(fs / 2) + 'px',
120 left: '50%',
121 transform: 'translateX(-50%)',
122 width: '35%',
123 height: '4px',
124 borderRadius: '2px',
125 background: 'rgba(255,255,255,0.6)',
126 zIndex: 3,
127 }
128 }) : null;
129
130 /* Side buttons */
131 var sidesEl = el('div', null,
132 el('div', { style: { position: 'absolute', right: '-3px', top: '18%', width: '3px', height: '10%', borderRadius: '0 2px 2px 0', background: frameColor, filter: 'brightness(0.8)' } }),
133 el('div', { style: { position: 'absolute', left: '-3px', top: '16%', width: '3px', height: '8%', borderRadius: '2px 0 0 2px', background: frameColor, filter: 'brightness(0.8)' } }),
134 el('div', { style: { position: 'absolute', left: '-3px', top: '26%', width: '3px', height: '8%', borderRadius: '2px 0 0 2px', background: frameColor, filter: 'brightness(0.8)' } })
135 );
136
137 /* Reflection */
138 var reflectionEl = a.showReflection ? el('div', {
139 style: {
140 position: 'absolute',
141 inset: 0,
142 borderRadius: sr + 'px',
143 background: 'linear-gradient(135deg, rgba(255,255,255,0.25) 0%, rgba(255,255,255,0) 60%)',
144 pointerEvents: 'none',
145 zIndex: 2,
146 }
147 }) : null;
148
149 return el('div', { style: outerStyle },
150 el('div', { style: deviceStyle },
151 sidesEl,
152 homeEl,
153 el('div', { style: screenStyle },
154 notchEl,
155 el('img', { src: imgUrl, alt: a.imageAlt, style: imgStyle }),
156 reflectionEl
157 )
158 )
159 );
160 }
161
162 /* ── LAPTOP ── */
163 if (a.deviceType === 'laptop') {
164 var lidW = sw;
165 var lidH = Math.round(lidW * 0.6);
166 var keyW = Math.round(lidW * 1.15);
167 var keyH = Math.round(lidW * 0.08);
168
169 var lidStyle = {
170 width: lidW + 'px',
171 height: lidH + 'px',
172 background: frameColor,
173 borderRadius: '12px 12px 0 0',
174 padding: '10px 10px 6px',
175 boxSizing: 'border-box',
176 position: 'relative',
177 boxShadow: shadowVal,
178 margin: '0 auto',
179 };
180
181 var screenAreaStyle = {
182 width: '100%',
183 height: '100%',
184 background: '#000',
185 borderRadius: sr + 'px',
186 overflow: 'hidden',
187 position: 'relative',
188 };
189
190 var keybaseStyle = {
191 width: keyW + 'px',
192 height: keyH + 'px',
193 background: frameColor,
194 filter: 'brightness(0.88)',
195 borderRadius: '0 0 8px 8px',
196 margin: '0 auto',
197 position: 'relative',
198 };
199
200 var hinge = el('div', {
201 style: {
202 width: Math.round(lidW * 0.15) + 'px',
203 height: '4px',
204 background: frameColor,
205 filter: 'brightness(0.7)',
206 margin: '0 auto',
207 }
208 });
209
210 return el('div', { style: outerStyle },
211 el('div', { style: lidStyle },
212 el('div', { style: screenAreaStyle },
213 el('img', { src: imgUrl, alt: a.imageAlt, style: { width: '100%', height: '100%', objectFit: 'cover', display: 'block' } }),
214 a.showReflection ? el('div', { style: { position: 'absolute', inset: 0, background: 'linear-gradient(135deg, rgba(255,255,255,0.2) 0%, transparent 55%)', pointerEvents: 'none' } }) : null
215 )
216 ),
217 hinge,
218 el('div', { style: keybaseStyle },
219 el('div', { style: { position: 'absolute', bottom: '5px', left: '50%', transform: 'translateX(-50%)', width: '30%', height: '6px', background: 'rgba(0,0,0,0.15)', borderRadius: '3px' } })
220 )
221 );
222 }
223
224 /* ── BROWSER ── */
225 if (a.deviceType === 'browser') {
226 var chromeH = a.showBrowserChrome ? 38 : 0;
227 var containerStyle = {
228 width: sw + 'px',
229 borderRadius: or + 'px',
230 overflow: 'hidden',
231 boxShadow: shadowVal,
232 display: 'flex',
233 flexDirection: 'column',
234 border: '1px solid rgba(0,0,0,0.12)',
235 };
236
237 var chromeBarStyle = {
238 display: 'flex',
239 alignItems: 'center',
240 gap: '6px',
241 padding: '0 12px',
242 height: chromeH + 'px',
243 background: a.frameColor === 'black' ? '#1f2937' : '#f3f4f6',
244 borderBottom: '1px solid rgba(0,0,0,0.1)',
245 flexShrink: 0,
246 };
247
248 var dot = function (color) { return el('div', { style: { width: '12px', height: '12px', borderRadius: '50%', background: color, flexShrink: 0 } }); };
249
250 var urlBarStyle = {
251 flex: 1,
252 height: '22px',
253 background: '#fff',
254 borderRadius: '4px',
255 border: '1px solid rgba(0,0,0,0.1)',
256 display: 'flex',
257 alignItems: 'center',
258 paddingLeft: '8px',
259 fontSize: (a.urlBarFontSize || 11) + 'px',
260 color: '#6b7280',
261 overflow: 'hidden',
262 whiteSpace: 'nowrap',
263 textOverflow: 'ellipsis',
264 fontFamily: 'monospace',
265 };
266
267 var viewportStyle = {
268 width: '100%',
269 paddingBottom: (100 / (ASPECTS.browser)) + '%',
270 position: 'relative',
271 background: '#fff',
272 };
273
274 return el('div', { style: outerStyle },
275 el('div', { style: containerStyle },
276 a.showBrowserChrome ? el('div', { style: chromeBarStyle },
277 dot('#ef4444'), dot('#f59e0b'), dot('#22c55e'),
278 el('div', { style: urlBarStyle }, a.browserUrl || 'https://example.com')
279 ) : null,
280 el('div', { style: viewportStyle },
281 el('img', { src: imgUrl, alt: a.imageAlt, style: { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', display: 'block' } }),
282 a.showReflection ? el('div', { style: { position: 'absolute', inset: 0, background: 'linear-gradient(135deg, rgba(255,255,255,0.18) 0%, transparent 50%)', pointerEvents: 'none' } }) : null
283 )
284 )
285 );
286 }
287
288 return null;
289 }
290
291 /* ── Register ──────────────────────────────────────────────────────────── */
292 registerBlockType('blockenberg/device-mockup', {
293 edit: function (props) {
294 var a = props.attributes;
295 var set = props.setAttributes;
296 var blockProps = useBlockProps({ className: 'bkdv-wrap' });
297
298 var wrapStyle = {
299 textAlign: (props.attributes.align === 'right' ? 'right' : props.attributes.align === 'center' ? 'center' : 'left'),
300 };
301
302 return el(wp.element.Fragment, null,
303 el(InspectorControls, null,
304
305 /* Device */
306 el(PanelBody, { title: __('Device', 'blockenberg'), initialOpen: true },
307 el(SelectControl, {
308 label: __('Device Type', 'blockenberg'),
309 value: a.deviceType,
310 options: [
311 { label: __('Phone', 'blockenberg'), value: 'phone' },
312 { label: __('Tablet', 'blockenberg'), value: 'tablet' },
313 { label: __('Laptop', 'blockenberg'), value: 'laptop' },
314 { label: __('Browser', 'blockenberg'), value: 'browser' },
315 ],
316 onChange: function (v) { set({ deviceType: v }); }
317 }),
318 el(SelectControl, {
319 label: __('Frame Color', 'blockenberg'),
320 value: a.frameColor,
321 options: [
322 { label: __('Silver', 'blockenberg'), value: 'silver' },
323 { label: __('Space Gray', 'blockenberg'), value: 'space-gray' },
324 { label: __('Gold', 'blockenberg'), value: 'gold' },
325 { label: __('White', 'blockenberg'), value: 'white' },
326 { label: __('Black', 'blockenberg'), value: 'black' },
327 { label: __('Custom', 'blockenberg'), value: 'custom' },
328 ],
329 onChange: function (v) { set({ frameColor: v }); }
330 }),
331 a.frameColor === 'custom' ? el(wp.components.ColorPicker, {
332 color: a.frameColorCustom,
333 onChange: function (v) { set({ frameColorCustom: v }); },
334 enableAlpha: false,
335 }) : null,
336 el(RangeControl, {
337 label: __('Max Width (px)', 'blockenberg'),
338 value: a.maxWidth,
339 min: 120, max: 900,
340 onChange: function (v) { set({ maxWidth: v }); }
341 }),
342 el(RangeControl, {
343 label: __('Scale (%)', 'blockenberg'),
344 value: a.scale,
345 min: 20, max: 200,
346 onChange: function (v) { set({ scale: v }); }
347 }),
348 el(RangeControl, {
349 label: __('Frame Border Width (px)', 'blockenberg'),
350 value: a.frameStroke,
351 min: 4, max: 32,
352 onChange: function (v) { set({ frameStroke: v }); }
353 }),
354 el(RangeControl, {
355 label: __('Outer Radius (px)', 'blockenberg'),
356 value: a.outerRadius,
357 min: 0, max: 80,
358 onChange: function (v) { set({ outerRadius: v }); }
359 }),
360 el(RangeControl, {
361 label: __('Screen Radius (px)', 'blockenberg'),
362 value: a.screenRadius,
363 min: 0, max: 24,
364 onChange: function (v) { set({ screenRadius: v }); }
365 })
366 ),
367
368 /* Screenshot */
369 el(PanelBody, { title: __('Screenshot / Image', 'blockenberg'), initialOpen: true },
370 el(MediaUploadCheck, null,
371 el(MediaUpload, {
372 onSelect: function (media) { set({ imageUrl: media.url, imageId: media.id, imageAlt: media.alt || '' }); },
373 allowedTypes: ['image'],
374 value: a.imageId,
375 render: function (ref) {
376 return el(Button, {
377 onClick: ref.open,
378 variant: a.imageUrl ? 'secondary' : 'primary',
379 style: { marginBottom: '8px' }
380 }, a.imageUrl ? __('Replace Screenshot', 'blockenberg') : __('Choose Screenshot', 'blockenberg'));
381 }
382 })
383 ),
384 a.imageUrl ? el(Button, {
385 variant: 'tertiary',
386 isDestructive: true,
387 onClick: function () { set({ imageUrl: '', imageId: 0, imageAlt: '' }); }
388 }, __('Remove Screenshot', 'blockenberg')) : null
389 ),
390
391 /* Tilt & Shadow */
392 el(PanelBody, { title: __('3D Tilt & Shadow', 'blockenberg'), initialOpen: false },
393 el(RangeControl, {
394 label: __('Tilt Y (rotateX °)', 'blockenberg'),
395 value: a.tiltX,
396 min: -30, max: 30,
397 onChange: function (v) { set({ tiltX: v }); }
398 }),
399 el(RangeControl, {
400 label: __('Tilt X (rotateY °)', 'blockenberg'),
401 value: a.tiltY,
402 min: -30, max: 30,
403 onChange: function (v) { set({ tiltY: v }); }
404 }),
405 el(ToggleControl, {
406 label: __('Drop Shadow', 'blockenberg'),
407 checked: a.shadow,
408 onChange: function (v) { set({ shadow: v }); },
409 __nextHasNoMarginBottom: true
410 }),
411 a.shadow ? el(RangeControl, {
412 label: __('Shadow Intensity (%)', 'blockenberg'),
413 value: a.shadowIntensity,
414 min: 5, max: 70,
415 onChange: function (v) { set({ shadowIntensity: v }); }
416 }) : null,
417 el(ToggleControl, {
418 label: __('Reflection', 'blockenberg'),
419 checked: a.showReflection,
420 onChange: function (v) { set({ showReflection: v }); },
421 __nextHasNoMarginBottom: true
422 }),
423 el(ToggleControl, {
424 label: __('Hover Lift Effect', 'blockenberg'),
425 checked: a.hoverLift,
426 onChange: function (v) { set({ hoverLift: v }); },
427 __nextHasNoMarginBottom: true
428 }),
429 el(ToggleControl, {
430 label: __('Animate on Scroll', 'blockenberg'),
431 checked: a.animateOnScroll,
432 onChange: function (v) { set({ animateOnScroll: v }); },
433 __nextHasNoMarginBottom: true
434 })
435 ),
436
437 /* Device-specific */
438 (a.deviceType === 'phone' || a.deviceType === 'tablet') ? el(PanelBody, { title: __('Phone / Tablet Details', 'blockenberg'), initialOpen: false },
439 el(ToggleControl, {
440 label: __('Show Notch', 'blockenberg'),
441 checked: a.showNotch,
442 onChange: function (v) { set({ showNotch: v }); },
443 __nextHasNoMarginBottom: true
444 }),
445 el(ToggleControl, {
446 label: __('Show Home Indicator', 'blockenberg'),
447 checked: a.showHomeIndicator,
448 onChange: function (v) { set({ showHomeIndicator: v }); },
449 __nextHasNoMarginBottom: true
450 })
451 ) : null,
452
453 a.deviceType === 'browser' ? el(PanelBody, { title: __('Browser Details', 'blockenberg'), initialOpen: false },
454 el(ToggleControl, {
455 label: __('Show Browser Chrome', 'blockenberg'),
456 checked: a.showBrowserChrome,
457 onChange: function (v) { set({ showBrowserChrome: v }); },
458 __nextHasNoMarginBottom: true
459 }),
460 a.showBrowserChrome ? el(TextControl, {
461 label: __('URL Bar Text', 'blockenberg'),
462 value: a.browserUrl,
463 onChange: function (v) { set({ browserUrl: v }); }
464 }) : null
465 ) : null,
466
467 /* Typography */
468 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
469 el(RangeControl, { label: __('URL bar font size (px)', 'blockenberg'), value: a.urlBarFontSize, min: 9, max: 16, onChange: function (v) { set({ urlBarFontSize: v }); }, __nextHasNoMarginBottom: true })
470 )
471 ),
472
473 /* Canvas */
474 el('div', blockProps,
475 el('div', { style: wrapStyle },
476 el(DevicePreview, { a: a })
477 )
478 )
479 );
480 },
481
482 save: function (props) {
483 var a = props.attributes;
484 var blockProps = wp.blockEditor.useBlockProps.save({ className: 'bkdv-wrap' });
485
486 return el('div', Object.assign({}, blockProps, {
487 'data-device': a.deviceType,
488 'data-frame-color': a.frameColor,
489 'data-frame-custom': a.frameColorCustom,
490 'data-max-width': a.maxWidth,
491 'data-scale': a.scale,
492 'data-tilt-x': a.tiltX,
493 'data-tilt-y': a.tiltY,
494 'data-shadow': String(a.shadow),
495 'data-shadow-int': a.shadowIntensity,
496 'data-reflection': String(a.showReflection),
497 'data-hover-lift': String(a.hoverLift),
498 'data-scroll-anim': String(a.animateOnScroll),
499 'data-frame-stroke': a.frameStroke,
500 'data-screen-radius': a.screenRadius,
501 'data-outer-radius': a.outerRadius,
502 'data-notch': String(a.showNotch),
503 'data-home-ind': String(a.showHomeIndicator),
504 'data-browser-chrome':String(a.showBrowserChrome),
505 'data-browser-url': a.browserUrl,
506 'data-img-url': a.imageUrl,
507 'data-img-alt': a.imageAlt,
508 'data-align': props.attributes.align || 'center',
509 }));
510 }
511 });
512 }() );
513