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

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

414 lines 27.2 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 useBlockProps = wp.blockEditor.useBlockProps;
11 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
12 var PanelBody = wp.components.PanelBody;
13 var ToggleControl = wp.components.ToggleControl;
14 var RangeControl = wp.components.RangeControl;
15 var SelectControl = wp.components.SelectControl;
16 var TextControl = wp.components.TextControl;
17 var Button = wp.components.Button;
18
19 var _fvtbTC, _fvtbTV;
20 function _tc() { return (_fvtbTC = _fvtbTC || window.bkbgTypographyControl); }
21 function _tv() { return (_fvtbTV = _fvtbTV || window.bkbgTypoCssVars); }
22 var IP = function () { return window.bkbgIconPicker; };
23
24 function updateItem(arr, idx, field, val) {
25 return arr.map(function (item, i) {
26 if (i !== idx) return item;
27 var p = {}; p[field] = val;
28 return Object.assign({}, item, p);
29 });
30 }
31
32 function moveItem(arr, from, to) {
33 var a = arr.slice();
34 var item = a.splice(from, 1)[0];
35 a.splice(to, 0, item);
36 return a;
37 }
38
39 /* ── TabEditor ── */
40 function TabEditor(props) {
41 var tabs = props.tabs;
42 var onChange = props.onChange;
43 var activeIdx = props.activeIdx;
44 var setActiveIdx = props.setActiveIdx;
45 var a = props.attributes;
46
47 function addTab() {
48 onChange(tabs.concat([{
49 label: 'New Feature', icon: '', iconType: 'custom-char', iconDashicon: '', iconImageUrl: '',
50 headline: 'Feature Headline', description: 'Describe this feature.',
51 bullets: ['Benefit one', 'Benefit two', 'Benefit three'],
52 ctaLabel: 'Learn More', ctaUrl: '#',
53 videoUrl: '', videoPoster: '', fallbackImageUrl: '', fallbackImageId: 0
54 }]));
55 setActiveIdx(tabs.length);
56 }
57
58 return el(Fragment, null,
59 tabs.map(function (tab, idx) {
60 var isOpen = idx === activeIdx;
61 return el('div', {
62 key: idx,
63 style: { border: '1px solid ' + (isOpen ? '#6366f1' : '#e5e7eb'), borderRadius: '6px', marginBottom: '6px', overflow: 'hidden' }
64 },
65 /* Accordion header */
66 el('div', {
67 style: { display: 'flex', alignItems: 'center', gap: '6px', padding: '7px 10px', background: isOpen ? '#f0f0ff' : '#f9fafb', cursor: 'pointer' },
68 onClick: function () { setActiveIdx(isOpen ? -1 : idx); }
69 },
70 el('span', { style: { fontSize: '14px' } }, (tab.iconType || 'custom-char') !== 'custom-char' && IP() ? IP().buildEditorIcon(el, tab.iconType, tab.icon, tab.iconDashicon, tab.iconImageUrl, tab.iconDashiconColor) : (tab.icon || '')),
71 el('span', { style: { flex: 1, fontSize: '12px', fontWeight: '600', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, tab.label || 'Tab ' + (idx + 1)),
72 el(Button, { icon: 'arrow-up-alt2', isSmall: true, disabled: idx === 0, onClick: function (e) { e.stopPropagation(); onChange(moveItem(tabs, idx, idx - 1)); setActiveIdx(idx - 1); } }),
73 el(Button, { icon: 'arrow-down-alt2', isSmall: true, disabled: idx === tabs.length - 1, onClick: function (e) { e.stopPropagation(); onChange(moveItem(tabs, idx, idx + 1)); setActiveIdx(idx + 1); } }),
74 el(Button, { icon: 'no-alt', isSmall: true, isDestructive: true, onClick: function (e) { e.stopPropagation(); var arr = tabs.slice(); arr.splice(idx, 1); onChange(arr); setActiveIdx(-1); } })
75 ),
76
77 isOpen && el('div', { style: { padding: '10px', display: 'flex', flexDirection: 'column', gap: '8px' } },
78 el(TextControl, { label: __('Tab Label', 'blockenberg'), value: tab.label || '', onChange: function (v) { onChange(updateItem(tabs, idx, 'label', v)); }, __nextHasNoMarginBottom: true }),
79 IP() ? el(IP().IconPickerControl, {
80 iconType: tab.iconType || 'custom-char',
81 customChar: tab.icon || '',
82 dashicon: tab.iconDashicon || '',
83 imageUrl: tab.iconImageUrl || '',
84 onChangeType: function (v) { onChange(updateItem(tabs, idx, 'iconType', v)); },
85 onChangeChar: function (v) { onChange(updateItem(tabs, idx, 'icon', v)); },
86 onChangeDashicon: function (v) { onChange(updateItem(tabs, idx, 'iconDashicon', v)); },
87 onChangeImageUrl: function (v) { onChange(updateItem(tabs, idx, 'iconImageUrl', v)); },
88 label: __('Icon', 'blockenberg')
89 }) : el(TextControl, { label: __('Icon Emoji', 'blockenberg'), value: tab.icon || '', onChange: function (v) { onChange(updateItem(tabs, idx, 'icon', v)); }, __nextHasNoMarginBottom: true }),
90 el(TextControl, { label: __('Headline', 'blockenberg'), value: tab.headline || '', onChange: function (v) { onChange(updateItem(tabs, idx, 'headline', v)); }, __nextHasNoMarginBottom: true }),
91 el('div', null,
92 el('label', { style: { fontSize: '11px', fontWeight: '600', display: 'block', marginBottom: '6px' } }, __('Description', 'blockenberg')),
93 el('textarea', {
94 value: tab.description || '',
95 onChange: function (e) { onChange(updateItem(tabs, idx, 'description', e.target.value)); },
96 style: { width: '100%', minHeight: '70px', fontSize: '12px', padding: '6px', border: '1px solid #e5e7eb', borderRadius: '4px', boxSizing: 'border-box', resize: 'vertical' }
97 })
98 ),
99 el('div', null,
100 el('label', { style: { fontSize: '11px', fontWeight: '600', display: 'block', marginBottom: '6px' } }, __('Bullet Points (one per line)', 'blockenberg')),
101 el('textarea', {
102 value: (tab.bullets || []).join('\n'),
103 onChange: function (e) { onChange(updateItem(tabs, idx, 'bullets', e.target.value.split('\n'))); },
104 style: { width: '100%', minHeight: '60px', fontSize: '12px', padding: '6px', border: '1px solid #e5e7eb', borderRadius: '4px', boxSizing: 'border-box', resize: 'vertical' }
105 })
106 ),
107 el(TextControl, { label: __('CTA Button Label', 'blockenberg'), value: tab.ctaLabel || '', onChange: function (v) { onChange(updateItem(tabs, idx, 'ctaLabel', v)); }, __nextHasNoMarginBottom: true }),
108 el(TextControl, { label: __('CTA URL', 'blockenberg'), value: tab.ctaUrl || '', onChange: function (v) { onChange(updateItem(tabs, idx, 'ctaUrl', v)); }, __nextHasNoMarginBottom: true }),
109 el('div', { style: { borderTop: '1px solid #e5e7eb', paddingTop: '8px', marginTop: '4px' } },
110 el(TextControl, { label: __('Video URL (mp4, webm)', 'blockenberg'), value: tab.videoUrl || '', onChange: function (v) { onChange(updateItem(tabs, idx, 'videoUrl', v)); }, __nextHasNoMarginBottom: true })
111 ),
112 el(TextControl, { label: __('Video Poster Image URL', 'blockenberg'), value: tab.videoPoster || '', onChange: function (v) { onChange(updateItem(tabs, idx, 'videoPoster', v)); }, __nextHasNoMarginBottom: true }),
113 /* Fallback image */
114 el('div', null,
115 el('label', { style: { fontSize: '11px', fontWeight: '600', display: 'block', marginBottom: '6px' } }, __('Fallback Image (if no video)', 'blockenberg')),
116 el(MediaUploadCheck, null,
117 el(MediaUpload, {
118 onSelect: function (media) {
119 onChange(tabs.map(function (item, i) {
120 if (i !== idx) return item;
121 return Object.assign({}, item, { fallbackImageUrl: media.url, fallbackImageId: media.id });
122 }));
123 },
124 allowedTypes: ['image'],
125 value: tab.fallbackImageId,
126 render: function (renderProps) {
127 return el('div', { style: { display: 'flex', flexDirection: 'column', gap: '6px' } },
128 tab.fallbackImageUrl && el('img', { src: tab.fallbackImageUrl, alt: '', style: { maxWidth: '100%', borderRadius: '4px', maxHeight: '80px', objectFit: 'cover' } }),
129 el(Button, { variant: 'secondary', isSmall: true, onClick: renderProps.open }, tab.fallbackImageUrl ? __('Replace', 'blockenberg') : __('Select Image', 'blockenberg')),
130 tab.fallbackImageUrl && el(Button, { isSmall: true, isDestructive: true, onClick: function () {
131 onChange(tabs.map(function (item, i) {
132 if (i !== idx) return item;
133 return Object.assign({}, item, { fallbackImageUrl: '', fallbackImageId: 0 });
134 }));
135 } }, __('Remove', 'blockenberg'))
136 );
137 }
138 })
139 )
140 )
141 )
142 );
143 }),
144 el(Button, { variant: 'secondary', onClick: addTab, style: { marginTop: '6px', width: '100%', justifyContent: 'center' } }, __('+ Add Feature Tab', 'blockenberg'))
145 );
146 }
147
148 /* ── Live preview ── */
149 function FVTPreview(props) {
150 var a = props.attributes;
151 var activeTab = props.activeTab;
152 var setActiveTab = props.setActiveTab;
153 var tabs = a.tabs || [];
154 if (!tabs.length) return el('div', { style: { padding: '24px', textAlign: 'center', color: '#9ca3af' } }, 'Add feature tabs in the sidebar');
155
156 var tab = tabs[Math.min(activeTab, tabs.length - 1)];
157 var isRightVideo = a.layout !== 'left-video';
158 var tabAreaPct = (a.tabAreaWidth || 40) + '%';
159 var videoPct = (100 - (a.tabAreaWidth || 40)) + '%';
160
161 /* Aspect ratio: "16/9" → paddingBottom = 9/16*100 */
162 var aspParts = (a.aspect || '16/9').split('/');
163 var aspPB = (+aspParts[1] / +aspParts[0] * 100).toFixed(2) + '%';
164
165 /* Tab buttons */
166 var tabButtons = el('div', { style: { display: 'flex', flexDirection: 'column', gap: '6px' } },
167 tabs.map(function (t, idx) {
168 var isActive = idx === activeTab;
169 var bg = isActive ? (a.activeTabBg || '#6366f1') : (a.idleTabBg || 'transparent');
170 var color = isActive ? (a.activeTabColor || '#fff') : (a.idleTabColor || '#374151');
171 return el('button', {
172 key: idx,
173 className: 'bkbg-fvtb-tab' + (isActive ? ' is-active' : ''),
174 onClick: function () { setActiveTab(idx); },
175 style: {
176 display: 'flex', alignItems: 'center', gap: '10px',
177 padding: a.tabStyle === 'pills' ? '10px 16px' : (a.tabStyle === 'cards' ? '14px 16px' : '12px 0'),
178 borderRadius: a.tabStyle === 'pills' ? '50px' : (a.tabStyle === 'cards' ? '10px' : '0'),
179 background: bg, color: color,
180 border: a.tabStyle === 'lines' ? ('0 0 0 0 solid transparent') : 'none',
181 borderLeft: a.tabStyle === 'lines' ? ('3px solid ' + (isActive ? (a.activeTabBg || '#6366f1') : 'transparent')) : undefined,
182 cursor: 'pointer',
183 textAlign: 'left',
184 transition: 'all 0.15s',
185 boxShadow: (a.tabStyle === 'cards' && isActive) ? '0 2px 8px rgba(0,0,0,0.1)' : 'none'
186 }
187 },
188 a.showIcons && el('span', { className: 'bkbg-fvtb-tab-icon', style: { fontSize: (a.iconSize || 24) + 'px', lineHeight: 1, flexShrink: 0 } }, (t.iconType || 'custom-char') !== 'custom-char' && IP() ? IP().buildEditorIcon(el, t.iconType, t.icon, t.iconDashicon, t.iconImageUrl, t.iconDashiconColor) : (t.icon || '')),
189 el('span', { className: 'bkbg-fvtb-tab-label' }, t.label || ''));
190 })
191 );
192
193 /* Content panel */
194 var content = el('div', { style: { flex: 1, display: 'flex', flexDirection: 'column', gap: '14px', justifyContent: 'center' } },
195 el('h3', { className: 'bkbg-fvtb-headline', style: { margin: 0, color: a.headlineColor || '#111827' } }, tab.headline || ''),
196 el('p', { className: 'bkbg-fvtb-desc', style: { margin: 0, color: a.descColor || '#4b5563' } }, tab.description || ''),
197 a.showBullets && (tab.bullets || []).length > 0 && el('ul', { className: 'bkbg-fvtb-bullets', style: { margin: 0, padding: 0, listStyle: 'none', display: 'flex', flexDirection: 'column', gap: '8px' } },
198 (tab.bullets || []).filter(Boolean).map(function (b, bi) {
199 return el('li', { key: bi, className: 'bkbg-fvtb-bullet', style: { display: 'flex', alignItems: 'flex-start', gap: '8px', color: a.bulletColor || '#374151' } },
200 el('span', { className: 'bkbg-fvtb-bullet-dot', style: { color: a.bulletDot || '#6366f1', flexShrink: 0 } }, ''),
201 el('span', { className: 'bkbg-fvtb-bullet-text' }, b)
202 );
203 })
204 ),
205 a.showCta && tab.ctaLabel && el('a', {
206 className: 'bkbg-fvtb-cta',
207 href: tab.ctaUrl || '#',
208 onClick: function (e) { e.preventDefault(); },
209 style: {
210 display: 'inline-flex', alignItems: 'center',
211 padding: '12px 24px',
212 background: a.ctaBg || '#6366f1', color: a.ctaColor || '#fff',
213 borderRadius: '8px', textDecoration: 'none'
214 }
215 }, tab.ctaLabel)
216 );
217
218 /* Video area */
219 var videoArea = el('div', {
220 style: {
221 position: 'relative', paddingBottom: aspPB,
222 height: 0,
223 background: a.videoBg || '#000',
224 borderRadius: (a.videoRadius || 12) + 'px',
225 overflow: 'hidden',
226 boxShadow: a.videoShadow ? '0 20px 40px rgba(0,0,0,0.15)' : 'none'
227 }
228 },
229 (tab.fallbackImageUrl || tab.videoUrl) ? el('img', {
230 src: tab.fallbackImageUrl || tab.videoPoster || '',
231 alt: tab.headline || '',
232 style: { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }
233 }) : el('div', {
234 style: {
235 position: 'absolute', inset: 0,
236 display: 'flex', alignItems: 'center', justifyContent: 'center',
237 background: 'linear-gradient(135deg, #1e1e2e 0%, #2d2d3f 100%)',
238 color: '#aaa', flexDirection: 'column', gap: '8px'
239 }
240 },
241 el('div', { style: { fontSize: '40px', opacity: 0.5 } }, ''),
242 el('p', { style: { margin: 0, fontSize: '12px', opacity: 0.5 } }, 'Add a video URL above')
243 )
244 );
245
246 /* Left panel: tabs list */
247 var leftPanel = el('div', { style: { width: tabAreaPct, flexShrink: 0, display: 'flex', flexDirection: 'column', gap: '20px', justifyContent: 'center' } },
248 tabButtons,
249 content
250 );
251
252 /* Right panel: video */
253 var rightPanel = el('div', { style: { flex: 1, minWidth: 0 } }, videoArea);
254
255 var wrapStyle = Object.assign({
256 paddingTop: (a.paddingTop || 60) + 'px',
257 paddingBottom: (a.paddingBottom || 60) + 'px',
258 background: a.bgColor || '',
259 display: 'flex',
260 gap: (a.gap || 48) + 'px',
261 alignItems: 'center',
262 flexWrap: 'wrap'
263 },
264 _tv()(a.typoHeadline, '--bkbg-fvtb-th-'),
265 _tv()(a.typoDesc, '--bkbg-fvtb-td-'),
266 _tv()(a.typoTabLabel, '--bkbg-fvtb-tl-'),
267 _tv()(a.typoBullet, '--bkbg-fvtb-tb-')
268 );
269
270 return el('div', { className: 'bkbg-fvtb-wrap', style: wrapStyle },
271 isRightVideo ? el(Fragment, null, leftPanel, rightPanel) : el(Fragment, null, rightPanel, leftPanel)
272 );
273 }
274
275 registerBlockType('blockenberg/feature-video-tabs', {
276 edit: function (props) {
277 var a = props.attributes;
278 var set = props.setAttributes;
279 var tabIdxState = useState(-1);
280 var tabIdx = tabIdxState[0];
281 var setTabIdx = tabIdxState[1];
282 var activeTabState = useState(0);
283 var activeTab = activeTabState[0];
284 var setActiveTab = activeTabState[1];
285
286 var blockProps = useBlockProps();
287
288 var inspector = el(InspectorControls, null,
289 el(PanelBody, { title: __('Feature Tabs', 'blockenberg'), initialOpen: true },
290 el(TabEditor, {
291 tabs: a.tabs,
292 onChange: function (v) { set({ tabs: v }); },
293 activeIdx: tabIdx,
294 setActiveIdx: setTabIdx,
295 attributes: a
296 })
297 ),
298
299 el(PanelBody, { title: __('Layout & Video', 'blockenberg'), initialOpen: false },
300 el(SelectControl, {
301 label: __('Video Position', 'blockenberg'),
302 value: a.layout,
303 options: [
304 { value: 'right-video', label: 'Video on Right' },
305 { value: 'left-video', label: 'Video on Left' }
306 ],
307 onChange: function (v) { set({ layout: v }); },
308 __nextHasNoMarginBottom: true
309 }),
310 el('div', { style: { marginTop: '8px' } },
311 el(SelectControl, {
312 label: __('Tab Style', 'blockenberg'),
313 value: a.tabStyle,
314 options: [
315 { value: 'pills', label: 'Pills' },
316 { value: 'lines', label: 'Left Border Lines' },
317 { value: 'cards', label: 'Cards' }
318 ],
319 onChange: function (v) { set({ tabStyle: v }); },
320 __nextHasNoMarginBottom: true
321 })
322 ),
323 el('div', { style: { marginTop: '8px' } },
324 el(RangeControl, { label: __('Tab Area Width %', 'blockenberg'), value: a.tabAreaWidth, min: 25, max: 65, onChange: function (v) { set({ tabAreaWidth: v }); }, __nextHasNoMarginBottom: true })
325 ),
326 el('div', { style: { marginTop: '8px' } },
327 el(RangeControl, { label: __('Column Gap (px)', 'blockenberg'), value: a.gap, min: 16, max: 120, onChange: function (v) { set({ gap: v }); }, __nextHasNoMarginBottom: true })
328 ),
329 el('div', { style: { marginTop: '8px' } },
330 el(SelectControl, {
331 label: __('Video Aspect Ratio', 'blockenberg'),
332 value: a.aspect,
333 options: [
334 { value: '16/9', label: '16:9 (Widescreen)' },
335 { value: '4/3', label: '4:3' },
336 { value: '3/2', label: '3:2' },
337 { value: '1/1', label: '1:1 (Square)' }
338 ],
339 onChange: function (v) { set({ aspect: v }); },
340 __nextHasNoMarginBottom: true
341 })
342 ),
343 el(ToggleControl, { label: __('Video Autoplay (muted)', 'blockenberg'), checked: a.videoAutoPlay, onChange: function (v) { set({ videoAutoPlay: v }); }, __nextHasNoMarginBottom: true }),
344 el(ToggleControl, { label: __('Loop Video', 'blockenberg'), checked: a.videoLoop, onChange: function (v) { set({ videoLoop: v }); }, __nextHasNoMarginBottom: true }),
345 el('div', { style: { marginTop: '8px' } },
346 el(RangeControl, { label: __('Video Corner Radius', 'blockenberg'), value: a.videoRadius, min: 0, max: 32, onChange: function (v) { set({ videoRadius: v }); }, __nextHasNoMarginBottom: true })
347 ),
348 el(ToggleControl, { label: __('Video Shadow', 'blockenberg'), checked: a.videoShadow, onChange: function (v) { set({ videoShadow: v }); }, __nextHasNoMarginBottom: true })
349 ),
350
351 el(PanelBody, { title: __('Display', 'blockenberg'), initialOpen: false },
352 el(ToggleControl, { label: __('Show Icons', 'blockenberg'), checked: a.showIcons, onChange: function (v) { set({ showIcons: v }); }, __nextHasNoMarginBottom: true }),
353 el(ToggleControl, { label: __('Show Bullet Points', 'blockenberg'), checked: a.showBullets, onChange: function (v) { set({ showBullets: v }); }, __nextHasNoMarginBottom: true }),
354 el(ToggleControl, { label: __('Show CTA Button', 'blockenberg'), checked: a.showCta, onChange: function (v) { set({ showCta: v }); }, __nextHasNoMarginBottom: true }),
355 el(ToggleControl, { label: __('Auto-Advance Tabs', 'blockenberg'), checked: a.autoAdvance, onChange: function (v) { set({ autoAdvance: v }); }, __nextHasNoMarginBottom: true }),
356 a.autoAdvance && el('div', { style: { marginTop: '8px' } },
357 el(RangeControl, { label: __('Auto-Advance Delay (ms)', 'blockenberg'), value: a.autoAdvanceDelay, min: 1000, max: 15000, step: 500, onChange: function (v) { set({ autoAdvanceDelay: v }); }, __nextHasNoMarginBottom: true })
358 )
359 ),
360
361 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
362 _tc() && el(_tc(), { label: __('Headline', 'blockenberg'), value: a.typoHeadline, onChange: function (v) { set({ typoHeadline: v }); } }),
363 _tc() && el(_tc(), { label: __('Description', 'blockenberg'), value: a.typoDesc, onChange: function (v) { set({ typoDesc: v }); } }),
364 _tc() && el(_tc(), { label: __('Bullet Text', 'blockenberg'), value: a.typoBullet, onChange: function (v) { set({ typoBullet: v }); } }),
365 _tc() && el(_tc(), { label: __('Tab Label', 'blockenberg'), value: a.typoTabLabel, onChange: function (v) { set({ typoTabLabel: v }); } }),
366 el('div', { style: { marginTop: '8px' } },
367 el(RangeControl, { label: __('Icon Size (px)', 'blockenberg'), value: a.iconSize, min: 14, max: 48, onChange: function (v) { set({ iconSize: v }); }, __nextHasNoMarginBottom: true })
368 )
369 ),
370 el(PanelBody, { title: __('Spacing', 'blockenberg'), initialOpen: false },
371 el('div', { style: { marginTop: '8px' } },
372 el(RangeControl, { label: __('Padding Top (px)', 'blockenberg'), value: a.paddingTop, min: 0, max: 200, step: 4, onChange: function (v) { set({ paddingTop: v }); }, __nextHasNoMarginBottom: true })
373 ),
374 el('div', { style: { marginTop: '8px' } },
375 el(RangeControl, { label: __('Padding Bottom (px)', 'blockenberg'), value: a.paddingBottom, min: 0, max: 200, step: 4, onChange: function (v) { set({ paddingBottom: v }); }, __nextHasNoMarginBottom: true })
376 )
377 ),
378
379 el(PanelColorSettings, {
380 title: __('Colors', 'blockenberg'),
381 initialOpen: false,
382 colorSettings: [
383 { value: a.bgColor, onChange: function (v) { set({ bgColor: v }); }, label: __('Section Background', 'blockenberg') },
384 { value: a.activeTabBg, onChange: function (v) { set({ activeTabBg: v }); }, label: __('Active Tab Background', 'blockenberg') },
385 { value: a.activeTabColor, onChange: function (v) { set({ activeTabColor: v }); }, label: __('Active Tab Text', 'blockenberg') },
386 { value: a.idleTabBg, onChange: function (v) { set({ idleTabBg: v }); }, label: __('Idle Tab Background', 'blockenberg') },
387 { value: a.idleTabColor, onChange: function (v) { set({ idleTabColor: v }); }, label: __('Idle Tab Text', 'blockenberg') },
388 { value: a.headlineColor, onChange: function (v) { set({ headlineColor: v }); }, label: __('Headline Color', 'blockenberg') },
389 { value: a.descColor, onChange: function (v) { set({ descColor: v }); }, label: __('Description Color', 'blockenberg') },
390 { value: a.bulletColor, onChange: function (v) { set({ bulletColor: v }); }, label: __('Bullet Text Color', 'blockenberg') },
391 { value: a.bulletDot, onChange: function (v) { set({ bulletDot: v }); }, label: __('Bullet Check Color', 'blockenberg') },
392 { value: a.ctaBg, onChange: function (v) { set({ ctaBg: v }); }, label: __('CTA Button Background', 'blockenberg') },
393 { value: a.ctaColor, onChange: function (v) { set({ ctaColor: v }); }, label: __('CTA Button Text', 'blockenberg') },
394 { value: a.videoBg, onChange: function (v) { set({ videoBg: v }); }, label: __('Video Background', 'blockenberg') }
395 ]
396 })
397 );
398
399 return el(Fragment, null,
400 inspector,
401 el('div', blockProps,
402 el(FVTPreview, { attributes: a, activeTab: activeTab, setActiveTab: setActiveTab })
403 )
404 );
405 },
406
407 save: function (props) {
408 return el('div', useBlockProps.save(),
409 el('div', { className: 'bkbg-fvtb-app', 'data-opts': JSON.stringify(props.attributes) })
410 );
411 }
412 });
413 }() );
414