PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.11
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.11
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-stack / frontend.js

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

143 lines 5.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () {
2 'use strict';
3
4 function ghostTransform(idx, style, rot, ox, oy) {
5 if (style === 'fan') {
6 var angle = (idx + 1) * (rot / 2);
7 var flip = idx % 2 === 0 ? 1 : -1;
8 return 'rotate(' + (flip * angle) + 'deg) translateY(' + ((idx + 1) * oy) + 'px)';
9 }
10 if (style === 'spread') {
11 return 'translateX(' + ((idx + 1) * ox * 1.5) + 'px) rotate(' + ((idx + 1) * (rot * 0.6)) + 'deg)';
12 }
13 /* pile */
14 return 'translateX(' + ((idx + 1) * ox * 0.5) + 'px) translateY(' + ((idx + 1) * oy) + 'px)';
15 }
16
17 function initStack(wrap) {
18 var stackEl = wrap.querySelector('.bkbg-cardstack');
19 if (!stackEl) return;
20
21 var cards = Array.from(wrap.querySelectorAll('.bkbg-cardstack-card'));
22 var dots = Array.from(wrap.querySelectorAll('.bkbg-cardstack-dot'));
23 if (cards.length < 2) return;
24
25 var style = stackEl.dataset.style || 'fan';
26 var rot = parseFloat(stackEl.dataset.rotation) || 5;
27 var ox = parseFloat(stackEl.dataset.offsetX) || 8;
28 var oy = parseFloat(stackEl.dataset.offsetY) || 8;
29 var behind = parseInt(stackEl.dataset.behind, 10) || 2;
30
31 var current = 0;
32
33 /* Position all cards as a stack */
34 function layout() {
35 cards.forEach(function (card, i) {
36 if (i === current) {
37 card.style.position = 'relative';
38 card.style.zIndex = cards.length;
39 card.style.transform = '';
40 card.style.opacity = '1';
41 card.style.pointerEvents = '';
42 card.classList.add('is-active');
43 } else {
44 var offset = (i - current + cards.length) % cards.length;
45 if (offset > behind) {
46 card.style.opacity = '0';
47 card.style.zIndex = '0';
48 } else {
49 card.style.position = 'absolute';
50 card.style.inset = '0';
51 card.style.zIndex = String(cards.length - offset);
52 card.style.transform = ghostTransform(offset - 1, style, rot, ox, oy);
53 card.style.opacity = String(1 - offset * 0.2);
54 card.style.pointerEvents = 'none';
55 }
56 card.classList.remove('is-active');
57 }
58 });
59
60 dots.forEach(function (dot, i) {
61 if (i === current) {
62 dot.classList.add('is-active');
63 dot.style.background = dot.dataset.activeColor || '#6c3fb5';
64 } else {
65 dot.classList.remove('is-active');
66 dot.style.background = '#d1d5db';
67 }
68 });
69 }
70
71 function advance() {
72 cards[current].classList.add('is-exit');
73 var prev = current;
74 current = (current + 1) % cards.length;
75
76 setTimeout(function () {
77 cards[prev].classList.remove('is-exit');
78 layout();
79 cards[current].classList.add('is-enter');
80 setTimeout(function () { cards[current].classList.remove('is-enter'); }, 350);
81 }, 240);
82 }
83
84 /* Click / touch on stack = cycle */
85 stackEl.addEventListener('click', advance);
86 stackEl.addEventListener('keydown', function (e) {
87 if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); advance(); }
88 });
89 stackEl.setAttribute('tabindex', '0');
90 stackEl.setAttribute('role', 'button');
91 stackEl.setAttribute('aria-label', 'Next card');
92
93 /* Dot nav */
94 dots.forEach(function (dot, i) {
95 dot.addEventListener('click', function (e) {
96 e.stopPropagation();
97 if (i === current) return;
98 current = i;
99 layout();
100 });
101 });
102
103 layout();
104
105 /* Animate in on scroll */
106 if (stackEl.dataset.animate === '1') {
107 if ('IntersectionObserver' in window) {
108 var io = new IntersectionObserver(function (entries) {
109 entries.forEach(function (entry) {
110 if (entry.isIntersecting) {
111 entry.target.classList.add('is-visible');
112 io.unobserve(entry.target);
113 }
114 });
115 }, { threshold: 0.15 });
116 io.observe(stackEl);
117 } else {
118 stackEl.classList.add('is-visible');
119 }
120 }
121
122 /* Hover: fan cards outward on hover */
123 if (style === 'fan') {
124 stackEl.addEventListener('mouseenter', function () {
125 cards.forEach(function (card, i) {
126 if (i !== current) {
127 var offset = (i - current + cards.length) % cards.length;
128 if (offset <= behind) {
129 card.style.transform = ghostTransform(offset - 1, 'spread', rot * 0.7, ox * 1.8, oy * 0.5);
130 card.style.opacity = String(1 - offset * 0.15);
131 }
132 }
133 });
134 });
135 stackEl.addEventListener('mouseleave', function () {
136 layout(); /* reset to normal arrangement */
137 });
138 }
139 }
140
141 document.querySelectorAll('.bkbg-cardstack-outer').forEach(initStack);
142 }());
143