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 / author-box / index.js

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

438 lines 24.5 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 useBlockProps = wp.blockEditor.useBlockProps;
8 var RichText = wp.blockEditor.RichText;
9 var MediaUpload = wp.blockEditor.MediaUpload;
10 var PanelBody = wp.components.PanelBody;
11 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
12 var Button = wp.components.Button;
13 var ToggleControl = wp.components.ToggleControl;
14 var RangeControl = wp.components.RangeControl;
15 var SelectControl = wp.components.SelectControl;
16 var TextControl = wp.components.TextControl;
17
18 /* ── Typography lazy getters ───────────────────────────────────────── */
19 var _Typo, _tv;
20 Object.defineProperty(window, '__bkbgAbTypoReady', { get: function () {
21 if (!_Typo) _Typo = window.bkbgTypographyControl;
22 if (!_tv) _tv = window.bkbgTypoCssVars;
23 return !!(_Typo && _tv);
24 }});
25 function getTypoCssVars() { window.__bkbgAbTypoReady; return _tv; }
26 function getTypoComponent() { window.__bkbgAbTypoReady; return _Typo; }
27
28 /* ── Shared wrap style ─────────────────────────────────────────────── */
29 function buildWrapStyle(a) {
30 var tv = getTypoCssVars();
31 var s = {
32 '--bkbg-ab-size': a.avatarSize + 'px',
33 '--bkbg-ab-pad': a.padding + 'px',
34 '--bkbg-ab-gap': a.gap + 'px',
35 '--bkbg-ab-bg': a.bgColor,
36 '--bkbg-ab-border': a.borderColor,
37 '--bkbg-ab-radius': a.borderRadius + 'px',
38 '--bkbg-ab-name-size': a.nameSize + 'px',
39 '--bkbg-ab-name-w': a.nameWeight,
40 '--bkbg-ab-name-clr': a.nameColor,
41 '--bkbg-ab-role-size': a.roleSize + 'px',
42 '--bkbg-ab-role-clr': a.roleColor,
43 '--bkbg-ab-bio-size': a.bioSize + 'px',
44 '--bkbg-ab-bio-clr': a.bioColor,
45 '--bkbg-ab-bio-lh': a.bioLineHeight,
46 '--bkbg-ab-link-clr': a.linkColor,
47 '--bkbg-ab-link-hov': a.linkHoverColor
48 };
49 if (tv) {
50 Object.assign(s, tv(a.nameTypo || {}, '--bkbg-ab-name-'));
51 Object.assign(s, tv(a.roleTypo || {}, '--bkbg-ab-role-'));
52 Object.assign(s, tv(a.bioTypo || {}, '--bkbg-ab-bio-'));
53 }
54 return s;
55 }
56
57 var fontWeightOptions = [
58 { label: '400', value: 400 }, { label: '500', value: 500 },
59 { label: '600', value: 600 }, { label: '700', value: 700 },
60 { label: '800', value: 800 }, { label: '900', value: 900 }
61 ];
62
63 var platformOptions = [
64 { label: 'Twitter / X', value: 'twitter' },
65 { label: 'LinkedIn', value: 'linkedin' },
66 { label: 'Facebook', value: 'facebook' },
67 { label: 'Instagram', value: 'instagram' },
68 { label: 'YouTube', value: 'youtube' },
69 { label: 'GitHub', value: 'github' },
70 { label: 'Website', value: 'website' },
71 { label: 'Email', value: 'email' }
72 ];
73
74 /* Platform SVG icons */
75 var platformIcons = {
76 twitter: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.744l7.73-8.835L1.254 2.25H8.08l4.253 5.622zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>',
77 linkedin: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M16 8a6 6 0 016 6v7h-4v-7a2 2 0 00-2-2 2 2 0 00-2 2v7h-4v-7a6 6 0 016-6zM2 9h4v12H2z"/><circle cx="4" cy="4" r="2"/></svg>',
78 facebook: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M18 2h-3a5 5 0 00-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 011-1h3z"/></svg>',
79 instagram: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="2" width="20" height="20" rx="5"/><circle cx="12" cy="12" r="4"/><circle cx="17.5" cy="6.5" r="1" fill="currentColor" stroke="none"/></svg>',
80 youtube: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M22.54 6.42a2.78 2.78 0 00-1.95-1.96C18.88 4 12 4 12 4s-6.88 0-8.59.46A2.78 2.78 0 001.46 6.42 29 29 0 001 12a29 29 0 00.46 5.58A2.78 2.78 0 003.41 19.6C5.12 20 12 20 12 20s6.88 0 8.59-.46a2.78 2.78 0 001.95-1.95A29 29 0 0023 12a29 29 0 00-.46-5.58zM9.75 15.02l6.5-3.02-6.5-3.02v6.04z"/></svg>',
81 github: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 00-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0020 4.77 5.07 5.07 0 0019.91 1S18.73.65 16 2.48a13.38 13.38 0 00-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 005 4.77a5.44 5.44 0 00-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 009 18.13V22"/></svg>',
82 website: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 014 10 15.3 15.3 0 01-4 10 15.3 15.3 0 01-4-10 15.3 15.3 0 014-10z"/></svg>',
83 email: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>'
84 };
85
86 function uid() { return Math.random().toString(36).slice(2, 7); }
87
88 function defaultLink() {
89 return { id: uid(), platform: 'website', url: '', label: 'Website' };
90 }
91
92 function AvatarPlaceholder(_ref) {
93 var size = _ref.size;
94 return el('div', {
95 className: 'bkbg-ab-avatar-placeholder',
96 style: { width: size + 'px', height: size + 'px' }
97 },
98 el('svg', { viewBox: '0 0 24 24', xmlns: 'http://www.w3.org/2000/svg' },
99 el('path', { d: 'M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2M12 11a4 4 0 100-8 4 4 0 000 8z', stroke: 'currentColor', strokeWidth: 2, fill: 'none' })
100 )
101 );
102 }
103
104 registerBlockType('blockenberg/author-box', {
105 title: __('Author Box', 'blockenberg'),
106 description: __('Blog author profile card with avatar, bio and social links.', 'blockenberg'),
107 category: 'bkbg-business',
108 icon: el('svg', { viewBox: '0 0 24 24', xmlns: 'http://www.w3.org/2000/svg' },
109 el('path', { d: 'M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2M12 11a4 4 0 100-8 4 4 0 000 8z', stroke: 'currentColor', strokeWidth: 2, fill: 'none' })
110 ),
111 attributes: {
112 name: { type: 'string', default: 'Jane Smith' },
113 role: { type: 'string', default: 'Content Writer' },
114 bio: { type: 'string', default: 'Jane writes about technology, design, and the future of the web. She has 10+ years of experience crafting compelling stories for top-tier publications.' },
115 avatarUrl: { type: 'string', default: '' },
116 avatarId: { type: 'number', default: 0 },
117 avatarSize: { type: 'number', default: 88 },
118 avatarShape: { type: 'string', default: 'circle' },
119 layout: { type: 'string', default: 'horizontal' },
120 showRole: { type: 'boolean', default: true },
121 links: { type: 'array', default: [
122 { id: 'l1', platform: 'twitter', url: '', label: 'Twitter' },
123 { id: 'l2', platform: 'linkedin', url: '', label: 'LinkedIn' },
124 { id: 'l3', platform: 'website', url: '', label: 'Website' }
125 ]},
126 showLinks: { type: 'boolean', default: true },
127 padding: { type: 'number', default: 28 },
128 gap: { type: 'number', default: 24 },
129 bgColor: { type: 'string', default: '#f9fafb' },
130 borderColor: { type: 'string', default: '#e5e7eb' },
131 borderRadius: { type: 'number', default: 14 },
132 showBorder: { type: 'boolean', default: true },
133 nameSize: { type: 'number', default: 20 },
134 nameWeight: { type: 'number', default: 700 },
135 nameColor: { type: 'string', default: '#111827' },
136 roleSize: { type: 'number', default: 14 },
137 roleColor: { type: 'string', default: '#6b7280' },
138 bioSize: { type: 'number', default: 15 },
139 bioColor: { type: 'string', default: '#374151' },
140 bioLineHeight: { type: 'string', default: '1.7' },
141 linkColor: { type: 'string', default: '#2563eb' },
142 linkHoverColor:{ type: 'string', default: '#1d4ed8' }
143 },
144
145 edit: function (props) {
146 var a = props.attributes;
147 var setAttr = props.setAttributes;
148 var links = a.links;
149
150 function updateLink(i, field, val) {
151 setAttr({ links: links.map(function (l, j) {
152 return j === i ? Object.assign({}, l, { [field]: val }) : l;
153 })});
154 }
155 function addLink() {
156 setAttr({ links: links.concat(defaultLink()) });
157 }
158 function removeLink(i) {
159 setAttr({ links: links.filter(function (_, j) { return j !== i; }) });
160 }
161 function moveLink(i, dir) {
162 var arr = links.slice();
163 var t = i + dir;
164 if (t < 0 || t >= arr.length) return;
165 var tmp = arr[i]; arr[i] = arr[t]; arr[t] = tmp;
166 setAttr({ links: arr });
167 }
168
169 var wrapStyle = buildWrapStyle(a);
170
171 var blockProps = useBlockProps({
172 className: 'bkbg-ab-wrap bkbg-ab-layout--' + a.layout + (a.showBorder ? ' bkbg-ab-bordered' : ''),
173 style: wrapStyle
174 });
175
176 var inspector = el(InspectorControls, null,
177
178 el(PanelBody, { title: __('Avatar', 'blockenberg'), initialOpen: true },
179 el(MediaUpload, {
180 onSelect: function (m) { setAttr({ avatarUrl: m.url, avatarId: m.id }); },
181 type: 'image', value: a.avatarId,
182 render: function (p) {
183 return el(Fragment, null,
184 a.avatarUrl && el('img', {
185 src: a.avatarUrl,
186 style: { width: 60, height: 60, borderRadius: '50%', objectFit: 'cover', display: 'block', marginBottom: 8 }
187 }),
188 el(Button, {
189 variant: a.avatarUrl ? 'secondary' : 'primary',
190 isSmall: true, onClick: p.open
191 }, a.avatarUrl ? __('Change Avatar', 'blockenberg') : __('Upload Avatar', 'blockenberg')),
192 a.avatarUrl && el(Button, {
193 variant: 'tertiary', isSmall: true, isDestructive: true,
194 onClick: function () { setAttr({ avatarUrl: '', avatarId: 0 }); },
195 style: { marginLeft: 8 }
196 }, __('Remove', 'blockenberg'))
197 );
198 }
199 }),
200 el(RangeControl, {
201 label: __('Avatar size (px)', 'blockenberg'),
202 value: a.avatarSize, min: 40, max: 200,
203 onChange: function (v) { setAttr({ avatarSize: v }); }
204 }),
205 el(SelectControl, {
206 label: __('Avatar shape', 'blockenberg'),
207 value: a.avatarShape,
208 options: [
209 { label: 'Circle', value: 'circle' },
210 { label: 'Rounded', value: 'rounded' },
211 { label: 'Square', value: 'square' }
212 ],
213 onChange: function (v) { setAttr({ avatarShape: v }); }
214 })
215 ),
216
217 el(PanelBody, { title: __('Layout & Style', 'blockenberg'), initialOpen: false },
218 el(SelectControl, {
219 label: __('Layout', 'blockenberg'),
220 value: a.layout,
221 options: [
222 { label: 'Horizontal (avatar left)', value: 'horizontal' },
223 { label: 'Vertical (avatar centered)',value: 'vertical' }
224 ],
225 onChange: function (v) { setAttr({ layout: v }); }
226 }),
227 el(RangeControl, {
228 label: __('Padding (px)', 'blockenberg'),
229 value: a.padding, min: 8, max: 64,
230 onChange: function (v) { setAttr({ padding: v }); }
231 }),
232 el(RangeControl, {
233 label: __('Gap (px)', 'blockenberg'),
234 value: a.gap, min: 8, max: 64,
235 onChange: function (v) { setAttr({ gap: v }); }
236 }),
237 el(RangeControl, {
238 label: __('Border radius (px)', 'blockenberg'),
239 value: a.borderRadius, min: 0, max: 40,
240 onChange: function (v) { setAttr({ borderRadius: v }); }
241 }),
242 el(ToggleControl, {
243 label: __('Show border', 'blockenberg'),
244 checked: a.showBorder,
245 onChange: function (v) { setAttr({ showBorder: v }); },
246 __nextHasNoMarginBottom: true
247 }),
248 el(ToggleControl, {
249 label: __('Show role / position', 'blockenberg'),
250 checked: a.showRole,
251 onChange: function (v) { setAttr({ showRole: v }); },
252 __nextHasNoMarginBottom: true
253 })
254 ),
255
256 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
257 getTypoComponent() && el(getTypoComponent(), {
258 label: __('Name Typography', 'blockenberg'),
259 value: a.nameTypo || {},
260 onChange: function (v) { setAttr({ nameTypo: v }); }
261 }),
262 getTypoComponent() && el(getTypoComponent(), {
263 label: __('Role Typography', 'blockenberg'),
264 value: a.roleTypo || {},
265 onChange: function (v) { setAttr({ roleTypo: v }); }
266 }),
267 getTypoComponent() && el(getTypoComponent(), {
268 label: __('Bio Typography', 'blockenberg'),
269 value: a.bioTypo || {},
270 onChange: function (v) { setAttr({ bioTypo: v }); }
271 })
272 ),
273
274 el(PanelBody, { title: __('Social Links', 'blockenberg'), initialOpen: false },
275 el(ToggleControl, {
276 label: __('Show social links', 'blockenberg'),
277 checked: a.showLinks,
278 onChange: function (v) { setAttr({ showLinks: v }); },
279 __nextHasNoMarginBottom: true
280 }),
281 a.showLinks && el(Fragment, null,
282 links.map(function (link, i) {
283 return el('div', { key: link.id, className: 'bkbg-ab-link-row' },
284 el(SelectControl, {
285 label: __('Platform', 'blockenberg'),
286 value: link.platform,
287 options: platformOptions,
288 onChange: function (v) { updateLink(i, 'platform', v); }
289 }),
290 el(TextControl, {
291 label: __('URL', 'blockenberg'),
292 type: 'url', value: link.url,
293 onChange: function (v) { updateLink(i, 'url', v); }
294 }),
295 el('div', { style: { display: 'flex', gap: 4, marginBottom: 12 } },
296 i > 0 && el(Button, { isSmall: true, variant: 'tertiary', onClick: function () { moveLink(i, -1); } }, ''),
297 i < links.length - 1 && el(Button, { isSmall: true, variant: 'tertiary', onClick: function () { moveLink(i, 1); } }, ''),
298 el(Button, { isSmall: true, variant: 'tertiary', isDestructive: true, onClick: function () { removeLink(i); } }, '')
299 )
300 );
301 }),
302 el(Button, {
303 variant: 'secondary', isSmall: true, onClick: addLink
304 }, __('+ Add Link', 'blockenberg'))
305 )
306 ),
307
308 el(PanelBody, { title: __('Colors', 'blockenberg'), initialOpen: false },
309 el(PanelColorSettings, {
310 title: __('Colors', 'blockenberg'),
311 initialOpen: false,
312 colorSettings: [
313 { label: __('Background', 'blockenberg'), value: a.bgColor, onChange: function(v){ setAttr({ bgColor: v||'#f9fafb' }); } },
314 { label: __('Border', 'blockenberg'), value: a.borderColor, onChange: function(v){ setAttr({ borderColor: v||'#e5e7eb' }); } },
315 { label: __('Name', 'blockenberg'), value: a.nameColor, onChange: function(v){ setAttr({ nameColor: v||'#111827' }); } },
316 { label: __('Role', 'blockenberg'), value: a.roleColor, onChange: function(v){ setAttr({ roleColor: v||'#6b7280' }); } },
317 { label: __('Bio text', 'blockenberg'), value: a.bioColor, onChange: function(v){ setAttr({ bioColor: v||'#374151' }); } },
318 { label: __('Link color', 'blockenberg'), value: a.linkColor, onChange: function(v){ setAttr({ linkColor: v||'#2563eb' }); } },
319 { label: __('Link (hover)', 'blockenberg'), value: a.linkHoverColor, onChange: function(v){ setAttr({ linkHoverColor: v||'#1d4ed8' }); } }
320 ]
321 })
322 )
323 );
324
325 /* ── Avatar image ── */
326 var avatarEl;
327 if (a.avatarUrl) {
328 avatarEl = el(MediaUpload, {
329 onSelect: function (m) { setAttr({ avatarUrl: m.url, avatarId: m.id }); },
330 type: 'image', value: a.avatarId,
331 render: function (p) {
332 return el('img', {
333 src: a.avatarUrl, alt: a.name,
334 className: 'bkbg-ab-avatar bkbg-ab-avatar--' + a.avatarShape,
335 style: { width: a.avatarSize + 'px', height: a.avatarSize + 'px', cursor: 'pointer' },
336 onClick: p.open, title: __('Click to change avatar', 'blockenberg')
337 });
338 }
339 });
340 } else {
341 avatarEl = el(AvatarPlaceholder, { size: a.avatarSize });
342 }
343
344 /* ── Social links ── */
345 var linksEl = a.showLinks && el('div', { className: 'bkbg-ab-links' },
346 links.filter(function (l) { return l.url; }).map(function (l) {
347 return el('a', {
348 key: l.id, href: '#',
349 className: 'bkbg-ab-link bkbg-ab-link--' + l.platform,
350 title: l.label || l.platform,
351 dangerouslySetInnerHTML: { __html: platformIcons[l.platform] || '' }
352 });
353 }),
354 links.filter(function (l) { return !l.url; }).map(function (l) {
355 return el('span', {
356 key: l.id,
357 className: 'bkbg-ab-link bkbg-ab-link--' + l.platform + ' bkbg-ab-link--empty',
358 title: (l.label || l.platform) + ' (no URL set)',
359 style: { opacity: 0.35 },
360 dangerouslySetInnerHTML: { __html: platformIcons[l.platform] || '' }
361 });
362 })
363 );
364
365 return el(Fragment, null,
366 inspector,
367 el('div', blockProps,
368 el('div', { className: 'bkbg-ab-inner' },
369 el('div', { className: 'bkbg-ab-avatar-col' }, avatarEl),
370 el('div', { className: 'bkbg-ab-content' },
371 el(TextControl, {
372 label: __('Name', 'blockenberg'),
373 value: a.name,
374 className: 'bkbg-ab-name-input',
375 onChange: function (v) { setAttr({ name: v }); }
376 }),
377 a.showRole && el(TextControl, {
378 label: __('Role', 'blockenberg'),
379 value: a.role,
380 className: 'bkbg-ab-role-input',
381 onChange: function (v) { setAttr({ role: v }); }
382 }),
383 el(RichText, {
384 tagName: 'p',
385 className: 'bkbg-ab-bio',
386 value: a.bio,
387 onChange: function (v) { setAttr({ bio: v }); },
388 placeholder: __('Author bio…', 'blockenberg'),
389 allowedFormats: ['core/bold', 'core/italic', 'core/link']
390 }),
391 linksEl
392 )
393 )
394 )
395 );
396 },
397
398 save: function (props) {
399 var a = props.attributes;
400 var blockProps = wp.blockEditor.useBlockProps.save({
401 className: 'bkbg-ab-wrap bkbg-ab-layout--' + a.layout + (a.showBorder ? ' bkbg-ab-bordered' : ''),
402 style: buildWrapStyle(a)
403 });
404
405 return el('div', blockProps,
406 el('div', { className: 'bkbg-ab-inner' },
407 el('div', { className: 'bkbg-ab-avatar-col' },
408 a.avatarUrl
409 ? el('img', {
410 src: a.avatarUrl, alt: a.name,
411 className: 'bkbg-ab-avatar bkbg-ab-avatar--' + a.avatarShape,
412 loading: 'lazy'
413 })
414 : el('div', { className: 'bkbg-ab-avatar-placeholder bkbg-ab-avatar--' + a.avatarShape })
415 ),
416 el('div', { className: 'bkbg-ab-content' },
417 el('h3', { className: 'bkbg-ab-name' }, a.name),
418 a.showRole && el('p', { className: 'bkbg-ab-role' }, a.role),
419 el(RichText.Content, { tagName: 'p', className: 'bkbg-ab-bio', value: a.bio }),
420 a.showLinks && el('div', { className: 'bkbg-ab-links' },
421 a.links.filter(function (l) { return l.url; }).map(function (l) {
422 return el('a', {
423 key: l.id,
424 href: l.platform === 'email' ? 'mailto:' + l.url : l.url,
425 className: 'bkbg-ab-link bkbg-ab-link--' + l.platform,
426 target: l.platform !== 'email' ? '_blank' : undefined,
427 rel: l.platform !== 'email' ? 'noopener noreferrer' : undefined,
428 'aria-label': l.label || l.platform
429 });
430 })
431 )
432 )
433 )
434 );
435 }
436 });
437 }() );
438