PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.14
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.14
2.0.14 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 / video-facade / index.js

index.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.14, at blocks/video-facade/index.js

327 lines 21.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 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 RichText = wp.blockEditor.RichText;
9 var useBlockProps = wp.blockEditor.useBlockProps;
10 var MediaUpload = wp.blockEditor.MediaUpload;
11 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
12 var PanelBody = wp.components.PanelBody;
13 var Button = wp.components.Button;
14 var ToggleControl = wp.components.ToggleControl;
15 var RangeControl = wp.components.RangeControl;
16 var SelectControl = wp.components.SelectControl;
17 var TextControl = wp.components.TextControl;
18 var ColorPicker = wp.components.ColorPicker;
19 var Popover = wp.components.Popover;
20
21 // Resolve the shared components lazily without defining globals for each block.
22 function _tc(label, key, attrs, setA) {
23 var Control = window.bkbgTypographyControl;
24 return Control ? el(Control, {
25 label: label,
26 value: attrs[key] || {},
27 onChange: function (value) { var update = {}; update[key] = value; setA(update); }
28 }) : null;
29 }
30 function _tvf(vars, key, attrs, prefix) {
31 var cssVars = window.bkbgTypoCssVars;
32 if (cssVars) Object.assign(vars, cssVars(attrs[key] || {}, prefix));
33 return vars;
34 }
35 function getTypoControl(label, key, attrs, setA) { return _tc(label, key, attrs, setA); }
36 function getTypoCssVars(attrs) {
37 var v = {};
38 _tvf(v, 'titleTypo', attrs, '--bkvf-tt-');
39 _tvf(v, 'subtitleTypo', attrs, '--bkvf-st-');
40 _tvf(v, 'captionTypo', attrs, '--bkvf-cp-');
41 return v;
42 }
43
44 var ASPECT_RATIOS = {
45 '16-9': 56.25,
46 '4-3': 75,
47 '21-9': 42.86,
48 '1-1': 100,
49 };
50
51 /* ── Helpers ──────────────────────────────────────────────────── */
52 function extractVideoInfo(url) {
53 url = (url || '').trim();
54 if (!url) return { provider: 'youtube', id: '' };
55
56 /* YouTube */
57 var ytMatch = url.match(/(?:youtube\.com\/(?:watch\?v=|embed\/)|youtu\.be\/)([A-Za-z0-9_-]{11})/);
58 if (ytMatch) return { provider: 'youtube', id: ytMatch[1] };
59
60 /* Vimeo */
61 var vmMatch = url.match(/vimeo\.com\/(?:video\/)?(\d+)/);
62 if (vmMatch) return { provider: 'vimeo', id: vmMatch[1] };
63
64 return { provider: 'youtube', id: '' };
65 }
66
67 function getAutoThumb(provider, videoId) {
68 if (provider === 'youtube' && videoId) {
69 return 'https://img.youtube.com/vi/' + videoId + '/maxresdefault.jpg';
70 }
71 return '';
72 }
73
74 function hexToRgba(hex, alpha) {
75 hex = (hex || '#000000').replace('#', '');
76 if (hex.length === 3) hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
77 var r = parseInt(hex.substr(0,2),16), g = parseInt(hex.substr(2,2),16), b = parseInt(hex.substr(4,2),16);
78 return 'rgba('+r+','+g+','+b+','+(alpha/100)+')';
79 }
80
81 function renderColorControl(key, label, value, onChange, openKey, setOpenKey) {
82 var isOpen = openKey === key;
83 return el('div', { key: key, style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '4px 0', gap: '8px' } },
84 el('span', { style: { fontSize: '12px', color: '#1e1e1e', flex: 1, lineHeight: 1.4 } }, label),
85 el('div', { style: { position: 'relative', flexShrink: 0 } },
86 el('button', { type: 'button', onClick: function () { setOpenKey(isOpen ? null : key); }, style: { width: '28px', height: '28px', borderRadius: '4px', border: isOpen ? '2px solid #007cba' : '2px solid #ddd', cursor: 'pointer', padding: 0, background: value || '#ccc' } }),
87 isOpen && el(Popover, { position: 'bottom left', onClose: function () { setOpenKey(null); } },
88 el('div', { style: { padding: '8px' } },
89 el(ColorPicker, { color: value, enableAlpha: true, onChange: onChange })
90 )
91 )
92 )
93 );
94 }
95
96 /* ── Play icon SVG ────────────────────────────────────────────── */
97 function PlayIcon(props) {
98 var size = props.size || 72;
99 var color = props.color || '#fff';
100 var bg = props.bg || '#6c3fb5';
101 var style = props.style || 'circle';
102 var pulsing = props.pulsing;
103
104 var inner = el('svg', { xmlns: 'http://www.w3.org/2000/svg', width: size * 0.45, height: size * 0.45, viewBox: '0 0 24 24', fill: color },
105 el('path', { d: 'M8 5v14l11-7z' })
106 );
107
108 if (style === 'minimal') {
109 return el('div', { style: { width: size + 'px', height: size + 'px', display: 'flex', alignItems: 'center', justifyContent: 'center', border: '3px solid ' + color, borderRadius: '50%', cursor: 'pointer' } }, inner);
110 }
111 if (style === 'oval') {
112 return el('div', { style: { padding: '14px 24px', display: 'flex', alignItems: 'center', gap: '10px', background: bg, borderRadius: '99px', cursor: 'pointer' } },
113 inner,
114 el('span', { style: { color: color, fontWeight: 700, fontSize: '16px', letterSpacing: '0.02em' } }, __('Watch', 'blockenberg'))
115 );
116 }
117 /* circle (default) */
118 return el('div', {
119 style: {
120 width: size + 'px', height: size + 'px',
121 borderRadius: '50%',
122 background: bg,
123 display: 'flex', alignItems: 'center', justifyContent: 'center',
124 cursor: 'pointer',
125 boxShadow: '0 8px 32px rgba(0,0,0,0.25)',
126 animation: pulsing ? 'bkbg-vf-pulse 2s ease-in-out infinite' : 'none',
127 }
128 }, inner);
129 }
130
131 /* ── Editor preview ───────────────────────────────────────────── */
132 function VideoPreview(props) {
133 var a = props.a;
134 var thumbSrc = a.thumbnailUrl || (a.autoThumb ? getAutoThumb(a.videoProvider, a.videoId) : '');
135 var ratio = ASPECT_RATIOS[a.aspectRatio] || 56.25;
136
137 return el('div', {
138 style: {
139 position: 'relative',
140 width: '100%',
141 paddingBottom: ratio + '%',
142 borderRadius: a.borderRadius + 'px',
143 overflow: 'hidden',
144 boxShadow: a.showShadow ? '0 12px 48px rgba(0,0,0,0.18)' : 'none',
145 background: '#111',
146 cursor: 'pointer',
147 }
148 },
149 /* Thumbnail */
150 thumbSrc && el('img', {
151 src: thumbSrc, alt: '',
152 style: { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }
153 }),
154 !thumbSrc && el('div', { style: { position: 'absolute', inset: 0, background: '#1e1e2e', display: 'flex', alignItems: 'center', justifyContent: 'center' } },
155 el('span', { style: { color: '#6b7280', fontSize: '14px', fontWeight: 500 } }, a.videoId ? '⏳ Thumbnail loading...' : '🎬 ' + __('Enter a YouTube or Vimeo URL in the panel →', 'blockenberg'))
156 ),
157
158 /* Overlay */
159 el('div', { style: { position: 'absolute', inset: 0, background: hexToRgba(a.overlayColor, a.overlayOpacity) } }),
160
161 /* Title / subtitle overlay */
162 (a.showTitle || a.showSubtitle) && el('div', { style: { position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'flex-end', padding: '32px 24px', textAlign: 'center' } },
163 a.showTitle && el('p', { className: 'bkbg-vf-title', style: { margin: 0, color: a.titleColor, textShadow: '0 2px 8px rgba(0,0,0,0.4)' } }, a.title || __('Video Title', 'blockenberg')),
164 a.showSubtitle && el('p', { className: 'bkbg-vf-subtitle', style: { margin: '6px 0 0', color: a.subtitleColor, textShadow: '0 1px 4px rgba(0,0,0,0.3)' } }, a.subtitle || __('Subtitle text', 'blockenberg'))
165 ),
166
167 /* Play button centered */
168 el('div', { style: { position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' } },
169 el(PlayIcon, { size: a.playSize, color: a.playColor, bg: a.playBg, style: a.playStyle, pulsing: a.playPulsing })
170 )
171 );
172 }
173
174 registerBlockType('blockenberg/video-facade', {
175 title: __('Video Facade', 'blockenberg'),
176 icon: 'video-alt3',
177 category: 'bkbg-media',
178
179 edit: function (props) {
180 var a = props.attributes;
181 var setAttributes = props.setAttributes;
182
183 var openColorKeyState = useState(null);
184 var openColorKey = openColorKeyState[0];
185 var setOpenColorKey = openColorKeyState[1];
186
187 var blockProps = useBlockProps((function () {
188 var s = getTypoCssVars(a);
189 s.backgroundColor = a.bgColor || undefined;
190 s.paddingTop = a.paddingTop + 'px';
191 s.paddingBottom = a.paddingBottom + 'px';
192 return { style: s };
193 })());
194
195 function cc(key, label, value, attrKey) {
196 return renderColorControl(key, label, value, function (v) {
197 var upd = {}; upd[attrKey] = v; setAttributes(upd);
198 }, openColorKey, setOpenColorKey);
199 }
200
201 function handleUrlChange(url) {
202 var info = extractVideoInfo(url);
203 setAttributes({ videoUrl: url, videoProvider: info.provider, videoId: info.id });
204 }
205
206 return el(Fragment, null,
207 el(InspectorControls, null,
208 el(PanelBody, { title: __('Video', 'blockenberg'), initialOpen: true },
209 el(TextControl, { label: __('YouTube or Vimeo URL', 'blockenberg'), value: a.videoUrl, onChange: handleUrlChange, placeholder: 'https://youtube.com/watch?v=...' }),
210 a.videoProvider && a.videoId && el('p', { style: { fontSize: '12px', color: '#888', margin: '0 0 8px', background: '#f0f4ff', padding: '4px 8px', borderRadius: '4px' } },
211 '�
212 ' + a.videoProvider.charAt(0).toUpperCase() + a.videoProvider.slice(1) + ' ID: ' + a.videoId
213 ),
214 el(SelectControl, { label: __('Aspect ratio', 'blockenberg'), value: a.aspectRatio, options: [{ label: '16:9 (Widescreen)', value: '16-9' }, { label: '4:3 (Classic)', value: '4-3' }, { label: '21:9 (Cinematic)', value: '21-9' }, { label: '1:1 (Square)', value: '1-1' }], onChange: function (v) { setAttributes({ aspectRatio: v }); } }),
215 el(ToggleControl, { label: __('Auto-load thumbnail from provider', 'blockenberg'), checked: a.autoThumb, onChange: function (v) { setAttributes({ autoThumb: v }); }, __nextHasNoMarginBottom: true }),
216 el('p', { style: { fontSize: '12px', color: '#888', margin: '4px 0 12px' } }, __('Or upload a custom thumbnail below:', 'blockenberg')),
217 el(MediaUploadCheck, null,
218 el(MediaUpload, { onSelect: function (m) { setAttributes({ thumbnailUrl: m.url, thumbnailId: m.id }); }, allowedTypes: ['image'], value: a.thumbnailId,
219 render: function (mp) {
220 return el(Fragment, null,
221 a.thumbnailUrl && el('img', { src: a.thumbnailUrl, style: { width: '100%', borderRadius: '6px', marginBottom: '6px', display: 'block' } }),
222 el(Button, { variant: 'secondary', size: 'compact', onClick: mp.open }, a.thumbnailUrl ? __('Change thumbnail', 'blockenberg') : __('Upload thumbnail', 'blockenberg')),
223 a.thumbnailUrl && el(Button, { variant: 'tertiary', size: 'compact', isDestructive: true, onClick: function () { setAttributes({ thumbnailUrl: '', thumbnailId: 0 }); }, style: { marginLeft: '6px' } }, __('Remove', 'blockenberg'))
224 );
225 }
226 })
227 )
228 ),
229 el(PanelBody, { title: __('Overlay & Play Button', 'blockenberg'), initialOpen: false },
230 RangeControl && el(RangeControl, { label: __('Overlay opacity (%)', 'blockenberg'), value: a.overlayOpacity, min: 0, max: 85, onChange: function (v) { setAttributes({ overlayOpacity: v }); } }),
231 cc('overlayColor', __('Overlay color', 'blockenberg'), a.overlayColor, 'overlayColor'),
232 el(SelectControl, { label: __('Play button style', 'blockenberg'), value: a.playStyle, options: [{ label: __('Circle', 'blockenberg'), value: 'circle' }, { label: __('Oval with text', 'blockenberg'), value: 'oval' }, { label: __('Minimal outline', 'blockenberg'), value: 'minimal' }], onChange: function (v) { setAttributes({ playStyle: v }); } }),
233 el(RangeControl, { label: __('Play button size (px)', 'blockenberg'), value: a.playSize, min: 40, max: 140, onChange: function (v) { setAttributes({ playSize: v }); } }),
234 cc('playBg', __('Play button background', 'blockenberg'), a.playBg, 'playBg'),
235 cc('playColor', __('Play icon color', 'blockenberg'), a.playColor, 'playColor'),
236 el(ToggleControl, { label: __('Pulsing animation', 'blockenberg'), checked: a.playPulsing, onChange: function (v) { setAttributes({ playPulsing: v }); }, __nextHasNoMarginBottom: true })
237 ),
238 el(PanelBody, { title: __('Overlay Text', 'blockenberg'), initialOpen: false },
239 el(ToggleControl, { label: __('Show title', 'blockenberg'), checked: a.showTitle, onChange: function (v) { setAttributes({ showTitle: v }); }, __nextHasNoMarginBottom: true }),
240 a.showTitle && el(TextControl, { label: __('Title', 'blockenberg'), value: a.title, onChange: function (v) { setAttributes({ title: v }); } }),
241 el(ToggleControl, { label: __('Show subtitle', 'blockenberg'), checked: a.showSubtitle, onChange: function (v) { setAttributes({ showSubtitle: v }); }, __nextHasNoMarginBottom: true }),
242 a.showSubtitle && el(TextControl, { label: __('Subtitle', 'blockenberg'), value: a.subtitle, onChange: function (v) { setAttributes({ subtitle: v }); } }),
243 cc('titleColor', __('Title color', 'blockenberg'), a.titleColor, 'titleColor'),
244 cc('subtitleColor', __('Subtitle color', 'blockenberg'), a.subtitleColor, 'subtitleColor')
245 ),
246 el(PanelBody, { title: __('Caption', 'blockenberg'), initialOpen: false },
247 el(ToggleControl, { label: __('Show caption', 'blockenberg'), checked: a.showCaption, onChange: function (v) { setAttributes({ showCaption: v }); }, __nextHasNoMarginBottom: true }),
248 a.showCaption && el(TextControl, { label: __('Caption text', 'blockenberg'), value: a.caption, onChange: function (v) { setAttributes({ caption: v }); } }),
249 a.showCaption && cc('captionColor', __('Caption color', 'blockenberg'), a.captionColor, 'captionColor')
250 ),
251 el(PanelBody, { title: __('Card Style', 'blockenberg'), initialOpen: false },
252 el(RangeControl, { label: __('Border radius (px)', 'blockenberg'), value: a.borderRadius, min: 0, max: 40, onChange: function (v) { setAttributes({ borderRadius: v }); } }),
253 el(ToggleControl, { label: __('Show shadow', 'blockenberg'), checked: a.showShadow, onChange: function (v) { setAttributes({ showShadow: v }); }, __nextHasNoMarginBottom: true }),
254 cc('bgColor', __('Section background', 'blockenberg'), a.bgColor, 'bgColor')
255 ),
256 el(PanelBody, { title: __('Spacing', 'blockenberg'), initialOpen: false },
257 el(RangeControl, { label: __('Padding top (px)', 'blockenberg'), value: a.paddingTop, min: 0, max: 200, onChange: function (v) { setAttributes({ paddingTop: v }); } }),
258 el(RangeControl, { label: __('Padding bottom (px)', 'blockenberg'), value: a.paddingBottom, min: 0, max: 200, onChange: function (v) { setAttributes({ paddingBottom: v }); } })
259 ),
260 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
261 getTypoControl(__('Title', 'blockenberg'), 'titleTypo', a, setAttributes),
262 getTypoControl(__('Subtitle', 'blockenberg'), 'subtitleTypo', a, setAttributes),
263 getTypoControl(__('Caption', 'blockenberg'), 'captionTypo', a, setAttributes)
264 )
265 ),
266
267 el('div', blockProps,
268 el(VideoPreview, { a: a }),
269 a.showCaption && a.caption && el('p', { className: 'bkbg-vf-caption', style: { marginTop: '10px', color: a.captionColor, textAlign: 'center' } }, a.caption)
270 )
271 );
272 },
273
274 save: function (props) {
275 var a = props.attributes;
276 var thumbSrc = a.thumbnailUrl || (a.autoThumb ? getAutoThumb(a.videoProvider, a.videoId) : '');
277 var ratio = ASPECT_RATIOS[a.aspectRatio] || 56.25;
278
279 return el('div', wp.blockEditor.useBlockProps.save((function () {
280 var _tv = getTypoCssVars(a);
281 _tv.backgroundColor = a.bgColor || undefined;
282 _tv.paddingTop = a.paddingTop + 'px';
283 _tv.paddingBottom = a.paddingBottom + 'px';
284 return { className: 'bkbg-vf-wrapper', style: _tv };
285 })()),
286 el('div', {
287 className: 'bkbg-vf-facade',
288 'data-provider': a.videoProvider,
289 'data-vid': a.videoId,
290 'data-pulsing': a.playPulsing ? '1' : '0',
291 style: {
292 position: 'relative', width: '100%',
293 paddingBottom: ratio + '%',
294 borderRadius: a.borderRadius + 'px',
295 overflow: 'hidden',
296 boxShadow: a.showShadow ? '0 12px 48px rgba(0,0,0,0.18)' : 'none',
297 background: '#111',
298 cursor: 'pointer',
299 }
300 },
301 thumbSrc && el('img', {
302 className: 'bkbg-vf-thumb', src: thumbSrc, alt: a.title || '',
303 loading: 'lazy',
304 style: { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', display: 'block' }
305 }),
306 el('div', { className: 'bkbg-vf-overlay', style: { position: 'absolute', inset: 0, background: hexToRgba(a.overlayColor, a.overlayOpacity) } }),
307 (a.showTitle || a.showSubtitle) && el('div', { className: 'bkbg-vf-text', style: { position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'flex-end', padding: '32px 24px', textAlign: 'center', pointerEvents: 'none' } },
308 a.showTitle && a.title && el('p', { className: 'bkbg-vf-title', style: { margin: 0, color: a.titleColor } }, a.title),
309 a.showSubtitle && a.subtitle && el('p', { className: 'bkbg-vf-subtitle', style: { margin: '6px 0 0', color: a.subtitleColor } }, a.subtitle)
310 ),
311 el('div', { className: 'bkbg-vf-play', style: { position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', pointerEvents: 'none' } },
312 a.playStyle === 'oval'
313 ? el('div', { style: { padding: '14px 24px', display: 'flex', alignItems: 'center', gap: '10px', background: a.playBg, borderRadius: '99px' } },
314 el('svg', { xmlns: 'http://www.w3.org/2000/svg', width: a.playSize * 0.45, height: a.playSize * 0.45, viewBox: '0 0 24 24', fill: a.playColor }, el('path', { d: 'M8 5v14l11-7z' })),
315 el('span', { style: { color: a.playColor, fontWeight: 700, fontSize: '16px' } }, 'Watch')
316 )
317 : el('div', { style: { width: a.playSize + 'px', height: a.playSize + 'px', borderRadius: a.playStyle === 'minimal' ? '50%' : '50%', background: a.playStyle === 'minimal' ? 'transparent' : a.playBg, border: a.playStyle === 'minimal' ? '3px solid ' + a.playColor : 'none', display: 'flex', alignItems: 'center', justifyContent: 'center' } },
318 el('svg', { xmlns: 'http://www.w3.org/2000/svg', width: a.playSize * 0.45, height: a.playSize * 0.45, viewBox: '0 0 24 24', fill: a.playColor }, el('path', { d: 'M8 5v14l11-7z' }))
319 )
320 )
321 ),
322 a.showCaption && a.caption && el('p', { className: 'bkbg-vf-caption', style: { marginTop: '10px', color: a.captionColor, textAlign: 'center' } }, a.caption)
323 );
324 }
325 });
326 }() );
327