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

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

190 lines 12.7 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 registerBlockType = wp.blocks.registerBlockType;
4 var useBlockProps = wp.blockEditor.useBlockProps;
5 var InspectorControls = wp.blockEditor.InspectorControls;
6 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
7 var MediaUpload = wp.blockEditor.MediaUpload;
8 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
9 var PanelBody = wp.components.PanelBody;
10 var TextControl = wp.components.TextControl;
11 var ToggleControl = wp.components.ToggleControl;
12 var SelectControl = wp.components.SelectControl;
13 var RangeControl = wp.components.RangeControl;
14 var Button = wp.components.Button;
15 var useState = wp.element.useState;
16
17 var _tc, _tv;
18 function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
19 function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
20
21 var ASPECTS = [
22 { label: '16:9 (Widescreen)', value: '16:9' },
23 { label: '4:3 (Classic)', value: '4:3' },
24 { label: '1:1 (Square)', value: '1:1' },
25 { label: '9:16 (Portrait)', value: '9:16' },
26 { label: '21:9 (Ultrawide)', value: '21:9' }
27 ];
28
29 var CORNERS = [
30 { label: 'Bottom Right', value: 'bottom-right' },
31 { label: 'Bottom Left', value: 'bottom-left' },
32 { label: 'Top Right', value: 'top-right' },
33 { label: 'Top Left', value: 'top-left' }
34 ];
35
36 var VIDEO_TYPES = [
37 { label: 'YouTube', value: 'youtube' },
38 { label: 'Vimeo', value: 'vimeo' },
39 { label: 'Self-Hosted (MP4)', value: 'self' }
40 ];
41
42 function getAspectPad(ratio) {
43 var parts = ratio.split(':');
44 var w = parseFloat(parts[0]) || 16;
45 var h = parseFloat(parts[1]) || 9;
46 return ((h / w) * 100).toFixed(2) + '%';
47 }
48
49 function getEmbedUrl(attr) {
50 var url = attr.videoUrl || '';
51 var type = attr.videoType || 'youtube';
52 var ytMatch = url.match(/(?:v=|youtu\.be\/)([A-Za-z0-9_-]{11})/);
53 var vmMatch = url.match(/vimeo\.com\/(\d+)/);
54 if (type === 'youtube' && ytMatch) {
55 var params = '?rel=0' + (attr.autoplay ? '&autoplay=1' : '') + (attr.muted ? '&mute=1' : '') + (attr.loop ? '&loop=1&playlist=' + ytMatch[1] : '') + (!attr.controls ? '&controls=0' : '');
56 return 'https://www.youtube.com/embed/' + ytMatch[1] + params;
57 }
58 if (type === 'vimeo' && vmMatch) {
59 var vparams = '?' + (attr.autoplay ? 'autoplay=1&' : '') + (attr.muted ? 'muted=1&' : '') + (attr.loop ? 'loop=1&' : '');
60 return 'https://player.vimeo.com/video/' + vmMatch[1] + vparams;
61 }
62 return url;
63 }
64
65 registerBlockType('blockenberg/sticky-video', {
66 edit: function (props) {
67 var attr = props.attributes;
68 var setAttr = props.setAttributes;
69 var blockProps = useBlockProps((function() {
70 var _tvf = getTypoCssVars();
71 var s = { background: attr.sectionBg || undefined };
72 if (_tvf) {
73 Object.assign(s, _tvf(attr.titleTypo, '--bksv-tt-'));
74 Object.assign(s, _tvf(attr.subtitleTypo, '--bksv-st-'));
75 }
76 s['--bksv-title-fw'] = attr.titleFontWeight || '700';
77 s['--bksv-title-lh'] = attr.titleLineHeight || 1.2;
78 s['--bksv-st-fw'] = attr.subtitleFontWeight || '400';
79 s['--bksv-st-lh'] = attr.subtitleLineHeight || 1.5;
80 return { style: s };
81 })());
82
83 var padBottom = getAspectPad(attr.aspectRatio);
84 var embedUrl = getEmbedUrl(attr);
85
86 return el('div', blockProps,
87 el(InspectorControls, {},
88
89 // Video Source
90 el(PanelBody, { title: 'Video Source', initialOpen: true },
91 el(SelectControl, { label: 'Type', value: attr.videoType, options: VIDEO_TYPES, onChange: function(v){ setAttr({ videoType: v }); }, __nextHasNoMarginBottom: true }),
92 el('div', { style: { marginTop: 12 } }),
93 el(TextControl, { label: 'Video URL', value: attr.videoUrl, placeholder: attr.videoType === 'youtube' ? 'https://www.youtube.com/watch?v=...' : attr.videoType === 'vimeo' ? 'https://vimeo.com/...' : 'https://example.com/video.mp4', onChange: function(v){ setAttr({ videoUrl: v }); }, __nextHasNoMarginBottom: true }),
94 el('div', { style: { marginTop: 12 } }),
95 el(TextControl, { label: 'Title', value: attr.title, onChange: function(v){ setAttr({ title: v }); }, __nextHasNoMarginBottom: true }),
96 el(ToggleControl, { label: 'Show title overlay', checked: attr.showTitle, onChange: function(v){ setAttr({ showTitle: v }); }, __nextHasNoMarginBottom: true }),
97 el(TextControl, { label: 'Subtitle (optional)', value: attr.subtitle, onChange: function(v){ setAttr({ subtitle: v }); }, __nextHasNoMarginBottom: true }),
98 el('div', { style: { marginTop: 8 } }),
99 el('p', { style: { fontSize: 11, color: '#64748b', margin: 0 } }, 'Poster image (shown before play):'),
100 el(MediaUploadCheck, {},
101 el(MediaUpload, {
102 onSelect: function(m){ setAttr({ posterUrl: m.url }); },
103 allowedTypes: ['image'],
104 value: attr.posterUrl,
105 render: function(obj) {
106 return el('div', { style: { marginTop: 6 } },
107 el(Button, { onClick: obj.open, variant: 'secondary', size: 'small' }, attr.posterUrl ? 'Change Poster' : 'Set Poster Image'),
108 attr.posterUrl && el(Button, { onClick: function(){ setAttr({ posterUrl: '' }); }, variant: 'link', isDestructive: true, size: 'small', style: { marginLeft: 8 } }, 'Remove')
109 );
110 }
111 })
112 )
113 ),
114
115 // Playback
116 el(PanelBody, { title: 'Playback Options', initialOpen: false },
117 el(ToggleControl, { label: 'Autoplay', checked: attr.autoplay, onChange: function(v){ setAttr({ autoplay: v }); }, __nextHasNoMarginBottom: true }),
118 el(ToggleControl, { label: 'Muted', checked: attr.muted, onChange: function(v){ setAttr({ muted: v }); }, __nextHasNoMarginBottom: true }),
119 el(ToggleControl, { label: 'Loop', checked: attr.loop, onChange: function(v){ setAttr({ loop: v }); }, __nextHasNoMarginBottom: true }),
120 el(ToggleControl, { label: 'Show controls', checked: attr.controls, onChange: function(v){ setAttr({ controls: v }); }, __nextHasNoMarginBottom: true }),
121 el(ToggleControl, { label: 'Show overlay play button', checked: attr.showOverlayPlay, onChange: function(v){ setAttr({ showOverlayPlay: v }); }, __nextHasNoMarginBottom: true })
122 ),
123
124 // Layout
125 el(PanelBody, { title: 'Player Layout', initialOpen: false },
126 el(SelectControl, { label: 'Aspect Ratio', value: attr.aspectRatio, options: ASPECTS, onChange: function(v){ setAttr({ aspectRatio: v }); }, __nextHasNoMarginBottom: true }),
127 el(RangeControl, { label: 'Border Radius', value: attr.borderRadius, min: 0, max: 40, onChange: function(v){ setAttr({ borderRadius: v }); }, __nextHasNoMarginBottom: true })
128 ),
129
130 // Sticky
131 el(PanelBody, { title: 'Sticky / PiP Behaviour', initialOpen: false },
132 el(ToggleControl, { label: 'Enable sticky-on-scroll', checked: attr.stickyEnabled, onChange: function(v){ setAttr({ stickyEnabled: v }); }, __nextHasNoMarginBottom: true }),
133 el(ToggleControl, { label: 'Only stick when playing', checked: attr.stickyOnPlay, onChange: function(v){ setAttr({ stickyOnPlay: v }); }, __nextHasNoMarginBottom: true }),
134 el(SelectControl, { label: 'Sticky Corner', value: attr.stickyCorner, options: CORNERS, onChange: function(v){ setAttr({ stickyCorner: v }); }, __nextHasNoMarginBottom: true }),
135 el(RangeControl, { label: 'Sticky Width (px)', value: attr.stickyWidth, min: 200, max: 500, step: 10, onChange: function(v){ setAttr({ stickyWidth: v }); }, __nextHasNoMarginBottom: true }),
136 el(RangeControl, { label: 'Edge Offset (px)', value: attr.stickyOffset, min: 8, max: 80, onChange: function(v){ setAttr({ stickyOffset: v }); }, __nextHasNoMarginBottom: true }),
137 el(RangeControl, { label: 'Sticky Radius (px)', value: attr.stickyRadius, min: 0, max: 30, onChange: function(v){ setAttr({ stickyRadius: v }); }, __nextHasNoMarginBottom: true })
138 ),
139
140 // Colors
141 el( PanelBody, { title: 'Typography', initialOpen: false },
142 getTypoControl() && getTypoControl()({ label: 'Title', value: attr.titleTypo, onChange: function (v) { setAttr({ titleTypo: v }); } }),
143 getTypoControl() && getTypoControl()({ label: 'Subtitle', value: attr.subtitleTypo, onChange: function (v) { setAttr({ subtitleTypo: v }); } })
144 ),
145 el(PanelColorSettings, {
146 title: 'Colors',
147 initialOpen: false,
148 colorSettings: [
149 { label: 'Overlay Background', value: attr.overlayBg, onChange: function(v){ setAttr({ overlayBg: v || 'rgba(0,0,0,0.35)' }); } },
150 { label: 'Overlay Text/Icon', value: attr.overlayColor, onChange: function(v){ setAttr({ overlayColor: v || '#ffffff' }); } },
151 { label: 'Sticky Bar BG', value: attr.stickyBg, onChange: function(v){ setAttr({ stickyBg: v || '#1e293b' }); } },
152 { label: 'Section Background', value: attr.sectionBg, onChange: function(v){ setAttr({ sectionBg: v || '' }); } }
153 ]
154 })
155 ),
156
157 // Editor preview
158 el('div', { className: 'bkbg-sv-preview', style: { borderRadius: attr.borderRadius + 'px', overflow: 'hidden', position: 'relative' } },
159 el('div', { style: { position: 'relative', paddingBottom: padBottom, background: '#0f172a', borderRadius: attr.borderRadius + 'px', overflow: 'hidden' } },
160 attr.posterUrl
161 ? el('img', { src: attr.posterUrl, style: { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', opacity: 0.7 } })
162 : el('div', { style: { position: 'absolute', inset: 0, background: 'linear-gradient(135deg,#1e293b,#334155)', display: 'flex', alignItems: 'center', justifyContent: 'center' } },
163 el('div', { style: { textAlign: 'center', color: '#94a3b8' } },
164 el('div', { style: { fontSize: 48, marginBottom: 8 } }, ''),
165 el('div', { style: { fontSize: 13 } }, attr.videoUrl ? attr.videoType.toUpperCase() + ' video set' : 'Enter a video URL in settings')
166 )
167 ),
168 attr.showTitle && attr.title && el('div', { style: { position: 'absolute', bottom: 0, left: 0, right: 0, padding: '20px 16px 12px', background: 'linear-gradient(to top,rgba(0,0,0,0.7) 0%,transparent 100%)', color: '#fff' } },
169 el('div', { className: 'bkbg-sv-title-text' }, attr.title),
170 attr.subtitle && el('div', { className: 'bkbg-sv-subtitle-text', style: { marginTop: 3 } }, attr.subtitle)
171 ),
172 attr.stickyEnabled && el('div', { style: { position: 'absolute', top: 10, right: 12, background: 'rgba(99,102,241,0.85)', color: '#fff', fontSize: 10, padding: '3px 8px', borderRadius: 20, fontWeight: 600 } }, '📌 Sticky enabled')
173 )
174 )
175 );
176 },
177
178 save: function (props) {
179 var attr = props.attributes;
180 var blockProps = useBlockProps.save();
181 return el('div', blockProps,
182 el('div', {
183 className: 'bkbg-sv-app',
184 'data-opts': JSON.stringify(attr)
185 })
186 );
187 }
188 });
189 }() );
190