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

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

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