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

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

329 lines 20.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 __ = wp.i18n.__;
5 var registerBlockType = wp.blocks.registerBlockType;
6 var InspectorControls = wp.blockEditor.InspectorControls;
7 var useBlockProps = wp.blockEditor.useBlockProps;
8 var InnerBlocks = wp.blockEditor.InnerBlocks;
9 var MediaUpload = wp.blockEditor.MediaUpload;
10 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
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 var Notice = wp.components.Notice;
19
20 /* ── hex alpha ─────────────────────────────────────────────────────────── */
21 function hexAlpha(opacity) {
22 return Math.round(opacity / 100 * 255).toString(16).padStart(2, '0');
23 }
24
25 function buildOverlayBg(a) {
26 if (a.overlayGradient) {
27 var hex1 = (a.overlayGradientStart || '#000000') + hexAlpha(a.overlayOpacity);
28 return 'linear-gradient(' + a.overlayGradientDir + ', ' + hex1 + ', ' + (a.overlayGradientEnd || 'transparent') + ')';
29 }
30 return (a.overlayColor || '#000000') + hexAlpha(a.overlayOpacity);
31 }
32
33 /* ── YouTube ID extractor ──────────────────────────────────────────────── */
34 function extractYoutubeId(raw) {
35 raw = raw.trim();
36 var m = raw.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]{11})/);
37 if (m) { return m[1]; }
38 if (/^[a-zA-Z0-9_-]{11}$/.test(raw)) { return raw; }
39 return raw;
40 }
41
42 function extractVimeoId(raw) {
43 raw = raw.trim();
44 var m = raw.match(/vimeo\.com\/(\d+)/);
45 if (m) { return m[1]; }
46 if (/^\d+$/.test(raw)) { return raw; }
47 return raw;
48 }
49
50 /* ── Editor preview ────────────────────────────────────────────────────── */
51 function EditorMediaPreview(props) {
52 var a = props.a;
53 if (a.videoType === 'hosted' && a.videoUrl) {
54 return el('video', {
55 src: a.videoUrl,
56 poster: a.posterUrl || undefined,
57 muted: true, loop: true, autoPlay: false,
58 style: { position: 'absolute', inset: '0', width: '100%', height: '100%', objectFit: a.objectFit, objectPosition: a.objectPosition, pointerEvents: 'none' }
59 });
60 }
61 if (a.posterUrl) {
62 return el('img', {
63 src: a.posterUrl, alt: a.posterAlt || '',
64 style: { position: 'absolute', inset: '0', width: '100%', height: '100%', objectFit: a.objectFit, objectPosition: a.objectPosition, pointerEvents: 'none' }
65 });
66 }
67 return el('div', {
68 style: { position: 'absolute', inset: '0', background: '#1e1e1e', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#6b7280', fontSize: '14px', pointerEvents: 'none' }
69 }, '' + (a.videoType === 'youtube' ? 'YouTube' : a.videoType === 'vimeo' ? 'Vimeo' : 'Video') + ' background (plays on frontend)');
70 }
71
72 registerBlockType('blockenberg/background-video', {
73 title: __('Background Video', 'blockenberg'),
74 icon: 'format-video',
75 category: 'bkbg-media',
76 description: __('Looping autoplay background video section.', 'blockenberg'),
77
78 edit: function (props) {
79 var attributes = props.attributes;
80 var setAttributes = props.setAttributes;
81 var a = attributes;
82
83 var justifyMap = { left: 'flex-start', center: 'center', right: 'flex-end' };
84 var alignMap = { top: 'flex-start', center: 'center', bottom: 'flex-end' };
85
86 var videoTypeOptions = [
87 { label: __('Self-Hosted (MP4/WebM)', 'blockenberg'), value: 'hosted' },
88 { label: __('YouTube', 'blockenberg'), value: 'youtube' },
89 { label: __('Vimeo', 'blockenberg'), value: 'vimeo' },
90 ];
91 var gradDirOptions = [
92 { label: 'Top → Bottom', value: 'to bottom' },
93 { label: 'Bottom → Top', value: 'to top' },
94 { label: 'Left → Right', value: 'to right' },
95 { label: 'Right → Left', value: 'to left' },
96 { label: 'Diagonal ↘', value: 'to bottom right' },
97 { label: 'Diagonal ↗', value: 'to top right' },
98 ];
99 var contentAlignOptions = [
100 { label: 'Left', value: 'left' },
101 { label: 'Center', value: 'center' },
102 { label: 'Right', value: 'right' },
103 ];
104 var contentVAlignOptions = [
105 { label: 'Top', value: 'top' },
106 { label: 'Middle', value: 'center' },
107 { label: 'Bottom', value: 'bottom' },
108 ];
109 var objectFitOptions = [
110 { label: 'Cover', value: 'cover' },
111 { label: 'Contain', value: 'contain' },
112 { label: 'Fill', value: 'fill' },
113 ];
114 var objectPosOptions = [
115 { label: 'Center', value: 'center center' },
116 { label: 'Top', value: 'top center' },
117 { label: 'Bottom', value: 'bottom center' },
118 { label: 'Left', value: 'center left' },
119 { label: 'Right', value: 'center right' },
120 ];
121
122 var blockProps = useBlockProps({ className: 'bkbv-outer' });
123
124 return el(Fragment, null,
125 el(InspectorControls, null,
126
127 /* — Video Source — */
128 el(PanelBody, { title: __('Video Source', 'blockenberg'), initialOpen: true },
129 el(SelectControl, { label: __('Video Type', 'blockenberg'), value: a.videoType, options: videoTypeOptions, onChange: function (v) { setAttributes({ videoType: v }); } }),
130
131 a.videoType === 'hosted' && el(MediaUploadCheck, null,
132 el(MediaUpload, {
133 onSelect: function (m) { setAttributes({ videoUrl: m.url, videoId: m.id }); },
134 allowedTypes: ['video'],
135 value: a.videoId,
136 render: function (ref) {
137 return el('div', null,
138 a.videoUrl && el('p', { style: { fontSize: '12px', color: '#6b7280', wordBreak: 'break-all', margin: '0 0 6px' } }, a.videoUrl.split('/').pop()),
139 el(Button, { variant: a.videoUrl ? 'secondary' : 'primary', onClick: ref.open, style: { width: '100%', marginBottom: '4px' } },
140 a.videoUrl ? __('Replace Video', 'blockenberg') : __('Choose Video File', 'blockenberg')
141 ),
142 a.videoUrl && el(Button, {
143 variant: 'tertiary', isDestructive: true,
144 onClick: function () { setAttributes({ videoUrl: '', videoId: 0 }); }
145 }, __('Remove Video', 'blockenberg'))
146 );
147 }
148 })
149 ),
150
151 a.videoType === 'youtube' && el('div', null,
152 el(TextControl, {
153 label: __('YouTube ID or URL', 'blockenberg'),
154 value: a.youtubeId,
155 placeholder: 'dQw4w9WgXcQ',
156 help: __('Paste the full URL or just the video ID.', 'blockenberg'),
157 onChange: function (v) { setAttributes({ youtubeId: extractYoutubeId(v) }); }
158 }),
159 el(Notice, { status: 'info', isDismissible: false },
160 __('YouTube will not autoplay unless muted. This is handled automatically.', 'blockenberg')
161 )
162 ),
163
164 a.videoType === 'vimeo' && el(TextControl, {
165 label: __('Vimeo ID or URL', 'blockenberg'),
166 value: a.vimeoId,
167 placeholder: '123456789',
168 help: __('Paste the full URL or just the numeric ID.', 'blockenberg'),
169 onChange: function (v) { setAttributes({ vimeoId: extractVimeoId(v) }); }
170 }),
171
172 el('hr', { style: { margin: '12px 0' } }),
173 el(MediaUploadCheck, null,
174 el(MediaUpload, {
175 onSelect: function (m) { setAttributes({ posterUrl: m.url, posterId: m.id, posterAlt: m.alt || '' }); },
176 allowedTypes: ['image'],
177 value: a.posterId,
178 render: function (ref) {
179 return el('div', null,
180 a.posterUrl && el('img', { src: a.posterUrl, style: { width: '100%', height: '60px', objectFit: 'cover', borderRadius: '4px', marginBottom: '6px' } }),
181 el(Button, { variant: a.posterUrl ? 'secondary' : 'tertiary', onClick: ref.open, style: { width: '100%', marginBottom: '4px' } },
182 a.posterUrl ? __('Replace Poster Image', 'blockenberg') : __('Set Poster / Fallback Image', 'blockenberg')
183 ),
184 a.posterUrl && el(Button, { variant: 'tertiary', isDestructive: true, onClick: function () { setAttributes({ posterUrl: '', posterId: 0 }); } }, __('Remove Poster', 'blockenberg'))
185 );
186 }
187 })
188 ),
189
190 el(SelectControl, { label: __('Object Fit', 'blockenberg'), value: a.objectFit, options: objectFitOptions, onChange: function (v) { setAttributes({ objectFit: v }); } }),
191 el(SelectControl, { label: __('Object Position', 'blockenberg'), value: a.objectPosition, options: objectPosOptions, onChange: function (v) { setAttributes({ objectPosition: v }); } })
192 ),
193
194 /* — Playback — */
195 el(PanelBody, { title: __('Playback', 'blockenberg'), initialOpen: false },
196 el(ToggleControl, { label: __('Loop', 'blockenberg'), checked: a.videoLoop, onChange: function (v) { setAttributes({ videoLoop: v }); }, __nextHasNoMarginBottom: true }),
197 el(ToggleControl, { label: __('Play only when in viewport', 'blockenberg'), checked: a.playOnlyInView, onChange: function (v) { setAttributes({ playOnlyInView: v }); }, __nextHasNoMarginBottom: true }),
198 el(ToggleControl, { label: __('Pause on hover', 'blockenberg'), checked: a.pauseOnHover, onChange: function (v) { setAttributes({ pauseOnHover: v }); }, __nextHasNoMarginBottom: true }),
199 a.videoType === 'hosted' && el(RangeControl, { label: __('Playback Speed', 'blockenberg'), value: a.playbackRate, min: 0.25, max: 2, step: 0.25, onChange: function (v) { setAttributes({ playbackRate: v }); } })
200 ),
201
202 /* — Overlay — */
203 el(PanelBody, { title: __('Overlay', 'blockenberg'), initialOpen: false },
204 el(RangeControl, { label: __('Overlay Opacity (%)', 'blockenberg'), value: a.overlayOpacity, min: 0, max: 100, onChange: function (v) { setAttributes({ overlayOpacity: v }); } }),
205 el(ToggleControl, { label: __('Gradient Overlay', 'blockenberg'), checked: a.overlayGradient, onChange: function (v) { setAttributes({ overlayGradient: v }); }, __nextHasNoMarginBottom: true }),
206 a.overlayGradient && el(SelectControl, { label: __('Direction', 'blockenberg'), value: a.overlayGradientDir, options: gradDirOptions, onChange: function (v) { setAttributes({ overlayGradientDir: v }); } })
207 ),
208
209 /* — Layout — */
210 el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: false },
211 el(RangeControl, { label: __('Min Height (px)', 'blockenberg'), value: a.minHeight, min: 100, max: 1200, onChange: function (v) { setAttributes({ minHeight: v }); } }),
212 el(RangeControl, { label: __('Padding Top', 'blockenberg'), value: a.paddingTop, min: 0, max: 300, onChange: function (v) { setAttributes({ paddingTop: v }); } }),
213 el(RangeControl, { label: __('Padding Bottom', 'blockenberg'), value: a.paddingBottom, min: 0, max: 300, onChange: function (v) { setAttributes({ paddingBottom: v }); } }),
214 el(RangeControl, { label: __('Padding Left', 'blockenberg'), value: a.paddingLeft, min: 0, max: 200, onChange: function (v) { setAttributes({ paddingLeft: v }); } }),
215 el(RangeControl, { label: __('Padding Right', 'blockenberg'), value: a.paddingRight, min: 0, max: 200, onChange: function (v) { setAttributes({ paddingRight: v }); } }),
216 el(RangeControl, { label: __('Content Max Width (px)', 'blockenberg'), value: a.contentMaxWidth, min: 200, max: 1600, onChange: function (v) { setAttributes({ contentMaxWidth: v }); } }),
217 el(SelectControl, { label: __('Horizontal Align', 'blockenberg'), value: a.contentAlign, options: contentAlignOptions, onChange: function (v) { setAttributes({ contentAlign: v }); } }),
218 el(SelectControl, { label: __('Vertical Align', 'blockenberg'), value: a.contentVAlign, options: contentVAlignOptions, onChange: function (v) { setAttributes({ contentVAlign: v }); } }),
219 el(RangeControl, { label: __('Border Radius (px)', 'blockenberg'), value: a.borderRadius, min: 0, max: 80, onChange: function (v) { setAttributes({ borderRadius: v }); } })
220 ),
221
222 /* — Colors — */
223 el(PanelColorSettings, {
224 title: __('Colors', 'blockenberg'),
225 initialOpen: false,
226 colorSettings: [
227 { label: __('Overlay Color', 'blockenberg'), value: a.overlayColor, onChange: function (v) { setAttributes({ overlayColor: v || '#000000' }); } },
228 { label: __('Gradient Start', 'blockenberg'), value: a.overlayGradientStart, onChange: function (v) { setAttributes({ overlayGradientStart: v || '#000000' }); } },
229 { label: __('Gradient End', 'blockenberg'), value: a.overlayGradientEnd, onChange: function (v) { setAttributes({ overlayGradientEnd: v || 'transparent' }); } },
230 { label: __('Content Text Color', 'blockenberg'), value: a.textColor, onChange: function (v) { setAttributes({ textColor: v || '#ffffff' }); } },
231 ]
232 })
233 ),
234
235 /* ── Canvas ─────────────────────────────────────────────────── */
236 el('div', {
237 ...blockProps,
238 style: { position: 'relative', overflow: 'hidden', minHeight: a.minHeight + 'px', borderRadius: a.borderRadius + 'px' }
239 },
240 /* video/poster preview */
241 el(EditorMediaPreview, { a: a }),
242
243 /* overlay */
244 el('div', { className: 'bkbv-overlay', style: { position: 'absolute', inset: '0', background: buildOverlayBg(a), zIndex: 1, pointerEvents: 'none' } }),
245
246 /* content */
247 el('div', {
248 className: 'bkbv-content',
249 style: {
250 position: 'relative', zIndex: 2, display: 'flex', flexDirection: 'column',
251 alignItems: justifyMap[a.contentAlign] || 'center',
252 justifyContent: alignMap[a.contentVAlign] || 'center',
253 minHeight: a.minHeight + 'px',
254 paddingTop: a.paddingTop + 'px', paddingBottom: a.paddingBottom + 'px',
255 paddingLeft: a.paddingLeft + 'px', paddingRight: a.paddingRight + 'px',
256 color: a.textColor,
257 }
258 },
259 el('div', { style: { width: '100%', maxWidth: a.contentMaxWidth + 'px' } },
260 el(InnerBlocks, { templateLock: false })
261 )
262 )
263 )
264 );
265 },
266
267 save: function (props) {
268 var a = props.attributes;
269 var blockProps = wp.blockEditor.useBlockProps.save({ className: 'bkbv-outer' });
270 var justifyMap = { left: 'flex-start', center: 'center', right: 'flex-end' };
271 var alignMap = { top: 'flex-start', center: 'center', bottom: 'flex-end' };
272
273 return el('div', {
274 ...blockProps,
275 style: { position: 'relative', overflow: 'hidden', minHeight: a.minHeight + 'px', borderRadius: a.borderRadius + 'px' },
276 'data-video-type': a.videoType,
277 'data-video-url': a.videoUrl,
278 'data-youtube-id': a.youtubeId,
279 'data-vimeo-id': a.vimeoId,
280 'data-loop': a.videoLoop ? '1' : '0',
281 'data-in-view': a.playOnlyInView ? '1' : '0',
282 'data-pause-hover': a.pauseOnHover ? '1' : '0',
283 'data-rate': a.playbackRate,
284 'data-object-fit': a.objectFit,
285 'data-object-pos': a.objectPosition,
286 },
287 /* media container — frontend.js creates the <video> or iframe here */
288 el('div', {
289 className: 'bkbv-media',
290 style: { position: 'absolute', inset: '0', zIndex: 0, overflow: 'hidden' },
291 'aria-hidden': 'true'
292 },
293 /* poster shown until video loads */
294 a.posterUrl && el('img', {
295 className: 'bkbv-poster',
296 src: a.posterUrl, alt: '',
297 style: { position: 'absolute', inset: '0', width: '100%', height: '100%', objectFit: a.objectFit, objectPosition: a.objectPosition }
298 })
299 ),
300
301 /* overlay */
302 el('div', {
303 className: 'bkbv-overlay',
304 style: { position: 'absolute', inset: '0', background: buildOverlayBg(a), zIndex: 1, pointerEvents: 'none' },
305 'aria-hidden': 'true'
306 }),
307
308 /* content */
309 el('div', {
310 className: 'bkbv-content',
311 style: {
312 position: 'relative', zIndex: 2, display: 'flex', flexDirection: 'column',
313 alignItems: justifyMap[a.contentAlign] || 'center',
314 justifyContent: alignMap[a.contentVAlign] || 'center',
315 minHeight: a.minHeight + 'px',
316 paddingTop: a.paddingTop + 'px', paddingBottom: a.paddingBottom + 'px',
317 paddingLeft: a.paddingLeft + 'px', paddingRight: a.paddingRight + 'px',
318 color: a.textColor,
319 }
320 },
321 el('div', { style: { width: '100%', maxWidth: a.contentMaxWidth + 'px' } },
322 el(InnerBlocks.Content, null)
323 )
324 )
325 );
326 }
327 });
328 }() );
329