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

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

476 lines 20.7 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 __ = wp.i18n.__;
5 var useState = wp.element.useState;
6
7 var registerBlockType = wp.blocks.registerBlockType;
8 var InspectorControls = wp.blockEditor.InspectorControls;
9 var BlockControls = wp.blockEditor.BlockControls;
10 var useBlockProps = wp.blockEditor.useBlockProps;
11
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 var ToolbarGroup = wp.components.ToolbarGroup;
19 var ToolbarButton = wp.components.ToolbarButton;
20
21 function normalizeImage(att) {
22 return {
23 id: att && att.id ? att.id : 0,
24 url: att && att.url ? att.url : '',
25 alt: att && att.alt ? att.alt : ''
26 };
27 }
28
29 function openMedia(onSelect) {
30 if (!wp || !wp.media) return;
31 var frame = wp.media({
32 title: __('Select Poster Image', 'blockenberg'),
33 button: { text: __('Use image', 'blockenberg') },
34 multiple: false,
35 library: { type: 'image' }
36 });
37 frame.on('select', function () {
38 var attachment = frame.state().get('selection').first().toJSON();
39 onSelect(attachment);
40 });
41 frame.open();
42 }
43
44 function extractVimeoId(input) {
45 if (!input) return '';
46 var s = String(input).trim();
47
48 // Allow numeric ID directly
49 if (/^\d+$/.test(s)) return s;
50
51 // vimeo.com/<id> or vimeo.com/channels/.../<id> or vimeo.com/groups/.../videos/<id>
52 var m = s.match(/vimeo\.com\/(?:.*\/)?(\d+)(?:$|\?|#|\/)/);
53 if (m && m[1]) return m[1];
54
55 // player.vimeo.com/video/<id>
56 m = s.match(/player\.vimeo\.com\/video\/(\d+)/);
57 if (m && m[1]) return m[1];
58
59 return '';
60 }
61
62 function fetchOEmbedPoster(videoUrl) {
63 if (!videoUrl) return Promise.resolve(null);
64 var endpoint = 'https://vimeo.com/api/oembed.json?url=' + encodeURIComponent(videoUrl);
65 return fetch(endpoint)
66 .then(function (r) { return r.ok ? r.json() : null; })
67 .then(function (data) {
68 if (!data) return null;
69 var embedSrc = '';
70 if (data.html) {
71 try {
72 var tmp = document.createElement('div');
73 tmp.innerHTML = data.html;
74 var iframe = tmp.querySelector('iframe');
75 if (iframe) embedSrc = iframe.getAttribute('src') || '';
76 } catch (e) {
77 embedSrc = '';
78 }
79 }
80 return {
81 url: data.thumbnail_url || '',
82 alt: data.title || '',
83 embedSrc: embedSrc
84 };
85 })
86 .catch(function () { return null; });
87 }
88
89 function withVimeoParams(baseUrl, updates) {
90 if (!baseUrl) return '';
91 try {
92 var u = new URL(baseUrl, window.location.href);
93 Object.keys(updates).forEach(function (k) {
94 u.searchParams.set(k, String(updates[k]));
95 });
96 return u.toString();
97 } catch (e) {
98 return baseUrl;
99 }
100 }
101
102 registerBlockType('blockenberg/vimeo-performance', {
103 title: __('Vimeo Performance Embed', 'blockenberg'),
104 icon: 'video-alt3',
105 category: 'bkbg-dev',
106 description: __('Vimeo embed with poster + lazy iframe created only on user click.', 'blockenberg'),
107
108 edit: function (props) {
109 var a = props.attributes;
110 var setAttributes = props.setAttributes;
111 var isSelected = props.isSelected;
112
113 var previewState = useState(false);
114 var previewOn = previewState[0];
115 var setPreviewOn = previewState[1];
116
117 var playingState = useState(false);
118 var isPlaying = playingState[0];
119 var setIsPlaying = playingState[1];
120
121 var fetchingState = useState(false);
122 var isFetching = fetchingState[0];
123 var setIsFetching = fetchingState[1];
124
125 function setVideoUrl(v) {
126 var id = extractVimeoId(v);
127 setAttributes({ videoUrl: v, videoId: id });
128 setIsPlaying(false);
129
130 if (a.posterSource === 'vimeo' && v) {
131 setIsFetching(true);
132 fetchOEmbedPoster(v).then(function (info) {
133 setIsFetching(false);
134 if (!info || !info.url) return;
135 setAttributes({
136 posterImage: { id: 0, url: info.url, alt: info.alt || '' },
137 embedSrc: info.embedSrc || ''
138 });
139 });
140 }
141 }
142
143 function setPoster(att) {
144 setAttributes({ posterImage: normalizeImage(att) });
145 }
146
147 var posterUrl = (a.posterSource === 'custom')
148 ? ((a.posterImage && a.posterImage.url) ? a.posterImage.url : '')
149 : ((a.posterImage && a.posterImage.url) ? a.posterImage.url : '');
150
151 var wrapperClass = 'bkbg-vmp-wrap bkbg-editor-wrap';
152 if (isSelected) wrapperClass += ' bkbg-block-selected';
153 if (a.shadow) wrapperClass += ' has-shadow';
154 wrapperClass += ' is-play-' + (a.playStyle || 'youtube');
155
156 var styleVars = {
157 '--bkbg-vmp-radius': (a.borderRadius || 0) + 'px',
158 '--bkbg-vmp-min-height': (a.minHeight || 350) + 'px',
159 '--bkbg-vmp-play-size': (a.playSize || 50) + 'px',
160 '--bkbg-vmp-play-bg': a.playBg,
161 '--bkbg-vmp-play-color': a.playColor,
162 '--bkbg-vmp-play-hover': (a.playHoverScale || 1.05)
163 };
164
165 var blockProps = useBlockProps({
166 className: wrapperClass,
167 style: styleVars,
168 'data-block-label': 'Vimeo'
169 });
170
171 var toolbar = el(BlockControls, {},
172 el(ToolbarGroup, {},
173 el(ToolbarButton, {
174 icon: 'visibility',
175 label: __('Toggle editor preview', 'blockenberg'),
176 isPressed: previewOn,
177 onClick: function () {
178 setPreviewOn(!previewOn);
179 setIsPlaying(false);
180 }
181 }),
182 el(ToolbarButton, {
183 icon: 'controls-repeat',
184 label: __('Reset', 'blockenberg'),
185 onClick: function () { setIsPlaying(false); }
186 })
187 )
188 );
189
190 var inspector = el(InspectorControls, {},
191 el(PanelBody, { title: __('Video', 'blockenberg'), initialOpen: true },
192 el(TextControl, {
193 label: __('Vimeo URL or Video ID', 'blockenberg'),
194 value: a.videoUrl,
195 onChange: setVideoUrl,
196 help: __('Paste a Vimeo link or a numeric ID.', 'blockenberg')
197 }),
198 el(ToggleControl, {
199 label: __('Do Not Track (dnt=1)', 'blockenberg'),
200 checked: !!a.dnt,
201 __nextHasNoMarginBottom: true,
202 onChange: function (v) { setAttributes({ dnt: v }); }
203 }),
204 el(ToggleControl, {
205 label: __('Autoplay on click', 'blockenberg'),
206 checked: !!a.autoplay,
207 __nextHasNoMarginBottom: true,
208 onChange: function (v) { setAttributes({ autoplay: v }); }
209 }),
210 el(ToggleControl, {
211 label: __('Muted', 'blockenberg'),
212 checked: !!a.muted,
213 __nextHasNoMarginBottom: true,
214 onChange: function (v) { setAttributes({ muted: v }); }
215 }),
216 el(ToggleControl, {
217 label: __('Loop', 'blockenberg'),
218 checked: !!a.loop,
219 __nextHasNoMarginBottom: true,
220 onChange: function (v) { setAttributes({ loop: v }); }
221 }),
222 el(ToggleControl, {
223 label: __('Show title/byline/portrait', 'blockenberg'),
224 checked: !!a.showTitleBylinePortrait,
225 __nextHasNoMarginBottom: true,
226 onChange: function (v) { setAttributes({ showTitleBylinePortrait: v }); }
227 })
228 ),
229
230 el(PanelBody, { title: __('Poster', 'blockenberg'), initialOpen: false },
231 el(SelectControl, {
232 label: __('Poster source', 'blockenberg'),
233 value: a.posterSource,
234 options: [
235 { label: __('Vimeo (oEmbed)', 'blockenberg'), value: 'vimeo' },
236 { label: __('Custom image', 'blockenberg'), value: 'custom' }
237 ],
238 onChange: function (v) {
239 setAttributes({ posterSource: v });
240 if (v === 'vimeo' && a.videoUrl) {
241 setIsFetching(true);
242 fetchOEmbedPoster(a.videoUrl).then(function (info) {
243 setIsFetching(false);
244 if (!info || !info.url) return;
245 setAttributes({
246 posterImage: { id: 0, url: info.url, alt: info.alt || '' },
247 embedSrc: info.embedSrc || ''
248 });
249 });
250 }
251 }
252 }),
253 a.posterSource === 'custom' && el('div', { className: 'bkbg-vmp-media' },
254 (a.posterImage && a.posterImage.url)
255 ? el('img', { className: 'bkbg-vmp-media-thumb', src: a.posterImage.url, alt: a.posterImage.alt || '' })
256 : el('div', { className: 'bkbg-vmp-media-placeholder' }, __('No poster selected', 'blockenberg')),
257 el(Button, {
258 variant: 'secondary',
259 onClick: function () { openMedia(setPoster); }
260 }, (a.posterImage && a.posterImage.url) ? __('Replace poster', 'blockenberg') : __('Select poster', 'blockenberg')),
261 (a.posterImage && a.posterImage.url) && el(Button, {
262 variant: 'tertiary',
263 isDestructive: true,
264 onClick: function () { setAttributes({ posterImage: { id: 0, url: '', alt: '' } }); }
265 }, __('Remove', 'blockenberg'))
266 ),
267 (a.posterSource === 'vimeo') && el('p', { style: { marginTop: '8px', opacity: 0.8 } },
268 isFetching ? __('Fetching Vimeo thumbnail…', 'blockenberg') : __('Uses Vimeo oEmbed thumbnail. Paste a valid Vimeo URL to fetch.', 'blockenberg')
269 )
270 ),
271
272 el(PanelBody, { title: __('Design', 'blockenberg'), initialOpen: false },
273 el(SelectControl, {
274 label: __('Aspect ratio', 'blockenberg'),
275 value: a.aspect,
276 options: [
277 { label: '4:3', value: '4/3' },
278 { label: '16:9', value: '16/9' },
279 { label: '1:1', value: '1/1' },
280 { label: __('Auto', 'blockenberg'), value: 'auto' }
281 ],
282 onChange: function (v) { setAttributes({ aspect: v }); }
283 }),
284 el(RangeControl, {
285 label: __('Minimum height (px)', 'blockenberg'),
286 value: a.minHeight,
287 onChange: function (v) { setAttributes({ minHeight: v }); },
288 min: 120,
289 max: 900
290 }),
291 el(RangeControl, {
292 label: __('Border radius', 'blockenberg'),
293 value: a.borderRadius,
294 onChange: function (v) { setAttributes({ borderRadius: v }); },
295 min: 0,
296 max: 40
297 }),
298 el(ToggleControl, {
299 label: __('Shadow', 'blockenberg'),
300 checked: !!a.shadow,
301 __nextHasNoMarginBottom: true,
302 onChange: function (v) { setAttributes({ shadow: v }); }
303 })
304 ),
305
306 el(PanelBody, { title: __('Play Button', 'blockenberg'), initialOpen: false },
307 el(SelectControl, {
308 label: __('Style', 'blockenberg'),
309 value: a.playStyle,
310 options: [
311 { label: __('YouTube', 'blockenberg'), value: 'youtube' },
312 { label: __('Circle', 'blockenberg'), value: 'circle' },
313 { label: __('Rounded', 'blockenberg'), value: 'rounded' },
314 { label: __('Square', 'blockenberg'), value: 'square' }
315 ],
316 onChange: function (v) { setAttributes({ playStyle: v }); }
317 }),
318 el(RangeControl, {
319 label: __('Size', 'blockenberg'),
320 value: a.playSize,
321 onChange: function (v) { setAttributes({ playSize: v }); },
322 min: 36,
323 max: 120
324 }),
325 el(RangeControl, {
326 label: __('Hover scale', 'blockenberg'),
327 value: a.playHoverScale || 1.05,
328 onChange: function (v) { setAttributes({ playHoverScale: v }); },
329 min: 1,
330 max: 1.2,
331 step: 0.01
332 })
333 )
334 );
335
336 function renderPoster() {
337 var hasVideo = !!a.videoId;
338
339 return el('div', { className: 'bkbg-vmp-inner' },
340 el('div', {
341 className: 'bkbg-vmp-media-layer',
342 style: { minHeight: (a.minHeight || 350) + 'px' }
343 },
344 posterUrl
345 ? el('img', {
346 className: 'bkbg-vmp-poster-img',
347 src: posterUrl,
348 alt: (a.posterImage && a.posterImage.alt) ? a.posterImage.alt : ''
349 })
350 : el('div', { className: 'bkbg-vmp-empty' },
351 el('p', {}, hasVideo
352 ? __('Poster will appear here (Vimeo thumbnail or custom).', 'blockenberg')
353 : __('Paste a Vimeo URL in the sidebar to get started.', 'blockenberg'))
354 )
355 ),
356 el('button', {
357 type: 'button',
358 className: 'bkbg-vmp-play',
359 disabled: !hasVideo,
360 onMouseDown: function (e) {
361 if (!isSelected) return;
362 e.stopPropagation();
363 },
364 onClick: function (e) {
365 if (!isSelected) return;
366 e.preventDefault();
367 e.stopPropagation();
368 if (!previewOn) return;
369 setIsPlaying(true);
370 },
371 'aria-label': __('Play Vimeo video', 'blockenberg')
372 },
373 el('span', { className: 'bkbg-vmp-play-triangle' })
374 )
375 );
376 }
377
378 function renderPreviewIframe() {
379 if (!a.videoId) return null;
380 if (!previewOn) return null;
381 if (!isPlaying) return null;
382
383 var chrome = a.showTitleBylinePortrait ? '1' : '0';
384 var base = a.embedSrc || ('https://player.vimeo.com/video/' + encodeURIComponent(a.videoId));
385 var src = withVimeoParams(base, {
386 autoplay: a.autoplay ? 1 : 0,
387 muted: a.muted ? 1 : 0,
388 loop: a.loop ? 1 : 0,
389 dnt: a.dnt ? 1 : 0,
390 title: chrome,
391 byline: chrome,
392 portrait: chrome
393 });
394
395 return el('div', { className: 'bkbg-vmp-inner' },
396 el('iframe', {
397 className: 'bkbg-vmp-iframe',
398 src: src,
399 title: __('Vimeo video', 'blockenberg'),
400 allow: 'autoplay; fullscreen; picture-in-picture',
401 allowFullScreen: true
402 })
403 );
404 }
405
406 var aspectStyle = (a.aspect && a.aspect !== 'auto') ? { aspectRatio: a.aspect } : {};
407
408 var content = el('div', blockProps,
409 el('div', Object.assign({ className: 'bkbg-vmp-aspect' + (a.aspect !== 'auto' ? ' has-aspect' : '') }, aspectStyle),
410 renderPreviewIframe() || renderPoster()
411 )
412 );
413
414 return el(Fragment, {}, toolbar, inspector, content);
415 },
416
417 save: function (props) {
418 var a = props.attributes;
419
420 var posterUrl = (a.posterImage && a.posterImage.url) ? a.posterImage.url : '';
421
422 var cls = 'bkbg-vmp-wrap';
423 if (a.shadow) cls += ' has-shadow';
424 cls += ' is-play-' + (a.playStyle || 'youtube');
425
426 var styleVars = {
427 '--bkbg-vmp-radius': (a.borderRadius || 0) + 'px',
428 '--bkbg-vmp-min-height': (a.minHeight || 350) + 'px',
429 '--bkbg-vmp-play-size': (a.playSize || 50) + 'px',
430 '--bkbg-vmp-play-bg': a.playBg,
431 '--bkbg-vmp-play-color': a.playColor,
432 '--bkbg-vmp-play-hover': (a.playHoverScale || 1.05)
433 };
434
435 var blockProps = useBlockProps.save({
436 className: cls,
437 style: styleVars,
438 'data-video-id': a.videoId || '',
439 'data-video-url': a.videoUrl || '',
440 'data-embed-src': a.embedSrc || '',
441 'data-dnt': a.dnt ? '1' : '0',
442 'data-autoplay': a.autoplay ? '1' : '0',
443 'data-muted': a.muted ? '1' : '0',
444 'data-loop': a.loop ? '1' : '0',
445 'data-chrome': a.showTitleBylinePortrait ? '1' : '0'
446 });
447
448 var aspectStyle = (a.aspect && a.aspect !== 'auto') ? { aspectRatio: a.aspect } : {};
449
450 return el('div', blockProps,
451 el('div', Object.assign({ className: 'bkbg-vmp-aspect' + (a.aspect !== 'auto' ? ' has-aspect' : '') }, aspectStyle),
452 el('div', { className: 'bkbg-vmp-inner' },
453 el('div', { className: 'bkbg-vmp-media-layer' },
454 posterUrl
455 ? el('img', {
456 className: 'bkbg-vmp-poster-img',
457 src: posterUrl,
458 alt: (a.posterImage && a.posterImage.alt) ? a.posterImage.alt : ''
459 })
460 : null
461 ),
462 el('button', {
463 type: 'button',
464 className: 'bkbg-vmp-play',
465 'aria-label': __('Play Vimeo video', 'blockenberg')
466 },
467 el('span', { className: 'bkbg-vmp-play-triangle' })
468 ),
469 el('div', { className: 'bkbg-vmp-embed' })
470 )
471 )
472 );
473 }
474 });
475 }() );
476