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

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

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