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 / frontend.js

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

494 lines 19.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () {
2 var ASPECT_PADDING = {
3 '1-1': '100%', '4-3': '75%', '3-2': '66.67%', '16-9': '56.25%', '21-9': '42.86%',
4 };
5 var SHADOWS = {
6 none: 'none',
7 sm: '0 1px 6px rgba(0,0,0,0.08)',
8 md: '0 4px 16px rgba(0,0,0,0.12)',
9 lg: '0 8px 32px rgba(0,0,0,0.16)',
10 };
11
12 function typoCssVarsForEl(typo, prefix, el) {
13 if (!typo) return;
14 if (typo.family) el.style.setProperty(prefix + 'font-family', "'" + typo.family + "', sans-serif");
15 if (typo.weight) el.style.setProperty(prefix + 'font-weight', typo.weight);
16 if (typo.transform) el.style.setProperty(prefix + 'text-transform', typo.transform);
17 if (typo.style) el.style.setProperty(prefix + 'font-style', typo.style);
18 if (typo.decoration) el.style.setProperty(prefix + 'text-decoration', typo.decoration);
19 var su = typo.sizeUnit || 'px';
20 if (typo.sizeDesktop !== '' && typo.sizeDesktop != null) el.style.setProperty(prefix + 'font-size-d', typo.sizeDesktop + su);
21 if (typo.sizeTablet !== '' && typo.sizeTablet != null) el.style.setProperty(prefix + 'font-size-t', typo.sizeTablet + su);
22 if (typo.sizeMobile !== '' && typo.sizeMobile != null) el.style.setProperty(prefix + 'font-size-m', typo.sizeMobile + su);
23 var lhu = typo.lineHeightUnit || 'px';
24 if (typo.lineHeightDesktop !== '' && typo.lineHeightDesktop != null) el.style.setProperty(prefix + 'line-height-d', typo.lineHeightDesktop + lhu);
25 if (typo.lineHeightTablet !== '' && typo.lineHeightTablet != null) el.style.setProperty(prefix + 'line-height-t', typo.lineHeightTablet + lhu);
26 if (typo.lineHeightMobile !== '' && typo.lineHeightMobile != null) el.style.setProperty(prefix + 'line-height-m', typo.lineHeightMobile + lhu);
27 var lsu = typo.letterSpacingUnit || 'px';
28 if (typo.letterSpacingDesktop !== '' && typo.letterSpacingDesktop != null) {
29 el.style.setProperty(prefix + 'letter-spacing-d', typo.letterSpacingDesktop + lsu);
30 el.style.setProperty(prefix + 'letter-spacing', typo.letterSpacingDesktop + lsu);
31 }
32 if (typo.letterSpacingTablet !== '' && typo.letterSpacingTablet != null) el.style.setProperty(prefix + 'letter-spacing-t', typo.letterSpacingTablet + lsu);
33 if (typo.letterSpacingMobile !== '' && typo.letterSpacingMobile != null) el.style.setProperty(prefix + 'letter-spacing-m', typo.letterSpacingMobile + lsu);
34 var wsu = typo.wordSpacingUnit || 'px';
35 if (typo.wordSpacingDesktop !== '' && typo.wordSpacingDesktop != null) {
36 el.style.setProperty(prefix + 'word-spacing-d', typo.wordSpacingDesktop + wsu);
37 el.style.setProperty(prefix + 'word-spacing', typo.wordSpacingDesktop + wsu);
38 }
39 if (typo.wordSpacingTablet !== '' && typo.wordSpacingTablet != null) el.style.setProperty(prefix + 'word-spacing-t', typo.wordSpacingTablet + wsu);
40 if (typo.wordSpacingMobile !== '' && typo.wordSpacingMobile != null) el.style.setProperty(prefix + 'word-spacing-m', typo.wordSpacingMobile + wsu);
41 }
42
43 function clamp(val, min, max) {
44 return Math.max(min, Math.min(max, val));
45 }
46
47 function buildCard(item, o) {
48 var shadow = SHADOWS[o.cardShadow] || SHADOWS.sm;
49 var layout = o.cardLayout || 'stacked';
50
51 var card = document.createElement('div');
52 card.className = 'bkbg-cc-card shadow-' + o.cardShadow + ' layout-' + layout;
53 card.style.borderRadius = o.cardBorderRadius + 'px';
54 card.style.background = layout !== 'overlay' ? o.cardBg : 'transparent';
55 card.style.boxShadow = layout !== 'overlay' ? shadow : 'none';
56 if (o.cardMinHeight > 0) card.style.minHeight = o.cardMinHeight + 'px';
57
58 // Image
59 if (layout !== 'text') {
60 var imgWrap = document.createElement('div');
61 imgWrap.className = 'bkbg-cc-img-wrap';
62
63 if (layout === 'overlay') {
64 imgWrap.style.position = 'absolute';
65 imgWrap.style.inset = '0';
66 }
67
68 if (item.imageUrl) {
69 if (layout === 'stacked' || layout === 'text') {
70 var ratio = document.createElement('div');
71 ratio.className = 'bkbg-cc-img-ratio';
72 ratio.style.paddingTop = ASPECT_PADDING[o.imageAspect] || '56.25%';
73 var img = document.createElement('img');
74 img.src = item.imageUrl;
75 img.alt = item.imageAlt || '';
76 img.loading = 'lazy';
77 ratio.appendChild(img);
78 imgWrap.appendChild(ratio);
79 } else if (layout === 'horizontal') {
80 imgWrap.style.width = '40%';
81 imgWrap.style.flexShrink = '0';
82 var img2 = document.createElement('img');
83 img2.src = item.imageUrl;
84 img2.alt = item.imageAlt || '';
85 img2.loading = 'lazy';
86 img2.style.width = '100%';
87 img2.style.height = '100%';
88 img2.style.objectFit = o.imageFit || 'cover';
89 img2.style.display = 'block';
90 imgWrap.appendChild(img2);
91 } else {
92 // overlay
93 var img3 = document.createElement('img');
94 img3.src = item.imageUrl;
95 img3.alt = item.imageAlt || '';
96 img3.loading = 'lazy';
97 img3.style.width = '100%';
98 img3.style.height = '100%';
99 img3.style.objectFit = o.imageFit || 'cover';
100 img3.style.display = 'block';
101 imgWrap.appendChild(img3);
102 }
103 } else if (layout !== 'overlay') {
104 imgWrap.className += ' bkbg-cc-img-placeholder';
105 var ratio2 = document.createElement('div');
106 ratio2.className = 'bkbg-cc-img-ratio';
107 ratio2.style.paddingTop = ASPECT_PADDING[o.imageAspect] || '56.25%';
108 imgWrap.style.fontSize = '28px';
109 imgWrap.appendChild(ratio2);
110 }
111
112 card.appendChild(imgWrap);
113 }
114
115 // Body
116 var body = document.createElement('div');
117 body.className = 'bkbg-cc-body';
118 body.style.padding = o.cardPadding + 'px';
119 var isOverlay = layout === 'overlay';
120 if (isOverlay) {
121 body.style.position = 'relative';
122 body.style.zIndex = '1';
123 body.style.marginTop = 'auto';
124 body.style.background = 'linear-gradient(180deg, transparent 10%, rgba(0,0,0,0.75) 100%)';
125 }
126
127 var accentColor = item.accentColor || o.eyebrowColor;
128
129 // Badge
130 if (o.showBadge && item.badge) {
131 var badge = document.createElement('span');
132 badge.className = 'bkbg-cc-badge';
133 badge.style.background = o.badgeBg;
134 badge.style.color = o.badgeColor;
135 badge.textContent = item.badge;
136 body.appendChild(badge);
137 }
138
139 // Eyebrow
140 if (o.showEyebrow && item.eyebrow) {
141 var eyebrow = document.createElement('div');
142 eyebrow.className = 'bkbg-cc-eyebrow';
143 eyebrow.style.color = isOverlay ? 'rgba(255,255,255,0.75)' : accentColor;
144 eyebrow.textContent = item.eyebrow;
145 body.appendChild(eyebrow);
146 }
147
148 // Heading
149 var heading = document.createElement('h3');
150 heading.className = 'bkbg-cc-heading';
151 heading.style.color = isOverlay ? '#fff' : o.headingColor;
152 heading.textContent = item.heading || '';
153 body.appendChild(heading);
154
155 // Description
156 if (o.showDescription && item.description) {
157 var desc = document.createElement('p');
158 desc.className = 'bkbg-cc-desc';
159 desc.style.color = isOverlay ? 'rgba(255,255,255,0.8)' : o.textColor;
160 desc.textContent = item.description;
161 body.appendChild(desc);
162 }
163
164 // Link
165 if (o.showLink && item.link) {
166 var link = document.createElement('a');
167 link.className = 'bkbg-cc-link style-' + (o.linkStyle || 'arrow');
168 link.href = item.link;
169 link.style.color = isOverlay ? '#fff' : o.linkColor;
170 if (o.linkStyle === 'button') {
171 link.style.background = isOverlay ? 'rgba(255,255,255,0.15)' : o.linkColor;
172 link.style.color = isOverlay ? '#fff' : '#fff';
173 }
174 var linkText = item.linkLabel || 'Learn more';
175 link.textContent = o.linkStyle === 'arrow' ? linkText + '' : linkText;
176 body.appendChild(link);
177 }
178
179 card.appendChild(body);
180 return card;
181 }
182
183 function initCarousel(appEl) {
184 if (appEl.dataset.rendered) return;
185 appEl.dataset.rendered = '1';
186
187 var opts;
188 try { opts = JSON.parse(appEl.dataset.opts || '{}'); } catch (e) { opts = {}; }
189 var o = Object.assign({
190 items: [],
191 cardLayout: 'stacked',
192 imageAspect: '16-9',
193 desktopCols: 3,
194 tabletCols: 2,
195 mobileCols: 1,
196 gap: 24,
197 cardMinHeight: 0,
198 cardBorderRadius:12,
199 cardShadow: 'sm',
200 showArrows: true,
201 showDots: true,
202 loop: true,
203 autoplay: false,
204 autoplaySpeed: 3000,
205 pauseOnHover: true,
206 showBadge: true,
207 showEyebrow: true,
208 showDescription: true,
209 showLink: true,
210 linkStyle: 'arrow',
211 imageFit: 'cover',
212 padding: 0,
213 cardPadding: 20,
214 // colors
215 containerBg: '',
216 cardBg: '#ffffff',
217 eyebrowColor: '#6366f1',
218 headingColor: '#111827',
219 textColor: '#6b7280',
220 linkColor: '#6366f1',
221 badgeBg: '#ede9fe',
222 badgeColor: '#6366f1',
223 arrowBg: '#ffffff',
224 arrowColor: '#111827',
225 dotActiveColor: '#6366f1',
226 dotInactiveColor:'#d1d5db',
227 headingSize: 18,
228 textSize: 14,
229 eyebrowSize: 11,
230 }, opts);
231
232 if (!o.items || !o.items.length) return;
233
234 // Root wrapper
235 var wrap = document.createElement('div');
236 wrap.className = 'bkbg-cc-wrap';
237 if (o.containerBg) wrap.style.background = o.containerBg;
238 if (o.padding > 0) wrap.style.padding = o.padding + 'px';
239
240 typoCssVarsForEl(o.typoHeading, '--bkbg-cc-h-', wrap);
241 typoCssVarsForEl(o.typoDesc, '--bkbg-cc-d-', wrap);
242 typoCssVarsForEl(o.typoEyebrow, '--bkbg-cc-e-', wrap);
243
244 // Viewport
245 var viewport = document.createElement('div');
246 viewport.className = 'bkbg-cc-viewport';
247 wrap.appendChild(viewport);
248
249 // Track
250 var track = document.createElement('div');
251 track.className = 'bkbg-cc-track';
252 viewport.appendChild(track);
253
254 // Build cards
255 o.items.forEach(function (item) {
256 var card = buildCard(item, o);
257 track.appendChild(card);
258 });
259
260 // Responsive columns helper
261 function getCols() {
262 var w = window.innerWidth;
263 if (w <= 600) return o.mobileCols;
264 if (w <= 1024) return o.tabletCols;
265 return o.desktopCols;
266 }
267
268 var total = o.items.length;
269 var currentIndex = 0;
270 var autoplayTimer = null;
271
272 // Compute & apply card widths
273 function applyCardWidths() {
274 var cols = getCols();
275 var totalGap = o.gap * (cols - 1);
276 var cardWidth = (viewport.offsetWidth - totalGap) / cols;
277 track.querySelectorAll('.bkbg-cc-card').forEach(function (card) {
278 card.style.width = cardWidth + 'px';
279 card.style.marginRight = o.gap + 'px';
280 });
281 goTo(currentIndex, false);
282 }
283
284 function getCardWidth() {
285 var cols = getCols();
286 var totalGap = o.gap * (cols - 1);
287 return (viewport.offsetWidth - totalGap) / cols;
288 }
289
290 function getStepWidth() {
291 return getCardWidth() + o.gap;
292 }
293
294 function maxIndex() {
295 var cols = getCols();
296 return o.loop ? total - 1 : Math.max(0, total - cols);
297 }
298
299 function goTo(idx, animate) {
300 if (animate === undefined) animate = true;
301 if (o.loop) {
302 idx = ((idx % total) + total) % total;
303 } else {
304 idx = clamp(idx, 0, maxIndex());
305 }
306 currentIndex = idx;
307 var offset = idx * getStepWidth();
308 track.style.transition = animate ? 'transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94)' : 'none';
309 track.style.transform = 'translateX(-' + offset + 'px)';
310 updateDots();
311 updateArrows();
312 }
313
314 function next() { goTo(currentIndex + 1); }
315 function prev() { goTo(currentIndex - 1); }
316
317 // Arrows
318 var arrowWrap = null;
319 if (o.showArrows) {
320 arrowWrap = document.createElement('div');
321 arrowWrap.className = 'bkbg-cc-arrows';
322
323 var btnPrev = document.createElement('button');
324 btnPrev.className = 'bkbg-cc-arrow';
325 btnPrev.style.background = o.arrowBg;
326 btnPrev.style.color = o.arrowColor;
327 btnPrev.innerHTML = '&#8592;';
328 btnPrev.addEventListener('click', function () { prev(); resetAutoplay(); });
329 arrowWrap.appendChild(btnPrev);
330
331 var btnNext = document.createElement('button');
332 btnNext.className = 'bkbg-cc-arrow';
333 btnNext.style.background = o.arrowBg;
334 btnNext.style.color = o.arrowColor;
335 btnNext.innerHTML = '&#8594;';
336 btnNext.addEventListener('click', function () { next(); resetAutoplay(); });
337 arrowWrap.appendChild(btnNext);
338
339 // Position arrows relative to viewport
340 viewport.style.position = 'relative';
341 wrap.style.position = 'relative';
342 wrap.appendChild(arrowWrap);
343 }
344
345 function updateArrows() {
346 if (!arrowWrap) return;
347 var arrows = arrowWrap.querySelectorAll('.bkbg-cc-arrow');
348 if (!o.loop) {
349 arrows[0].classList.toggle('is-disabled', currentIndex <= 0);
350 arrows[0].disabled = currentIndex <= 0;
351 arrows[1].classList.toggle('is-disabled', currentIndex >= maxIndex());
352 arrows[1].disabled = currentIndex >= maxIndex();
353 }
354 }
355
356 // Dots
357 var dotsWrap = null;
358 var dotEls = [];
359 if (o.showDots) {
360 dotsWrap = document.createElement('div');
361 dotsWrap.className = 'bkbg-cc-dots';
362 for (var d = 0; d < total; d++) {
363 (function (di) {
364 var dot = document.createElement('button');
365 dot.className = 'bkbg-cc-dot' + (di === 0 ? ' is-active' : '');
366 dot.style.background = di === 0 ? o.dotActiveColor : o.dotInactiveColor;
367 dot.addEventListener('click', function () { goTo(di); resetAutoplay(); });
368 dotEls.push(dot);
369 dotsWrap.appendChild(dot);
370 })(d);
371 }
372 wrap.appendChild(dotsWrap);
373 }
374
375 function updateDots() {
376 dotEls.forEach(function (dot, i) {
377 var active = i === currentIndex;
378 dot.classList.toggle('is-active', active);
379 dot.style.background = active ? o.dotActiveColor : o.dotInactiveColor;
380 dot.style.width = active ? '20px' : '8px';
381 });
382 }
383
384 // Autoplay
385 function startAutoplay() {
386 if (!o.autoplay) return;
387 autoplayTimer = setInterval(function () { next(); }, o.autoplaySpeed);
388 }
389
390 function stopAutoplay() {
391 if (autoplayTimer) { clearInterval(autoplayTimer); autoplayTimer = null; }
392 }
393
394 function resetAutoplay() {
395 stopAutoplay();
396 startAutoplay();
397 }
398
399 if (o.autoplay && o.pauseOnHover) {
400 wrap.addEventListener('mouseenter', stopAutoplay);
401 wrap.addEventListener('mouseleave', startAutoplay);
402 }
403
404 // Drag / swipe
405 var dragStartX = 0;
406 var dragDeltaX = 0;
407 var isDragging = false;
408
409 function onDragStart(x) {
410 isDragging = true;
411 dragStartX = x;
412 dragDeltaX = 0;
413 track.classList.add('is-dragging');
414 stopAutoplay();
415 }
416
417 function onDragMove(x) {
418 if (!isDragging) return;
419 dragDeltaX = x - dragStartX;
420 var offset = (currentIndex * getStepWidth()) - dragDeltaX;
421 track.style.transition = 'none';
422 track.style.transform = 'translateX(-' + offset + 'px)';
423 }
424
425 function onDragEnd() {
426 if (!isDragging) return;
427 isDragging = false;
428 track.classList.remove('is-dragging');
429 var threshold = getStepWidth() * 0.2;
430 if (dragDeltaX < -threshold) {
431 next();
432 } else if (dragDeltaX > threshold) {
433 prev();
434 } else {
435 goTo(currentIndex);
436 }
437 startAutoplay();
438 }
439
440 // Touch events
441 track.addEventListener('touchstart', function (e) {
442 onDragStart(e.touches[0].clientX);
443 }, { passive: true });
444 track.addEventListener('touchmove', function (e) {
445 onDragMove(e.touches[0].clientX);
446 }, { passive: true });
447 track.addEventListener('touchend', function () { onDragEnd(); });
448
449 // Mouse drag
450 track.addEventListener('mousedown', function (e) {
451 e.preventDefault();
452 onDragStart(e.clientX);
453 });
454 document.addEventListener('mousemove', function (e) {
455 if (isDragging) onDragMove(e.clientX);
456 });
457 document.addEventListener('mouseup', function () {
458 if (isDragging) onDragEnd();
459 });
460
461 // Prevent click on links during drag
462 track.addEventListener('click', function (e) {
463 if (Math.abs(dragDeltaX) > 5) e.preventDefault();
464 });
465
466 // Resize
467 var resizeTimer;
468 window.addEventListener('resize', function () {
469 clearTimeout(resizeTimer);
470 resizeTimer = setTimeout(applyCardWidths, 100);
471 });
472
473 // Initialize
474 appEl.parentNode.insertBefore(wrap, appEl);
475 appEl.style.display = 'none';
476
477 // Delay to let layout settle
478 requestAnimationFrame(function () {
479 applyCardWidths();
480 startAutoplay();
481 });
482 }
483
484 function init() {
485 document.querySelectorAll('.bkbg-cc-app').forEach(initCarousel);
486 }
487
488 if (document.readyState !== 'loading') {
489 init();
490 } else {
491 document.addEventListener('DOMContentLoaded', init);
492 }
493 })();
494