PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.6
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.6
2.0.14 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 / team-members / index.js

index.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.6, at blocks/team-members/index.js

1,234 lines 61.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 wp.domReady(function () {
2 var el = wp.element.createElement;
3 var Fragment = wp.element.Fragment;
4 var __ = wp.i18n.__;
5 var useState = wp.element.useState;
6 var useRef = wp.element.useRef;
7 var useEffect = wp.element.useEffect;
8
9 var registerBlockType = wp.blocks.registerBlockType;
10
11 var InspectorControls = wp.blockEditor.InspectorControls;
12 var BlockControls = wp.blockEditor.BlockControls;
13 var RichText = wp.blockEditor.RichText;
14 var useBlockProps = wp.blockEditor.useBlockProps;
15
16 var PanelBody = wp.components.PanelBody;
17 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
18 var ToggleControl = wp.components.ToggleControl;
19 var RangeControl = wp.components.RangeControl;
20 var SelectControl = wp.components.SelectControl;
21 var Button = wp.components.Button;
22 var ToolbarGroup = wp.components.ToolbarGroup;
23 var ToolbarButton = wp.components.ToolbarButton;
24 var TextControl = wp.components.TextControl;
25
26 var SOCIAL_TYPES = [
27 { value: 'website', label: __('Website', 'blockenberg'), icon: 'admin-site' },
28 { value: 'email', label: __('Email', 'blockenberg'), icon: 'email' },
29 { value: 'phone', label: __('Phone', 'blockenberg'), icon: 'phone' },
30 { value: 'facebook', label: __('Facebook', 'blockenberg'), icon: 'facebook' },
31 { value: 'twitter', label: __('X (Twitter)', 'blockenberg'), icon: null },
32 { value: 'instagram', label: __('Instagram', 'blockenberg'), icon: 'instagram' },
33 { value: 'linkedin', label: __('LinkedIn', 'blockenberg'), icon: 'linkedin' },
34 { value: 'github', label: __('GitHub', 'blockenberg'), icon: null }
35 ];
36
37 function clone(obj) {
38 var out = {};
39 for (var k in obj) out[k] = obj[k];
40 return out;
41 }
42
43 function getPhotoRadius(shape, radiusPx) {
44 if (shape === 'circle') return '999px';
45 if (shape === 'square') return '0px';
46 // rounded
47 return (radiusPx || 12) + 'px';
48 }
49
50 function getSocialIcon(type) {
51 var found = SOCIAL_TYPES.filter(function (t) { return t.value === type; })[0];
52 return found ? found.icon : 'admin-site';
53 }
54
55 function renderSocialIcon(type) {
56 // X (Twitter)
57 if (type === 'twitter') {
58 return el('svg', {
59 viewBox: '0 0 24 24',
60 width: '24',
61 height: '24',
62 'aria-hidden': 'true',
63 focusable: 'false'
64 },
65 // Simplified X mark
66 el('path', {
67 d: 'M18.3 2H21l-6.6 7.6L22 22h-6.2l-4.8-6.3L5.5 22H3l7.1-8.2L2 2h6.3l4.3 5.7L18.3 2Zm-1.1 18h1.4L7 3.9H5.5L17.2 20Z'
68 })
69 );
70 }
71
72 // GitHub (octocat)
73 if (type === 'github') {
74 return el('svg', {
75 viewBox: '0 0 24 24',
76 width: '24',
77 height: '24',
78 'aria-hidden': 'true',
79 focusable: 'false'
80 },
81 el('path', {
82 d: 'M12 .5C5.65.5.5 5.82.5 12.39c0 5.24 3.44 9.68 8.2 11.25.6.11.82-.26.82-.58 0-.29-.01-1.06-.02-2.07-3.34.75-4.04-1.66-4.04-1.66-.55-1.43-1.33-1.81-1.33-1.81-1.08-.76.08-.74.08-.74 1.2.09 1.83 1.26 1.83 1.26 1.06 1.87 2.78 1.33 3.46 1.01.11-.79.42-1.33.76-1.63-2.66-.31-5.46-1.38-5.46-6.14 0-1.36.47-2.47 1.24-3.34-.12-.31-.54-1.56.12-3.25 0 0 1.01-.33 3.3 1.28a11.1 11.1 0 0 1 3.01-.42c1.02.01 2.05.14 3.01.42 2.28-1.61 3.29-1.28 3.29-1.28.66 1.69.24 2.94.12 3.25.77.87 1.24 1.98 1.24 3.34 0 4.78-2.8 5.83-5.48 6.14.43.38.82 1.13.82 2.29 0 1.65-.02 2.98-.02 3.39 0 .32.22.7.83.58 4.76-1.57 8.2-6.01 8.2-11.25C23.5 5.82 18.35.5 12 .5Z'
83 })
84 );
85 }
86
87 var dash = getSocialIcon(type) || 'admin-site';
88 return el('span', { className: 'dashicons dashicons-' + dash });
89 }
90
91 function normalizeUrl(type, url) {
92 if (!url) return '';
93 var v = String(url).trim();
94 if (!v) return '';
95
96 if (type === 'email') {
97 if (v.indexOf('mailto:') === 0) return v;
98 return 'mailto:' + v;
99 }
100 if (type === 'phone') {
101 if (v.indexOf('tel:') === 0) return v;
102 return 'tel:' + v;
103 }
104 if (v.charAt(0) === '#') return v;
105 // If it already has a scheme (http:, https:, etc), keep it.
106 if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(v)) return v;
107 return 'https://' + v;
108 }
109
110 function openMedia(onSelect) {
111 if (!wp || !wp.media) return;
112 var frame = wp.media({
113 title: __('Select Image', 'blockenberg'),
114 button: { text: __('Use image', 'blockenberg') },
115 multiple: false
116 });
117
118 frame.on('select', function () {
119 var attachment = frame.state().get('selection').first().toJSON();
120 onSelect(attachment);
121 });
122
123 frame.open();
124 }
125
126 registerBlockType('blockenberg/team-members', {
127 title: __('Team Members', 'blockenberg'),
128 icon: 'groups',
129 category: 'blockenberg',
130 description: __('Show your team members with photos, roles, bios, and social links.', 'blockenberg'),
131
132 edit: function (props) {
133 var attributes = props.attributes;
134 var setAttributes = props.setAttributes;
135 var isSelected = props.isSelected;
136 var a = attributes;
137
138 var activeState = useState(0);
139 var activeIndex = activeState[0];
140 var setActiveIndex = activeState[1];
141
142 function setMembers(members) {
143 setAttributes({ members: members });
144 }
145
146 function updateMember(index, field, value) {
147 var next = a.members.map(function (m, i) {
148 if (i !== index) return m;
149 var nm = clone(m);
150 nm[field] = value;
151 return nm;
152 });
153 setMembers(next);
154 }
155
156 function updateMemberImage(index, image) {
157 var img = {
158 id: image && image.id ? image.id : 0,
159 url: image && image.url ? image.url : '',
160 alt: image && image.alt ? image.alt : ''
161 };
162 updateMember(index, 'image', img);
163 }
164
165 function addMember() {
166 var next = a.members.concat([
167 {
168 name: __('New Member', 'blockenberg'),
169 role: __('Role', 'blockenberg'),
170 bio: __('Write a short bio. Click to edit.', 'blockenberg'),
171 image: { id: 0, url: '', alt: '' },
172 socials: [
173 { type: 'linkedin', url: '' },
174 { type: 'website', url: '' }
175 ]
176 }
177 ]);
178 setMembers(next);
179 setActiveIndex(next.length - 1);
180 }
181
182 function removeMember(index) {
183 if (a.members.length <= 1) return;
184 var next = a.members.filter(function (_, i) { return i !== index; });
185 setMembers(next);
186 setActiveIndex(Math.max(0, Math.min(activeIndex, next.length - 1)));
187 }
188
189 function duplicateMember(index) {
190 var next = a.members.slice();
191 var copy = clone(a.members[index]);
192 copy.image = clone(copy.image || { id: 0, url: '', alt: '' });
193 copy.socials = (copy.socials || []).map(function (s) { return clone(s); });
194 next.splice(index + 1, 0, copy);
195 setMembers(next);
196 setActiveIndex(index + 1);
197 }
198
199 function moveMember(index, dir) {
200 var ni = index + dir;
201 if (ni < 0 || ni >= a.members.length) return;
202 var next = a.members.slice();
203 var tmp = next[index];
204 next[index] = next[ni];
205 next[ni] = tmp;
206 setMembers(next);
207 setActiveIndex(ni);
208 }
209
210 function updateSocial(memberIndex, socialIndex, field, value) {
211 var next = a.members.map(function (m, i) {
212 if (i !== memberIndex) return m;
213 var nm = clone(m);
214 var socials = (nm.socials || []).slice();
215 var s = clone(socials[socialIndex] || { type: 'website', url: '' });
216 s[field] = value;
217 socials[socialIndex] = s;
218 nm.socials = socials;
219 return nm;
220 });
221 setMembers(next);
222 }
223
224 function addSocial(memberIndex) {
225 var next = a.members.map(function (m, i) {
226 if (i !== memberIndex) return m;
227 var nm = clone(m);
228 var socials = (nm.socials || []).slice();
229 socials.push({ type: 'website', url: '' });
230 nm.socials = socials;
231 return nm;
232 });
233 setMembers(next);
234 }
235
236 function removeSocial(memberIndex, socialIndex) {
237 var next = a.members.map(function (m, i) {
238 if (i !== memberIndex) return m;
239 var nm = clone(m);
240 var socials = (nm.socials || []).filter(function (_, si) { return si !== socialIndex; });
241 nm.socials = socials;
242 return nm;
243 });
244 setMembers(next);
245 }
246
247 var fontWeightOptions = [
248 { label: '300', value: 300 },
249 { label: '400', value: 400 },
250 { label: '500', value: 500 },
251 { label: '600', value: 600 },
252 { label: '700', value: 700 },
253 { label: '800', value: 800 }
254 ];
255
256 var columnsOptions = [
257 { label: '1', value: 1 },
258 { label: '2', value: 2 },
259 { label: '3', value: 3 },
260 { label: '4', value: 4 }
261 ];
262
263 var alignOptions = [
264 { label: __('Left', 'blockenberg'), value: 'left' },
265 { label: __('Center', 'blockenberg'), value: 'center' },
266 { label: __('Right', 'blockenberg'), value: 'right' }
267 ];
268
269 var imageShapeOptions = [
270 { label: __('Circle', 'blockenberg'), value: 'circle' },
271 { label: __('Rounded', 'blockenberg'), value: 'rounded' },
272 { label: __('Square', 'blockenberg'), value: 'square' }
273 ];
274
275 var activeMember = a.members[activeIndex] || a.members[0];
276
277 var inspector = el(InspectorControls, {},
278 el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: true },
279 el(SelectControl, {
280 label: __('Layout Style', 'blockenberg'),
281 value: a.layoutStyle,
282 options: [
283 { label: __('Cards (Grid)', 'blockenberg'), value: 'cards' },
284 { label: __('Carousel', 'blockenberg'), value: 'carousel' }
285 ],
286 onChange: function (v) { setAttributes({ layoutStyle: v }); }
287 }),
288 el(SelectControl, {
289 label: a.layoutStyle === 'carousel' ? __('Visible Cards', 'blockenberg') : __('Columns', 'blockenberg'),
290 value: a.columns,
291 options: columnsOptions,
292 onChange: function (v) { setAttributes({ columns: parseInt(v, 10) }); }
293 }),
294 el(RangeControl, {
295 label: __('Gap', 'blockenberg'),
296 value: a.gap,
297 min: 8,
298 max: 60,
299 onChange: function (v) { setAttributes({ gap: v }); }
300 }),
301 a.layoutStyle === 'carousel' && el(ToggleControl, {
302 label: __('Show Carousel Navigation', 'blockenberg'),
303 checked: a.carouselNav,
304 __nextHasNoMarginBottom: true,
305 onChange: function (v) { setAttributes({ carouselNav: v }); }
306 }),
307 a.layoutStyle === 'carousel' && el(SelectControl, {
308 label: __('Arrow Style', 'blockenberg'),
309 value: a.carouselNavStyle,
310 options: [
311 { label: __('Top Right', 'blockenberg'), value: 'top' },
312 { label: __('Overlay Sides', 'blockenberg'), value: 'overlay' },
313 { label: __('Bottom Center', 'blockenberg'), value: 'bottom' }
314 ],
315 onChange: function (v) { setAttributes({ carouselNavStyle: v }); }
316 }),
317 a.layoutStyle === 'carousel' && el(ToggleControl, {
318 label: __('Peek Next Card', 'blockenberg'),
319 checked: a.carouselPeek,
320 __nextHasNoMarginBottom: true,
321 onChange: function (v) { setAttributes({ carouselPeek: v }); }
322 }),
323 a.layoutStyle === 'carousel' && el(SelectControl, {
324 label: __('Indicators', 'blockenberg'),
325 value: a.carouselIndicators,
326 options: [
327 { label: __('None', 'blockenberg'), value: 'none' },
328 { label: __('Dots', 'blockenberg'), value: 'dots' },
329 { label: __('Progress Bar', 'blockenberg'), value: 'progress' },
330 { label: __('Fraction (1/6)', 'blockenberg'), value: 'fraction' }
331 ],
332 onChange: function (v) { setAttributes({ carouselIndicators: v }); }
333 }),
334 a.layoutStyle === 'carousel' && a.carouselIndicators === 'dots' && el(SelectControl, {
335 label: __('Dots Style', 'blockenberg'),
336 value: a.carouselDotsStyle,
337 options: [
338 { label: __('Dots', 'blockenberg'), value: 'default' },
339 { label: __('Active Pill', 'blockenberg'), value: 'pill' }
340 ],
341 onChange: function (v) { setAttributes({ carouselDotsStyle: v }); }
342 }),
343 a.layoutStyle === 'carousel' && a.carouselIndicators === 'dots' && el(RangeControl, {
344 label: __('Dot Size', 'blockenberg'),
345 value: a.carouselIndicatorSize,
346 min: 6,
347 max: 16,
348 onChange: function (v) { setAttributes({ carouselIndicatorSize: v }); }
349 }),
350 a.layoutStyle === 'carousel' && a.carouselIndicators === 'progress' && el(SelectControl, {
351 label: __('Progress Style', 'blockenberg'),
352 value: a.carouselProgressStyle,
353 options: [
354 { label: __('Contained', 'blockenberg'), value: 'default' },
355 { label: __('Edge-to-edge', 'blockenberg'), value: 'edge' }
356 ],
357 onChange: function (v) { setAttributes({ carouselProgressStyle: v }); }
358 }),
359 a.layoutStyle === 'carousel' && a.carouselIndicators === 'progress' && el(RangeControl, {
360 label: __('Progress Height', 'blockenberg'),
361 value: a.carouselProgressHeight,
362 min: 2,
363 max: 14,
364 onChange: function (v) { setAttributes({ carouselProgressHeight: v }); }
365 }),
366 a.layoutStyle === 'carousel' && a.carouselIndicators === 'fraction' && el(RangeControl, {
367 label: __('Fraction Text Size', 'blockenberg'),
368 value: a.carouselFractionSize,
369 min: 10,
370 max: 18,
371 onChange: function (v) { setAttributes({ carouselFractionSize: v }); }
372 }),
373 a.layoutStyle === 'carousel' && a.carouselIndicators !== 'none' && el(SelectControl, {
374 label: __('Indicators Position', 'blockenberg'),
375 value: a.carouselIndicatorsPosition,
376 options: [
377 { label: __('Below', 'blockenberg'), value: 'below' },
378 { label: __('Above', 'blockenberg'), value: 'top' }
379 ],
380 onChange: function (v) { setAttributes({ carouselIndicatorsPosition: v }); }
381 }),
382 a.layoutStyle === 'carousel' && el(ToggleControl, {
383 label: __('Infinite Loop', 'blockenberg'),
384 checked: a.infiniteScroll !== false,
385 __nextHasNoMarginBottom: true,
386 onChange: function (v) { setAttributes({ infiniteScroll: !!v }); }
387 }),
388 a.layoutStyle === 'carousel' && el(ToggleControl, {
389 label: __('Auto-scroll (continuous)', 'blockenberg'),
390 help: __('Runs on the live site (frontend). Please check it there.', 'blockenberg'),
391 checked: !!a.autoScroll,
392 __nextHasNoMarginBottom: true,
393 onChange: function (v) { setAttributes({ autoScroll: !!v }); }
394 }),
395 a.layoutStyle === 'carousel' && a.autoScroll && el(RangeControl, {
396 label: __('Auto-scroll speed (px/sec)', 'blockenberg'),
397 value: a.autoScrollSpeed || 40,
398 min: 5,
399 max: 200,
400 step: 1,
401 onChange: function (v) { setAttributes({ autoScrollSpeed: v }); }
402 }),
403 a.layoutStyle === 'carousel' && a.autoScroll && el(ToggleControl, {
404 label: __('Pause on hover', 'blockenberg'),
405 checked: a.autoScrollPauseOnHover !== false,
406 __nextHasNoMarginBottom: true,
407 onChange: function (v) { setAttributes({ autoScrollPauseOnHover: !!v }); }
408 }),
409 el(SelectControl, {
410 label: __('Text Align', 'blockenberg'),
411 value: a.textAlign,
412 options: alignOptions,
413 onChange: function (v) { setAttributes({ textAlign: v }); }
414 })
415 ),
416
417 el(PanelBody, { title: __('Card', 'blockenberg'), initialOpen: false },
418 el(RangeControl, {
419 label: __('Padding', 'blockenberg'),
420 value: a.cardPadding,
421 min: 8,
422 max: 60,
423 onChange: function (v) { setAttributes({ cardPadding: v }); }
424 }),
425 el(RangeControl, {
426 label: __('Border Radius', 'blockenberg'),
427 value: a.cardRadius,
428 min: 0,
429 max: 40,
430 onChange: function (v) { setAttributes({ cardRadius: v }); }
431 }),
432 el(RangeControl, {
433 label: __('Border Width', 'blockenberg'),
434 value: a.cardBorderWidth,
435 min: 0,
436 max: 6,
437 onChange: function (v) { setAttributes({ cardBorderWidth: v }); }
438 }),
439 el(ToggleControl, {
440 label: __('Shadow', 'blockenberg'),
441 checked: a.cardShadow,
442 __nextHasNoMarginBottom: true,
443 onChange: function (v) { setAttributes({ cardShadow: v }); }
444 }),
445 el(ToggleControl, {
446 label: __('Hover Lift', 'blockenberg'),
447 checked: a.hoverLift,
448 __nextHasNoMarginBottom: true,
449 onChange: function (v) { setAttributes({ hoverLift: v }); }
450 }),
451 a.hoverLift && el(RangeControl, {
452 label: __('Lift Amount', 'blockenberg'),
453 value: a.hoverLiftAmount,
454 min: 0,
455 max: 20,
456 onChange: function (v) { setAttributes({ hoverLiftAmount: v }); }
457 })
458 ),
459
460 el(PanelBody, { title: __('Image', 'blockenberg'), initialOpen: false },
461 el(SelectControl, {
462 label: __('Shape', 'blockenberg'),
463 value: a.imageShape,
464 options: imageShapeOptions,
465 onChange: function (v) { setAttributes({ imageShape: v }); }
466 }),
467 el(RangeControl, {
468 label: __('Size', 'blockenberg'),
469 value: a.imageSize,
470 min: 40,
471 max: 200,
472 onChange: function (v) { setAttributes({ imageSize: v }); }
473 })
474 ),
475
476 el(PanelBody, { title: __('Content', 'blockenberg'), initialOpen: false },
477 el(ToggleControl, {
478 label: __('Show Role', 'blockenberg'),
479 checked: a.showRole,
480 __nextHasNoMarginBottom: true,
481 onChange: function (v) { setAttributes({ showRole: v }); }
482 }),
483 el(ToggleControl, {
484 label: __('Show Bio', 'blockenberg'),
485 checked: a.showBio,
486 __nextHasNoMarginBottom: true,
487 onChange: function (v) { setAttributes({ showBio: v }); }
488 }),
489 el(ToggleControl, {
490 label: __('Show Social Links', 'blockenberg'),
491 checked: a.showSocials,
492 __nextHasNoMarginBottom: true,
493 onChange: function (v) { setAttributes({ showSocials: v }); }
494 })
495 ),
496
497 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
498 el(RangeControl, {
499 label: __('Name Size', 'blockenberg'),
500 value: a.nameSize,
501 min: 12,
502 max: 40,
503 onChange: function (v) { setAttributes({ nameSize: v }); }
504 }),
505 el(SelectControl, {
506 label: __('Name Weight', 'blockenberg'),
507 value: a.nameWeight,
508 options: fontWeightOptions,
509 onChange: function (v) { setAttributes({ nameWeight: parseInt(v, 10) }); }
510 }),
511 el(RangeControl, {
512 label: __('Role Size', 'blockenberg'),
513 value: a.roleSize,
514 min: 10,
515 max: 28,
516 onChange: function (v) { setAttributes({ roleSize: v }); }
517 }),
518 el(SelectControl, {
519 label: __('Role Weight', 'blockenberg'),
520 value: a.roleWeight,
521 options: fontWeightOptions,
522 onChange: function (v) { setAttributes({ roleWeight: parseInt(v, 10) }); }
523 }),
524 el(RangeControl, {
525 label: __('Bio Size', 'blockenberg'),
526 value: a.bioSize,
527 min: 10,
528 max: 24,
529 onChange: function (v) { setAttributes({ bioSize: v }); }
530 }),
531 el(RangeControl, {
532 label: __('Social Icon Size', 'blockenberg'),
533 value: a.socialSize,
534 min: 12,
535 max: 30,
536 onChange: function (v) { setAttributes({ socialSize: v }); }
537 })
538 ),
539
540 el(PanelColorSettings, {
541 title: __('Colors', 'blockenberg'),
542 initialOpen: false,
543 colorSettings: [
544 { value: a.cardBg, onChange: function (c) { setAttributes({ cardBg: c }); }, label: __('Card Background', 'blockenberg') },
545 { value: a.cardBorderColor, onChange: function (c) { setAttributes({ cardBorderColor: c }); }, label: __('Card Border', 'blockenberg') },
546 { value: a.nameColor, onChange: function (c) { setAttributes({ nameColor: c }); }, label: __('Name', 'blockenberg') },
547 { value: a.roleColor, onChange: function (c) { setAttributes({ roleColor: c }); }, label: __('Role', 'blockenberg') },
548 { value: a.bioColor, onChange: function (c) { setAttributes({ bioColor: c }); }, label: __('Bio', 'blockenberg') },
549 { value: a.socialColor, onChange: function (c) { setAttributes({ socialColor: c }); }, label: __('Social', 'blockenberg') },
550 { value: a.socialHoverColor, onChange: function (c) { setAttributes({ socialHoverColor: c }); }, label: __('Social Hover', 'blockenberg') },
551
552 { value: a.carouselIndicatorColor, onChange: function (c) { setAttributes({ carouselIndicatorColor: c }); }, label: __('Carousel Indicator', 'blockenberg') },
553 { value: a.carouselIndicatorActiveColor, onChange: function (c) { setAttributes({ carouselIndicatorActiveColor: c }); }, label: __('Carousel Indicator (Active)', 'blockenberg') },
554 { value: a.carouselProgressBg, onChange: function (c) { setAttributes({ carouselProgressBg: c }); }, label: __('Carousel Progress (Track)', 'blockenberg') },
555 { value: a.carouselProgressFg, onChange: function (c) { setAttributes({ carouselProgressFg: c }); }, label: __('Carousel Progress (Fill)', 'blockenberg') },
556 { value: a.carouselFractionColor, onChange: function (c) { setAttributes({ carouselFractionColor: c }); }, label: __('Carousel Fraction', 'blockenberg') }
557 ]
558 }),
559
560 el(PanelBody, { title: __('Selected Member', 'blockenberg'), initialOpen: false },
561 activeMember ? el('div', {},
562 el('div', { style: { marginBottom: '10px', fontWeight: 600 } }, __('Member', 'blockenberg') + ': ' + (activeIndex + 1)),
563 el(Button, {
564 isSecondary: true,
565 onClick: function () {
566 openMedia(function (att) {
567 updateMemberImage(activeIndex, { id: att.id, url: att.url, alt: att.alt || '' });
568 });
569 }
570 }, activeMember.image && activeMember.image.url ? __('Change Photo', 'blockenberg') : __('Select Photo', 'blockenberg')),
571 activeMember.image && activeMember.image.url && el(Button, {
572 isLink: true,
573 isDestructive: true,
574 onClick: function () { updateMemberImage(activeIndex, { id: 0, url: '', alt: '' }); }
575 }, __('Remove Photo', 'blockenberg')),
576
577 el('hr', { style: { margin: '14px 0' } }),
578
579 el('div', { style: { marginBottom: '8px', fontWeight: 600 } }, __('Social Links', 'blockenberg')),
580 (activeMember.socials || []).map(function (s, si) {
581 return el('div', { key: si, style: { marginBottom: '10px', padding: '10px', border: '1px solid #e5e7eb', borderRadius: '6px' } },
582 el(SelectControl, {
583 label: __('Type', 'blockenberg'),
584 value: s.type,
585 options: SOCIAL_TYPES.map(function (t) { return { label: t.label, value: t.value }; }),
586 onChange: function (v) { updateSocial(activeIndex, si, 'type', v); }
587 }),
588 el(TextControl, {
589 label: __('URL', 'blockenberg'),
590 value: s.url,
591 onChange: function (v) { updateSocial(activeIndex, si, 'url', v); }
592 }),
593 el(Button, {
594 isDestructive: true,
595 isSecondary: true,
596 onClick: function () { removeSocial(activeIndex, si); }
597 }, __('Remove', 'blockenberg'))
598 );
599 }),
600 el(Button, {
601 isPrimary: true,
602 onClick: function () { addSocial(activeIndex); }
603 }, __('+ Add Social Link', 'blockenberg'))
604 ) : el('div', {}, __('Select a card to edit its member settings.', 'blockenberg'))
605 )
606 );
607
608 var blockControls = el(BlockControls, {},
609 el(ToolbarGroup, {},
610 el(ToolbarButton, {
611 icon: 'plus',
612 label: __('Add Member', 'blockenberg'),
613 onClick: addMember
614 })
615 )
616 );
617
618 var styleVars = {
619 '--bkbg-tm-columns': String(a.columns),
620 '--bkbg-tm-gap': a.gap + 'px',
621 '--bkbg-tm-carousel-peek': a.carouselPeek ? '20%' : '0px',
622 '--bkbg-tm-card-bg': a.cardBg,
623 '--bkbg-tm-card-border-color': a.cardBorderColor,
624 '--bkbg-tm-card-border-width': a.cardBorderWidth + 'px',
625 '--bkbg-tm-card-radius': a.cardRadius + 'px',
626 '--bkbg-tm-card-padding': a.cardPadding + 'px',
627 '--bkbg-tm-text-align': a.textAlign,
628 '--bkbg-tm-photo-size': a.imageSize + 'px',
629 '--bkbg-tm-photo-radius': getPhotoRadius(a.imageShape, 12),
630 '--bkbg-tm-name-color': a.nameColor,
631 '--bkbg-tm-role-color': a.roleColor,
632 '--bkbg-tm-bio-color': a.bioColor,
633 '--bkbg-tm-name-size': a.nameSize + 'px',
634 '--bkbg-tm-role-size': a.roleSize + 'px',
635 '--bkbg-tm-bio-size': a.bioSize + 'px',
636 '--bkbg-tm-name-weight': String(a.nameWeight),
637 '--bkbg-tm-role-weight': String(a.roleWeight),
638 '--bkbg-tm-social-color': a.socialColor,
639 '--bkbg-tm-social-hover': a.socialHoverColor,
640 '--bkbg-tm-social-size': a.socialSize + 'px',
641 '--bkbg-tm-hover-lift': a.hoverLiftAmount + 'px',
642
643 '--bkbg-tm-indicator-size': (a.carouselIndicatorSize || 8) + 'px',
644 '--bkbg-tm-indicator-color': a.carouselIndicatorColor,
645 '--bkbg-tm-indicator-active-color': a.carouselIndicatorActiveColor,
646 '--bkbg-tm-progress-height': (a.carouselProgressHeight || 6) + 'px',
647 '--bkbg-tm-progress-bg': a.carouselProgressBg,
648 '--bkbg-tm-progress-fg': a.carouselProgressFg,
649 '--bkbg-tm-fraction-color': a.carouselFractionColor,
650 '--bkbg-tm-fraction-size': (a.carouselFractionSize || 13) + 'px'
651 };
652
653 var classes = [
654 'bkbg-tm-wrap',
655 'bkbg-tm-align-' + a.textAlign
656 ];
657 if (a.hoverLift) classes.push('bkbg-tm-hover-lift');
658 if (a.layoutStyle === 'carousel') {
659 classes.push('bkbg-tm-nav-' + (a.carouselNavStyle || 'top'));
660 classes.push('bkbg-tm-indicators-' + (a.carouselIndicators || 'none'));
661 classes.push('bkbg-tm-indicators-pos-' + (a.carouselIndicatorsPosition || 'below'));
662
663 if (a.carouselIndicators === 'dots') {
664 classes.push('bkbg-tm-dots-style-' + (a.carouselDotsStyle || 'default'));
665 }
666 if (a.carouselIndicators === 'progress') {
667 classes.push('bkbg-tm-progress-style-' + (a.carouselProgressStyle || 'default'));
668 }
669 }
670
671 var blockProps = useBlockProps({
672 className: 'bkbg-editor-wrap ' + classes.join(' '),
673 'data-block-label': 'Team'
674 });
675
676 var trackRef = useRef(null);
677
678 var activeSlideState = useState(0);
679 var activeSlide = activeSlideState[0];
680 var setActiveSlide = activeSlideState[1];
681
682 var progressState = useState(0);
683 var progressPct = progressState[0];
684 var setProgressPct = progressState[1];
685
686 function getSlides() {
687 if (!trackRef.current) return [];
688 return trackRef.current.querySelectorAll('.bkbg-tm-slide');
689 }
690
691 function scrollToIndex(i) {
692 if (!trackRef.current) return;
693 var slides = getSlides();
694 if (!slides.length) return;
695 var idx = Math.max(0, Math.min(slides.length - 1, i));
696 slides[idx].scrollIntoView({ behavior: 'smooth', inline: 'start', block: 'nearest' });
697 }
698
699 function scrollCarousel(dir) {
700 if (!trackRef.current) return;
701 var track = trackRef.current;
702 var slides = getSlides();
703 if (!slides.length) return;
704
705 var trackRect = track.getBoundingClientRect();
706 var currentIndex = 0;
707 for (var i = 0; i < slides.length; i++) {
708 var r = slides[i].getBoundingClientRect();
709 if (r.left >= trackRect.left - 10) {
710 currentIndex = i;
711 break;
712 }
713 }
714
715 scrollToIndex(currentIndex + dir);
716 }
717
718 useEffect(function () {
719 if (!trackRef.current) return;
720 var track = trackRef.current;
721
722 function getScrollPct() {
723 var max = track.scrollWidth - track.clientWidth;
724 if (max <= 0) return 0;
725 var pct = (track.scrollLeft / max) * 100;
726 if (pct < 0) pct = 0;
727 if (pct > 100) pct = 100;
728 return pct;
729 }
730
731 function updateActiveFromScroll() {
732 var slides = getSlides();
733 if (!slides.length) return;
734 var trackRect = track.getBoundingClientRect();
735 var idx = 0;
736 for (var i = 0; i < slides.length; i++) {
737 var r = slides[i].getBoundingClientRect();
738 if (r.left >= trackRect.left - 10) {
739 idx = i;
740 break;
741 }
742 }
743 setActiveSlide(idx);
744
745 // Smooth progress update
746 setProgressPct(getScrollPct());
747 }
748
749 updateActiveFromScroll();
750 track.addEventListener('scroll', updateActiveFromScroll, { passive: true });
751 return function () {
752 track.removeEventListener('scroll', updateActiveFromScroll);
753 };
754 }, [a.layoutStyle, a.members.length]);
755
756 function renderIndicators() {
757 if (a.carouselIndicators === 'none') return null;
758 if (a.carouselIndicators === 'progress') {
759 return el('div', { className: 'bkbg-tm-indicators' },
760 el('div', { className: 'bkbg-tm-progress' },
761 el('div', { className: 'bkbg-tm-progress-bar', style: { width: progressPct + '%' } })
762 )
763 );
764 }
765
766 if (a.carouselIndicators === 'fraction') {
767 var total = Math.max(1, (a.members || []).length);
768 return el('div', { className: 'bkbg-tm-indicators' },
769 el('div', { className: 'bkbg-tm-fraction', 'aria-label': __('Carousel position', 'blockenberg') },
770 el('span', { className: 'bkbg-tm-fraction-current' }, String(activeSlide + 1)),
771 el('span', { 'aria-hidden': 'true' }, '/'),
772 el('span', { className: 'bkbg-tm-fraction-total' }, String(total))
773 )
774 );
775 }
776
777 return el('div', { className: 'bkbg-tm-indicators' },
778 el('div', { className: 'bkbg-tm-dots', role: 'tablist', 'aria-label': __('Team Members Carousel', 'blockenberg') },
779 (a.members || []).map(function (_, i) {
780 return el('button', {
781 key: i,
782 type: 'button',
783 className: 'bkbg-tm-dot' + (i === activeSlide ? ' is-active' : ''),
784 'aria-label': __('Go to slide', 'blockenberg') + ' ' + (i + 1),
785 onClick: function (e) { e.preventDefault(); e.stopPropagation(); scrollToIndex(i); }
786 });
787 })
788 )
789 );
790 }
791
792 function renderPhoto(member, index) {
793 var hasImg = member.image && member.image.url;
794 if (hasImg) {
795 return el('div', {
796 className: 'bkbg-tm-photo',
797 onClick: function (e) {
798 e.preventDefault();
799 e.stopPropagation();
800 setActiveIndex(index);
801 openMedia(function (att) {
802 updateMemberImage(index, { id: att.id, url: att.url, alt: att.alt || '' });
803 });
804 },
805 title: __('Click to change photo', 'blockenberg')
806 },
807 el('img', { src: member.image.url, alt: member.image.alt || '' })
808 );
809 }
810
811 return el('div', {
812 className: 'bkbg-tm-photo',
813 onClick: function (e) {
814 e.preventDefault();
815 e.stopPropagation();
816 setActiveIndex(index);
817 openMedia(function (att) {
818 updateMemberImage(index, { id: att.id, url: att.url, alt: att.alt || '' });
819 });
820 }
821 },
822 el('div', { className: 'bkbg-tm-photo-placeholder' },
823 el('span', { className: 'dashicons dashicons-format-image' }),
824 el('div', {}, __('Add Photo', 'blockenberg'))
825 )
826 );
827 }
828
829 function renderSocials(member) {
830 if (!a.showSocials) return null;
831 var socials = (member.socials || []).filter(function (s) { return s && s.url; });
832 if (!socials.length) return null;
833
834 return el('div', { className: 'bkbg-tm-socials' },
835 socials.map(function (s, si) {
836 var href = normalizeUrl(s.type, s.url);
837 var isDirect = s.type === 'email' || s.type === 'phone';
838 return el('a', {
839 key: si,
840 className: 'bkbg-tm-social',
841 href: href,
842 rel: isDirect ? undefined : 'noopener noreferrer',
843 target: isDirect ? undefined : '_blank'
844 },
845 renderSocialIcon(s.type)
846 );
847 })
848 );
849 }
850
851 function renderMemberCard(member, index) {
852 var isActive = index === activeIndex;
853 var cardClasses = ['bkbg-tm-card'];
854 if (a.cardShadow) cardClasses.push('has-shadow');
855 if (isActive && isSelected) cardClasses.push('is-active');
856
857 return el('div', {
858 key: index,
859 className: cardClasses.join(' '),
860 onClick: function () { setActiveIndex(index); }
861 },
862 el('div', { className: 'bkbg-tm-actions' },
863 el(Button, {
864 icon: 'arrow-left-alt2',
865 label: __('Move Left', 'blockenberg'),
866 disabled: index === 0,
867 onClick: function (e) { e.preventDefault(); e.stopPropagation(); moveMember(index, -1); }
868 }),
869 el(Button, {
870 icon: 'arrow-right-alt2',
871 label: __('Move Right', 'blockenberg'),
872 disabled: index === a.members.length - 1,
873 onClick: function (e) { e.preventDefault(); e.stopPropagation(); moveMember(index, 1); }
874 }),
875 el(Button, {
876 icon: 'admin-page',
877 label: __('Duplicate', 'blockenberg'),
878 onClick: function (e) { e.preventDefault(); e.stopPropagation(); duplicateMember(index); }
879 }),
880 el(Button, {
881 icon: 'trash',
882 label: __('Remove', 'blockenberg'),
883 isDestructive: true,
884 disabled: a.members.length <= 1,
885 onClick: function (e) { e.preventDefault(); e.stopPropagation(); removeMember(index); }
886 })
887 ),
888
889 renderPhoto(member, index),
890
891 el(RichText, {
892 tagName: 'h3',
893 className: 'bkbg-tm-name',
894 value: member.name,
895 placeholder: __('Member Name', 'blockenberg'),
896 onChange: function (v) { updateMember(index, 'name', v); }
897 }),
898
899 a.showRole && el(RichText, {
900 tagName: 'div',
901 className: 'bkbg-tm-role',
902 value: member.role,
903 placeholder: __('Role / Position', 'blockenberg'),
904 onChange: function (v) { updateMember(index, 'role', v); }
905 }),
906
907 a.showBio && el(RichText, {
908 tagName: 'p',
909 className: 'bkbg-tm-bio',
910 value: member.bio,
911 placeholder: __('Short bio…', 'blockenberg'),
912 onChange: function (v) { updateMember(index, 'bio', v); }
913 }),
914
915 renderSocials(member)
916 );
917 }
918
919 function renderMembers() {
920 if (a.layoutStyle === 'carousel') {
921 var indicators = renderIndicators();
922 var nav = a.carouselNav && el('div', { className: 'bkbg-tm-nav' },
923 el('button', {
924 type: 'button',
925 className: 'bkbg-tm-nav-btn bkbg-tm-prev',
926 'aria-label': __('Previous', 'blockenberg'),
927 onClick: function (e) { e.preventDefault(); e.stopPropagation(); scrollCarousel(-1); }
928 }, el('span', { className: 'dashicons dashicons-arrow-left-alt2' })),
929 el('button', {
930 type: 'button',
931 className: 'bkbg-tm-nav-btn bkbg-tm-next',
932 'aria-label': __('Next', 'blockenberg'),
933 onClick: function (e) { e.preventDefault(); e.stopPropagation(); scrollCarousel(1); }
934 }, el('span', { className: 'dashicons dashicons-arrow-right-alt2' }))
935 );
936
937 return el('div', { className: 'bkbg-tm-carousel' + (a.carouselPeek ? ' bkbg-tm-carousel-peek' : '') },
938 (a.carouselIndicatorsPosition === 'top') && indicators,
939 el('div', { className: 'bkbg-tm-carousel-inner' },
940 nav,
941 el('div', { className: 'bkbg-tm-track', ref: trackRef },
942 a.members.map(function (member, index) {
943 return el('div', { key: index, className: 'bkbg-tm-slide' }, renderMemberCard(member, index));
944 })
945 )
946 ),
947 (a.carouselIndicatorsPosition !== 'top') && indicators
948 );
949 }
950
951 return el('div', { className: 'bkbg-tm-grid' },
952 a.members.map(function (member, index) {
953 return renderMemberCard(member, index);
954 })
955 );
956 }
957
958 return el(Fragment, {},
959 inspector,
960 blockControls,
961 el('div', Object.assign({}, blockProps, { style: styleVars }),
962 renderMembers(),
963
964 el('div', { className: 'bkbg-editor-actions' },
965 el(Button, { variant: 'secondary', icon: 'plus-alt2', onClick: addMember }, __('Add Member', 'blockenberg'))
966 )
967 )
968 );
969 },
970
971 save: function (props) {
972 var a = props.attributes;
973
974 function getPhotoRadius(shape, radiusPx) {
975 if (shape === 'circle') return '999px';
976 if (shape === 'square') return '0px';
977 return (radiusPx || 12) + 'px';
978 }
979
980 function getSocialIcon(type) {
981 var map = {
982 website: 'admin-site',
983 email: 'email',
984 phone: 'phone',
985 facebook: 'facebook',
986 twitter: null,
987 instagram: 'instagram',
988 linkedin: 'linkedin',
989 github: null
990 };
991 return map[type] || 'admin-site';
992 }
993
994 function renderSocialIcon(type) {
995 if (type === 'twitter') {
996 return el('svg', {
997 viewBox: '0 0 24 24',
998 width: '24',
999 height: '24',
1000 'aria-hidden': 'true',
1001 focusable: 'false'
1002 },
1003 el('path', {
1004 d: 'M18.3 2H21l-6.6 7.6L22 22h-6.2l-4.8-6.3L5.5 22H3l7.1-8.2L2 2h6.3l4.3 5.7L18.3 2Zm-1.1 18h1.4L7 3.9H5.5L17.2 20Z'
1005 })
1006 );
1007 }
1008 if (type === 'github') {
1009 return el('svg', {
1010 viewBox: '0 0 24 24',
1011 width: '24',
1012 height: '24',
1013 'aria-hidden': 'true',
1014 focusable: 'false'
1015 },
1016 el('path', {
1017 d: 'M12 .5C5.65.5.5 5.82.5 12.39c0 5.24 3.44 9.68 8.2 11.25.6.11.82-.26.82-.58 0-.29-.01-1.06-.02-2.07-3.34.75-4.04-1.66-4.04-1.66-.55-1.43-1.33-1.81-1.33-1.81-1.08-.76.08-.74.08-.74 1.2.09 1.83 1.26 1.83 1.26 1.06 1.87 2.78 1.33 3.46 1.01.11-.79.42-1.33.76-1.63-2.66-.31-5.46-1.38-5.46-6.14 0-1.36.47-2.47 1.24-3.34-.12-.31-.54-1.56.12-3.25 0 0 1.01-.33 3.3 1.28a11.1 11.1 0 0 1 3.01-.42c1.02.01 2.05.14 3.01.42 2.28-1.61 3.29-1.28 3.29-1.28.66 1.69.24 2.94.12 3.25.77.87 1.24 1.98 1.24 3.34 0 4.78-2.8 5.83-5.48 6.14.43.38.82 1.13.82 2.29 0 1.65-.02 2.98-.02 3.39 0 .32.22.7.83.58 4.76-1.57 8.2-6.01 8.2-11.25C23.5 5.82 18.35.5 12 .5Z'
1018 })
1019 );
1020 }
1021 var dash = getSocialIcon(type) || 'admin-site';
1022 return el('span', { className: 'dashicons dashicons-' + dash });
1023 }
1024
1025 function normalizeUrl(type, url) {
1026 if (!url) return '';
1027 var v = String(url).trim();
1028 if (!v) return '';
1029 if (type === 'email') {
1030 if (v.indexOf('mailto:') === 0) return v;
1031 return 'mailto:' + v;
1032 }
1033 if (type === 'phone') {
1034 if (v.indexOf('tel:') === 0) return v;
1035 return 'tel:' + v;
1036 }
1037 if (v.charAt(0) === '#') return v;
1038 if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(v)) return v;
1039 return 'https://' + v;
1040 }
1041
1042 var styleVars = {
1043 '--bkbg-tm-columns': String(a.columns),
1044 '--bkbg-tm-gap': a.gap + 'px',
1045 '--bkbg-tm-carousel-peek': a.carouselPeek ? '20%' : '0px',
1046 '--bkbg-tm-card-bg': a.cardBg,
1047 '--bkbg-tm-card-border-color': a.cardBorderColor,
1048 '--bkbg-tm-card-border-width': a.cardBorderWidth + 'px',
1049 '--bkbg-tm-card-radius': a.cardRadius + 'px',
1050 '--bkbg-tm-card-padding': a.cardPadding + 'px',
1051 '--bkbg-tm-text-align': a.textAlign,
1052 '--bkbg-tm-photo-size': a.imageSize + 'px',
1053 '--bkbg-tm-photo-radius': getPhotoRadius(a.imageShape, 12),
1054 '--bkbg-tm-name-color': a.nameColor,
1055 '--bkbg-tm-role-color': a.roleColor,
1056 '--bkbg-tm-bio-color': a.bioColor,
1057 '--bkbg-tm-name-size': a.nameSize + 'px',
1058 '--bkbg-tm-role-size': a.roleSize + 'px',
1059 '--bkbg-tm-bio-size': a.bioSize + 'px',
1060 '--bkbg-tm-name-weight': String(a.nameWeight),
1061 '--bkbg-tm-role-weight': String(a.roleWeight),
1062 '--bkbg-tm-social-color': a.socialColor,
1063 '--bkbg-tm-social-hover': a.socialHoverColor,
1064 '--bkbg-tm-social-size': a.socialSize + 'px',
1065 '--bkbg-tm-hover-lift': a.hoverLiftAmount + 'px',
1066
1067 '--bkbg-tm-indicator-size': (a.carouselIndicatorSize || 8) + 'px',
1068 '--bkbg-tm-indicator-color': a.carouselIndicatorColor,
1069 '--bkbg-tm-indicator-active-color': a.carouselIndicatorActiveColor,
1070 '--bkbg-tm-progress-height': (a.carouselProgressHeight || 6) + 'px',
1071 '--bkbg-tm-progress-bg': a.carouselProgressBg,
1072 '--bkbg-tm-progress-fg': a.carouselProgressFg,
1073 '--bkbg-tm-fraction-color': a.carouselFractionColor,
1074 '--bkbg-tm-fraction-size': (a.carouselFractionSize || 13) + 'px'
1075 };
1076
1077 var classes = [
1078 'bkbg-tm-wrap',
1079 'bkbg-tm-align-' + a.textAlign
1080 ];
1081 if (a.hoverLift) classes.push('bkbg-tm-hover-lift');
1082 if (a.layoutStyle === 'carousel') {
1083 classes.push('bkbg-tm-nav-' + (a.carouselNavStyle || 'top'));
1084 classes.push('bkbg-tm-indicators-' + (a.carouselIndicators || 'none'));
1085 classes.push('bkbg-tm-indicators-pos-' + (a.carouselIndicatorsPosition || 'below'));
1086
1087 if (a.carouselIndicators === 'dots') {
1088 classes.push('bkbg-tm-dots-style-' + (a.carouselDotsStyle || 'default'));
1089 }
1090 if (a.carouselIndicators === 'progress') {
1091 classes.push('bkbg-tm-progress-style-' + (a.carouselProgressStyle || 'default'));
1092 }
1093 }
1094
1095 var blockProps = useBlockProps.save({
1096 className: classes.join(' '),
1097 style: styleVars,
1098 'data-bkbg-infinite': a.infiniteScroll === false ? '0' : '1',
1099 'data-bkbg-autoscroll': a.autoScroll ? '1' : '0',
1100 'data-bkbg-autoscroll-speed': String(a.autoScrollSpeed || 40),
1101 'data-bkbg-autoscroll-hover': a.autoScrollPauseOnHover === false ? '0' : '1'
1102 });
1103
1104 function renderSocials(member) {
1105 if (!a.showSocials) return null;
1106 var socials = (member.socials || []).filter(function (s) { return s && s.url; });
1107 if (!socials.length) return null;
1108
1109 return el('div', { className: 'bkbg-tm-socials' },
1110 socials.map(function (s, si) {
1111 var href = normalizeUrl(s.type, s.url);
1112 var isDirect = s.type === 'email' || s.type === 'phone';
1113 return el('a', {
1114 key: si,
1115 className: 'bkbg-tm-social',
1116 href: href,
1117 rel: isDirect ? undefined : 'noopener noreferrer',
1118 target: isDirect ? undefined : '_blank'
1119 },
1120 renderSocialIcon(s.type)
1121 );
1122 })
1123 );
1124 }
1125
1126 return el('div', blockProps,
1127 a.layoutStyle === 'carousel'
1128 ? el('div', { className: 'bkbg-tm-carousel' + (a.carouselPeek ? ' bkbg-tm-carousel-peek' : '') },
1129 (a.carouselIndicatorsPosition === 'top') && (a.carouselIndicators !== 'none') && el('div', { className: 'bkbg-tm-indicators' },
1130 a.carouselIndicators === 'progress'
1131 ? el('div', { className: 'bkbg-tm-progress' }, el('div', { className: 'bkbg-tm-progress-bar', style: { width: '0%' } }))
1132 : a.carouselIndicators === 'fraction'
1133 ? el('div', { className: 'bkbg-tm-fraction', 'aria-label': __('Carousel position', 'blockenberg') },
1134 el('span', { className: 'bkbg-tm-fraction-current', 'data-bkbg-fraction': 'current' }, '1'),
1135 el('span', { 'aria-hidden': 'true' }, '/'),
1136 el('span', { className: 'bkbg-tm-fraction-total', 'data-bkbg-fraction': 'total' }, String(Math.max(1, (a.members || []).length)))
1137 )
1138 : el('div', { className: 'bkbg-tm-dots', role: 'tablist', 'aria-label': __('Team Members Carousel', 'blockenberg') },
1139 (a.members || []).map(function (_, i) {
1140 return el('button', {
1141 key: i,
1142 type: 'button',
1143 className: 'bkbg-tm-dot' + (i === 0 ? ' is-active' : ''),
1144 'data-bkbg-dot': String(i),
1145 'aria-label': __('Go to slide', 'blockenberg') + ' ' + (i + 1)
1146 });
1147 })
1148 )
1149 ),
1150 el('div', { className: 'bkbg-tm-carousel-inner' },
1151 a.carouselNav && el('div', { className: 'bkbg-tm-nav' },
1152 el('button', {
1153 type: 'button',
1154 className: 'bkbg-tm-nav-btn bkbg-tm-prev',
1155 'data-bkbg-nav': 'prev',
1156 'aria-label': __('Previous', 'blockenberg')
1157 }, el('span', { className: 'dashicons dashicons-arrow-left-alt2' })),
1158 el('button', {
1159 type: 'button',
1160 className: 'bkbg-tm-nav-btn bkbg-tm-next',
1161 'data-bkbg-nav': 'next',
1162 'aria-label': __('Next', 'blockenberg')
1163 }, el('span', { className: 'dashicons dashicons-arrow-right-alt2' }))
1164 ),
1165 el('div', { className: 'bkbg-tm-track' },
1166 (a.members || []).map(function (member, index) {
1167 var cardClasses = ['bkbg-tm-card'];
1168 if (a.cardShadow) cardClasses.push('has-shadow');
1169
1170 return el('div', { key: index, className: 'bkbg-tm-slide' },
1171 el('div', { className: cardClasses.join(' ') },
1172 el('div', { className: 'bkbg-tm-photo' },
1173 member.image && member.image.url
1174 ? el('img', { src: member.image.url, alt: (member.image.alt || '') })
1175 : el('div', { className: 'bkbg-tm-photo-placeholder' },
1176 el('span', { className: 'dashicons dashicons-format-image' })
1177 )
1178 ),
1179 el(RichText.Content, { tagName: 'h3', className: 'bkbg-tm-name', value: member.name }),
1180 a.showRole && el(RichText.Content, { tagName: 'div', className: 'bkbg-tm-role', value: member.role }),
1181 a.showBio && el(RichText.Content, { tagName: 'p', className: 'bkbg-tm-bio', value: member.bio }),
1182 renderSocials(member)
1183 )
1184 );
1185 })
1186 )
1187 ),
1188 (a.carouselIndicatorsPosition !== 'top') && (a.carouselIndicators !== 'none') && el('div', { className: 'bkbg-tm-indicators' },
1189 a.carouselIndicators === 'progress'
1190 ? el('div', { className: 'bkbg-tm-progress' }, el('div', { className: 'bkbg-tm-progress-bar', style: { width: '0%' } }))
1191 : a.carouselIndicators === 'fraction'
1192 ? el('div', { className: 'bkbg-tm-fraction', 'aria-label': __('Carousel position', 'blockenberg') },
1193 el('span', { className: 'bkbg-tm-fraction-current', 'data-bkbg-fraction': 'current' }, '1'),
1194 el('span', { 'aria-hidden': 'true' }, '/'),
1195 el('span', { className: 'bkbg-tm-fraction-total', 'data-bkbg-fraction': 'total' }, String(Math.max(1, (a.members || []).length)))
1196 )
1197 : el('div', { className: 'bkbg-tm-dots', role: 'tablist', 'aria-label': __('Team Members Carousel', 'blockenberg') },
1198 (a.members || []).map(function (_, i) {
1199 return el('button', {
1200 key: i,
1201 type: 'button',
1202 className: 'bkbg-tm-dot' + (i === 0 ? ' is-active' : ''),
1203 'data-bkbg-dot': String(i),
1204 'aria-label': __('Go to slide', 'blockenberg') + ' ' + (i + 1)
1205 });
1206 })
1207 )
1208 )
1209 )
1210 : el('div', { className: 'bkbg-tm-grid' },
1211 (a.members || []).map(function (member, index) {
1212 var cardClasses = ['bkbg-tm-card'];
1213 if (a.cardShadow) cardClasses.push('has-shadow');
1214
1215 return el('div', { key: index, className: cardClasses.join(' ') },
1216 el('div', { className: 'bkbg-tm-photo' },
1217 member.image && member.image.url
1218 ? el('img', { src: member.image.url, alt: (member.image.alt || '') })
1219 : el('div', { className: 'bkbg-tm-photo-placeholder' },
1220 el('span', { className: 'dashicons dashicons-format-image' })
1221 )
1222 ),
1223 el(RichText.Content, { tagName: 'h3', className: 'bkbg-tm-name', value: member.name }),
1224 a.showRole && el(RichText.Content, { tagName: 'div', className: 'bkbg-tm-role', value: member.role }),
1225 a.showBio && el(RichText.Content, { tagName: 'p', className: 'bkbg-tm-bio', value: member.bio }),
1226 renderSocials(member)
1227 );
1228 })
1229 )
1230 );
1231 }
1232 });
1233 });
1234