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

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

318 lines 21.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 __ = wp.i18n.__;
4 var registerBlockType = wp.blocks.registerBlockType;
5 var useBlockProps = wp.blockEditor.useBlockProps;
6 var InspectorControls = wp.blockEditor.InspectorControls;
7 var MediaUpload = wp.blockEditor.MediaUpload;
8 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
9 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
10 var PanelBody = wp.components.PanelBody;
11 var TextControl = wp.components.TextControl;
12 var TextareaControl = wp.components.TextareaControl;
13 var ToggleControl = wp.components.ToggleControl;
14 var RangeControl = wp.components.RangeControl;
15 var SelectControl = wp.components.SelectControl;
16 var Button = wp.components.Button;
17 var useState = wp.element.useState;
18
19 function getTypographyControl() { return (window.bkbgTypographyControl || function () { return null; }); }
20 function getTypoCssVars() { return (window.bkbgTypoCssVars || function () { return {}; }); }
21 function _tv(typo, prefix) { var fn = getTypoCssVars(); return fn(typo || {}, prefix); }
22
23 /* ── Card preview in editor ── */
24 function CardPreview(props) {
25 var card = props.card;
26 var attr = props.attr;
27 var isActive = props.isActive;
28
29 var cardStyle = {
30 width: attr.cardWidth + 'px',
31 height: attr.cardHeight + 'px',
32 borderRadius: attr.cardRadius + 'px',
33 background: card.imageUrl ? 'none' : attr.cardBg,
34 backgroundColor: attr.cardBg,
35 position: 'relative',
36 overflow: 'hidden',
37 display: 'flex',
38 flexDirection: 'column',
39 justifyContent: 'flex-end',
40 boxShadow: isActive ? '0 32px 64px rgba(0,0,0,0.4)' : '0 16px 32px rgba(0,0,0,0.2)',
41 transition: 'all 0.3s'
42 };
43
44 var overlayStyle = {
45 position: 'absolute', inset: 0, zIndex: 1,
46 background: 'linear-gradient(to top, rgba(0,0,0,' + (attr.overlayStrength / 100) + ') 40%, transparent 100%)'
47 };
48
49 var contentStyle = {
50 position: 'relative', zIndex: 2,
51 padding: '24px'
52 };
53
54 return el('div', { style: cardStyle },
55 card.imageUrl && el('img', {
56 src: card.imageUrl, alt: card.imageAlt,
57 style: { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: attr.imageFit }
58 }),
59 el('div', { style: overlayStyle }),
60 el('div', { style: contentStyle },
61 attr.showTag && card.tag && el('span', {
62 style: {
63 display: 'inline-block', background: attr.tagBg, color: attr.tagColor,
64 fontSize: attr.tagSize + 'px', fontWeight: 700, textTransform: 'uppercase',
65 letterSpacing: '0.08em', padding: '3px 10px', borderRadius: '100px',
66 marginBottom: '10px'
67 }
68 }, card.tag),
69 attr.showTitle && card.title && el('div', {
70 className: 'bkbg-cds-title',
71 style: { color: attr.titleColor, marginBottom: '10px' }
72 }, card.title),
73 attr.showDescription && card.description && el('div', {
74 className: 'bkbg-cds-desc',
75 style: { color: attr.descColor, marginBottom: '14px' }
76 }, card.description),
77 attr.showLink && card.linkLabel && el('span', {
78 style: { color: attr.linkColor, fontWeight: 600, fontSize: '14px' }
79 }, card.linkLabel + '')
80 )
81 );
82 }
83
84 registerBlockType('blockenberg/card-deck-slider', {
85 edit: function (props) {
86 var attr = props.attributes;
87 var setAttr = props.setAttributes;
88 var cards = attr.cards;
89 var blockProps = useBlockProps({ style: Object.assign({ background: attr.sectionBg || undefined }, _tv(attr.typoTitle, '--bkbg-cds-tt-'), _tv(attr.typoDesc, '--bkbg-cds-td-')) });
90 var previewIdx = useState(0);
91 var activeIdx = previewIdx[0];
92 var setActiveIdx = previewIdx[1];
93
94 function updateCard(idx, key, val) {
95 var next = cards.map(function (c, i) {
96 if (i !== idx) return c;
97 var updated = {};
98 Object.keys(c).forEach(function (k) { updated[k] = c[k]; });
99 updated[key] = val;
100 return updated;
101 });
102 setAttr({ cards: next });
103 }
104
105 function addCard() {
106 setAttr({
107 cards: cards.concat([{ imageUrl: '', imageAlt: '', tag: 'New', title: 'Card Title', description: 'Card description text goes here.', linkUrl: '', linkLabel: 'Learn More' }])
108 });
109 }
110
111 function removeCard(idx) {
112 if (cards.length <= 2) return;
113 var next = cards.filter(function (_, i) { return i !== idx; });
114 setAttr({ cards: next });
115 if (activeIdx >= next.length) setActiveIdx(next.length - 1);
116 }
117
118 /* Editor deck preview — show active card with stacked cards behind */
119 var deckPreview = el('div', {
120 style: {
121 display: 'flex',
122 flexDirection: 'column',
123 alignItems: 'center',
124 padding: '60px 20px',
125 background: attr.sectionBg || '#f8fafc',
126 borderRadius: '16px'
127 }
128 },
129 el('div', { style: { position: 'relative', width: attr.cardWidth + 'px', height: attr.cardHeight + 'px' } },
130 /* stacked cards behind */
131 cards.slice(1, Math.min(attr.stackCount + 1, cards.length)).map(function (card, i) {
132 var depth = i + 1;
133 var angle = attr.deckStyle === 'fan' ? (depth % 2 === 0 ? 1 : -1) * attr.stackAngle * depth / 2 : 0;
134 var offsetY = attr.deckStyle === 'stack' ? -depth * attr.stackOffset : 0;
135 var scale = 1 - depth * attr.stackScale;
136 return el('div', {
137 key: i,
138 style: {
139 position: 'absolute', inset: 0,
140 transform: 'rotate(' + angle + 'deg) translateY(' + offsetY + 'px) scale(' + scale + ')',
141 transformOrigin: 'bottom center',
142 zIndex: cards.length - depth
143 }
144 }, el(CardPreview, { card: card, attr: attr, isActive: false }));
145 }),
146 /* active card on top */
147 el('div', { style: { position: 'absolute', inset: 0, zIndex: cards.length } },
148 el(CardPreview, { card: cards[activeIdx] || cards[0], attr: attr, isActive: true })
149 )
150 ),
151 attr.showArrows && el('div', { style: { display: 'flex', gap: '12px', marginTop: '28px' } },
152 el('button', {
153 style: { width: '44px', height: '44px', borderRadius: '50%', border: 'none', background: attr.arrowBg, color: attr.arrowColor, cursor: 'pointer', fontSize: '18px' },
154 onClick: function () { setActiveIdx((activeIdx - 1 + cards.length) % cards.length); }
155 }, ''),
156 el('button', {
157 style: { width: '44px', height: '44px', borderRadius: '50%', border: 'none', background: attr.arrowBg, color: attr.arrowColor, cursor: 'pointer', fontSize: '18px' },
158 onClick: function () { setActiveIdx((activeIdx + 1) % cards.length); }
159 }, '')
160 ),
161 attr.showDots && el('div', { style: { display: 'flex', gap: '8px', marginTop: '16px' } },
162 cards.map(function (_, i) {
163 return el('div', {
164 key: i,
165 style: {
166 width: i === activeIdx ? '24px' : '8px',
167 height: '8px', borderRadius: '100px',
168 background: i === activeIdx ? attr.dotColor : '#cbd5e1',
169 transition: 'all 0.3s', cursor: 'pointer'
170 },
171 onClick: function () { setActiveIdx(i); }
172 });
173 })
174 )
175 );
176
177 /* Inspector */
178 var inspector = el(InspectorControls, {},
179 /* Cards Panel */
180 el(PanelBody, { title: __('Cards', 'blockenberg'), initialOpen: true },
181 cards.map(function (card, idx) {
182 return el(PanelBody, {
183 key: idx,
184 title: (card.title || 'Card ' + (idx + 1)).substring(0, 30),
185 initialOpen: idx === 0
186 },
187 el(MediaUploadCheck, {},
188 el(MediaUpload, {
189 onSelect: function (media) { setAttr({ cards: cards.map(function (c, i) { return i !== idx ? c : Object.assign({}, c, { imageUrl: media.url, imageAlt: media.alt || '' }); }) }); },
190 allowedTypes: ['image'],
191 value: card.imageId,
192 render: function (ref) {
193 return el('div', { style: { marginBottom: '12px' } },
194 card.imageUrl && el('img', { src: card.imageUrl, style: { width: '100%', height: '80px', objectFit: 'cover', borderRadius: '8px', marginBottom: '8px' } }),
195 el(Button, { onClick: ref.open, variant: 'secondary', __nextHasNoMarginBottom: true, style: { width: '100%' } },
196 card.imageUrl ? __('Change Image', 'blockenberg') : __('Upload Image', 'blockenberg')
197 ),
198 card.imageUrl && el(Button, {
199 onClick: function () { updateCard(idx, 'imageUrl', ''); },
200 variant: 'tertiary', isDestructive: true, style: { width: '100%', marginTop: '4px' }
201 }, __('Remove Image', 'blockenberg'))
202 );
203 }
204 })
205 ),
206 el(TextControl, { label: __('Tag / Category', 'blockenberg'), value: card.tag, onChange: function (v) { updateCard(idx, 'tag', v); }, __nextHasNoMarginBottom: true }),
207 el('div', { style: { marginTop: '8px' } }),
208 el(TextControl, { label: __('Title', 'blockenberg'), value: card.title, onChange: function (v) { updateCard(idx, 'title', v); }, __nextHasNoMarginBottom: true }),
209 el('div', { style: { marginTop: '8px' } }),
210 el(TextareaControl, { label: __('Description', 'blockenberg'), value: card.description, onChange: function (v) { updateCard(idx, 'description', v); }, rows: 3, __nextHasNoMarginBottom: true }),
211 el('div', { style: { marginTop: '8px' } }),
212 el(TextControl, { label: __('Link URL', 'blockenberg'), value: card.linkUrl, onChange: function (v) { updateCard(idx, 'linkUrl', v); }, type: 'url', __nextHasNoMarginBottom: true }),
213 el('div', { style: { marginTop: '8px' } }),
214 el(TextControl, { label: __('Link Label', 'blockenberg'), value: card.linkLabel, onChange: function (v) { updateCard(idx, 'linkLabel', v); }, __nextHasNoMarginBottom: true }),
215 cards.length > 2 && el(Button, {
216 onClick: function () { removeCard(idx); },
217 variant: 'tertiary', isDestructive: true, style: { marginTop: '12px' }
218 }, __('Remove Card', 'blockenberg'))
219 );
220 }),
221 el(Button, { onClick: addCard, variant: 'secondary', style: { width: '100%', marginTop: '8px' } }, __('+ Add Card', 'blockenberg'))
222 ),
223 /* Layout Panel */
224 el(PanelBody, { title: __('Layout & Deck Style', 'blockenberg'), initialOpen: false },
225 el(SelectControl, {
226 label: __('Deck Style', 'blockenberg'),
227 value: attr.deckStyle,
228 options: [
229 { label: 'Fan (rotated arc)', value: 'fan' },
230 { label: 'Stack (offset depth)', value: 'stack' },
231 { label: 'Cascade (angled left)', value: 'cascade' }
232 ],
233 onChange: function (v) { setAttr({ deckStyle: v }); },
234 __nextHasNoMarginBottom: true
235 }),
236 el('div', { style: { marginTop: '16px' } }),
237 el(RangeControl, { label: __('Card Width (px)', 'blockenberg'), value: attr.cardWidth, min: 200, max: 600, onChange: function (v) { setAttr({ cardWidth: v }); }, __nextHasNoMarginBottom: true }),
238 el('div', { style: { marginTop: '8px' } }),
239 el(RangeControl, { label: __('Card Height (px)', 'blockenberg'), value: attr.cardHeight, min: 200, max: 700, onChange: function (v) { setAttr({ cardHeight: v }); }, __nextHasNoMarginBottom: true }),
240 el('div', { style: { marginTop: '8px' } }),
241 el(RangeControl, { label: __('Corner Radius (px)', 'blockenberg'), value: attr.cardRadius, min: 0, max: 48, onChange: function (v) { setAttr({ cardRadius: v }); }, __nextHasNoMarginBottom: true }),
242 el('div', { style: { marginTop: '8px' } }),
243 el(RangeControl, { label: __('Stacked Cards Visible', 'blockenberg'), value: attr.stackCount, min: 1, max: 4, onChange: function (v) { setAttr({ stackCount: v }); }, __nextHasNoMarginBottom: true }),
244 el('div', { style: { marginTop: '8px' } }),
245 el(RangeControl, { label: __('Stack Angle (°)', 'blockenberg'), value: attr.stackAngle, min: 0, max: 30, onChange: function (v) { setAttr({ stackAngle: v }); }, __nextHasNoMarginBottom: true }),
246 el('div', { style: { marginTop: '8px' } }),
247 el(RangeControl, { label: __('Stack Offset (px)', 'blockenberg'), value: attr.stackOffset, min: 0, max: 60, onChange: function (v) { setAttr({ stackOffset: v }); }, __nextHasNoMarginBottom: true }),
248 el('div', { style: { marginTop: '8px' } }),
249 el(RangeControl, { label: __('Image Overlay Strength (%)', 'blockenberg'), value: attr.overlayStrength, min: 0, max: 100, onChange: function (v) { setAttr({ overlayStrength: v }); }, __nextHasNoMarginBottom: true }),
250 el('div', { style: { marginTop: '8px' } }),
251 el(SelectControl, {
252 label: __('Image Fit', 'blockenberg'),
253 value: attr.imageFit,
254 options: [{ label: 'Cover', value: 'cover' }, { label: 'Contain', value: 'contain' }],
255 onChange: function (v) { setAttr({ imageFit: v }); },
256 __nextHasNoMarginBottom: true
257 })
258 ),
259 /* Content Panel */
260 el(PanelBody, { title: __('Content Display', 'blockenberg'), initialOpen: false },
261 el(ToggleControl, { label: __('Show Tag / Category', 'blockenberg'), checked: attr.showTag, onChange: function (v) { setAttr({ showTag: v }); }, __nextHasNoMarginBottom: true }),
262 el(ToggleControl, { label: __('Show Title', 'blockenberg'), checked: attr.showTitle, onChange: function (v) { setAttr({ showTitle: v }); }, __nextHasNoMarginBottom: true }),
263 el(ToggleControl, { label: __('Show Description', 'blockenberg'), checked: attr.showDescription, onChange: function (v) { setAttr({ showDescription: v }); }, __nextHasNoMarginBottom: true }),
264 el(ToggleControl, { label: __('Show Link', 'blockenberg'), checked: attr.showLink, onChange: function (v) { setAttr({ showLink: v }); }, __nextHasNoMarginBottom: true }),
265 el('div', { style: { marginTop: '8px' } }),
266 el('div', { style: { marginTop: '8px' } }),
267 el('div', { style: { marginTop: '8px' } }),
268 ),
269 /* Animation Panel */
270 el(PanelBody, { title: __('Animation & Controls', 'blockenberg'), initialOpen: false },
271 el(ToggleControl, { label: __('Show Arrows', 'blockenberg'), checked: attr.showArrows, onChange: function (v) { setAttr({ showArrows: v }); }, __nextHasNoMarginBottom: true }),
272 el(ToggleControl, { label: __('Show Dots', 'blockenberg'), checked: attr.showDots, onChange: function (v) { setAttr({ showDots: v }); }, __nextHasNoMarginBottom: true }),
273 el(ToggleControl, { label: __('Auto Play', 'blockenberg'), checked: attr.autoPlay, onChange: function (v) { setAttr({ autoPlay: v }); }, __nextHasNoMarginBottom: true }),
274 attr.autoPlay && el(RangeControl, { label: __('Auto Interval (ms)', 'blockenberg'), value: attr.autoInterval, min: 1000, max: 8000, step: 500, onChange: function (v) { setAttr({ autoInterval: v }); }, __nextHasNoMarginBottom: true }),
275 el('div', { style: { marginTop: '8px' } }),
276 el(RangeControl, { label: __('Transition Duration (ms)', 'blockenberg'), value: attr.transitionDuration, min: 200, max: 1200, step: 50, onChange: function (v) { setAttr({ transitionDuration: v }); }, __nextHasNoMarginBottom: true })
277 ),
278 /* Colors */
279
280 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
281 el(getTypographyControl(), { label: __('Title', 'blockenberg'), value: attr.typoTitle, onChange: function (v) { setAttr({ typoTitle: v }); } }),
282 el(getTypographyControl(), { label: __('Description', 'blockenberg'), value: attr.typoDesc, onChange: function (v) { setAttr({ typoDesc: v }); } }),
283 el(RangeControl, { label: __('Tag Size (px)', 'blockenberg'), value: attr.tagSize, min: 9, max: 18, onChange: function (v) { setAttr({ tagSize: v }); }, __nextHasNoMarginBottom: true })
284 ),
285 el(PanelColorSettings, {
286 title: __('Colors', 'blockenberg'),
287 initialOpen: false,
288 colorSettings: [
289 { label: __('Card Background', 'blockenberg'), value: attr.cardBg, onChange: function (v) { setAttr({ cardBg: v || '#1e293b' }); } },
290 { label: __('Title Color', 'blockenberg'), value: attr.titleColor, onChange: function (v) { setAttr({ titleColor: v || '#ffffff' }); } },
291 { label: __('Description Color', 'blockenberg'), value: attr.descColor, onChange: function (v) { setAttr({ descColor: v || '#94a3b8' }); } },
292 { label: __('Tag Background', 'blockenberg'), value: attr.tagBg, onChange: function (v) { setAttr({ tagBg: v || '#6366f1' }); } },
293 { label: __('Tag Text', 'blockenberg'), value: attr.tagColor, onChange: function (v) { setAttr({ tagColor: v || '#ffffff' }); } },
294 { label: __('Link Color', 'blockenberg'), value: attr.linkColor, onChange: function (v) { setAttr({ linkColor: v || '#818cf8' }); } },
295 { label: __('Dot / Active Color', 'blockenberg'), value: attr.dotColor, onChange: function (v) { setAttr({ dotColor: v || '#6366f1' }); } },
296 { label: __('Arrow Background', 'blockenberg'), value: attr.arrowBg, onChange: function (v) { setAttr({ arrowBg: v || '#ffffff' }); } },
297 { label: __('Arrow Icon', 'blockenberg'), value: attr.arrowColor, onChange: function (v) { setAttr({ arrowColor: v || '#0f172a' }); } },
298 { label: __('Section Background', 'blockenberg'), value: attr.sectionBg, onChange: function (v) { setAttr({ sectionBg: v || '' }); } }
299 ]
300 })
301 );
302
303 return el('div', blockProps, inspector, deckPreview);
304 },
305
306 save: function (props) {
307 var attr = props.attributes;
308 var blockProps = useBlockProps.save();
309 return el('div', blockProps,
310 el('div', {
311 className: 'bkbg-cds-app',
312 'data-opts': JSON.stringify(attr)
313 })
314 );
315 }
316 });
317 }() );
318