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

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

374 lines 27.4 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 MediaUpload = wp.blockEditor.MediaUpload;
9 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
10 var RichText = wp.blockEditor.RichText;
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 TextControl = wp.components.TextControl;
17 var ColorPicker = wp.components.ColorPicker;
18 var Popover = wp.components.Popover;
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, 'sectionTitleTypo', attrs, '--bkvg-stt-');
38 _tvf(v, 'cardTitleTypo', attrs, '--bkvg-ct-');
39 _tvf(v, 'descTypo', attrs, '--bkvg-ds-');
40 return v;
41 }
42
43 var COLUMNS_OPTIONS = [
44 { label: '1', value: 1 },
45 { label: '2', value: 2 },
46 { label: '3', value: 3 },
47 { label: '4', value: 4 },
48 ];
49
50 var ASPECT_OPTIONS = [
51 { label: '16:9', value: '16/9' },
52 { label: '4:3', value: '4/3' },
53 { label: '1:1', value: '1/1' },
54 ];
55
56 function makeId() { return 'vg' + Math.random().toString(36).substr(2, 5); }
57
58 /* Extract YouTube video ID from any YouTube URL */
59 function getYoutubeId(url) {
60 if (!url) return null;
61 var match = url.match(/(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))([\w-]{11})/);
62 return match ? match[1] : null;
63 }
64
65 function getThumbUrl(video) {
66 if (video.thumbUrl) return video.thumbUrl;
67 var ytId = getYoutubeId(video.url);
68 if (ytId) return 'https://img.youtube.com/vi/' + ytId + '/mqdefault.jpg';
69 return '';
70 }
71
72 function buildWrapStyle(a) {
73 var s = getTypoCssVars(a);
74 s['--bkbg-vg-accent'] = a.accentColor;
75 s['--bkbg-vg-play-btn-bg'] = a.playBtnBg;
76 s['--bkbg-vg-play-btn-color'] = a.playBtnColor;
77 s['--bkbg-vg-card-bg'] = a.cardBg;
78 s['--bkbg-vg-card-border'] = a.cardBorder;
79 s['--bkbg-vg-dur-bg'] = a.durationBg;
80 s['--bkbg-vg-dur-color'] = a.durationColor;
81 s['--bkbg-vg-filter-active-bg'] = a.filterActiveBg;
82 s['--bkbg-vg-filter-active-c'] = a.filterActiveColor;
83 s['--bkbg-vg-filter-bg'] = a.filterInactiveBg;
84 s['--bkbg-vg-filter-c'] = a.filterInactiveColor;
85 s['--bkbg-vg-title-color'] = a.sectionTitleColor;
86 s['--bkbg-vg-card-title-c'] = a.titleColor;
87 s['--bkbg-vg-desc-color'] = a.descColor;
88 s['--bkbg-vg-cat-color'] = a.categoryColor;
89 s['--bkbg-vg-card-r'] = a.cardRadius + 'px';
90 s['--bkbg-vg-thumb-r'] = a.thumbRadius + 'px';
91 s['--bkbg-vg-play-size'] = a.playBtnSize + 'px';
92 s.paddingTop = a.paddingTop + 'px';
93 s.paddingBottom = a.paddingBottom + 'px';
94 s.backgroundColor = a.bgColor || undefined;
95 return s;
96 }
97
98 function renderColorControl(key, label, value, onChange, openKey, setOpenKey) {
99 var isOpen = openKey === key;
100 return el('div', { key: key, style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '4px 0', gap: '8px' } },
101 el('span', { style: { fontSize: '12px', color: '#1e1e1e', flex: 1, lineHeight: 1.4 } }, label),
102 el('div', { style: { position: 'relative', flexShrink: 0 } },
103 el('button', { type: 'button', onClick: function () { setOpenKey(isOpen ? null : key); }, style: { width: '28px', height: '28px', borderRadius: '4px', border: isOpen ? '2px solid #007cba' : '2px solid #ddd', cursor: 'pointer', padding: 0, background: value || '#ccc' } }),
104 isOpen && el(Popover, { position: 'bottom left', onClose: function () { setOpenKey(null); } },
105 el('div', { style: { padding: '8px' } },
106 el(ColorPicker, { color: value, enableAlpha: true, onChange: onChange })
107 )
108 )
109 )
110 );
111 }
112
113 /* ── Video Card preview ─────────────────────────────────────────── */
114 function VideoCard(props) {
115 var video = props.video;
116 var a = props.a;
117 var thumbUrl = getThumbUrl(video);
118 var ar = a.aspectRatio.replace('/', '/'); /* keep as-is for aspect-ratio CSS */
119
120 return el('div', { className: 'bkbg-vg-card', style: { background: a.cardBg, border: '1px solid ' + a.cardBorder, borderRadius: a.cardRadius + 'px', overflow: 'hidden' } },
121 /* Thumbnail */
122 el('div', { className: 'bkbg-vg-thumb-wrap', style: { position: 'relative', aspectRatio: a.aspectRatio, background: '#111', overflow: 'hidden', borderRadius: a.thumbRadius + 'px' } },
123 thumbUrl
124 ? el('img', { src: thumbUrl, alt: video.title, style: { width: '100%', height: '100%', objectFit: 'cover', display: 'block' } })
125 : el('div', { style: { width: '100%', height: '100%', background: '#374151', display: 'flex', alignItems: 'center', justifyContent: 'center' } },
126 el('span', { style: { fontSize: '28px' } }, '🎥')
127 ),
128 /* Play button */
129 el('div', { className: 'bkbg-vg-play-btn', style: { position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%,-50%)', width: a.playBtnSize + 'px', height: a.playBtnSize + 'px', borderRadius: '50%', background: a.playBtnBg, display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: '0 4px 20px rgba(0,0,0,0.3)' } },
130 el('span', { style: { color: a.playBtnColor, fontSize: a.playBtnSize * 0.4 + 'px', marginLeft: '3px' } }, '▶')
131 ),
132 /* Duration */
133 a.showDuration && video.duration && el('div', { className: 'bkbg-vg-duration', style: { position: 'absolute', bottom: '8px', right: '8px', background: a.durationBg, color: a.durationColor, fontSize: '11px', fontWeight: 700, padding: '2px 7px', borderRadius: '5px' } }, video.duration),
134 /* Category pill */
135 a.showCategory && video.category && el('div', { className: 'bkbg-vg-cat', style: { position: 'absolute', top: '8px', left: '8px', background: a.accentColor, color: '#fff', fontSize: '11px', fontWeight: 700, padding: '2px 8px', borderRadius: '99px' } }, video.category)
136 ),
137 /* Card body */
138 el('div', { className: 'bkbg-vg-card-body', style: { padding: '12px 14px' } },
139 el('p', { className: 'bkbg-vg-card-title', style: { margin: '0 0 4px', color: a.titleColor } }, video.title || __('Untitled', 'blockenberg')),
140 a.showDescription && video.description && el('p', { className: 'bkbg-vg-desc', style: { margin: 0, color: a.descColor } }, video.description)
141 )
142 );
143 }
144
145 registerBlockType('blockenberg/video-grid', {
146 title: __('Video Grid', 'blockenberg'),
147 icon: 'video-alt3',
148 category: 'bkbg-media',
149
150 edit: function (props) {
151 var a = props.attributes;
152 var setAttributes = props.setAttributes;
153
154 var openColorKeyState = useState(null);
155 var openColorKey = openColorKeyState[0];
156 var setOpenColorKey = openColorKeyState[1];
157
158 var editingVideoState = useState(null);
159 var editingVideo = editingVideoState[0];
160 var setEditingVideo = editingVideoState[1];
161
162 var blockProps = useBlockProps({ style: buildWrapStyle(a) });
163
164 function cc(key, label, attrKey) {
165 return renderColorControl(key, label, a[attrKey], function (val) {
166 var upd = {}; upd[attrKey] = val; setAttributes(upd);
167 }, openColorKey, setOpenColorKey);
168 }
169
170 function updateVideo(id, key, val) {
171 setAttributes({ videos: a.videos.map(function (v) {
172 if (v.id !== id) return v;
173 var u = Object.assign({}, v); u[key] = val; return u;
174 }) });
175 }
176
177 function removeVideo(id) {
178 setAttributes({ videos: a.videos.filter(function (v) { return v.id !== id; }) });
179 if (editingVideo === id) setEditingVideo(null);
180 }
181
182 function addVideo() {
183 var newId = makeId();
184 setAttributes({ videos: a.videos.concat([{ id: newId, title: __('New Video', 'blockenberg'), url: '', thumbUrl: '', thumbId: 0, duration: '', category: '', description: '' }]) });
185 setEditingVideo(newId);
186 }
187
188 /* Get unique categories for filter preview */
189 var categories = a.videos.reduce(function (acc, v) {
190 if (v.category && acc.indexOf(v.category) === -1) acc.push(v.category);
191 return acc;
192 }, []);
193
194 return el(Fragment, null,
195 el(InspectorControls, null,
196 el(PanelBody, { title: __('Videos', 'blockenberg'), initialOpen: true },
197 a.videos.map(function (video, idx) {
198 var isEditing = editingVideo === video.id;
199 return el('div', { key: video.id, style: { border: '1px solid #e0e0e0', borderRadius: '8px', marginBottom: '8px', overflow: 'hidden' } },
200 /* Collapsed header */
201 el('div', { onClick: function () { setEditingVideo(isEditing ? null : video.id); }, style: { display: 'flex', alignItems: 'center', gap: '8px', padding: '8px 10px', cursor: 'pointer', background: isEditing ? '#f0e9ff' : '#fafafa', borderBottom: isEditing ? '1px solid #e0e0e0' : 'none' } },
202 el('span', { style: { fontSize: '12px', fontWeight: 700, color: '#555', minWidth: '18px' } }, (idx + 1) + '.'),
203 el('span', { style: { flex: 1, fontSize: '12px', fontWeight: 600, color: '#333', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, video.title || __('Untitled', 'blockenberg')),
204 el('span', { style: { fontSize: '12px', color: '#6c3fb5', fontWeight: 700 } }, isEditing ? '▲' : '▼')
205 ),
206 isEditing && el('div', { style: { padding: '10px' } },
207 el(TextControl, { label: __('Title', 'blockenberg'), value: video.title, onChange: function (v) { updateVideo(video.id, 'title', v); } }),
208 el(TextControl, { label: __('Video URL (YouTube/Vimeo)', 'blockenberg'), value: video.url, onChange: function (v) { updateVideo(video.id, 'url', v); } }),
209 el(TextControl, { label: __('Duration', 'blockenberg'), value: video.duration, onChange: function (v) { updateVideo(video.id, 'duration', v); }, placeholder: '12:34' }),
210 el(TextControl, { label: __('Category', 'blockenberg'), value: video.category, onChange: function (v) { updateVideo(video.id, 'category', v); } }),
211 el(TextControl, { label: __('Short description', 'blockenberg'), value: video.description, onChange: function (v) { updateVideo(video.id, 'description', v); } }),
212 el('div', { style: { marginBottom: '8px' } },
213 el('p', { style: { fontSize: '12px', fontWeight: 600, color: '#1e1e1e', marginBottom: '4px' } }, __('Thumbnail', 'blockenberg')),
214 video.thumbUrl && el('img', { src: video.thumbUrl, alt: '', style: { width: '100%', borderRadius: '6px', marginBottom: '6px', display: 'block' } }),
215 !video.thumbUrl && el('p', { style: { fontSize: '11px', color: '#999', marginBottom: '6px' } }, __('Auto-generated from YouTube URL, or upload custom thumb.', 'blockenberg')),
216 el(MediaUploadCheck, null,
217 el(MediaUpload, { onSelect: function (media) { setAttributes({ videos: a.videos.map(function (v) { return v.id !== video.id ? v : Object.assign({}, v, { thumbUrl: media.url, thumbId: media.id }); }) }); }, allowedTypes: ['image'], value: video.thumbId,
218 render: function (renderProps) {
219 return el(Button, { variant: 'secondary', size: 'compact', onClick: renderProps.open }, video.thumbUrl ? __('Change thumbnail', 'blockenberg') : __('Upload thumbnail', 'blockenberg'));
220 }
221 })
222 ),
223 video.thumbUrl && el(Button, { variant: 'tertiary', isDestructive: true, size: 'compact', onClick: function () { updateVideo(video.id, 'thumbUrl', ''); updateVideo(video.id, 'thumbId', 0); }, style: { marginLeft: '6px' } }, __('Remove thumb', 'blockenberg'))
224 ),
225 el(Button, { isDestructive: true, variant: 'tertiary', size: 'compact', onClick: function () { removeVideo(video.id); } }, __('Remove video', 'blockenberg'))
226 )
227 );
228 }),
229 el(Button, { variant: 'secondary', onClick: addVideo, style: { width: '100%', justifyContent: 'center' } }, '+ ' + __('Add Video', 'blockenberg'))
230 ),
231 el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: false },
232 el('fieldset', { style: { border: 'none', padding: 0, margin: '0 0 12px' } },
233 el('legend', { style: { fontSize: '12px', fontWeight: 600, color: '#1e1e1e', marginBottom: '6px' } }, __('Columns', 'blockenberg')),
234 el('div', { style: { display: 'flex', gap: '6px' } },
235 COLUMNS_OPTIONS.map(function (opt) {
236 return el('button', { key: opt.value, type: 'button', onClick: function () { setAttributes({ columns: opt.value }); },
237 style: { flex: 1, padding: '6px', border: a.columns === opt.value ? '2px solid #6c3fb5' : '1px solid #ddd', borderRadius: '6px', background: a.columns === opt.value ? '#f0e9ff' : '#fff', cursor: 'pointer', fontWeight: 700, color: a.columns === opt.value ? '#6c3fb5' : '#555' }
238 }, opt.label);
239 })
240 )
241 ),
242 el('fieldset', { style: { border: 'none', padding: 0, margin: '0 0 12px' } },
243 el('legend', { style: { fontSize: '12px', fontWeight: 600, color: '#1e1e1e', marginBottom: '6px' } }, __('Aspect ratio', 'blockenberg')),
244 el('div', { style: { display: 'flex', gap: '6px' } },
245 ASPECT_OPTIONS.map(function (opt) {
246 return el('button', { key: opt.value, type: 'button', onClick: function () { setAttributes({ aspectRatio: opt.value }); },
247 style: { flex: 1, padding: '6px', border: a.aspectRatio === opt.value ? '2px solid #6c3fb5' : '1px solid #ddd', borderRadius: '6px', background: a.aspectRatio === opt.value ? '#f0e9ff' : '#fff', cursor: 'pointer', fontWeight: 700, color: a.aspectRatio === opt.value ? '#6c3fb5' : '#555' }
248 }, opt.label);
249 })
250 )
251 ),
252 el(RangeControl, { label: __('Gap (px)', 'blockenberg'), value: a.gap, min: 8, max: 48, onChange: function (v) { setAttributes({ gap: v }); } })
253 ),
254 el(PanelBody, { title: __('Display', 'blockenberg'), initialOpen: false },
255 el(ToggleControl, { label: __('Show category filter bar', 'blockenberg'), checked: a.showFilter, onChange: function (v) { setAttributes({ showFilter: v }); }, __nextHasNoMarginBottom: true }),
256 el(ToggleControl, { label: __('Show category pill on card', 'blockenberg'), checked: a.showCategory, onChange: function (v) { setAttributes({ showCategory: v }); }, __nextHasNoMarginBottom: true }),
257 el(ToggleControl, { label: __('Show duration badge', 'blockenberg'), checked: a.showDuration, onChange: function (v) { setAttributes({ showDuration: v }); }, __nextHasNoMarginBottom: true }),
258 el(ToggleControl, { label: __('Show description', 'blockenberg'), checked: a.showDescription, onChange: function (v) { setAttributes({ showDescription: v }); }, __nextHasNoMarginBottom: true })
259 ),
260 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
261 getTypoControl(__('Section Title', 'blockenberg'), 'sectionTitleTypo', a, setAttributes),
262 getTypoControl(__('Card Title', 'blockenberg'), 'cardTitleTypo', a, setAttributes),
263 getTypoControl(__('Description', 'blockenberg'), 'descTypo', a, setAttributes)
264 ),
265 el(PanelBody, { title: __('Appearance', 'blockenberg'), initialOpen: false },
266 el(RangeControl, { label: __('Card radius (px)', 'blockenberg'), value: a.cardRadius, min: 0, max: 24, onChange: function (v) { setAttributes({ cardRadius: v }); } }),
267 el(RangeControl, { label: __('Thumb radius (px)', 'blockenberg'), value: a.thumbRadius, min: 0, max: 24, onChange: function (v) { setAttributes({ thumbRadius: v }); } }),
268 el(RangeControl, { label: __('Play button size (px)','blockenberg'), value: a.playBtnSize, min: 32, max: 96, onChange: function (v) { setAttributes({ playBtnSize: v }); } })
269 ),
270 el(PanelBody, { title: __('Colors', 'blockenberg'), initialOpen: false },
271 cc('accentColor', __('Accent', 'blockenberg'), 'accentColor'),
272 cc('sectionTitleColor', __('Section title', 'blockenberg'), 'sectionTitleColor'),
273 cc('titleColor', __('Card title', 'blockenberg'), 'titleColor'),
274 cc('descColor', __('Description', 'blockenberg'), 'descColor'),
275 cc('categoryColor', __('Category text', 'blockenberg'), 'categoryColor'),
276 cc('cardBg', __('Card bg', 'blockenberg'), 'cardBg'),
277 cc('cardBorder', __('Card border', 'blockenberg'), 'cardBorder'),
278 cc('playBtnBg', __('Play btn bg', 'blockenberg'), 'playBtnBg'),
279 cc('playBtnColor', __('Play btn icon', 'blockenberg'), 'playBtnColor'),
280 cc('durationBg', __('Duration badge bg', 'blockenberg'), 'durationBg'),
281 cc('durationColor', __('Duration text', 'blockenberg'), 'durationColor'),
282 cc('filterActiveBg', __('Filter active bg', 'blockenberg'), 'filterActiveBg'),
283 cc('filterActiveColor', __('Filter active text','blockenberg'), 'filterActiveColor'),
284 cc('filterInactiveBg', __('Filter inactive bg','blockenberg'), 'filterInactiveBg'),
285 cc('filterInactiveColor', __('Filter inactive', 'blockenberg'), 'filterInactiveColor'),
286 cc('bgColor', __('Section bg', 'blockenberg'), 'bgColor')
287 ),
288 el(PanelBody, { title: __('Spacing', 'blockenberg'), initialOpen: false },
289 el(RangeControl, { label: __('Padding top', 'blockenberg'), value: a.paddingTop, min: 0, max: 200, onChange: function (v) { setAttributes({ paddingTop: v }); } }),
290 el(RangeControl, { label: __('Padding bottom', 'blockenberg'), value: a.paddingBottom, min: 0, max: 200, onChange: function (v) { setAttributes({ paddingBottom: v }); } })
291 )
292 ),
293
294 el('div', blockProps,
295 /* Section title */
296 (a.title || a.subtitle) && el('div', { className: 'bkbg-vg-section-hdr', style: { textAlign: 'center', marginBottom: '28px' } },
297 el(RichText, { tagName: 'h2', className: 'bkbg-vg-section-title', value: a.title, onChange: function (v) { setAttributes({ title: v }); }, placeholder: __('Grid title…', 'blockenberg'), style: { color: a.sectionTitleColor, margin: '0 0 8px' } }),
298 el(RichText, { tagName: 'p', className: 'bkbg-vg-section-sub', value: a.subtitle, onChange: function (v) { setAttributes({ subtitle: v }); }, placeholder: __('Subtitle…', 'blockenberg'), style: { color: a.descColor, margin: 0 } })
299 ),
300 /* Filter bar preview */
301 a.showFilter && categories.length > 0 && el('div', { className: 'bkbg-vg-filter-bar', style: { display: 'flex', flexWrap: 'wrap', gap: '8px', marginBottom: '24px' } },
302 el('button', { type: 'button', className: 'bkbg-vg-filter-btn is-active', style: { padding: '7px 18px', borderRadius: '99px', border: 'none', background: a.filterActiveBg, color: a.filterActiveColor, fontSize: '13px', fontWeight: 700, cursor: 'pointer' } }, __('All', 'blockenberg')),
303 categories.map(function (cat) {
304 return el('button', { key: cat, type: 'button', className: 'bkbg-vg-filter-btn', style: { padding: '7px 18px', borderRadius: '99px', border: 'none', background: a.filterInactiveBg, color: a.filterInactiveColor, fontSize: '13px', fontWeight: 600, cursor: 'pointer' } }, cat);
305 })
306 ),
307 /* Grid */
308 el('div', { className: 'bkbg-vg-grid', style: { display: 'grid', gridTemplateColumns: 'repeat(' + a.columns + ', 1fr)', gap: a.gap + 'px' } },
309 a.videos.map(function (video) {
310 return el(VideoCard, { key: video.id, video: video, a: a });
311 }),
312 a.videos.length === 0 && el('div', { style: { gridColumn: '1/-1', textAlign: 'center', padding: '48px', color: '#aaa', background: '#fafafa', borderRadius: '8px', border: '2px dashed #ddd' } },
313 el('span', { style: { fontSize: '32px', display: 'block', marginBottom: '10px' } }, '🎬'),
314 el('p', { style: { margin: 0, fontWeight: 700 } }, __('Add your first video →', 'blockenberg'))
315 )
316 )
317 )
318 );
319 },
320
321 save: function (props) {
322 var a = props.attributes;
323 var categories = a.videos.reduce(function (acc, v) {
324 if (v.category && acc.indexOf(v.category) === -1) acc.push(v.category);
325 return acc;
326 }, []);
327
328 return el('div', wp.blockEditor.useBlockProps.save({ className: 'bkbg-vg-wrapper', style: buildWrapStyle(a) }),
329 (a.title || a.subtitle) && el('div', { className: 'bkbg-vg-section-hdr' },
330 el(RichText.Content, { tagName: 'h2', className: 'bkbg-vg-section-title', value: a.title }),
331 el(RichText.Content, { tagName: 'p', className: 'bkbg-vg-section-sub', value: a.subtitle })
332 ),
333 a.showFilter && categories.length > 0 && el('div', { className: 'bkbg-vg-filter-bar' },
334 el('button', { type: 'button', className: 'bkbg-vg-filter-btn is-active', 'data-category': '' }, __('All', 'blockenberg')),
335 categories.map(function (cat) {
336 return el('button', { key: cat, type: 'button', className: 'bkbg-vg-filter-btn', 'data-category': cat }, cat);
337 })
338 ),
339 el('div', { className: 'bkbg-vg-grid', style: { '--bkbg-vg-cols': a.columns, gap: a.gap + 'px' } },
340 a.videos.map(function (video) {
341 var thumbUrl = getThumbUrl(video);
342 var ytId = getYoutubeId(video.url);
343 var embedUrl = ytId ? 'https://www.youtube.com/embed/' + ytId + '?autoplay=1' : video.url;
344 return el('div', { key: video.id, className: 'bkbg-vg-card', 'data-embed-url': embedUrl, 'data-category': video.category || '' },
345 el('div', { className: 'bkbg-vg-thumb-wrap' },
346 thumbUrl
347 ? el('img', { src: thumbUrl, alt: video.title, loading: 'lazy', className: 'bkbg-vg-thumb' })
348 : el('div', { className: 'bkbg-vg-thumb-placeholder' }),
349 el('button', { type: 'button', className: 'bkbg-vg-play-btn', 'aria-label': __('Play video', 'blockenberg') },
350 el('span', { className: 'bkbg-vg-play-icon', 'aria-hidden': 'true' }, '▶')
351 ),
352 a.showDuration && video.duration && el('div', { className: 'bkbg-vg-duration' }, video.duration),
353 a.showCategory && video.category && el('div', { className: 'bkbg-vg-cat-pill' }, video.category)
354 ),
355 el('div', { className: 'bkbg-vg-card-body' },
356 el('p', { className: 'bkbg-vg-card-title' }, video.title),
357 a.showDescription && video.description && el('p', { className: 'bkbg-vg-desc' }, video.description)
358 )
359 );
360 })
361 ),
362 /* Lightbox modal */
363 el('div', { className: 'bkbg-vg-modal', role: 'dialog', 'aria-modal': 'true', 'aria-label': __('Video player', 'blockenberg'), hidden: true },
364 el('div', { className: 'bkbg-vg-modal-overlay' }),
365 el('div', { className: 'bkbg-vg-modal-content' },
366 el('button', { type: 'button', className: 'bkbg-vg-modal-close', 'aria-label': __('Close', 'blockenberg') }, '✕'),
367 el('div', { className: 'bkbg-vg-modal-iframe-wrap' })
368 )
369 )
370 );
371 }
372 });
373 }() );
374