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 / image-cards / index.js

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

280 lines 19.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* ====================================================
2 Image Cards Block — editor (index.js)
3 Block: blockenberg/image-cards
4 ==================================================== */
5 ( function () {
6 var el = wp.element.createElement;
7 var Fragment = wp.element.Fragment;
8 var useState = wp.element.useState;
9 var RichText = wp.blockEditor.RichText;
10 var MediaUpload = wp.blockEditor.MediaUpload;
11 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
12 var registerBlockType = wp.blocks.registerBlockType;
13 var InspectorControls = wp.blockEditor.InspectorControls;
14 var useBlockProps = wp.blockEditor.useBlockProps;
15 var PanelBody = wp.components.PanelBody;
16 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
17 var RangeControl = wp.components.RangeControl;
18 var SelectControl = wp.components.SelectControl;
19 var ToggleControl = wp.components.ToggleControl;
20 var TextControl = wp.components.TextControl;
21 var Button = wp.components.Button;
22 var ColorPicker = wp.components.ColorPicker;
23 var Popover = wp.components.Popover;
24 var __ = wp.i18n.__;
25
26 var _TypographyControl, _typoCssVars;
27 function getTypographyControl() { return _TypographyControl || (_TypographyControl = window.bkbgTypographyControl); }
28 function getTypoCssVars() { return _typoCssVars || (_typoCssVars = window.bkbgTypoCssVars); }
29
30 function uid() { return 'c' + Math.random().toString(36).slice(2,7); }
31 function move(arr, from, to) { var a=arr.slice(); a.splice(to,0,a.splice(from,1)[0]); return a; }
32
33 function buildWrapStyle(a) {
34 var s = {
35 '--bkbg-imgc-cols': a.columns,
36 '--bkbg-imgc-gap': a.gap + 'px',
37 '--bkbg-imgc-r': a.cardRadius + 'px',
38 '--bkbg-imgc-img-r': a.imageRadius + 'px',
39 '--bkbg-imgc-aspect': a.imageAspect,
40 '--bkbg-imgc-pt': a.paddingTop + 'px',
41 '--bkbg-imgc-pb': a.paddingBottom + 'px',
42 '--bkbg-imgc-card-bg': a.cardBg,
43 '--bkbg-imgc-brd': a.cardBorderColor,
44 '--bkbg-imgc-title-c': a.titleColor,
45 '--bkbg-imgc-desc-c': a.descColor,
46 '--bkbg-imgc-cta-c': a.ctaColor,
47 '--bkbg-imgc-ph-bg': a.imagePlaceholderBg,
48 '--bkbg-imgc-title-sz':a.titleSize + 'px',
49 '--bkbg-imgc-title-w': a.titleFontWeight || '700',
50 '--bkbg-imgc-title-lh':a.titleLineHeight || 1.3,
51 '--bkbg-imgc-desc-sz': a.descSize + 'px',
52 '--bkbg-imgc-desc-w': a.descFontWeight || '400',
53 '--bkbg-imgc-desc-lh': a.descLineHeight || 1.6,
54 background: a.sectionBg || undefined,
55 };
56 var _tv2 = getTypoCssVars();
57 if (_tv2) {
58 Object.assign(s, _tv2(a.titleTypo, '--bkbg-imgc-tt-'));
59 Object.assign(s, _tv2(a.descTypo, '--bkbg-imgc-ds-'));
60 }
61 return s;
62 }
63
64 /* ── Single card preview ── */
65 function ImageCard(props) {
66 var card = props.card;
67 var a = props.a;
68 var onChange = props.onChange;
69
70 var imgEl = el('div', { className: 'bkbg-imgc-image-wrap' },
71 card.imageUrl
72 ? el('img', { src: card.imageUrl, alt: card.title, className: 'bkbg-imgc-img' })
73 : el('div', { className: 'bkbg-imgc-img-placeholder' },
74 el(MediaUploadCheck, null,
75 el(MediaUpload, {
76 onSelect: function(m){ onChange({ imageUrl:m.url, imageId:m.id }); },
77 allowedTypes: ['image'], value: card.imageId,
78 render: function(o){ return el(Button, { onClick:o.open, variant:'secondary', isSmall:true, className:'bkbg-imgc-upload-btn' }, __('Upload image','blockenberg')); }
79 })
80 )
81 )
82 );
83
84 return el('article', { className: 'bkbg-imgc-card bkbg-imgc-card--' + a.cardStyle + (a.hoverEffect !== 'none' ? ' bkbg-imgc-hover--' + a.hoverEffect : '') },
85 imgEl,
86 el('div', { className: 'bkbg-imgc-body' },
87 a.showTag && card.tag ? el('span', { className: 'bkbg-imgc-tag', style: { background: card.accentColor + '22', color: card.accentColor } }, card.tag) : null,
88 el('h3', { className: 'bkbg-imgc-title' }, card.title),
89 a.showDescription ? el('p', { className: 'bkbg-imgc-desc' }, card.description) : null,
90 a.showCta && card.ctaLabel ? el('a', { href: card.ctaUrl||'#', className: 'bkbg-imgc-cta bkbg-imgc-cta--' + a.ctaStyle, style: { color: card.accentColor || a.ctaColor }, onClick: function(e){ e.preventDefault(); } },
91 card.ctaLabel,
92 a.ctaStyle === 'link-arrow' ? el('span', { className: 'bkbg-imgc-arrow' }, '') : null
93 ) : null
94 )
95 );
96 }
97
98 var BkbgColorSwatch = function (props) {
99 var _st = useState(false); var isOpen = _st[0]; var setOpen = _st[1];
100 return el(Fragment, {},
101 el('div', { style: { display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '8px' } },
102 el('span', { style: { fontSize: '11px', fontWeight: 500, textTransform: 'uppercase', color: '#1e1e1e' } }, props.label),
103 el('button', {
104 type: 'button',
105 onClick: function () { setOpen(!isOpen); },
106 style: { width: '28px', height: '28px', borderRadius: '4px', border: '1px solid #ccc', background: props.value || '#ffffff', cursor: 'pointer', padding: 0 }
107 })
108 ),
109 isOpen && el(Popover, { onClose: function () { setOpen(false); } },
110 el('div', { style: { padding: '8px' } },
111 el(ColorPicker, { color: props.value, onChangeComplete: function (c) { props.onChange(c.hex); }, enableAlpha: true })
112 )
113 )
114 );
115 };
116
117 registerBlockType('blockenberg/image-cards', {
118 edit: function (props) {
119 var a = props.attributes;
120 var setAttr = props.setAttributes;
121 var cards = a.cards;
122
123 var _act = useState(null);
124 var activeId = _act[0]; var setActiveId = _act[1];
125
126 function updateCard(id, patch) {
127 setAttr({ cards: cards.map(function(c){ return c.id===id ? Object.assign({},c,patch) : c; }) });
128 }
129
130 var blockProps = useBlockProps({
131 className: 'bkbg-imgc-wrap',
132 style: buildWrapStyle(a)
133 });
134
135 function cardRow(card, idx) {
136 var isActive = activeId === card.id;
137 return el('div', { key: card.id },
138 el('div', {
139 style: { display:'flex', alignItems:'center', gap:'6px', background: isActive?'#f3f0ff':'#f8f9fa', border: isActive?'1px solid #6c3fb5':'1px solid #e2e8f0', borderRadius:'6px', padding:'6px 8px', marginBottom:'4px', cursor:'pointer' },
140 onClick: function(){ setActiveId(isActive ? null : card.id); }
141 },
142 card.imageUrl ? el('img', { src:card.imageUrl, style:{ height:'24px', width:'36px', objectFit:'cover', borderRadius:'3px', flexShrink:0 } }) : el('span', { style:{ fontSize:'16px' } }, '🖼️'),
143 el('span', { style:{ flex:1, fontSize:'13px', fontWeight:500, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' } }, card.title || '(untitled)'),
144 el(Button, { icon:'arrow-up', isSmall:true, disabled:idx===0, onClick:function(e){ e.stopPropagation(); setAttr({cards:move(cards,idx,idx-1)}); } }),
145 el(Button, { icon:'arrow-down', isSmall:true, disabled:idx===cards.length-1, onClick:function(e){ e.stopPropagation(); setAttr({cards:move(cards,idx,idx+1)}); } }),
146 el(Button, { icon:'no-alt', isSmall:true, isDestructive:true, onClick:function(e){ e.stopPropagation(); setAttr({cards:cards.filter(function(_,i){ return i!==idx; })}); if(activeId===card.id) setActiveId(null); } })
147 ),
148 isActive ? el('div', { style:{ padding:'10px', background:'#faf8ff', border:'1px solid #6c3fb5', borderTop:'none', borderRadius:'0 0 6px 6px', marginBottom:'4px' } },
149 el(TextControl, { label:__('Title','blockenberg'), value:card.title, onChange:function(v){ updateCard(card.id,{title:v}); } }),
150 el(TextControl, { label:__('Description','blockenberg'), value:card.description, onChange:function(v){ updateCard(card.id,{description:v}); } }),
151 el(TextControl, { label:__('Tag/category','blockenberg'), value:card.tag, onChange:function(v){ updateCard(card.id,{tag:v}); } }),
152 el(TextControl, { label:__('CTA label','blockenberg'), value:card.ctaLabel, onChange:function(v){ updateCard(card.id,{ctaLabel:v}); } }),
153 el(TextControl, { label:__('CTA URL','blockenberg'), value:card.ctaUrl, onChange:function(v){ updateCard(card.id,{ctaUrl:v}); } }),
154 el(BkbgColorSwatch, { label:__('Accent color','blockenberg'), value:card.accentColor, onChange:function(v){ updateCard(card.id,{accentColor:v}); } }),
155 el(MediaUploadCheck, null,
156 el(MediaUpload, {
157 onSelect:function(m){ updateCard(card.id,{imageUrl:m.url,imageId:m.id}); },
158 allowedTypes:['image'], value:card.imageId,
159 render:function(o){ return el(Button, { onClick:o.open, variant:'secondary', isSmall:true }, card.imageUrl ? __('Change image','blockenberg') : __('Upload image','blockenberg')); }
160 })
161 ),
162 card.imageUrl ? el(Button, { isDestructive:true, variant:'tertiary', isSmall:true, style:{marginTop:'4px'}, onClick:function(){ updateCard(card.id,{imageUrl:'',imageId:0}); } }, __('Remove image','blockenberg')) : null
163 ) : null
164 );
165 }
166
167 var inspector = el(InspectorControls, null,
168 el(PanelBody, { title:__('Cards','blockenberg'), initialOpen:true },
169 cards.map(function(c,i){ return cardRow(c,i); }),
170 el(Button, { variant:'primary', style:{ marginTop:'8px', width:'100%' },
171 onClick:function(){
172 var n = { id:uid(), imageUrl:'', imageId:0, tag:'Category', title:'New Card Title', description:'Card description goes here.', ctaLabel:'Read more', ctaUrl:'#', accentColor:'#6c3fb5' };
173 setAttr({ cards: cards.concat([n]) });
174 setActiveId(n.id);
175 }
176 }, __('+ Add Card','blockenberg'))
177 ),
178 el(PanelBody, { title:__('Layout & Style','blockenberg'), initialOpen:false },
179 el(SelectControl, { label:__('Card style','blockenberg'), value:a.cardStyle, options:[
180 {label:'Shadow card',value:'shadow'},
181 {label:'Bordered',value:'bordered'},
182 {label:'Flat',value:'flat'},
183 {label:'Overlay (image bg)',value:'overlay'},
184 ], onChange:function(v){ setAttr({cardStyle:v}); } }),
185 el(SelectControl, { label:__('Hover effect','blockenberg'), value:a.hoverEffect, options:[
186 {label:'Lift',value:'lift'},
187 {label:'Scale image',value:'scale'},
188 {label:'None',value:'none'},
189 ], onChange:function(v){ setAttr({hoverEffect:v}); } }),
190 el(SelectControl, { label:__('CTA style','blockenberg'), value:a.ctaStyle, options:[
191 {label:'Link + arrow',value:'link-arrow'},
192 {label:'Solid button',value:'btn'},
193 ], onChange:function(v){ setAttr({ctaStyle:v}); } }),
194 el(SelectControl, { label:__('Image aspect ratio','blockenberg'), value:a.imageAspect, options:[
195 {label:'16:9',value:'16/9'},
196 {label:'4:3',value:'4/3'},
197 {label:'3:2',value:'3/2'},
198 {label:'1:1 (square)',value:'1/1'},
199 {label:'2:3 (portrait)',value:'2/3'},
200 ], onChange:function(v){ setAttr({imageAspect:v}); } }),
201 el(RangeControl, { label:__('Columns','blockenberg'), value:a.columns, min:1, max:4, onChange:function(v){ setAttr({columns:v}); } }),
202 el(RangeControl, { label:__('Gap (px)','blockenberg'), value:a.gap, min:8, max:80, onChange:function(v){ setAttr({gap:v}); } }),
203 el(RangeControl, { label:__('Card radius (px)','blockenberg'), value:a.cardRadius, min:0, max:32, onChange:function(v){ setAttr({cardRadius:v}); } }),
204 el(RangeControl, { label:__('Image radius (px)','blockenberg'), value:a.imageRadius, min:0, max:24, onChange:function(v){ setAttr({imageRadius:v}); } }),
205 el(ToggleControl, { label:__('Equal height cards','blockenberg'), checked:a.equalHeight, onChange:function(v){ setAttr({equalHeight:v}); } }),
206 el(ToggleControl, { label:__('Show tag/category','blockenberg'), checked:a.showTag, onChange:function(v){ setAttr({showTag:v}); } }),
207 el(ToggleControl, { label:__('Show description','blockenberg'), checked:a.showDescription, onChange:function(v){ setAttr({showDescription:v}); } }),
208 el(ToggleControl, { label:__('Show CTA','blockenberg'), checked:a.showCta, onChange:function(v){ setAttr({showCta:v}); } })
209 ),
210 el(PanelBody, { title:__('Typography','blockenberg'), initialOpen:false },
211 getTypographyControl() && el( getTypographyControl(), { label: __( 'Title', 'blockenberg' ), value: a.titleTypo || {}, onChange: function(v){ setAttr({ titleTypo: v }); } }),
212 getTypographyControl() && el( getTypographyControl(), { label: __( 'Description', 'blockenberg' ), value: a.descTypo || {}, onChange: function(v){ setAttr({ descTypo: v }); } })
213 ),
214 el(PanelBody, { title:__('Spacing','blockenberg'), initialOpen:false },
215 el(RangeControl, { label:__('Padding top (px)','blockenberg'), value:a.paddingTop, min:0, max:160, onChange:function(v){ setAttr({paddingTop:v}); } }),
216 el(RangeControl, { label:__('Padding bottom (px)','blockenberg'), value:a.paddingBottom, min:0, max:160, onChange:function(v){ setAttr({paddingBottom:v}); } })
217 ),
218 el(PanelColorSettings, { title:__('Colors','blockenberg'), initialOpen:false, colorSettings:[
219 {label:__('Section bg','blockenberg'), value:a.sectionBg, onChange:function(v){ setAttr({sectionBg:v||''}); }},
220 {label:__('Card bg','blockenberg'), value:a.cardBg, onChange:function(v){ setAttr({cardBg:v||''}); }},
221 {label:__('Card border','blockenberg'), value:a.cardBorderColor, onChange:function(v){ setAttr({cardBorderColor:v||''}); }},
222 {label:__('Title','blockenberg'), value:a.titleColor, onChange:function(v){ setAttr({titleColor:v||''}); }},
223 {label:__('Description','blockenberg'),value:a.descColor, onChange:function(v){ setAttr({descColor:v||''}); }},
224 {label:__('CTA default','blockenberg'),value:a.ctaColor, onChange:function(v){ setAttr({ctaColor:v||''}); }},
225 {label:__('Image placeholder','blockenberg'),value:a.imagePlaceholderBg, onChange:function(v){ setAttr({imagePlaceholderBg:v||''}); }},
226 ] })
227 );
228
229 return el(Fragment, null,
230 inspector,
231 el('div', blockProps,
232 el('div', { className: 'bkbg-imgc-grid' + (a.equalHeight ? ' bkbg-imgc-equal-h' : '') },
233 cards.map(function(card) {
234 return el(ImageCard, { key:card.id, card:card, a:a, onChange:function(patch){ updateCard(card.id,patch); } });
235 })
236 )
237 )
238 );
239 },
240
241 save: function (props) {
242 var a = props.attributes;
243 var blockProps = wp.blockEditor.useBlockProps.save({
244 className: 'bkbg-imgc-wrap',
245 style: (function(){
246 var s = {
247 '--bkbg-imgc-cols': a.columns, '--bkbg-imgc-gap': a.gap+'px', '--bkbg-imgc-r': a.cardRadius+'px', '--bkbg-imgc-img-r': a.imageRadius+'px', '--bkbg-imgc-aspect': a.imageAspect, '--bkbg-imgc-pt': a.paddingTop+'px', '--bkbg-imgc-pb': a.paddingBottom+'px', '--bkbg-imgc-card-bg': a.cardBg, '--bkbg-imgc-brd': a.cardBorderColor, '--bkbg-imgc-title-c': a.titleColor, '--bkbg-imgc-desc-c': a.descColor, '--bkbg-imgc-cta-c': a.ctaColor, '--bkbg-imgc-ph-bg': a.imagePlaceholderBg, '--bkbg-imgc-title-sz': a.titleSize+'px', '--bkbg-imgc-title-w': a.titleFontWeight||'700', '--bkbg-imgc-title-lh': a.titleLineHeight||1.3, '--bkbg-imgc-desc-sz': a.descSize+'px', '--bkbg-imgc-desc-w': a.descFontWeight||'400', '--bkbg-imgc-desc-lh': a.descLineHeight||1.6, background: a.sectionBg||undefined,
248 };
249 var _tv2 = getTypoCssVars();
250 if (_tv2) {
251 Object.assign(s, _tv2(a.titleTypo, '--bkbg-imgc-tt-'));
252 Object.assign(s, _tv2(a.descTypo, '--bkbg-imgc-ds-'));
253 }
254 return s;
255 })()
256 });
257 return el('div', blockProps,
258 el('div', { className: 'bkbg-imgc-grid' + (a.equalHeight ? ' bkbg-imgc-equal-h' : '') },
259 a.cards.map(function(card) {
260 return el('article', { key:card.id, className:'bkbg-imgc-card bkbg-imgc-card--'+a.cardStyle+(a.hoverEffect!=='none'?' bkbg-imgc-hover--'+a.hoverEffect:'') },
261 el('div', { className:'bkbg-imgc-image-wrap' },
262 card.imageUrl ? el('img', { src:card.imageUrl, alt:card.title, className:'bkbg-imgc-img', loading:'lazy' }) : null
263 ),
264 el('div', { className:'bkbg-imgc-body' },
265 a.showTag && card.tag ? el('span', { className:'bkbg-imgc-tag', style:{ background:card.accentColor+'22', color:card.accentColor } }, card.tag) : null,
266 el('h3', { className:'bkbg-imgc-title' }, card.title),
267 a.showDescription ? el('p', { className:'bkbg-imgc-desc' }, card.description) : null,
268 a.showCta && card.ctaLabel ? el('a', { href:card.ctaUrl||'#', className:'bkbg-imgc-cta bkbg-imgc-cta--'+a.ctaStyle, style:{ color:card.accentColor||a.ctaColor } },
269 card.ctaLabel,
270 a.ctaStyle==='link-arrow' ? el('span', { className:'bkbg-imgc-arrow' }, '') : null
271 ) : null
272 )
273 );
274 })
275 )
276 );
277 }
278 });
279 }() );
280