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

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

683 lines 35.0 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 RichText = wp.blockEditor.RichText;
12 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
13 var PanelBody = wp.components.PanelBody;
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 Button = wp.components.Button;
19 var ColorPicker = wp.components.ColorPicker;
20 var Popover = wp.components.Popover;
21
22 function getTypographyControl() { return (window.bkbgTypographyControl || function () { return null; }); }
23 function getTypoCssVars() { return (window.bkbgTypoCssVars || function () { return {}; }); }
24 function _tv(typo, prefix) { var fn = getTypoCssVars(); return fn(typo || {}, prefix); }
25
26 var CARD_LAYOUT_OPTIONS = [
27 { label: 'Stacked (Image top)', value: 'stacked' },
28 { label: 'Overlay (Image bg)', value: 'overlay' },
29 { label: 'Horizontal', value: 'horizontal' },
30 { label: 'Text only', value: 'text' },
31 ];
32
33 var ASPECT_OPTIONS = [
34 { label: '1:1 Square', value: '1-1' },
35 { label: '4:3', value: '4-3' },
36 { label: '3:2', value: '3-2' },
37 { label: '16:9', value: '16-9' },
38 { label: '21:9', value: '21-9' },
39 ];
40
41 var SHADOW_OPTIONS = [
42 { label: 'None', value: 'none' },
43 { label: 'Small', value: 'sm' },
44 { label: 'Medium', value: 'md' },
45 { label: 'Large', value: 'lg' },
46 ];
47
48 var LINK_STYLE_OPTIONS = [
49 { label: 'Arrow link →', value: 'arrow' },
50 { label: 'Button', value: 'button' },
51 { label: 'Underline', value: 'underline' },
52 ];
53
54 var ASPECT_PADDING = {
55 '1-1': '100%', '4-3': '75%', '3-2': '66.67%', '16-9': '56.25%', '21-9': '42.86%',
56 };
57
58 var SHADOWS = {
59 none: 'none',
60 sm: '0 1px 6px rgba(0,0,0,0.08)',
61 md: '0 4px 16px rgba(0,0,0,0.12)',
62 lg: '0 8px 32px rgba(0,0,0,0.16)',
63 };
64
65 function updateItem(items, idx, field, val) {
66 return items.map(function (it, i) {
67 if (i !== idx) return it;
68 var p = {}; p[field] = val;
69 return Object.assign({}, it, p);
70 });
71 }
72
73 function renderCardPreview(item, a, idx) {
74 var shadow = SHADOWS[a.cardShadow] || SHADOWS.sm;
75 var pt = ASPECT_PADDING[a.imageAspect] || '56.25%';
76
77 var cardStyle = {
78 background: a.cardLayout === 'overlay' ? 'transparent' : a.cardBg,
79 borderRadius: a.cardBorderRadius + 'px',
80 boxShadow: a.cardLayout === 'overlay' ? 'none' : shadow,
81 overflow: 'hidden',
82 display: 'flex',
83 flexDirection: a.cardLayout === 'horizontal' ? 'row' : 'column',
84 minHeight: a.cardMinHeight > 0 ? a.cardMinHeight + 'px' : 'auto',
85 position: 'relative',
86 flex: '0 0 calc(' + (100 / a.desktopCols) + '% - ' + (a.gap * (a.desktopCols - 1) / a.desktopCols) + 'px)',
87 };
88
89 var hasImage = item.imageUrl;
90 var accentColor = item.accentColor || a.eyebrowColor;
91
92 var imageSection = (a.cardLayout !== 'text' && hasImage) ? el('div', {
93 style: {
94 position: 'relative',
95 paddingTop: a.cardLayout === 'horizontal' ? '0' : pt,
96 height: a.cardLayout === 'horizontal' ? '100%' : '0',
97 width: a.cardLayout === 'horizontal' ? '40%' : '100%',
98 flexShrink: a.cardLayout === 'horizontal' ? 0 : 1,
99 overflow: 'hidden',
100 }
101 },
102 el('img', {
103 src: item.imageUrl,
104 alt: item.imageAlt || '',
105 style: {
106 position: a.cardLayout === 'horizontal' ? 'static' : 'absolute',
107 inset: '0',
108 width: '100%',
109 height: a.cardLayout === 'horizontal' ? '140px' : '100%',
110 objectFit: a.imageFit || 'cover',
111 display: 'block',
112 }
113 })
114 ) : (a.cardLayout !== 'text' && !hasImage && a.cardLayout !== 'overlay') ? el('div', {
115 style: {
116 paddingTop: a.cardLayout === 'horizontal' ? '0' : pt,
117 height: a.cardLayout === 'horizontal' ? '120px' : '0',
118 width: a.cardLayout === 'horizontal' ? '40%' : '100%',
119 background: '#f3f4f6',
120 display: 'flex',
121 alignItems: 'center',
122 justifyContent: 'center',
123 color: '#9ca3af',
124 fontSize: 12,
125 flexShrink: 0,
126 overflow: 'hidden',
127 }
128 }, a.cardLayout !== 'horizontal' ? null : '🖼') : null;
129
130 var bodyStyle = {
131 padding: a.cardPadding + 'px',
132 display: 'flex',
133 flexDirection: 'column',
134 gap: '8px',
135 flex: '1',
136 };
137
138 if (a.cardLayout === 'overlay' && hasImage) {
139 bodyStyle.position = 'absolute';
140 bodyStyle.inset = '0';
141 bodyStyle.background = 'linear-gradient(180deg, transparent 30%, rgba(0,0,0,0.75) 100%)';
142 bodyStyle.justifyContent = 'flex-end';
143 bodyStyle.borderRadius = a.cardBorderRadius + 'px';
144 }
145
146 var body = el('div', { className: 'bkbg-cc-body', style: bodyStyle },
147 a.showBadge && item.badge && el('span', {
148 style: {
149 display: 'inline-block',
150 fontSize: 11,
151 fontWeight: 700,
152 letterSpacing: '0.05em',
153 textTransform: 'uppercase',
154 padding: '3px 10px',
155 borderRadius: 999,
156 background: a.badgeBg,
157 color: a.badgeColor,
158 alignSelf: 'flex-start',
159 }
160 }, item.badge),
161 a.showEyebrow && item.eyebrow && el('div', {
162 className: 'bkbg-cc-eyebrow',
163 style: {
164 color: a.cardLayout === 'overlay' ? 'rgba(255,255,255,0.75)' : accentColor,
165 }
166 }, item.eyebrow),
167 el('h3', {
168 className: 'bkbg-cc-heading',
169 style: {
170 margin: 0,
171 color: a.cardLayout === 'overlay' ? '#fff' : a.headingColor,
172 }
173 }, item.heading || 'Card Title'),
174 a.showDescription && item.description && el('p', {
175 className: 'bkbg-cc-desc',
176 style: {
177 margin: 0,
178 color: a.cardLayout === 'overlay' ? 'rgba(255,255,255,0.8)' : a.textColor,
179 flexGrow: 1,
180 }
181 }, item.description),
182 a.showLink && item.link && el('a', {
183 href: '#',
184 style: {
185 display: 'inline-flex',
186 alignItems: 'center',
187 gap: 4,
188 fontSize: 14,
189 fontWeight: 600,
190 color: a.cardLayout === 'overlay' ? '#fff' : a.linkColor,
191 textDecoration: a.linkStyle === 'underline' ? 'underline' : 'none',
192 marginTop: 4,
193 }
194 }, (item.linkLabel || 'Learn more'), a.linkStyle === 'arrow' ? '' : '')
195 );
196
197 var cardContent = [];
198 if (a.cardLayout === 'overlay' && hasImage) {
199 cardContent = [
200 el('img', {
201 src: item.imageUrl,
202 alt: item.imageAlt || '',
203 style: {
204 position: 'absolute',
205 inset: '0',
206 width: '100%',
207 height: '100%',
208 objectFit: 'cover',
209 display: 'block',
210 }
211 }),
212 body
213 ];
214 } else if (a.cardLayout === 'horizontal') {
215 cardContent = [imageSection, body].filter(Boolean);
216 } else {
217 cardContent = [imageSection, body].filter(Boolean);
218 }
219
220 return el('div', { key: idx, className: 'bkbg-cc-card shadow-' + a.cardShadow + ' layout-' + a.cardLayout, style: cardStyle }, ...cardContent);
221 }
222
223 var BkbgColorSwatch = function (props) {
224 var _st = useState(false); var isOpen = _st[0]; var setOpen = _st[1];
225 return el(Fragment, {},
226 el('div', { style: { display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '8px' } },
227 el('span', { style: { fontSize: '11px', fontWeight: 500, textTransform: 'uppercase', color: '#1e1e1e' } }, props.label),
228 el('button', {
229 type: 'button',
230 onClick: function () { setOpen(!isOpen); },
231 style: { width: '28px', height: '28px', borderRadius: '4px', border: '1px solid #ccc', background: props.value || '#ffffff', cursor: 'pointer', padding: 0 }
232 })
233 ),
234 isOpen && el(Popover, { onClose: function () { setOpen(false); } },
235 el('div', { style: { padding: '8px' } },
236 el(ColorPicker, { color: props.value, onChangeComplete: function (c) { props.onChange(c.hex); }, enableAlpha: true })
237 )
238 )
239 );
240 };
241
242 registerBlockType('blockenberg/card-carousel', {
243 title: __('Card Carousel', 'blockenberg'),
244 icon: 'slides',
245 category: 'bkbg-effects',
246 description: __('Horizontal drag-to-scroll card carousel with rich cards, autoplay, arrows, dots, and responsive columns.', 'blockenberg'),
247
248 edit: function (props) {
249 var a = props.attributes;
250 var set = props.setAttributes;
251 var activeState = useState(0);
252 var activeCard = activeState[0];
253 var setActiveCard = activeState[1];
254 var blockProps = useBlockProps({ style: Object.assign({ background: a.containerBg || '', padding: a.padding + 'px' }, _tv(a.typoHeading, '--bkbg-cc-h-'), _tv(a.typoDesc, '--bkbg-cc-d-'), _tv(a.typoEyebrow, '--bkbg-cc-e-')) });
255
256 function addItem() {
257 var newItem = {
258 eyebrow: 'Category',
259 heading: 'New Card Title',
260 description: 'Write a short description for this card.',
261 imageUrl: '', imageId: 0, imageAlt: '',
262 link: '', linkLabel: 'Learn more',
263 badge: '',
264 accentColor: '#6366f1',
265 };
266 set({ items: a.items.concat([newItem]) });
267 }
268
269 function removeItem(i) {
270 if (a.items.length <= 1) return;
271 set({ items: a.items.filter(function (_, idx) { return idx !== i; }) });
272 setActiveCard(Math.max(0, activeCard - 1));
273 }
274
275 function moveItem(i, dir) {
276 var items = a.items.slice();
277 var j = i + dir;
278 if (j < 0 || j >= items.length) return;
279 var tmp = items[i]; items[i] = items[j]; items[j] = tmp;
280 set({ items: items });
281 setActiveCard(j);
282 }
283
284 var activeItem = a.items[activeCard] || a.items[0];
285
286 return el(Fragment, null,
287 el(InspectorControls, null,
288 // Items Panel
289 el(PanelBody, { title: 'Cards (' + a.items.length + ')', initialOpen: true },
290 a.items.map(function (item, i) {
291 return el('div', {
292 key: i,
293 style: {
294 border: i === activeCard ? '2px solid #6366f1' : '1px solid #e5e7eb',
295 borderRadius: 8,
296 padding: '8px',
297 marginBottom: 8,
298 background: i === activeCard ? '#f5f3ff' : '#fff',
299 cursor: 'pointer',
300 }
301 },
302 el('div', {
303 style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: i === activeCard ? 8 : 0 },
304 onClick: function () { setActiveCard(i); }
305 },
306 el('span', { style: { fontWeight: 600, fontSize: 13, color: '#374151' } }, (i + 1) + '. ' + (item.heading || 'Card')),
307 el('div', { style: { display: 'flex', gap: 3 } },
308 el(Button, { onClick: function (e) { e.stopPropagation(); moveItem(i, -1); }, variant: 'tertiary', size: 'small', disabled: i === 0, __nextHasNoMarginBottom: true }, ''),
309 el(Button, { onClick: function (e) { e.stopPropagation(); moveItem(i, 1); }, variant: 'tertiary', size: 'small', disabled: i === a.items.length - 1, __nextHasNoMarginBottom: true }, ''),
310 el(Button, { onClick: function (e) { e.stopPropagation(); removeItem(i); }, isDestructive: true, variant: 'tertiary', size: 'small', __nextHasNoMarginBottom: true }, '')
311 )
312 ),
313 i === activeCard && el(Fragment, null,
314 el(TextControl, {
315 label: 'Eyebrow',
316 value: item.eyebrow,
317 onChange: function (v) { set({ items: updateItem(a.items, i, 'eyebrow', v) }); },
318 __nextHasNoMarginBottom: true,
319 }),
320 el('div', { style: { height: 8 } }),
321 el(TextControl, {
322 label: 'Heading',
323 value: item.heading,
324 onChange: function (v) { set({ items: updateItem(a.items, i, 'heading', v) }); },
325 __nextHasNoMarginBottom: true,
326 }),
327 el('div', { style: { height: 8 } }),
328 el(TextControl, {
329 label: 'Description',
330 value: item.description,
331 onChange: function (v) { set({ items: updateItem(a.items, i, 'description', v) }); },
332 __nextHasNoMarginBottom: true,
333 }),
334 el('div', { style: { height: 8 } }),
335 el(TextControl, {
336 label: 'Badge',
337 value: item.badge,
338 onChange: function (v) { set({ items: updateItem(a.items, i, 'badge', v) }); },
339 __nextHasNoMarginBottom: true,
340 }),
341 el('div', { style: { height: 8 } }),
342 el(TextControl, {
343 label: 'Link URL',
344 value: item.link,
345 placeholder: 'https://',
346 onChange: function (v) { set({ items: updateItem(a.items, i, 'link', v) }); },
347 __nextHasNoMarginBottom: true,
348 }),
349 el('div', { style: { height: 8 } }),
350 el(TextControl, {
351 label: 'Link Label',
352 value: item.linkLabel,
353 onChange: function (v) { set({ items: updateItem(a.items, i, 'linkLabel', v) }); },
354 __nextHasNoMarginBottom: true,
355 }),
356 el('div', { style: { height: 8 } }),
357 el(BkbgColorSwatch, {
358 label: 'Accent Color (hex)',
359 value: item.accentColor,
360 onChange: function (v) { set({ items: updateItem(a.items, i, 'accentColor', v) }); },
361 }),
362 el('div', { style: { height: 8 } }),
363 el('div', { style: { marginBottom: 4, fontSize: 11, fontWeight: 600, textTransform: 'uppercase', color: '#6b7280', letterSpacing: '0.5px' } }, 'Image'),
364 el(MediaUploadCheck, null,
365 el(MediaUpload, {
366 onSelect: function (media) {
367 var items2 = updateItem(a.items, i, 'imageUrl', media.url);
368 items2 = updateItem(items2, i, 'imageId', media.id);
369 items2 = updateItem(items2, i, 'imageAlt', media.alt || '');
370 set({ items: items2 });
371 },
372 allowedTypes: ['image'],
373 value: item.imageId,
374 render: function (ref) {
375 return el(Button, {
376 onClick: ref.open,
377 variant: item.imageUrl ? 'secondary' : 'primary',
378 size: 'small',
379 __nextHasNoMarginBottom: true,
380 }, item.imageUrl ? 'Change Image' : 'Add Image');
381 }
382 })
383 ),
384 item.imageUrl && el(Button, {
385 onClick: function () {
386 var items2 = updateItem(a.items, i, 'imageUrl', '');
387 items2 = updateItem(items2, i, 'imageId', 0);
388 set({ items: items2 });
389 },
390 isDestructive: true, variant: 'link', size: 'small',
391 style: { display: 'block', marginTop: 4 },
392 __nextHasNoMarginBottom: true,
393 }, 'Remove Image')
394 )
395 );
396 }),
397 el(Button, {
398 onClick: addItem,
399 variant: 'secondary',
400 style: { width: '100%', justifyContent: 'center', marginTop: 4 },
401 __nextHasNoMarginBottom: true,
402 }, '+ Add Card')
403 ),
404
405 // Layout Panel
406 el(PanelBody, { title: 'Layout & Columns', initialOpen: false },
407 el(SelectControl, {
408 label: 'Card Layout',
409 value: a.cardLayout,
410 options: CARD_LAYOUT_OPTIONS,
411 onChange: function (v) { set({ cardLayout: v }); },
412 __nextHasNoMarginBottom: true,
413 }),
414 a.cardLayout !== 'text' && el(Fragment, null,
415 el('div', { style: { height: 12 } }),
416 el(SelectControl, {
417 label: 'Image Aspect Ratio',
418 value: a.imageAspect,
419 options: ASPECT_OPTIONS,
420 onChange: function (v) { set({ imageAspect: v }); },
421 __nextHasNoMarginBottom: true,
422 })
423 ),
424 el('div', { style: { height: 12 } }),
425 el(RangeControl, {
426 label: 'Desktop Columns',
427 value: a.desktopCols,
428 min: 1, max: 6,
429 onChange: function (v) { set({ desktopCols: v }); },
430 __nextHasNoMarginBottom: true,
431 }),
432 el('div', { style: { height: 12 } }),
433 el(RangeControl, {
434 label: 'Tablet Columns',
435 value: a.tabletCols,
436 min: 1, max: 4,
437 onChange: function (v) { set({ tabletCols: v }); },
438 __nextHasNoMarginBottom: true,
439 }),
440 el('div', { style: { height: 12 } }),
441 el(RangeControl, {
442 label: 'Mobile Columns',
443 value: a.mobileCols,
444 min: 1, max: 2,
445 onChange: function (v) { set({ mobileCols: v }); },
446 __nextHasNoMarginBottom: true,
447 }),
448 el('div', { style: { height: 12 } }),
449 el(RangeControl, {
450 label: 'Gap (px)',
451 value: a.gap,
452 min: 0, max: 60,
453 onChange: function (v) { set({ gap: v }); },
454 __nextHasNoMarginBottom: true,
455 }),
456 el('div', { style: { height: 12 } }),
457 el(RangeControl, {
458 label: 'Min Card Height (0 = auto)',
459 value: a.cardMinHeight,
460 min: 0, max: 600,
461 onChange: function (v) { set({ cardMinHeight: v }); },
462 __nextHasNoMarginBottom: true,
463 }),
464 el('div', { style: { height: 12 } }),
465 el(RangeControl, {
466 label: 'Card Border Radius (px)',
467 value: a.cardBorderRadius,
468 min: 0, max: 32,
469 onChange: function (v) { set({ cardBorderRadius: v }); },
470 __nextHasNoMarginBottom: true,
471 }),
472 el('div', { style: { height: 12 } }),
473 el(SelectControl, {
474 label: 'Card Shadow',
475 value: a.cardShadow,
476 options: SHADOW_OPTIONS,
477 onChange: function (v) { set({ cardShadow: v }); },
478 __nextHasNoMarginBottom: true,
479 }),
480 el('div', { style: { height: 12 } }),
481 el(RangeControl, {
482 label: 'Card Inner Padding (px)',
483 value: a.cardPadding,
484 min: 8, max: 48,
485 onChange: function (v) { set({ cardPadding: v }); },
486 __nextHasNoMarginBottom: true,
487 }),
488 el('div', { style: { height: 12 } }),
489 el(RangeControl, {
490 label: 'Outer Padding (px)',
491 value: a.padding,
492 min: 0, max: 80,
493 onChange: function (v) { set({ padding: v }); },
494 __nextHasNoMarginBottom: true,
495 })
496 ),
497
498 // Navigation Panel
499 el(PanelBody, { title: 'Navigation & Autoplay', initialOpen: false },
500 el(ToggleControl, {
501 label: 'Show Arrows',
502 checked: a.showArrows,
503 onChange: function (v) { set({ showArrows: v }); },
504 __nextHasNoMarginBottom: true,
505 }),
506 el('div', { style: { height: 8 } }),
507 el(ToggleControl, {
508 label: 'Show Dots',
509 checked: a.showDots,
510 onChange: function (v) { set({ showDots: v }); },
511 __nextHasNoMarginBottom: true,
512 }),
513 el('div', { style: { height: 8 } }),
514 el(ToggleControl, {
515 label: 'Loop (infinite)',
516 checked: a.loop,
517 onChange: function (v) { set({ loop: v }); },
518 __nextHasNoMarginBottom: true,
519 }),
520 el('div', { style: { height: 8 } }),
521 el(ToggleControl, {
522 label: 'Autoplay',
523 checked: a.autoplay,
524 onChange: function (v) { set({ autoplay: v }); },
525 __nextHasNoMarginBottom: true,
526 }),
527 a.autoplay && el(Fragment, null,
528 el('div', { style: { height: 8 } }),
529 el(RangeControl, {
530 label: 'Autoplay Speed (ms)',
531 value: a.autoplaySpeed,
532 min: 1000, max: 10000, step: 500,
533 onChange: function (v) { set({ autoplaySpeed: v }); },
534 __nextHasNoMarginBottom: true,
535 }),
536 el('div', { style: { height: 8 } }),
537 el(ToggleControl, {
538 label: 'Pause on Hover',
539 checked: a.pauseOnHover,
540 onChange: function (v) { set({ pauseOnHover: v }); },
541 __nextHasNoMarginBottom: true,
542 })
543 )
544 ),
545
546 // Content Toggles Panel
547 el(PanelBody, { title: 'Card Content', initialOpen: false },
548 el(ToggleControl, {
549 label: 'Show Badge',
550 checked: a.showBadge,
551 onChange: function (v) { set({ showBadge: v }); },
552 __nextHasNoMarginBottom: true,
553 }),
554 el('div', { style: { height: 8 } }),
555 el(ToggleControl, {
556 label: 'Show Eyebrow',
557 checked: a.showEyebrow,
558 onChange: function (v) { set({ showEyebrow: v }); },
559 __nextHasNoMarginBottom: true,
560 }),
561 el('div', { style: { height: 8 } }),
562 el(ToggleControl, {
563 label: 'Show Description',
564 checked: a.showDescription,
565 onChange: function (v) { set({ showDescription: v }); },
566 __nextHasNoMarginBottom: true,
567 }),
568 el('div', { style: { height: 8 } }),
569 el(ToggleControl, {
570 label: 'Show Link',
571 checked: a.showLink,
572 onChange: function (v) { set({ showLink: v }); },
573 __nextHasNoMarginBottom: true,
574 }),
575 a.showLink && el(Fragment, null,
576 el('div', { style: { height: 8 } }),
577 el(SelectControl, {
578 label: 'Link Style',
579 value: a.linkStyle,
580 options: LINK_STYLE_OPTIONS,
581 onChange: function (v) { set({ linkStyle: v }); },
582 __nextHasNoMarginBottom: true,
583 })
584 ),
585 el('div', { style: { height: 12 } }),
586 el('div', { style: { height: 8 } }),
587 el('div', { style: { height: 8 } }),
588 ),
589
590 // Colors Panel
591
592 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
593 el(getTypographyControl(), { label: __('Heading', 'blockenberg'), value: a.typoHeading, onChange: function (v) { set({ typoHeading: v }); } }),
594 el(getTypographyControl(), { label: __('Description', 'blockenberg'), value: a.typoDesc, onChange: function (v) { set({ typoDesc: v }); } }),
595 el(getTypographyControl(), { label: __('Eyebrow', 'blockenberg'), value: a.typoEyebrow, onChange: function (v) { set({ typoEyebrow: v }); } })
596 ),
597 el(PanelColorSettings, {
598 title: 'Colors',
599 initialOpen: false,
600 colorSettings: [
601 { label: 'Container Background', value: a.containerBg, onChange: function (v) { set({ containerBg: v || '' }); } },
602 { label: 'Card Background', value: a.cardBg, onChange: function (v) { set({ cardBg: v || '#ffffff' }); } },
603 { label: 'Eyebrow Color', value: a.eyebrowColor, onChange: function (v) { set({ eyebrowColor: v || '#6366f1' }); } },
604 { label: 'Heading Color', value: a.headingColor, onChange: function (v) { set({ headingColor: v || '#111827' }); } },
605 { label: 'Text Color', value: a.textColor, onChange: function (v) { set({ textColor: v || '#6b7280' }); } },
606 { label: 'Link Color', value: a.linkColor, onChange: function (v) { set({ linkColor: v || '#6366f1' }); } },
607 { label: 'Badge Background', value: a.badgeBg, onChange: function (v) { set({ badgeBg: v || '#ede9fe' }); } },
608 { label: 'Badge Text', value: a.badgeColor, onChange: function (v) { set({ badgeColor: v || '#6366f1' }); } },
609 { label: 'Arrow Background', value: a.arrowBg, onChange: function (v) { set({ arrowBg: v || '#ffffff' }); } },
610 { label: 'Arrow Color', value: a.arrowColor, onChange: function (v) { set({ arrowColor: v || '#111827' }); } },
611 { label: 'Active Dot Color', value: a.dotActiveColor, onChange: function (v) { set({ dotActiveColor: v || '#6366f1' }); } },
612 { label: 'Inactive Dot Color', value: a.dotInactiveColor, onChange: function (v) { set({ dotInactiveColor: v || '#d1d5db' }); } },
613 ]
614 })
615 ),
616
617 // Editor Preview
618 el('div', blockProps,
619 // Cards strip (horizontal scroll preview)
620 el('div', {
621 style: {
622 display: 'flex',
623 gap: a.gap + 'px',
624 overflowX: 'auto',
625 paddingBottom: 8,
626 }
627 },
628 a.items.map(function (item, i) {
629 return renderCardPreview(item, a, i);
630 })
631 ),
632 // Navigation preview
633 el('div', { style: { marginTop: 16, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 16 } },
634 a.showArrows && el('button', {
635 disabled: true,
636 style: {
637 width: 36, height: 36, borderRadius: '50%',
638 background: a.arrowBg, border: '1px solid #e5e7eb',
639 color: a.arrowColor, cursor: 'not-allowed', fontSize: 16,
640 }
641 }, ''),
642 a.showDots && el('div', { style: { display: 'flex', gap: 6 } },
643 a.items.map(function (_, i) {
644 return el('div', {
645 key: i,
646 style: {
647 width: i === 0 ? 20 : 8,
648 height: 8,
649 borderRadius: 999,
650 background: i === 0 ? a.dotActiveColor : a.dotInactiveColor,
651 transition: 'all 0.3s',
652 }
653 });
654 })
655 ),
656 a.showArrows && el('button', {
657 disabled: true,
658 style: {
659 width: 36, height: 36, borderRadius: '50%',
660 background: a.arrowBg, border: '1px solid #e5e7eb',
661 color: a.arrowColor, cursor: 'not-allowed', fontSize: 16,
662 }
663 }, '')
664 ),
665 // Info strip
666 el('div', { style: { marginTop: 8, fontSize: 11, color: '#9ca3af', textAlign: 'center' } },
667 '🎠 ' + a.items.length + ' cards | Desktop: ' + a.desktopCols + ' cols | Autoplay: ' + (a.autoplay ? 'on' : 'off') + ' | Drag enabled on frontend'
668 )
669 )
670 );
671 },
672
673 save: function (props) {
674 return el('div', useBlockProps.save(),
675 el('div', {
676 className: 'bkbg-cc-app',
677 'data-opts': JSON.stringify(props.attributes),
678 })
679 );
680 }
681 });
682 }() );
683