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

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

336 lines 22.6 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 MediaUpload = wp.blockEditor.MediaUpload;
10 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
11 var useBlockProps = wp.blockEditor.useBlockProps;
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, 'badgeTypo', attrs, '--bkvs-bg-');
39 _tvf(v, 'headlineTypo', attrs, '--bkvs-hd-');
40 _tvf(v, 'subtextTypo', attrs, '--bkvs-st-');
41 _tvf(v, 'statValueTypo', attrs, '--bkvs-sv-');
42 _tvf(v, 'statLabelTypo', attrs, '--bkvs-sl-');
43 return v;
44 }
45
46 var VIDEO_TYPE_OPTIONS = [
47 { label: 'YouTube', value: 'youtube' },
48 { label: 'Vimeo', value: 'vimeo' },
49 { label: __('Self-hosted', 'blockenberg'), value: 'self' },
50 ];
51 var ASPECT_OPTIONS = [
52 { label: '16 / 9', value: '16/9' },
53 { label: '4 / 3', value: '4/3' },
54 { label: '21 / 9', value: '21/9' },
55 { label: '1 / 1', value: '1/1' },
56 ];
57 var LAYOUT_OPTIONS = [
58 { label: __('Full hero', 'blockenberg'), value: 'hero' },
59 { label: __('Centered card', 'blockenberg'), value: 'card' },
60 { label: __('Content above', 'blockenberg'), value: 'above' },
61 ];
62 var ALIGN_OPTIONS = [
63 { label: __('Left', 'blockenberg'), value: 'left' },
64 { label: __('Center', 'blockenberg'), value: 'center' },
65 { label: __('Right', 'blockenberg'), value: 'right' },
66 ];
67 var PLAY_STYLE_OPTIONS = [
68 { label: __('Circle', 'blockenberg'), value: 'circle' },
69 { label: __('Square', 'blockenberg'), value: 'square' },
70 { label: __('Pill', 'blockenberg'), value: 'pill' },
71 ];
72
73 function makeId() { return 'st' + Math.random().toString(36).substr(2, 5); }
74
75 function ratioPercent(r) {
76 var p = r.split('/');
77 return (parseInt(p[1], 10) / parseInt(p[0], 10) * 100).toFixed(2) + '%';
78 }
79
80 function buildWrapStyle(a) {
81 var s = getTypoCssVars(a);
82 s['--bkbg-vs-accent'] = a.accentColor;
83 s['--bkbg-vs-headline'] = a.headlineColor;
84 s['--bkbg-vs-subtext'] = a.subtextColor;
85 s['--bkbg-vs-badge-bg'] = a.badgeBg;
86 s['--bkbg-vs-badge-color'] = a.badgeColor;
87 s['--bkbg-vs-play-bg'] = a.playBg;
88 s['--bkbg-vs-play-color'] = a.playColor;
89 s['--bkbg-vs-stat-value'] = a.statValueColor;
90 s['--bkbg-vs-stat-label'] = a.statLabelColor;
91 s['--bkbg-vs-stat-divider'] = a.statDividerColor;
92 s['--bkbg-vs-br'] = a.borderRadius + 'px';
93 s['--bkbg-vs-max-w'] = a.maxWidth + 'px';
94 s['--bkbg-vs-play-sz'] = a.playButtonSize + 'px';
95 s['--bkbg-vs-text-align'] = a.textAlign;
96 s.paddingTop = a.paddingTop + 'px';
97 s.paddingBottom = a.paddingBottom + 'px';
98 s.backgroundColor = a.bgColor || undefined;
99 return s;
100 }
101
102 function renderColorControl(key, label, value, onChange, openKey, setOpenKey) {
103 var isOpen = openKey === key;
104 return el('div', { key: key, style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '4px 0', gap: '8px' } },
105 el('span', { style: { fontSize: '12px', color: '#1e1e1e', flex: 1, lineHeight: 1.4 } }, label),
106 el('div', { style: { position: 'relative', flexShrink: 0 } },
107 el('button', { type: 'button', title: value || 'default', onClick: function () { setOpenKey(isOpen ? null : key); },
108 style: { width: '28px', height: '28px', borderRadius: '4px', border: isOpen ? '2px solid #007cba' : '2px solid #ddd', cursor: 'pointer', padding: 0, background: value || '#cccccc' }
109 }),
110 isOpen && el(Popover, { position: 'bottom left', onClose: function () { setOpenKey(null); } },
111 el('div', { style: { padding: '8px' } },
112 el(ColorPicker, { color: value, enableAlpha: true, onChange: onChange })
113 )
114 )
115 )
116 );
117 }
118
119 /* ── Video embed preview (editor only) ──────────────────────────────── */
120 function VideoEmbed(props) {
121 var a = props.a;
122 var src = '';
123 if (a.videoType === 'youtube' && a.youtubeId) {
124 src = 'https://www.youtube-nocookie.com/embed/' + a.youtubeId + '?controls=1';
125 } else if (a.videoType === 'vimeo' && a.vimeoId) {
126 src = 'https://player.vimeo.com/video/' + a.vimeoId;
127 }
128
129 var boxStyle = { position: 'relative', borderRadius: a.borderRadius + 'px', overflow: 'hidden', boxShadow: a.showShadow ? '0 24px 64px rgba(0,0,0,0.18)' : 'none', background: '#111', maxWidth: a.maxWidth + 'px', margin: '0 auto' };
130
131 if (src) {
132 return el('div', { style: boxStyle },
133 el('div', { style: { paddingBottom: ratioPercent(a.aspectRatio) } }),
134 el('iframe', { src: src, frameBorder: '0', allowFullScreen: true, style: { position: 'absolute', inset: 0, width: '100%', height: '100%' } })
135 );
136 }
137
138 /* placeholder with thumbnail */
139 return el('div', { style: Object.assign({}, boxStyle, { cursor: 'default' }) },
140 el('div', { style: { paddingBottom: ratioPercent(a.aspectRatio) } }),
141 el('div', { style: { position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: '16px' } },
142 a.posterUrl
143 ? el('img', { src: a.posterUrl, alt: '', style: { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' } })
144 : null,
145 el('div', { style: { position: 'relative', zIndex: 2, width: a.playButtonSize + 'px', height: a.playButtonSize + 'px', borderRadius: a.playButtonStyle === 'circle' ? '50%' : a.playButtonStyle === 'pill' ? '99px' : '12px', background: a.playBg, display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: '0 4px 24px rgba(0,0,0,0.2)' } },
146 el('span', { style: { fontSize: (a.playButtonSize * 0.35) + 'px', color: a.playColor, marginLeft: '4px' } }, '▶')
147 ),
148 !a.posterUrl && el('p', { style: { position: 'relative', zIndex: 2, color: '#9ca3af', fontSize: '13px', margin: 0 } },
149 a.videoType === 'youtube' ? __('Enter YouTube Video ID in sidebar →', 'blockenberg') :
150 a.videoType === 'vimeo' ? __('Enter Vimeo Video ID in sidebar →', 'blockenberg') :
151 __('Add a thumbnail image in sidebar →', 'blockenberg')
152 )
153 )
154 );
155 }
156
157 registerBlockType('blockenberg/video-section', {
158 title: __('Video Section', 'blockenberg'),
159 icon: 'format-video',
160 category: 'bkbg-media',
161
162 edit: function (props) {
163 var a = props.attributes;
164 var setAttributes = props.setAttributes;
165
166 var openColorKeyState = useState(null);
167 var openColorKey = openColorKeyState[0];
168 var setOpenColorKey = openColorKeyState[1];
169
170 var blockProps = useBlockProps({ style: buildWrapStyle(a) });
171
172 function cc(key, label, attrKey) {
173 return renderColorControl(key, label, a[attrKey], function (val) {
174 var upd = {}; upd[attrKey] = val; setAttributes(upd);
175 }, openColorKey, setOpenColorKey);
176 }
177
178 function updateStat(id, key, val) {
179 setAttributes({ stats: a.stats.map(function (s) { if (s.id !== id) return s; var u = Object.assign({}, s); u[key] = val; return u; }) });
180 }
181
182 var contentBox = a.showContent && el('div', { className: 'bkbg-vs-content', style: { textAlign: a.textAlign, marginBottom: a.contentPosition === 'above' ? '40px' : '40px', marginTop: a.contentPosition === 'below' ? '40px' : 0 } },
183 a.showBadge && el('span', { className: 'bkbg-vs-badge', style: { display: 'inline-block', background: a.badgeBg, color: a.badgeColor, padding: '5px 16px', borderRadius: '99px', marginBottom: '16px' } }, a.badge),
184 el(RichText, { tagName: a.headlineTag, className: 'bkbg-vs-headline', value: a.headline, onChange: function (v) { setAttributes({ headline: v }); }, placeholder: __('Video section headline…', 'blockenberg'), style: { color: a.headlineColor, margin: '0 0 14px' } }),
185 el(RichText, { tagName: 'p', className: 'bkbg-vs-subtext', value: a.subtext, onChange: function (v) { setAttributes({ subtext: v }); }, placeholder: __('Supporting description…', 'blockenberg'), style: { color: a.subtextColor, margin: 0 } })
186 );
187
188 var statsBox = a.showStats && el('div', { className: 'bkbg-vs-stats', style: { display: 'flex', alignItems: 'center', justifyContent: a.textAlign === 'center' ? 'center' : 'flex-start', gap: '0', marginTop: '40px', flexWrap: 'wrap' } },
189 a.stats.map(function (s, i) {
190 return el(Fragment, { key: s.id },
191 i > 0 && el('div', { style: { width: '1px', height: '40px', background: a.statDividerColor, margin: '0 32px', flexShrink: 0 } }),
192 el('div', { style: { textAlign: 'center' } },
193 el('div', { className: 'bkbg-vs-stat-value', style: { color: a.statValueColor } }, s.value),
194 el('div', { className: 'bkbg-vs-stat-label', style: { color: a.statLabelColor, marginTop: '4px' } }, s.label)
195 )
196 );
197 })
198 );
199
200 return el(Fragment, null,
201 el(InspectorControls, null,
202 el(PanelBody, { title: __('Video Source', 'blockenberg'), initialOpen: true },
203 el(SelectControl, { label: __('Video type', 'blockenberg'), value: a.videoType, options: VIDEO_TYPE_OPTIONS, onChange: function (v) { setAttributes({ videoType: v }); } }),
204 a.videoType === 'youtube' && el(TextControl, { label: __('YouTube Video ID', 'blockenberg'), help: __('e.g. dQw4w9WgXcQ from the URL', 'blockenberg'), value: a.youtubeId, onChange: function (v) { setAttributes({ youtubeId: v.trim() }); } }),
205 a.videoType === 'vimeo' && el(TextControl, { label: __('Vimeo Video ID', 'blockenberg'), help: __('e.g. 148751763 from the URL', 'blockenberg'), value: a.vimeoId, onChange: function (v) { setAttributes({ vimeoId: v.trim() }); } }),
206 a.videoType === 'self' && el(TextControl, { label: __('Video URL (.mp4)', 'blockenberg'), value: a.videoUrl, onChange: function (v) { setAttributes({ videoUrl: v }); } }),
207 el('p', { style: { fontSize: '12px', color: '#6b7280', margin: '4px 0 8px' } }, __('Thumbnail / poster image:', 'blockenberg')),
208 el(MediaUploadCheck, null,
209 el(MediaUpload, {
210 onSelect: function (m) { setAttributes({ posterUrl: m.url, posterId: m.id }); },
211 allowedTypes: ['image'], value: a.posterId,
212 render: function (p) { return el('div', null,
213 a.posterUrl && el('img', { src: a.posterUrl, style: { width: '100%', borderRadius: '6px', marginBottom: '6px' } }),
214 el(Button, { onClick: p.open, variant: a.posterUrl ? 'secondary' : 'primary', size: 'compact' }, a.posterUrl ? __('Replace thumbnail', 'blockenberg') : __('Add thumbnail', 'blockenberg'))
215 ); }
216 })
217 )
218 ),
219 el(PanelBody, { title: __('Layout & Display', 'blockenberg'), initialOpen: false },
220 el(SelectControl, { label: __('Aspect ratio', 'blockenberg'), value: a.aspectRatio, options: ASPECT_OPTIONS, onChange: function (v) { setAttributes({ aspectRatio: v }); } }),
221 el(SelectControl, { label: __('Play button style','blockenberg'), value: a.playButtonStyle, options: PLAY_STYLE_OPTIONS, onChange: function (v) { setAttributes({ playButtonStyle: v }); } }),
222 el(RangeControl, { label: __('Play button size (px)', 'blockenberg'), value: a.playButtonSize, min: 48, max: 120, onChange: function (v) { setAttributes({ playButtonSize: v }); } }),
223 el(RangeControl, { label: __('Video max width (px)', 'blockenberg'), value: a.maxWidth, min: 400, max: 1400, onChange: function (v) { setAttributes({ maxWidth: v }); } }),
224 el(RangeControl, { label: __('Border radius (px)', 'blockenberg'), value: a.borderRadius, min: 0, max: 32, onChange: function (v) { setAttributes({ borderRadius: v }); } }),
225 el(ToggleControl, { label: __('Video shadow', 'blockenberg'), checked: a.showShadow, onChange: function (v) { setAttributes({ showShadow: v }); }, __nextHasNoMarginBottom: true }),
226 el(SelectControl, { label: __('Text alignment', 'blockenberg'), value: a.textAlign, options: ALIGN_OPTIONS, onChange: function (v) { setAttributes({ textAlign: v }); } })
227 ),
228 el(PanelBody, { title: __('Content', 'blockenberg'), initialOpen: false },
229 el(ToggleControl, { label: __('Show content block', 'blockenberg'), checked: a.showContent, onChange: function (v) { setAttributes({ showContent: v }); }, __nextHasNoMarginBottom: true }),
230 a.showContent && el(ToggleControl, { label: __('Show badge', 'blockenberg'), checked: a.showBadge, onChange: function (v) { setAttributes({ showBadge: v }); }, __nextHasNoMarginBottom: true }),
231 a.showContent && a.showBadge && el(TextControl, { label: __('Badge text', 'blockenberg'), value: a.badge, onChange: function (v) { setAttributes({ badge: v }); } }),
232 a.showContent && el(SelectControl, { label: __('Headline tag', 'blockenberg'), value: a.headlineTag, options: ['h1','h2','h3','h4'].map(function (t) { return { label: t.toUpperCase(), value: t }; }), onChange: function (v) { setAttributes({ headlineTag: v }); } })
233 ),
234 el(PanelBody, { title: __('Stats', 'blockenberg'), initialOpen: false },
235 el(ToggleControl, { label: __('Show stats strip', 'blockenberg'), checked: a.showStats, onChange: function (v) { setAttributes({ showStats: v }); }, __nextHasNoMarginBottom: true }),
236 a.showStats && a.stats.map(function (s, i) {
237 return el(PanelBody, { key: s.id, title: __('Stat', 'blockenberg') + ' ' + (i + 1), initialOpen: false },
238 el(TextControl, { label: __('Value', 'blockenberg'), value: s.value, onChange: function (v) { updateStat(s.id, 'value', v); } }),
239 el(TextControl, { label: __('Label', 'blockenberg'), value: s.label, onChange: function (v) { updateStat(s.id, 'label', v); } })
240 );
241 }),
242 a.showStats && el(Button, { variant: 'secondary', size: 'compact', onClick: function () { setAttributes({ stats: a.stats.concat([{ id: makeId(), value: '99%', label: 'Satisfaction' }]) }); } }, __('+ Add Stat', 'blockenberg'))
243 ),
244 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
245 getTypoControl(__('Badge', 'blockenberg'), 'badgeTypo', a, setAttributes),
246 getTypoControl(__('Headline', 'blockenberg'), 'headlineTypo', a, setAttributes),
247 getTypoControl(__('Subtext', 'blockenberg'), 'subtextTypo', a, setAttributes),
248 getTypoControl(__('Stat Value', 'blockenberg'), 'statValueTypo', a, setAttributes),
249 getTypoControl(__('Stat Label', 'blockenberg'), 'statLabelTypo', a, setAttributes)
250 ),
251 el(PanelBody, { title: __('Colors', 'blockenberg'), initialOpen: false },
252 cc('accentColor', __('Accent', 'blockenberg'), 'accentColor'),
253 cc('headlineColor', __('Headline', 'blockenberg'), 'headlineColor'),
254 cc('subtextColor', __('Subtext', 'blockenberg'), 'subtextColor'),
255 cc('badgeBg', __('Badge background', 'blockenberg'), 'badgeBg'),
256 cc('badgeColor', __('Badge text', 'blockenberg'), 'badgeColor'),
257 cc('playBg', __('Play button bg', 'blockenberg'), 'playBg'),
258 cc('playColor', __('Play button icon', 'blockenberg'), 'playColor'),
259 cc('statValueColor',__('Stat value', 'blockenberg'), 'statValueColor'),
260 cc('statLabelColor',__('Stat label', 'blockenberg'), 'statLabelColor'),
261 cc('statDividerColor',__('Stat divider', 'blockenberg'), 'statDividerColor'),
262 cc('bgColor', __('Section background','blockenberg'), 'bgColor')
263 ),
264 el(PanelBody, { title: __('Spacing', 'blockenberg'), initialOpen: false },
265 el(RangeControl, { label: __('Padding top (px)', 'blockenberg'), value: a.paddingTop, min: 0, max: 200, onChange: function (v) { setAttributes({ paddingTop: v }); } }),
266 el(RangeControl, { label: __('Padding bottom (px)', 'blockenberg'), value: a.paddingBottom, min: 0, max: 200, onChange: function (v) { setAttributes({ paddingBottom: v }); } })
267 )
268 ),
269
270 el('div', blockProps,
271 a.showContent && contentBox,
272 el(VideoEmbed, { a: a }),
273 statsBox
274 )
275 );
276 },
277
278 save: function (props) {
279 var a = props.attributes;
280
281 var src = '';
282 if (a.videoType === 'youtube' && a.youtubeId) {
283 src = 'https://www.youtube-nocookie.com/embed/' + a.youtubeId + '?controls=1&rel=0';
284 } else if (a.videoType === 'vimeo' && a.vimeoId) {
285 src = 'https://player.vimeo.com/video/' + a.vimeoId;
286 }
287 var hasSrc = !!src;
288 var hasVideo = hasSrc || a.videoType === 'self';
289
290 var contentBox = a.showContent && el('div', { className: 'bkbg-vs-content' },
291 a.showBadge && el('span', { className: 'bkbg-vs-badge' }, a.badge),
292 el(RichText.Content, { tagName: a.headlineTag, value: a.headline, className: 'bkbg-vs-headline' }),
293 el(RichText.Content, { tagName: 'p', value: a.subtext, className: 'bkbg-vs-subtext' })
294 );
295
296 var videoBox = el('div', { className: 'bkbg-vs-video-wrap', style: { maxWidth: a.maxWidth + 'px', margin: '0 auto' } },
297 el('div', { className: 'bkbg-vs-video-frame', style: { position: 'relative', borderRadius: a.borderRadius + 'px', overflow: 'hidden', boxShadow: a.showShadow ? '0 24px 64px rgba(0,0,0,0.18)' : 'none', background: '#111' } },
298 el('div', { style: { paddingBottom: (a.aspectRatio.split('/').reduce(function (_, v, i, arr) { return i === 1 ? (parseInt(v,10) / parseInt(arr[0],10) * 100).toFixed(2) : _; }, '56.25') + '%') } }),
299 hasSrc
300 ? el('iframe', { src: src, frameBorder: '0', allow: 'autoplay; fullscreen; picture-in-picture', allowFullScreen: true, loading: 'lazy', className: 'bkbg-vs-iframe', style: { position: 'absolute', inset: 0, width: '100%', height: '100%' } })
301 : null,
302 a.videoType === 'self' && a.videoUrl
303 ? el('video', { src: a.videoUrl, controls: true, poster: a.posterUrl || undefined, className: 'bkbg-vs-selfhosted', style: { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' } })
304 : null,
305 !hasVideo && a.posterUrl
306 ? el(Fragment, null,
307 el('img', { src: a.posterUrl, alt: '', style: { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' } }),
308 el('button', { type: 'button', className: 'bkbg-vs-play-btn', 'aria-label': __('Play video', 'blockenberg') },
309 el('span', { className: 'bkbg-vs-play-icon' }, '▶')
310 )
311 )
312 : null
313 )
314 );
315
316 var statsBox = a.showStats && el('div', { className: 'bkbg-vs-stats' },
317 a.stats.map(function (s, i) {
318 return el(Fragment, { key: s.id },
319 i > 0 && el('div', { className: 'bkbg-vs-stat-divider', 'aria-hidden': 'true' }),
320 el('div', { className: 'bkbg-vs-stat' },
321 el('div', { className: 'bkbg-vs-stat-value' }, s.value),
322 el('div', { className: 'bkbg-vs-stat-label' }, s.label)
323 )
324 );
325 })
326 );
327
328 return el('div', wp.blockEditor.useBlockProps.save({ className: 'bkbg-vs-wrapper', style: buildWrapStyle(a) }),
329 contentBox,
330 videoBox,
331 statsBox
332 );
333 }
334 });
335 }() );
336