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

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

614 lines 35.1 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 useBlockProps = wp.blockEditor.useBlockProps;
9 var RichText = wp.blockEditor.RichText;
10 var PanelBody = wp.components.PanelBody;
11 var ColorPicker = wp.components.ColorPicker;
12 var Popover = wp.components.Popover;
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 Notice = wp.components.Notice;
19
20 // Resolve the shared components lazily without defining globals for each block.
21 function _tc(label, key, attrs, setA) {
22 var Control = window.bkbgTypographyControl;
23 return Control ? el(Control, {
24 label: label,
25 value: attrs[key] || {},
26 onChange: function (value) { var update = {}; update[key] = value; setA(update); }
27 }) : null;
28 }
29 function _tvf(vars, key, attrs, prefix) {
30 var cssVars = window.bkbgTypoCssVars;
31 if (cssVars) Object.assign(vars, cssVars(attrs[key] || {}, prefix));
32 return vars;
33 }
34 function getTypoControl(label, key, attrs, setA) { return _tc(label, key, attrs, setA); }
35 function getTypoCssVars(attrs) {
36 var v = {};
37 _tvf(v, 'buttonTypo', attrs, '--bkvpo-bt-');
38 _tvf(v, 'subtextTypo', attrs, '--bkvpo-st-');
39 return v;
40 }
41
42 // ── Color swatch control (same pattern as google-map / blockquote) ──────────
43 function makeColorControl(openKey, setOpenKey) {
44 return function renderColorControl(key, label, value, onChange) {
45 var isOpen = openKey === key;
46 return el('div', {
47 key: key,
48 style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '4px 0', gap: '8px' }
49 },
50 el('span', { style: { fontSize: '12px', color: '#1e1e1e', flex: 1, lineHeight: 1.4 } }, label),
51 el('div', { style: { position: 'relative', flexShrink: 0 } },
52 el('button', {
53 type: 'button',
54 title: value || 'none',
55 onClick: function () { setOpenKey(isOpen ? null : key); },
56 style: {
57 width: '28px', height: '28px', borderRadius: '4px',
58 border: isOpen ? '2px solid #007cba' : '2px solid #ddd',
59 cursor: 'pointer', padding: 0, display: 'block',
60 background: value || '#ffffff', flexShrink: 0
61 }
62 }),
63 isOpen && el(Popover, { position: 'bottom left', onClose: function () { setOpenKey(null); } },
64 el('div', { style: { padding: '8px' }, onMouseDown: function (e) { e.stopPropagation(); } },
65 el('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '6px' } },
66 el('strong', { style: { fontSize: '12px' } }, label),
67 el(Button, { icon: 'no-alt', isSmall: true, onClick: function () { setOpenKey(null); } })
68 ),
69 el(ColorPicker, { color: value, enableAlpha: true, onChange: onChange })
70 )
71 )
72 )
73 );
74 };
75 }
76
77 // ── Section sub-header ───────────────────────────────────────────────────────
78 function sectionLabel(text) {
79 return el('p', {
80 style: { margin: '8px 0 4px', fontWeight: 600, fontSize: '11px', textTransform: 'uppercase', color: '#888', letterSpacing: '.5px' }
81 }, text);
82 }
83
84 // ── Build play icon SVG element ──────────────────────────────────────────────
85 function buildPlayIconEl(a) {
86 var sz = a.iconSize + 'px';
87 if (a.iconStyle === 'circle') {
88 // Circle with play triangle inside
89 return el('svg', {
90 viewBox: '0 0 40 40', width: sz, height: sz,
91 style: { display: 'block', flexShrink: 0 },
92 xmlns: 'http://www.w3.org/2000/svg'
93 },
94 el('circle', { cx: '20', cy: '20', r: '19', fill: 'rgba(255,255,255,0.2)', stroke: a.iconColor, strokeWidth: '1.5' }),
95 el('polygon', { points: '16,13 16,27 28,20', fill: a.iconColor })
96 );
97 }
98 if (a.iconStyle === 'circle-filled') {
99 return el('svg', {
100 viewBox: '0 0 40 40', width: sz, height: sz,
101 style: { display: 'block', flexShrink: 0 },
102 xmlns: 'http://www.w3.org/2000/svg'
103 },
104 el('circle', { cx: '20', cy: '20', r: '20', fill: a.iconColor }),
105 el('polygon', { points: '16,13 16,27 28,20', fill: 'currentColor' })
106 );
107 }
108 // 'triangle' — simple play triangle
109 return el('svg', {
110 viewBox: '0 0 24 24', width: sz, height: sz,
111 style: { display: 'block', flexShrink: 0, fill: a.iconColor },
112 xmlns: 'http://www.w3.org/2000/svg'
113 },
114 el('path', { d: 'M8 5v14l11-7z' })
115 );
116 }
117
118 // ── Compute button inline style ──────────────────────────────────────────────
119 function buildButtonStyle(a) {
120 var isOutline = a.buttonStyle === 'outline' || a.buttonStyle === 'outline-pill';
121 var isPill = a.buttonStyle === 'pill' || a.buttonStyle === 'outline-pill';
122 var isGradient = a.buttonStyle === 'gradient';
123 var isGhost = a.buttonStyle === 'ghost';
124
125 var bg, color, border;
126 if (isGhost) {
127 bg = 'rgba(255,255,255,0.15)';
128 color = a.buttonTextColor;
129 border = '1px solid rgba(255,255,255,0.35)';
130 } else if (isGradient) {
131 bg = 'linear-gradient(' + a.gradientAngle + 'deg, ' + a.gradientFrom + ', ' + a.gradientTo + ')';
132 color = a.buttonTextColor;
133 border = a.buttonBorderWidth + 'px solid transparent';
134 } else if (isOutline) {
135 bg = 'transparent';
136 color = a.buttonBorderColor;
137 border = a.buttonBorderWidth + 'px solid ' + a.buttonBorderColor;
138 } else {
139 // solid / pill
140 bg = a.buttonBg;
141 color = a.buttonTextColor;
142 border = a.buttonBorderWidth + 'px solid ' + a.buttonBg;
143 }
144
145 var radius = isPill ? '9999px' : (a.buttonBorderRadius + 'px');
146 var shadow = a.showButtonShadow
147 ? '0 ' + a.buttonShadowV + 'px ' + a.buttonShadowBlur + 'px ' + a.buttonShadowSpread + 'px ' + a.buttonShadowColor
148 : 'none';
149
150 var style = {
151 display: 'inline-flex',
152 alignItems: 'center',
153 justifyContent: 'center',
154 gap: Math.round(a.buttonFontSize * 0.5) + 'px',
155 padding: a.buttonPaddingV + 'px ' + a.buttonPaddingH + 'px',
156 background: bg,
157 color: color,
158 border: border,
159 borderRadius: radius,
160 cursor: 'pointer',
161 boxShadow: shadow,
162 transition: 'all 0.22s cubic-bezier(.4,0,.2,1)',
163 whiteSpace: 'nowrap',
164 userSelect: 'none'
165 };
166
167 if (isGhost) {
168 style.backdropFilter = 'blur(10px)';
169 style.WebkitBackdropFilter = 'blur(10px)';
170 }
171
172 return style;
173 }
174
175 // ── Get aspect-ratio padding-bottom percentage ───────────────────────────────
176 function aspectPadding(ratio) {
177 var map = { '16-9': '56.25%', '4-3': '75%', '1-1': '100%', '21-9': '42.86%' };
178 return map[ratio] || '56.25%';
179 }
180
181 // ────────────────────────────────────────────────────────────────────────────
182 registerBlockType('blockenberg/video-popup', {
183 title: __('Video Popup Button', 'blockenberg'),
184 icon: 'controls-play',
185 category: 'bkbg-media',
186
187 edit: function (props) {
188 var a = props.attributes;
189 var setAttributes = props.setAttributes;
190
191 var openColorKeyState = useState(null);
192 var openColorKey = openColorKeyState[0];
193 var setOpenColorKey = openColorKeyState[1];
194 var renderColorControl = makeColorControl(openColorKey, setOpenColorKey);
195
196 // Button style options
197 var buttonStyleOptions = [
198 { label: __('Solid', 'blockenberg'), value: 'solid' },
199 { label: __('Outline', 'blockenberg'), value: 'outline' },
200 { label: __('Pill (solid)','blockenberg'), value: 'pill' },
201 { label: __('Pill (outline)','blockenberg'), value: 'outline-pill' },
202 { label: __('Gradient', 'blockenberg'), value: 'gradient' },
203 { label: __('Ghost (glass)','blockenberg'), value: 'ghost' }
204 ];
205
206 var buttonSizeOptions = [
207 { label: __('Small', 'blockenberg'), value: 'sm' },
208 { label: __('Medium', 'blockenberg'), value: 'md' },
209 { label: __('Large', 'blockenberg'), value: 'lg' },
210 { label: __('Extra Large', 'blockenberg'), value: 'xl' }
211 ];
212
213 var buttonAlignOptions = [
214 { label: __('Left', 'blockenberg'), value: 'left' },
215 { label: __('Center', 'blockenberg'), value: 'center' },
216 { label: __('Right', 'blockenberg'), value: 'right' }
217 ];
218
219 var fontWeightOptions = [
220 { label: '300 — Light', value: 300 },
221 { label: '400 — Regular', value: 400 },
222 { label: '500 — Medium', value: 500 },
223 { label: '600 — Semi Bold', value: 600 },
224 { label: '700 — Bold', value: 700 },
225 { label: '800 — ExtraBold', value: 800 },
226 { label: '900 — Black', value: 900 }
227 ];
228
229 var textTransformOptions = [
230 { label: __('None', 'blockenberg'), value: 'none' },
231 { label: __('Uppercase', 'blockenberg'), value: 'uppercase' },
232 { label: __('Capitalize', 'blockenberg'), value: 'capitalize' },
233 { label: __('Lowercase', 'blockenberg'), value: 'lowercase' }
234 ];
235
236 var iconStyleOptions = [
237 { label: __('Circle outline + play', 'blockenberg'), value: 'circle' },
238 { label: __('Circle filled + play', 'blockenberg'), value: 'circle-filled' },
239 { label: __('Triangle only', 'blockenberg'), value: 'triangle' }
240 ];
241
242 var iconPositionOptions = [
243 { label: __('Left of text', 'blockenberg'), value: 'left' },
244 { label: __('Right of text', 'blockenberg'), value: 'right' }
245 ];
246
247 var aspectRatioOptions = [
248 { label: __('16:9 — Widescreen (default)', 'blockenberg'), value: '16-9' },
249 { label: __('21:9 — Ultrawide', 'blockenberg'), value: '21-9' },
250 { label: __('4:3 — Classic', 'blockenberg'), value: '4-3' },
251 { label: __('1:1 — Square', 'blockenberg'), value: '1-1' }
252 ];
253
254 // Size preset handler
255 function applySize(v) {
256 var presets = {
257 sm: { buttonSize: 'sm', buttonFontSize: 13, buttonPaddingV: 8, buttonPaddingH: 18 },
258 md: { buttonSize: 'md', buttonFontSize: 15, buttonPaddingV: 13, buttonPaddingH: 26 },
259 lg: { buttonSize: 'lg', buttonFontSize: 18, buttonPaddingV: 16, buttonPaddingH: 32 },
260 xl: { buttonSize: 'xl', buttonFontSize: 22, buttonPaddingV: 20, buttonPaddingH: 44 }
261 };
262 setAttributes(presets[v] || { buttonSize: v });
263 }
264
265 var isOutline = a.buttonStyle === 'outline' || a.buttonStyle === 'outline-pill';
266 var isGradient = a.buttonStyle === 'gradient';
267 var isGhost = a.buttonStyle === 'ghost';
268
269 var btnStyle = buildButtonStyle(a);
270 var iconEl = a.showIcon ? buildPlayIconEl(a) : null;
271
272 // ── Inspector ─────────────────────────────────────────────────────
273 var inspector = el(InspectorControls, {},
274
275 // ── Video Source ──────────────────────────────────────────────
276 el(PanelBody, { title: __('Video Source', 'blockenberg'), initialOpen: true },
277 el(TextControl, {
278 label: __('Video URL', 'blockenberg'),
279 help: __('YouTube, Vimeo, or any direct video/embed URL.', 'blockenberg'),
280 value: a.videoUrl,
281 onChange: function (v) { setAttributes({ videoUrl: v }); }
282 }),
283 a.videoUrl && el(Notice, { status: 'info', isDismissible: false },
284 __('Preview is shown in the editor. Popup opens on the frontend.', 'blockenberg')
285 ),
286 el('hr', {}),
287 el(ToggleControl, {
288 label: __('Autoplay when popup opens', 'blockenberg'),
289 checked: a.autoplay,
290 __nextHasNoMarginBottom: true,
291 onChange: function (v) { setAttributes({ autoplay: v }); }
292 }),
293 el(ToggleControl, {
294 label: __('Muted', 'blockenberg'),
295 help: __('Start muted (needed for autoplay in some browsers).', 'blockenberg'),
296 checked: a.muted,
297 __nextHasNoMarginBottom: true,
298 onChange: function (v) { setAttributes({ muted: v }); }
299 })
300 ),
301
302 // ── Button Style ──────────────────────────────────────────────
303 el(PanelBody, { title: __('Button Style', 'blockenberg'), initialOpen: true },
304 el(SelectControl, {
305 label: __('Button Style', 'blockenberg'),
306 value: a.buttonStyle,
307 options: buttonStyleOptions,
308 onChange: function (v) { setAttributes({ buttonStyle: v }); }
309 }),
310 isGhost && el(Notice, { status: 'warning', isDismissible: false, style: { margin: '0 0 8px' } },
311 __('Ghost style looks best over dark/image backgrounds.', 'blockenberg')
312 ),
313 el(SelectControl, {
314 label: __('Size Preset', 'blockenberg'),
315 value: a.buttonSize,
316 options: buttonSizeOptions,
317 onChange: applySize
318 }),
319 el(SelectControl, {
320 label: __('Alignment', 'blockenberg'),
321 value: a.buttonAlign,
322 options: buttonAlignOptions,
323 onChange: function (v) { setAttributes({ buttonAlign: v }); }
324 }),
325 el('hr', {}),
326 sectionLabel(__('Spacing & Shape', 'blockenberg')),
327 el(RangeControl, {
328 label: __('Padding Vertical (px)', 'blockenberg'),
329 value: a.buttonPaddingV, min: 4, max: 40,
330 onChange: function (v) { setAttributes({ buttonPaddingV: v }); }
331 }),
332 el(RangeControl, {
333 label: __('Padding Horizontal (px)', 'blockenberg'),
334 value: a.buttonPaddingH, min: 8, max: 80,
335 onChange: function (v) { setAttributes({ buttonPaddingH: v }); }
336 }),
337 el(RangeControl, {
338 label: __('Border Radius (px)', 'blockenberg'),
339 help: __('Ignored for Pill styles (always fully rounded).', 'blockenberg'),
340 value: a.buttonBorderRadius, min: 0, max: 40,
341 onChange: function (v) { setAttributes({ buttonBorderRadius: v }); }
342 })
343 ),
344
345 // ── Button Colors ─────────────────────────────────────────────
346
347 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
348 getTypoControl(__('Button Text', 'blockenberg'), 'buttonTypo', a, setAttributes),
349 a.showSubtext && el(Fragment, {},
350 getTypoControl(__('Subtext', 'blockenberg'), 'subtextTypo', a, setAttributes),
351 renderColorControl('subtextColor', __('Text Color', 'blockenberg'), a.subtextColor, function (c) { setAttributes({ subtextColor: c }); }),
352 el(RangeControl, {
353 label: __('Spacing Above (px)', 'blockenberg'),
354 value: a.subtextSpacing, min: 4, max: 32,
355 onChange: function (v) { setAttributes({ subtextSpacing: v }); }
356 })
357 )
358 ),
359 el(PanelBody, { title: __('Button Colors', 'blockenberg'), initialOpen: false },
360 isGradient
361 ? el(Fragment, {},
362 sectionLabel(__('Gradient', 'blockenberg')),
363 renderColorControl('gradientFrom', __('Gradient From', 'blockenberg'), a.gradientFrom, function (c) { setAttributes({ gradientFrom: c }); }),
364 renderColorControl('gradientTo', __('Gradient To', 'blockenberg'), a.gradientTo, function (c) { setAttributes({ gradientTo: c }); }),
365 el(RangeControl, {
366 label: __('Gradient Angle (deg)', 'blockenberg'),
367 value: a.gradientAngle, min: 0, max: 360,
368 onChange: function (v) { setAttributes({ gradientAngle: v }); }
369 }),
370 el('hr', {}),
371 renderColorControl('buttonTextColor', __('Text Color', 'blockenberg'), a.buttonTextColor, function (c) { setAttributes({ buttonTextColor: c }); })
372 )
373 : el(Fragment, {},
374 isOutline
375 ? el(Fragment, {},
376 renderColorControl('buttonBorderColor', __('Border & Text Color', 'blockenberg'), a.buttonBorderColor, function (c) { setAttributes({ buttonBorderColor: c }); }),
377 el(RangeControl, {
378 label: __('Border Width (px)', 'blockenberg'),
379 value: a.buttonBorderWidth, min: 1, max: 8,
380 onChange: function (v) { setAttributes({ buttonBorderWidth: v }); }
381 })
382 )
383 : isGhost
384 ? el(Fragment, {},
385 renderColorControl('buttonTextColor', __('Text Color', 'blockenberg'), a.buttonTextColor, function (c) { setAttributes({ buttonTextColor: c }); })
386 )
387 : el(Fragment, {},
388 renderColorControl('buttonBg', __('Background', 'blockenberg'), a.buttonBg, function (c) { setAttributes({ buttonBg: c }); }),
389 renderColorControl('buttonBgHover', __('Background (hover)', 'blockenberg'), a.buttonBgHover, function (c) { setAttributes({ buttonBgHover: c }); }),
390 renderColorControl('buttonTextColor', __('Text Color', 'blockenberg'), a.buttonTextColor, function (c) { setAttributes({ buttonTextColor: c }); }),
391 renderColorControl('buttonTextColorHover', __('Text Color (hover)', 'blockenberg'), a.buttonTextColorHover, function (c) { setAttributes({ buttonTextColorHover: c }); })
392 )
393 )
394 ),
395
396 // ── Button Shadow ─────────────────────────────────────────────
397 el(PanelBody, { title: __('Button Shadow', 'blockenberg'), initialOpen: false },
398 el(ToggleControl, {
399 label: __('Enable Shadow', 'blockenberg'),
400 checked: a.showButtonShadow,
401 __nextHasNoMarginBottom: true,
402 onChange: function (v) { setAttributes({ showButtonShadow: v }); }
403 }),
404 a.showButtonShadow && el(Fragment, {},
405 renderColorControl('buttonShadowColor', __('Shadow Color', 'blockenberg'), a.buttonShadowColor, function (c) { setAttributes({ buttonShadowColor: c }); }),
406 el(RangeControl, { label: __('Vertical (px)', 'blockenberg'), value: a.buttonShadowV, min: -20, max: 40, onChange: function (v) { setAttributes({ buttonShadowV: v }); } }),
407 el(RangeControl, { label: __('Blur (px)', 'blockenberg'), value: a.buttonShadowBlur, min: 0, max: 80, onChange: function (v) { setAttributes({ buttonShadowBlur: v }); } }),
408 el(RangeControl, { label: __('Spread (px)', 'blockenberg'), value: a.buttonShadowSpread, min: -10, max: 30, onChange: function (v) { setAttributes({ buttonShadowSpread: v }); } })
409 )
410 ),
411
412 // ── Play Icon ─────────────────────────────────────────────────
413 el(PanelBody, { title: __('Play Icon', 'blockenberg'), initialOpen: false },
414 el(ToggleControl, {
415 label: __('Show Play Icon', 'blockenberg'),
416 checked: a.showIcon,
417 __nextHasNoMarginBottom: true,
418 onChange: function (v) { setAttributes({ showIcon: v }); }
419 }),
420 a.showIcon && el(Fragment, {},
421 el(SelectControl, {
422 label: __('Icon Style', 'blockenberg'),
423 value: a.iconStyle,
424 options: iconStyleOptions,
425 onChange: function (v) { setAttributes({ iconStyle: v }); }
426 }),
427 el(SelectControl, {
428 label: __('Position', 'blockenberg'),
429 value: a.iconPosition,
430 options: iconPositionOptions,
431 onChange: function (v) { setAttributes({ iconPosition: v }); }
432 }),
433 el(RangeControl, {
434 label: __('Icon Size (px)', 'blockenberg'),
435 value: a.iconSize, min: 12, max: 48,
436 onChange: function (v) { setAttributes({ iconSize: v }); }
437 }),
438 renderColorControl('iconColor', __('Icon Color', 'blockenberg'), a.iconColor, function (c) { setAttributes({ iconColor: c }); })
439 )
440 ),
441
442 // ── Subtext ───────────────────────────────────────────────────
443 el(PanelBody, { title: __('Subtext Below Button', 'blockenberg'), initialOpen: false },
444 el(ToggleControl, {
445 label: __('Show Subtext', 'blockenberg'),
446 checked: a.showSubtext,
447 __nextHasNoMarginBottom: true,
448 onChange: function (v) { setAttributes({ showSubtext: v }); }
449 }),
450 ),
451
452 // ── Popup Settings ────────────────────────────────────────────
453 el(PanelBody, { title: __('Popup Settings', 'blockenberg'), initialOpen: false },
454 el(SelectControl, {
455 label: __('Aspect Ratio', 'blockenberg'),
456 value: a.aspectRatio,
457 options: aspectRatioOptions,
458 onChange: function (v) { setAttributes({ aspectRatio: v }); }
459 }),
460 el(RangeControl, {
461 label: __('Max Width (px)', 'blockenberg'),
462 value: a.popupMaxWidth, min: 320, max: 1600, step: 10,
463 onChange: function (v) { setAttributes({ popupMaxWidth: v }); }
464 }),
465 el(RangeControl, {
466 label: __('Content Border Radius (px)', 'blockenberg'),
467 value: a.popupBorderRadius, min: 0, max: 32,
468 onChange: function (v) { setAttributes({ popupBorderRadius: v }); }
469 }),
470 el('hr', {}),
471 sectionLabel(__('Overlay', 'blockenberg')),
472 renderColorControl('overlayColor', __('Overlay Color', 'blockenberg'), a.overlayColor, function (c) { setAttributes({ overlayColor: c }); }),
473 el(ToggleControl, {
474 label: __('Backdrop Blur', 'blockenberg'),
475 checked: a.overlayBlur,
476 __nextHasNoMarginBottom: true,
477 onChange: function (v) { setAttributes({ overlayBlur: v }); }
478 }),
479 a.overlayBlur && el(RangeControl, {
480 label: __('Blur Amount (px)', 'blockenberg'),
481 value: a.overlayBlurAmount, min: 2, max: 24,
482 onChange: function (v) { setAttributes({ overlayBlurAmount: v }); }
483 }),
484 el('hr', {}),
485 sectionLabel(__('Close Button', 'blockenberg')),
486 renderColorControl('closeButtonColor', __('Close Icon Color', 'blockenberg'), a.closeButtonColor, function (c) { setAttributes({ closeButtonColor: c }); }),
487 el(ToggleControl, {
488 label: __('Close on overlay click', 'blockenberg'),
489 checked: a.closeOnOverlay,
490 __nextHasNoMarginBottom: true,
491 onChange: function (v) { setAttributes({ closeOnOverlay: v }); }
492 })
493 )
494 );
495
496 // ── Editor output ─────────────────────────────────────────────────
497 return el(Fragment, {},
498 inspector,
499 el('div', useBlockProps((function () {
500 var s = getTypoCssVars(a);
501 return { style: s, className: 'bkbg-vp-outer' };
502 })()),
503 el('div', { className: 'bkbg-vp-btn-outer', style: { textAlign: a.buttonAlign } },
504 el('div', { className: 'bkbg-vp-btn-wrap' },
505 el('button', { className: 'bkbg-vp-btn', type: 'button', style: btnStyle },
506 a.showIcon && a.iconPosition === 'left' && iconEl,
507 el(RichText, {
508 tagName: 'span',
509 className: 'bkbg-vp-btn-label',
510 value: a.buttonText,
511 onChange: function (v) { setAttributes({ buttonText: v }); },
512 allowedFormats: [],
513 placeholder: __('Button label…', 'blockenberg')
514 }),
515 a.showIcon && a.iconPosition === 'right' && iconEl
516 ),
517 a.showSubtext && el(RichText, {
518 tagName: 'p',
519 className: 'bkbg-vp-subtext',
520 value: a.subtext,
521 onChange: function (v) { setAttributes({ subtext: v }); },
522 allowedFormats: ['core/bold', 'core/italic', 'core/link'],
523 placeholder: __('e.g. No credit card required', 'blockenberg'),
524 style: {
525 color: a.subtextColor,
526 marginTop: a.subtextSpacing + 'px',
527 marginBottom: 0,
528 padding: 0,
529 textAlign: a.buttonAlign
530 }
531 })
532 )
533 )
534 )
535 );
536 },
537
538 // ── Save ────────────────────────────────────────────────────────────────
539 save: function (props) {
540 var a = props.attributes;
541 var RichTextContent = wp.blockEditor.RichText.Content;
542 var btnStyle = buildButtonStyle(a);
543
544 // Build play icon for save (returns string-safe SVG via el)
545 var iconEl = null;
546 if (a.showIcon) {
547 var sz = a.iconSize + 'px';
548 if (a.iconStyle === 'circle') {
549 iconEl = el('svg', { viewBox: '0 0 40 40', width: sz, height: sz, style: { display: 'block', flexShrink: 0 }, xmlns: 'http://www.w3.org/2000/svg', 'aria-hidden': 'true' },
550 el('circle', { cx: '20', cy: '20', r: '19', fill: 'rgba(255,255,255,0.2)', stroke: a.iconColor, strokeWidth: '1.5' }),
551 el('polygon', { points: '16,13 16,27 28,20', fill: a.iconColor })
552 );
553 } else if (a.iconStyle === 'circle-filled') {
554 iconEl = el('svg', { viewBox: '0 0 40 40', width: sz, height: sz, style: { display: 'block', flexShrink: 0 }, xmlns: 'http://www.w3.org/2000/svg', 'aria-hidden': 'true' },
555 el('circle', { cx: '20', cy: '20', r: '20', fill: a.iconColor }),
556 el('polygon', { points: '16,13 16,27 28,20', fill: '#fff' })
557 );
558 } else {
559 iconEl = el('svg', { viewBox: '0 0 24 24', width: sz, height: sz, style: { display: 'block', flexShrink: 0, fill: a.iconColor }, xmlns: 'http://www.w3.org/2000/svg', 'aria-hidden': 'true' },
560 el('path', { d: 'M8 5v14l11-7z' })
561 );
562 }
563 }
564
565 return el('div', wp.blockEditor.useBlockProps.save((function () {
566 var s = getTypoCssVars(a);
567 return {
568 className: 'bkbg-vp-outer',
569 style: s,
570 'data-vp-url': a.videoUrl,
571 'data-vp-autoplay': a.autoplay ? '1' : '0',
572 'data-vp-muted': a.muted ? '1' : '0',
573 'data-vp-max-width': String(a.popupMaxWidth),
574 'data-vp-radius': String(a.popupBorderRadius),
575 'data-vp-ratio': a.aspectRatio,
576 'data-vp-overlay': a.overlayColor,
577 'data-vp-blur': a.overlayBlur ? String(a.overlayBlurAmount) : '0',
578 'data-vp-close-overlay': a.closeOnOverlay ? '1' : '0',
579 'data-vp-close-color': a.closeButtonColor
580 }; })()),
581 el('div', { className: 'bkbg-vp-btn-outer', style: { textAlign: a.buttonAlign } },
582 el('div', { className: 'bkbg-vp-btn-wrap' },
583 el('button', {
584 className: 'bkbg-vp-btn',
585 type: 'button',
586 style: btnStyle,
587 'aria-haspopup': 'dialog',
588 'aria-label': (a.buttonText ? a.buttonText.replace(/<[^>]+>/g, '') : 'Watch video') + ' — opens video popup',
589 'data-vp-bg-hover': a.buttonBgHover,
590 'data-vp-text-hover': a.buttonTextColorHover
591 },
592 a.showIcon && a.iconPosition === 'left' && iconEl,
593 el(RichTextContent, { tagName: 'span', className: 'bkbg-vp-btn-label', value: a.buttonText }),
594 a.showIcon && a.iconPosition === 'right' && iconEl
595 ),
596 a.showSubtext && el(RichTextContent, {
597 tagName: 'p',
598 className: 'bkbg-vp-subtext',
599 value: a.subtext,
600 style: {
601 color: a.subtextColor,
602 marginTop: a.subtextSpacing + 'px',
603 marginBottom: 0,
604 padding: 0,
605 textAlign: a.buttonAlign
606 }
607 })
608 )
609 )
610 );
611 }
612 });
613 }() );
614