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

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

377 lines 20.2 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 __ = wp.i18n.__;
5 var registerBlockType = wp.blocks.registerBlockType;
6 var InspectorControls = wp.blockEditor.InspectorControls;
7 var MediaUpload = wp.blockEditor.MediaUpload;
8 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
9 var useBlockProps = wp.blockEditor.useBlockProps;
10 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
11 var PanelBody = wp.components.PanelBody;
12 var ToggleControl = wp.components.ToggleControl;
13 var RangeControl = wp.components.RangeControl;
14 var SelectControl = wp.components.SelectControl;
15 var TextControl = wp.components.TextControl;
16 var Button = wp.components.Button;
17
18 function getTypographyControl() {
19 return (typeof window.bkbgTypographyControl !== 'undefined') ? window.bkbgTypographyControl : null;
20 }
21 function getTypoCssVars() {
22 return (typeof window.bkbgTypoCssVars !== 'undefined') ? window.bkbgTypoCssVars : function () { return {}; };
23 }
24 function _tv(typoObj, prefix) { return getTypoCssVars()(typoObj || {}, prefix); }
25
26 var STYLE_OPTIONS = [
27 { label: 'Card (bordered + shadow)', value: 'card' },
28 { label: 'Flat (no shadow)', value: 'flat' },
29 { label: 'Minimal (no border)', value: 'minimal' },
30 { label: 'Badge (accent header)', value: 'badge' },
31 ];
32 var ORIENTATION_OPTIONS = [
33 { label: 'Vertical (stacked)', value: 'vertical' },
34 { label: 'Horizontal (side by side)', value: 'horizontal' },
35 ];
36 var ALIGN_OPTIONS = [
37 { label: 'Left', value: 'left' },
38 { label: 'Center', value: 'center' },
39 { label: 'Right', value: 'right' },
40 ];
41 var PLATFORM_OPTIONS = [
42 { label: 'LinkedIn', value: 'linkedin' },
43 { label: 'GitHub', value: 'github' },
44 { label: 'Twitter/X', value: 'twitter' },
45 { label: 'Instagram', value: 'instagram' },
46 { label: 'YouTube', value: 'youtube' },
47 { label: 'Dribbble', value: 'dribbble' },
48 { label: 'Behance', value: 'behance' },
49 { label: 'Custom', value: 'custom' },
50 ];
51 var PLATFORM_ICONS = {
52 linkedin: 'in',
53 github: '</>',
54 twitter: 'X',
55 instagram: 'IG',
56 youtube: '',
57 dribbble: '',
58 behance: '',
59 custom: '🔗',
60 };
61 var CONTACT_ICONS = { email: '', phone: '', address: '📍', website: '🌐' };
62
63 function updateLink(arr, idx, field, val) {
64 return arr.map(function (lnk, i) {
65 if (i !== idx) return lnk;
66 var p = {}; p[field] = val;
67 return Object.assign({}, lnk, p);
68 });
69 }
70
71 function initials(name) {
72 return (name || 'SJ').split(' ').map(function (w) { return w[0] || ''; }).join('').slice(0, 2).toUpperCase();
73 }
74
75 // ── Editor preview builder ──
76 function buildPreview(a) {
77 var isHoriz = a.cardOrientation === 'horizontal';
78 var isBadge = a.style === 'badge';
79
80 var wrapStyle = {
81 maxWidth: a.maxWidth + 'px',
82 borderRadius: a.borderRadius + 'px',
83 background: a.cardBg,
84 overflow: 'hidden',
85 fontFamily: 'inherit',
86 };
87 if (a.style === 'card' || a.style === 'flat') {
88 wrapStyle.border = '1px solid ' + a.cardBorder;
89 }
90 if (a.style === 'card' && a.showShadow) {
91 wrapStyle.boxShadow = '0 4px 24px rgba(0,0,0,0.10)';
92 }
93
94 var innerAlign = a.align === 'center' ? 'center' : a.align === 'right' ? 'flex-end' : 'flex-start';
95
96 // Avatar
97 var avatarEl;
98 if (a.showPhoto && a.photoUrl) {
99 avatarEl = el('img', {
100 src: a.photoUrl,
101 style: {
102 width: a.avatarSize + 'px', height: a.avatarSize + 'px',
103 borderRadius: '50%', objectFit: 'cover',
104 border: '3px solid ' + (isBadge ? '#fff' : a.cardBg),
105 flexShrink: 0,
106 }
107 });
108 } else if (a.showPhoto) {
109 avatarEl = el('div', {
110 style: {
111 width: a.avatarSize + 'px', height: a.avatarSize + 'px', borderRadius: '50%',
112 background: a.accentBg, color: '#fff', display: 'flex', alignItems: 'center',
113 justifyContent: 'center', fontSize: Math.round(a.avatarSize * 0.38) + 'px',
114 fontWeight: 700, flexShrink: 0,
115 border: '3px solid ' + (isBadge ? '#fff' : a.cardBg),
116 }
117 }, initials(a.name));
118 }
119
120 // Badge header
121 var headerEl = isBadge
122 ? el('div', {
123 style: {
124 background: a.accentBg, height: 72,
125 display: 'flex', alignItems: 'flex-end',
126 justifyContent: 'center', paddingBottom: (a.avatarSize / 2) + 'px',
127 }
128 })
129 : null;
130
131 // Text block
132 var textBlock = el('div', null,
133 el('div', {
134 className: 'bkbg-cc-name',
135 style: {
136 color: a.nameColor,
137 marginBottom: 2,
138 }
139 }, a.name || 'Name'),
140 (a.jobTitle || a.company) && el('div', {
141 className: 'bkbg-cc-meta',
142 style: { color: a.titleColor, marginBottom: 2 }
143 }, [a.jobTitle, a.company].filter(Boolean).join(' · ')),
144 );
145
146 // Contact fields
147 var contactEl = el('div', {
148 style: { marginTop: 10, display: 'flex', flexDirection: 'column', gap: 4 }
149 },
150 ['email', 'phone', 'address', 'website'].filter(function (f) { return !!a[f]; }).map(function (f) {
151 return el('div', {
152 key: f,
153 className: 'bkbg-cc-contact-row',
154 style: { display: 'flex', alignItems: 'center', gap: 6, color: a.companyColor }
155 },
156 el('span', { style: { fontSize: 13 } }, CONTACT_ICONS[f]),
157 el('span', null, a[f])
158 );
159 })
160 );
161
162 // Social links
163 var linksEl = a.showLinks && a.links.length > 0 && el('div', {
164 style: { display: 'flex', gap: 8, marginTop: 12, justifyContent: innerAlign, flexWrap: 'wrap' }
165 },
166 a.links.map(function (lnk, i) {
167 return el('div', {
168 key: i,
169 style: {
170 width: 32, height: 32, borderRadius: 8,
171 background: a.iconBg, color: a.iconColor,
172 display: 'flex', alignItems: 'center', justifyContent: 'center',
173 fontSize: 11, fontWeight: 700, cursor: 'default',
174 }
175 }, PLATFORM_ICONS[lnk.platform] || '🔗');
176 })
177 );
178
179 // Divider
180 var dividerEl = a.showDivider && el('div', {
181 style: { height: 1, background: a.dividerColor, margin: '12px 0' }
182 });
183
184 // Assemble body
185 var bodyStyle = {
186 padding: a.cardPadding + 'px',
187 display: 'flex',
188 gap: 16,
189 flexDirection: isHoriz ? 'row' : 'column',
190 alignItems: isHoriz ? 'center' : innerAlign,
191 textAlign: isHoriz ? 'left' : a.align,
192 };
193 if (isBadge) { bodyStyle.marginTop = -(a.avatarSize / 2) + 'px'; }
194
195 return el('div', { style: wrapStyle },
196 headerEl,
197 el('div', { style: bodyStyle },
198 isBadge
199 ? el('div', { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12 } },
200 avatarEl,
201 textBlock,
202 dividerEl,
203 contactEl,
204 linksEl
205 )
206 : el(Fragment, null,
207 avatarEl,
208 el('div', { style: { flex: 1 } },
209 textBlock,
210 dividerEl,
211 contactEl,
212 linksEl
213 )
214 )
215 )
216 );
217 }
218
219 registerBlockType('blockenberg/contact-card', {
220 title: __('Contact Card', 'blockenberg'),
221 icon: 'id-alt',
222 category: 'bkbg-business',
223 description: __('Business card with photo, contact details, social links. Multiple style variants.', 'blockenberg'),
224
225 edit: function (props) {
226 var a = props.attributes;
227 var set = props.setAttributes;
228 var blockProps = useBlockProps({ style: Object.assign(
229 { display: 'flex', justifyContent: a.align === 'center' ? 'center' : a.align === 'right' ? 'flex-end' : 'flex-start' },
230 _tv(a.typoName, '--bkcc-name-'),
231 _tv(a.typoMeta, '--bkcc-meta-')
232 ) });
233
234 return el(Fragment, null,
235 el(InspectorControls, null,
236
237 // Identity
238 el(PanelBody, { title: 'Identity', initialOpen: true },
239 el(TextControl, { label: 'Name', value: a.name, onChange: function (v) { set({ name: v }); }, __nextHasNoMarginBottom: true }),
240 el('div', { style: { height: 8 } }),
241 el(TextControl, { label: 'Job title', value: a.jobTitle, onChange: function (v) { set({ jobTitle: v }); }, __nextHasNoMarginBottom: true }),
242 el('div', { style: { height: 8 } }),
243 el(TextControl, { label: 'Company', value: a.company, onChange: function (v) { set({ company: v }); }, __nextHasNoMarginBottom: true }),
244 el('div', { style: { height: 8 } }),
245 el(ToggleControl, { label: 'Show photo', checked: a.showPhoto, onChange: function (v) { set({ showPhoto: v }); }, __nextHasNoMarginBottom: true }),
246 a.showPhoto && el(Fragment, null,
247 el('div', { style: { height: 6 } }),
248 el(MediaUploadCheck, null,
249 el(MediaUpload, {
250 onSelect: function (m) { set({ photoUrl: m.url, photoId: m.id }); },
251 allowedTypes: ['image'],
252 value: a.photoId,
253 render: function (mProps) {
254 return el(Fragment, null,
255 a.photoUrl && el('img', { src: a.photoUrl, style: { width: '100%', height: 80, objectFit: 'cover', borderRadius: 8, marginBottom: 6 } }),
256 el(Button, { variant: 'secondary', size: 'small', onClick: mProps.open, __nextHasNoMarginBottom: true }, a.photoUrl ? 'Replace Photo' : 'Select Photo'),
257 a.photoUrl && el(Button, { isDestructive: true, variant: 'link', size: 'small', onClick: function () { set({ photoUrl: '', photoId: 0 }); }, style: { marginLeft: 8 }, __nextHasNoMarginBottom: true }, 'Remove')
258 );
259 }
260 })
261 )
262 )
263 ),
264
265 // Contact Details
266 el(PanelBody, { title: 'Contact Details', initialOpen: false },
267 ['email', 'phone', 'address', 'website'].map(function (f, i) {
268 return el(Fragment, { key: f },
269 i > 0 && el('div', { style: { height: 8 } }),
270 el(TextControl, { label: f.charAt(0).toUpperCase() + f.slice(1), value: a[f], onChange: function (v) { var p = {}; p[f] = v; set(p); }, __nextHasNoMarginBottom: true })
271 );
272 })
273 ),
274
275 // Social Links
276 el(PanelBody, { title: 'Social Links (' + a.links.length + ')', initialOpen: false },
277 el(ToggleControl, { label: 'Show links', checked: a.showLinks, onChange: function (v) { set({ showLinks: v }); }, __nextHasNoMarginBottom: true }),
278 a.showLinks && el(Fragment, null,
279 el('div', { style: { height: 8 } }),
280 a.links.map(function (lnk, i) {
281 return el('div', {
282 key: i,
283 style: { border: '1px solid #e5e7eb', borderRadius: 8, padding: '8px 10px', marginBottom: 8, background: '#fafafa' }
284 },
285 el('div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 } },
286 el('span', { style: { fontSize: 16 } }, PLATFORM_ICONS[lnk.platform] || '🔗'),
287 el('span', { style: { fontSize: 13, fontWeight: 600 } }, lnk.label || lnk.platform)
288 ),
289 el(SelectControl, { label: 'Platform', value: lnk.platform, options: PLATFORM_OPTIONS, onChange: function (v) { set({ links: updateLink(a.links, i, 'platform', v) }); }, __nextHasNoMarginBottom: true }),
290 el('div', { style: { height: 6 } }),
291 el(TextControl, { label: 'URL', value: lnk.url, onChange: function (v) { set({ links: updateLink(a.links, i, 'url', v) }); }, __nextHasNoMarginBottom: true }),
292 el('div', { style: { height: 6 } }),
293 el(TextControl, { label: 'Link label (for aria)', value: lnk.label, onChange: function (v) { set({ links: updateLink(a.links, i, 'label', v) }); }, __nextHasNoMarginBottom: true }),
294 el('div', { style: { height: 4 } }),
295 el(Button, { isDestructive: true, variant: 'link', size: 'small', onClick: function () { set({ links: a.links.filter(function (_, idx) { return idx !== i; }) }); }, __nextHasNoMarginBottom: true }, 'Remove')
296 );
297 }),
298 el(Button, { variant: 'secondary', onClick: function () { set({ links: a.links.concat([{ platform: 'custom', url: '', label: 'Link' }]) }); }, style: { width: '100%', justifyContent: 'center' }, __nextHasNoMarginBottom: true }, '+ Add Link')
299 )
300 ),
301
302 // Appearance
303 el(PanelBody, { title: 'Appearance', initialOpen: false },
304 el(SelectControl, { label: 'Card style', value: a.style, options: STYLE_OPTIONS, onChange: function (v) { set({ style: v }); }, __nextHasNoMarginBottom: true }),
305 el('div', { style: { height: 8 } }),
306 el(SelectControl, { label: 'Orientation', value: a.cardOrientation, options: ORIENTATION_OPTIONS, onChange: function (v) { set({ cardOrientation: v }); }, __nextHasNoMarginBottom: true }),
307 el('div', { style: { height: 8 } }),
308 el(SelectControl, { label: 'Alignment', value: a.align, options: ALIGN_OPTIONS, onChange: function (v) { set({ align: v }); }, __nextHasNoMarginBottom: true }),
309 el('div', { style: { height: 8 } }),
310 el(RangeControl, { label: 'Max width (px)', value: a.maxWidth, min: 200, max: 700, onChange: function (v) { set({ maxWidth: v }); }, __nextHasNoMarginBottom: true }),
311 el('div', { style: { height: 8 } }),
312 el(RangeControl, { label: 'Avatar size (px)', value: a.avatarSize, min: 48, max: 140, onChange: function (v) { set({ avatarSize: v }); }, __nextHasNoMarginBottom: true }),
313 el('div', { style: { height: 8 } }),
314 el(RangeControl, { label: 'Border radius (px)', value: a.borderRadius, min: 0, max: 32, onChange: function (v) { set({ borderRadius: v }); }, __nextHasNoMarginBottom: true }),
315 el('div', { style: { height: 8 } }),
316 el(RangeControl, { label: 'Card padding (px)', value: a.cardPadding, min: 12, max: 60, onChange: function (v) { set({ cardPadding: v }); }, __nextHasNoMarginBottom: true }),
317 el('div', { style: { height: 8 } }),
318 el('div', { style: { height: 8 } }),
319 el('div', { style: { height: 8 } }),
320 el(ToggleControl, { label: 'Show shadow', checked: a.showShadow, onChange: function (v) { set({ showShadow: v }); }, __nextHasNoMarginBottom: true }),
321 el('div', { style: { height: 4 } }),
322 el(ToggleControl, { label: 'Show divider', checked: a.showDivider, onChange: function (v) { set({ showDivider: v }); }, __nextHasNoMarginBottom: true })
323 ),
324
325 // Colors
326
327 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
328 (function () {
329 var TC = getTypographyControl();
330 if (!TC) return el('p', { style: { color: '#999', fontSize: '12px', padding: '8px 0' } }, __('Typography control loading…', 'blockenberg'));
331 return el(Fragment, {},
332 el(TC, {
333 label: __('Name', 'blockenberg'),
334 value: a.typoName || {},
335 onChange: function (v) { set({ typoName: v }); }
336 }),
337 el(TC, {
338 label: __('Meta / Details', 'blockenberg'),
339 value: a.typoMeta || {},
340 onChange: function (v) { set({ typoMeta: v }); }
341 })
342 );
343 })()
344 ),
345 el(PanelColorSettings, {
346 title: 'Colors',
347 initialOpen: false,
348 colorSettings: [
349 { label: 'Card Background', value: a.cardBg, onChange: function (v) { set({ cardBg: v || '#ffffff' }); } },
350 { label: 'Card Border', value: a.cardBorder, onChange: function (v) { set({ cardBorder: v || '#e5e7eb' }); } },
351 { label: 'Accent (header / avatar bg)', value: a.accentBg, onChange: function (v) { set({ accentBg: v || '#6366f1' }); } },
352 { label: 'Name Color', value: a.nameColor, onChange: function (v) { set({ nameColor: v || '#111827' }); } },
353 { label: 'Title Color', value: a.titleColor, onChange: function (v) { set({ titleColor: v || '#6b7280' }); } },
354 { label: 'Detail Text', value: a.companyColor, onChange: function (v) { set({ companyColor: v || '#374151' }); } },
355 { label: 'Icon Background', value: a.iconBg, onChange: function (v) { set({ iconBg: v || '#f1f5f9' }); } },
356 { label: 'Icon Color', value: a.iconColor, onChange: function (v) { set({ iconColor: v || '#374151' }); } },
357 { label: 'Link Hover Color', value: a.linkColor, onChange: function (v) { set({ linkColor: v || '#6366f1' }); } },
358 { label: 'Divider Color', value: a.dividerColor, onChange: function (v) { set({ dividerColor: v || '#f1f5f9' }); } },
359 ]
360 })
361 ),
362
363 // ── Editor Preview ──
364 el('div', blockProps,
365 buildPreview(a)
366 )
367 );
368 },
369
370 save: function (props) {
371 return el('div', useBlockProps.save({ style: Object.assign({}, _tv(props.attributes.typoName, '--bkcc-name-'), _tv(props.attributes.typoMeta, '--bkcc-meta-')) }),
372 el('div', { className: 'bkbg-cc-app', 'data-opts': JSON.stringify(props.attributes) })
373 );
374 }
375 });
376 }() );
377