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 / progress-bar / index.js

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

701 lines 33.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 ( function () {
2 var el = wp.element.createElement;
3 var Fragment = wp.element.Fragment;
4 var __ = wp.i18n.__;
5 var useState = wp.element.useState;
6 var registerBlockType = wp.blocks.registerBlockType;
7 var InspectorControls = wp.blockEditor.InspectorControls;
8 var BlockControls = wp.blockEditor.BlockControls;
9 var useBlockProps = wp.blockEditor.useBlockProps;
10 var PanelBody = wp.components.PanelBody;
11 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
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 var ColorPicker = wp.components.ColorPicker;
18 var Popover = wp.components.Popover;
19 var ToolbarGroup = wp.components.ToolbarGroup;
20 var ToolbarButton = wp.components.ToolbarButton;
21
22 var _tc; function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
23 var _tv; function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
24
25 var v1Attributes = {
26 items: { type: 'array', default: [{ label: 'Web Design', percentage: 90, color: '#3b82f6' }, { label: 'Development', percentage: 85, color: '#10b981' }, { label: 'Marketing', percentage: 75, color: '#f59e0b' }] },
27 layoutStyle: { type: 'string', default: 'default' },
28 showPercentage: { type: 'boolean', default: true },
29 showLabels: { type: 'boolean', default: true },
30 animateOnScroll: { type: 'boolean', default: true },
31 animationDuration: { type: 'number', default: 1000 },
32 barHeight: { type: 'number', default: 20 },
33 barSpacing: { type: 'number', default: 24 },
34 barRadius: { type: 'number', default: 10 },
35 trackColor: { type: 'string', default: '#e5e7eb' },
36 labelColor: { type: 'string', default: '#1f2937' },
37 percentageColor: { type: 'string', default: '#6b7280' },
38 labelSize: { type: 'number', default: 16 },
39 percentageSize: { type: 'number', default: 14 },
40 labelWeight: { type: 'number', default: 500 },
41 percentagePosition: { type: 'string', default: 'right' },
42 stripes: { type: 'boolean', default: false },
43 stripesAnimated: { type: 'boolean', default: false },
44 glowEffect: { type: 'boolean', default: false },
45 gradient: { type: 'boolean', default: false },
46 gradientAngle: { type: 'number', default: 90 },
47 title: { type: 'string', default: '' },
48 showTitle: { type: 'boolean', default: false },
49 titleColor: { type: 'string', default: '#1f2937' },
50 titleSize: { type: 'number', default: 24 },
51 titleFontWeight: { type: 'number', default: 600 },
52 titleLineHeight: { type: 'number', default: 1.3 },
53 labelLineHeight: { type: 'number', default: 1.4 }
54 };
55
56 registerBlockType('blockenberg/progress-bar', {
57 title: __('Progress Bar', 'blockenberg'),
58 icon: 'chart-bar',
59 category: 'bkbg-content',
60 description: __('Display animated progress bars with customizable styles and labels.', 'blockenberg'),
61
62 deprecated: [{
63 attributes: v1Attributes,
64 save: function (props) {
65 var a = props.attributes;
66 var wrapStyle = {
67 '--bkbg-pb-bar-height': a.barHeight + 'px',
68 '--bkbg-pb-bar-spacing': a.barSpacing + 'px',
69 '--bkbg-pb-bar-radius': a.barRadius + 'px',
70 '--bkbg-pb-track-color': a.trackColor,
71 '--bkbg-pb-label-color': a.labelColor,
72 '--bkbg-pb-label-size': a.labelSize + 'px',
73 '--bkbg-pb-label-weight': a.labelWeight,
74 '--bkbg-pb-label-line-height': a.labelLineHeight,
75 '--bkbg-pb-percentage-color': a.percentageColor,
76 '--bkbg-pb-percentage-size': a.percentageSize + 'px',
77 '--bkbg-pb-title-color': a.titleColor,
78 '--bkbg-pb-title-size': a.titleSize + 'px',
79 '--bkbg-pb-title-font-weight': a.titleFontWeight,
80 '--bkbg-pb-title-line-height': a.titleLineHeight,
81 '--bkbg-pb-animation-duration': a.animationDuration + 'ms'
82 };
83 var itemsData = a.items.map(function (item) {
84 return item.label + '|' + item.percentage + '|' + item.color;
85 }).join(';;');
86 var layoutClass = 'bkbg-pb-wrap';
87 layoutClass += ' bkbg-pb-' + a.layoutStyle;
88 if (a.stripes) layoutClass += ' bkbg-pb-striped';
89 if (a.stripesAnimated) layoutClass += ' bkbg-pb-striped-animated';
90 if (a.glowEffect) layoutClass += ' bkbg-pb-glow';
91 if (a.gradient) layoutClass += ' bkbg-pb-gradient';
92 var titleEl = null;
93 if (a.showTitle && a.title) {
94 titleEl = el('div', { className: 'bkbg-pb-title' }, a.title);
95 }
96 return el('div', {
97 className: layoutClass,
98 style: wrapStyle,
99 'data-layout': a.layoutStyle,
100 'data-items': itemsData,
101 'data-animate': a.animateOnScroll ? '1' : '0',
102 'data-show-labels': a.showLabels ? '1' : '0',
103 'data-show-percentage': a.showPercentage ? '1' : '0',
104 'data-percentage-position': a.percentagePosition,
105 'data-gradient': a.gradient ? '1' : '0',
106 'data-gradient-angle': a.gradientAngle,
107 'data-glow': a.glowEffect ? '1' : '0'
108 },
109 titleEl,
110 el('div', { className: 'bkbg-pb-list' })
111 );
112 }
113 }],
114
115 edit: function (props) {
116 var attributes = props.attributes;
117 var setAttributes = props.setAttributes;
118 var isSelected = props.isSelected;
119 var a = attributes;
120 var TC = getTypoControl();
121
122 // State for editing
123 var editingState = useState(null);
124 var editing = editingState[0];
125 var setEditing = editingState[1];
126
127 // State for hovered item
128 var hoveredState = useState(null);
129 var hovered = hoveredState[0];
130 var setHovered = hoveredState[1];
131
132 // State for color picker
133 var colorPickerState = useState(null);
134 var colorPicker = colorPickerState[0];
135 var setColorPicker = colorPickerState[1];
136
137 var layoutOptions = [
138 { label: __('Default', 'blockenberg'), value: 'default' },
139 { label: __('Rounded', 'blockenberg'), value: 'rounded' },
140 { label: __('Flat', 'blockenberg'), value: 'flat' },
141 { label: __('Thin', 'blockenberg'), value: 'thin' },
142 { label: __('Thick', 'blockenberg'), value: 'thick' }
143 ];
144
145 var percentagePositionOptions = [
146 { label: __('Right of Label', 'blockenberg'), value: 'right' },
147 { label: __('Inside Bar', 'blockenberg'), value: 'inside' },
148 { label: __('Above Bar', 'blockenberg'), value: 'above' },
149 { label: __('Hidden', 'blockenberg'), value: 'hidden' }
150 ];
151
152 // Update item
153 function updateItem(index, key, value) {
154 var newItems = a.items.slice();
155 newItems[index] = Object.assign({}, newItems[index]);
156 newItems[index][key] = value;
157 setAttributes({ items: newItems });
158 }
159
160 // Add item
161 function addItem() {
162 var colors = ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899'];
163 var newColor = colors[a.items.length % colors.length];
164 var newItems = a.items.concat([{
165 label: __('New Skill', 'blockenberg'),
166 percentage: 50,
167 color: newColor
168 }]);
169 setAttributes({ items: newItems });
170 }
171
172 // Remove item
173 function removeItem(index) {
174 var newItems = a.items.filter(function (_, i) { return i !== index; });
175 setAttributes({ items: newItems });
176 }
177
178 // Move item
179 function moveItem(index, direction) {
180 var newIndex = index + direction;
181 if (newIndex < 0 || newIndex >= a.items.length) return;
182 var newItems = a.items.slice();
183 var temp = newItems[index];
184 newItems[index] = newItems[newIndex];
185 newItems[newIndex] = temp;
186 setAttributes({ items: newItems });
187 }
188
189 // Inspector controls
190 var inspector = el(InspectorControls, {},
191 el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: true },
192 el(SelectControl, {
193 label: __('Style', 'blockenberg'),
194 value: a.layoutStyle,
195 options: layoutOptions,
196 onChange: function (v) { setAttributes({ layoutStyle: v }); }
197 }),
198 el(RangeControl, {
199 label: __('Bar Height', 'blockenberg'),
200 value: a.barHeight,
201 onChange: function (v) { setAttributes({ barHeight: v }); },
202 min: 4,
203 max: 60
204 }),
205 el(RangeControl, {
206 label: __('Bar Spacing', 'blockenberg'),
207 value: a.barSpacing,
208 onChange: function (v) { setAttributes({ barSpacing: v }); },
209 min: 8,
210 max: 60
211 }),
212 el(RangeControl, {
213 label: __('Border Radius', 'blockenberg'),
214 value: a.barRadius,
215 onChange: function (v) { setAttributes({ barRadius: v }); },
216 min: 0,
217 max: 30
218 })
219 ),
220
221 el(PanelBody, { title: __('Labels & Percentage', 'blockenberg'), initialOpen: false },
222 el(ToggleControl, {
223 label: __('Show Labels', 'blockenberg'),
224 checked: a.showLabels,
225 __nextHasNoMarginBottom: true,
226 onChange: function (v) { setAttributes({ showLabels: v }); }
227 }),
228 el(ToggleControl, {
229 label: __('Show Percentage', 'blockenberg'),
230 checked: a.showPercentage,
231 __nextHasNoMarginBottom: true,
232 onChange: function (v) { setAttributes({ showPercentage: v }); }
233 }),
234 a.showPercentage && el(SelectControl, {
235 label: __('Percentage Position', 'blockenberg'),
236 value: a.percentagePosition,
237 options: percentagePositionOptions,
238 onChange: function (v) { setAttributes({ percentagePosition: v }); }
239 }),
240 ),
241
242 el(PanelBody, { title: __('Effects', 'blockenberg'), initialOpen: false },
243 el(ToggleControl, {
244 label: __('Animate on Scroll', 'blockenberg'),
245 checked: a.animateOnScroll,
246 __nextHasNoMarginBottom: true,
247 onChange: function (v) { setAttributes({ animateOnScroll: v }); }
248 }),
249 a.animateOnScroll && el(RangeControl, {
250 label: __('Animation Duration (ms)', 'blockenberg'),
251 value: a.animationDuration,
252 onChange: function (v) { setAttributes({ animationDuration: v }); },
253 min: 200,
254 max: 3000,
255 step: 100
256 }),
257 el(ToggleControl, {
258 label: __('Stripes', 'blockenberg'),
259 checked: a.stripes,
260 __nextHasNoMarginBottom: true,
261 onChange: function (v) { setAttributes({ stripes: v }); }
262 }),
263 a.stripes && el(ToggleControl, {
264 label: __('Animated Stripes', 'blockenberg'),
265 checked: a.stripesAnimated,
266 __nextHasNoMarginBottom: true,
267 onChange: function (v) { setAttributes({ stripesAnimated: v }); }
268 }),
269 el(ToggleControl, {
270 label: __('Glow Effect', 'blockenberg'),
271 checked: a.glowEffect,
272 __nextHasNoMarginBottom: true,
273 onChange: function (v) { setAttributes({ glowEffect: v }); }
274 }),
275 el(ToggleControl, {
276 label: __('Gradient', 'blockenberg'),
277 checked: a.gradient,
278 __nextHasNoMarginBottom: true,
279 onChange: function (v) { setAttributes({ gradient: v }); }
280 }),
281 a.gradient && el(RangeControl, {
282 label: __('Gradient Angle', 'blockenberg'),
283 value: a.gradientAngle,
284 onChange: function (v) { setAttributes({ gradientAngle: v }); },
285 min: 0,
286 max: 360
287 })
288 ),
289
290 el(PanelBody, { title: __('Title', 'blockenberg'), initialOpen: false },
291 el(ToggleControl, {
292 label: __('Show Title', 'blockenberg'),
293 checked: a.showTitle,
294 __nextHasNoMarginBottom: true,
295 onChange: function (v) { setAttributes({ showTitle: v }); }
296 }),
297 ),
298
299 el(PanelBody, { title: __('Progress Items', 'blockenberg'), initialOpen: false },
300 a.items.map(function (item, index) {
301 return el('div', { key: index, className: 'bkbg-pb-item-control' },
302 el('div', { className: 'bkbg-pb-item-header' },
303 el('span', { className: 'bkbg-pb-item-title' }, item.label || __('Item', 'blockenberg') + ' ' + (index + 1)),
304 el('div', { className: 'bkbg-pb-item-actions' },
305 el(Button, {
306 icon: 'arrow-up-alt2',
307 size: 'small',
308 disabled: index === 0,
309 onClick: function () { moveItem(index, -1); }
310 }),
311 el(Button, {
312 icon: 'arrow-down-alt2',
313 size: 'small',
314 disabled: index === a.items.length - 1,
315 onClick: function () { moveItem(index, 1); }
316 }),
317 el(Button, {
318 icon: 'trash',
319 size: 'small',
320 isDestructive: true,
321 onClick: function () { removeItem(index); }
322 })
323 )
324 ),
325 el(TextControl, {
326 label: __('Label', 'blockenberg'),
327 value: item.label,
328 onChange: function (v) { updateItem(index, 'label', v); }
329 }),
330 el(RangeControl, {
331 label: __('Percentage', 'blockenberg'),
332 value: item.percentage,
333 onChange: function (v) { updateItem(index, 'percentage', v); },
334 min: 0,
335 max: 100
336 }),
337 el('div', { className: 'bkbg-pb-color-row' },
338 el('span', {}, __('Color', 'blockenberg')),
339 el('button', {
340 className: 'bkbg-pb-color-btn',
341 style: { backgroundColor: item.color },
342 onClick: function () {
343 setColorPicker(colorPicker === index ? null : index);
344 }
345 }),
346 colorPicker === index && el(Popover, {
347 position: 'bottom left',
348 onClose: function () { setColorPicker(null); }
349 },
350 el(ColorPicker, {
351 color: item.color,
352 onChangeComplete: function (c) {
353 updateItem(index, 'color', c.hex);
354 }
355 })
356 )
357 )
358 );
359 }),
360 el(Button, {
361 variant: 'secondary',
362 onClick: addItem,
363 className: 'bkbg-pb-add-btn'
364 }, __('+ Add Progress Bar', 'blockenberg'))
365 ),
366
367
368 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
369 TC && a.showTitle && el(TC, { label: __('Title', 'blockenberg'), value: a.titleTypo || {}, onChange: function(v) { setAttributes({ titleTypo: v }); } }),
370 TC && a.showLabels && el(TC, { label: __('Label', 'blockenberg'), value: a.labelTypo || {}, onChange: function(v) { setAttributes({ labelTypo: v }); } }),
371 TC && a.showPercentage && el(TC, { label: __('Percentage', 'blockenberg'), value: a.pctTypo || {}, onChange: function(v) { setAttributes({ pctTypo: v }); } })
372 ),
373 el(PanelColorSettings, {
374 title: __('Colors', 'blockenberg'),
375 initialOpen: false,
376 colorSettings: [
377 {
378 value: a.trackColor,
379 onChange: function (v) { setAttributes({ trackColor: v }); },
380 label: __('Track Color', 'blockenberg')
381 },
382 {
383 value: a.labelColor,
384 onChange: function (v) { setAttributes({ labelColor: v }); },
385 label: __('Label Color', 'blockenberg')
386 },
387 {
388 value: a.percentageColor,
389 onChange: function (v) { setAttributes({ percentageColor: v }); },
390 label: __('Percentage Color', 'blockenberg')
391 },
392 {
393 value: a.titleColor,
394 onChange: function (v) { setAttributes({ titleColor: v }); },
395 label: __('Title Color', 'blockenberg')
396 }
397 ]
398 })
399 );
400
401 // Title element
402 var titleEl = null;
403 if (a.showTitle) {
404 if (editing === 'title') {
405 titleEl = el('input', {
406 type: 'text',
407 className: 'bkbg-pb-title bkbg-pb-input-active',
408 value: a.title,
409 autoFocus: true,
410 placeholder: __('Progress Title', 'blockenberg'),
411 onChange: function (e) { setAttributes({ title: e.target.value }); },
412 onBlur: function () { setEditing(null); },
413 onKeyDown: function (e) { if (e.key === 'Enter') setEditing(null); }
414 });
415 } else {
416 titleEl = el('div', {
417 className: 'bkbg-pb-title bkbg-pb-clickable',
418 onClick: function () { setEditing('title'); }
419 }, a.title || __('Progress Title', 'blockenberg'));
420 }
421 }
422
423 // Lighten color helper
424 function lightenColor(hex, percent) {
425 var num = parseInt(hex.replace('#', ''), 16);
426 var r = Math.min(255, (num >> 16) + Math.round(255 * percent / 100));
427 var g = Math.min(255, ((num >> 8) & 0x00FF) + Math.round(255 * percent / 100));
428 var b = Math.min(255, (num & 0x0000FF) + Math.round(255 * percent / 100));
429 return '#' + (0x1000000 + (r << 16) + (g << 8) + b).toString(16).slice(1);
430 }
431
432 // Get bar style
433 function getBarStyle(item) {
434 var style = {
435 width: item.percentage + '%'
436 };
437
438 // Background
439 if (a.gradient) {
440 var lighterColor = lightenColor(item.color, 30);
441 style.background = 'linear-gradient(' + a.gradientAngle + 'deg, ' + item.color + ' 0%, ' + lighterColor + ' 50%, ' + item.color + ' 100%)';
442 style.backgroundSize = '200% 100%';
443 } else {
444 style.backgroundColor = item.color;
445 }
446
447 // Stripes overlay
448 if (a.stripes) {
449 var stripesBg = 'linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)';
450 if (a.gradient) {
451 var lighterColor = lightenColor(item.color, 30);
452 style.background = stripesBg + ', linear-gradient(' + a.gradientAngle + 'deg, ' + item.color + ' 0%, ' + lighterColor + ' 50%, ' + item.color + ' 100%)';
453 } else {
454 style.background = stripesBg + ', ' + item.color;
455 }
456 style.backgroundSize = '40px 40px, 100% 100%';
457 }
458
459 // Glow effect
460 if (a.glowEffect) {
461 style.boxShadow = '0 0 10px ' + item.color + ', 0 0 20px ' + item.color + '60';
462 }
463
464 return style;
465 }
466
467 // Build progress bars
468 var progressBars = a.items.map(function (item, index) {
469 var labelKey = 'label-' + index;
470
471 // Percentage element based on position
472 var percentageEl = null;
473 if (a.showPercentage && a.percentagePosition !== 'hidden') {
474 percentageEl = el('span', { className: 'bkbg-pb-percentage' }, item.percentage + '%');
475 }
476
477 // Label element - editable
478 var labelEl = null;
479 if (a.showLabels) {
480 if (editing === labelKey) {
481 labelEl = el('input', {
482 type: 'text',
483 className: 'bkbg-pb-label bkbg-pb-input-active',
484 value: item.label,
485 autoFocus: true,
486 onChange: function (e) { updateItem(index, 'label', e.target.value); },
487 onBlur: function () { setEditing(null); },
488 onKeyDown: function (e) { if (e.key === 'Enter') setEditing(null); }
489 });
490 } else {
491 labelEl = el('span', {
492 className: 'bkbg-pb-label bkbg-pb-clickable',
493 onClick: function () { setEditing(labelKey); }
494 }, item.label);
495 }
496 }
497
498 // Header with label and percentage (if position is right)
499 var headerEl = null;
500 if (a.showLabels || (a.showPercentage && a.percentagePosition === 'right')) {
501 headerEl = el('div', { className: 'bkbg-pb-header' },
502 labelEl,
503 a.percentagePosition === 'right' && percentageEl
504 );
505 }
506
507 // Above bar percentage
508 var aboveEl = null;
509 if (a.showPercentage && a.percentagePosition === 'above') {
510 aboveEl = el('div', { className: 'bkbg-pb-above' },
511 el('span', {
512 className: 'bkbg-pb-percentage-above',
513 style: { left: item.percentage + '%' }
514 }, item.percentage + '%')
515 );
516 }
517
518 // Bar classes
519 var barClasses = 'bkbg-pb-bar';
520 if (a.stripes) barClasses += ' bkbg-pb-striped';
521 if (a.stripesAnimated) barClasses += ' bkbg-pb-striped-animated';
522
523 // Item actions (visible on hover)
524 var itemActions = el('div', { className: 'bkbg-pb-item-actions' },
525 el(Button, {
526 icon: 'arrow-up-alt2',
527 size: 'small',
528 disabled: index === 0,
529 onClick: function (e) { e.stopPropagation(); moveItem(index, -1); },
530 label: __('Move Up', 'blockenberg')
531 }),
532 el(Button, {
533 icon: 'arrow-down-alt2',
534 size: 'small',
535 disabled: index === a.items.length - 1,
536 onClick: function (e) { e.stopPropagation(); moveItem(index, 1); },
537 label: __('Move Down', 'blockenberg')
538 }),
539 el(Button, {
540 icon: 'admin-page',
541 size: 'small',
542 onClick: function (e) {
543 e.stopPropagation();
544 var newItems = a.items.slice();
545 newItems.splice(index + 1, 0, Object.assign({}, item));
546 setAttributes({ items: newItems });
547 },
548 label: __('Duplicate', 'blockenberg')
549 }),
550 el(Button, {
551 icon: 'trash',
552 size: 'small',
553 isDestructive: true,
554 onClick: function (e) { e.stopPropagation(); removeItem(index); },
555 label: __('Delete', 'blockenberg')
556 })
557 );
558
559 var itemClass = 'bkbg-pb-item';
560 if (hovered === index) itemClass += ' bkbg-pb-item-hovered';
561
562 return el('div', {
563 className: itemClass,
564 key: index,
565 onMouseEnter: function () { setHovered(index); },
566 onMouseLeave: function () { setHovered(null); }
567 },
568 itemActions,
569 headerEl,
570 aboveEl,
571 el('div', { className: 'bkbg-pb-track' },
572 el('div', {
573 className: barClasses,
574 style: getBarStyle(item)
575 },
576 a.percentagePosition === 'inside' && el('span', { className: 'bkbg-pb-percentage-inside' }, item.percentage + '%')
577 )
578 )
579 );
580 });
581
582 // Layout classes
583 var layoutClass = 'bkbg-pb-wrap bkbg-editor-wrap';
584 layoutClass += ' bkbg-pb-' + a.layoutStyle;
585 if (isSelected) layoutClass += ' bkbg-block-selected';
586
587 // Block props for proper selection handling
588 var blockProps = useBlockProps((function () {
589 var _tv = getTypoCssVars();
590 var s = {
591 '--bkbg-pb-bar-height': a.barHeight + 'px',
592 '--bkbg-pb-bar-spacing': a.barSpacing + 'px',
593 '--bkbg-pb-bar-radius': a.barRadius + 'px',
594 '--bkbg-pb-track-color': a.trackColor,
595 '--bkbg-pb-label-color': a.labelColor,
596 '--bkbg-pb-percentage-color': a.percentageColor,
597 '--bkbg-pb-title-color': a.titleColor,
598 '--bkbg-pb-animation-duration': a.animationDuration + 'ms'
599 };
600 if (_tv) {
601 Object.assign(s, _tv(a.titleTypo || {}, '--bkbg-pb-tt-'));
602 Object.assign(s, _tv(a.labelTypo || {}, '--bkbg-pb-lb-'));
603 Object.assign(s, _tv(a.pctTypo || {}, '--bkbg-pb-pct-'));
604 }
605 return { className: layoutClass, style: s, 'data-layout': a.layoutStyle, 'data-block-label': 'Progress Bar' };
606 })());
607
608 // Block toolbar
609 var blockToolbar = el(BlockControls, {},
610 el(ToolbarGroup, {},
611 el(ToolbarButton, {
612 icon: 'plus-alt2',
613 label: __('Add Progress Bar', 'blockenberg'),
614 onClick: addItem
615 })
616 )
617 );
618
619 // Add item action bar (shared editor UI)
620 var addButton = el('div', { className: 'bkbg-editor-actions' },
621 el(Button, {
622 variant: 'secondary',
623 icon: 'plus-alt2',
624 onClick: addItem
625 }, __('Add Bar', 'blockenberg'))
626 );
627
628 return el(Fragment, {},
629 blockToolbar,
630 inspector,
631 el('div', blockProps,
632 titleEl,
633 el('div', { className: 'bkbg-pb-list' }, progressBars),
634 addButton
635 )
636 );
637 },
638
639 save: function (props) {
640 var a = props.attributes;
641
642 // CSS variables
643 var wrapStyle = (function () {
644 var _tv = getTypoCssVars();
645 var s = {
646 '--bkbg-pb-bar-height': a.barHeight + 'px',
647 '--bkbg-pb-bar-spacing': a.barSpacing + 'px',
648 '--bkbg-pb-bar-radius': a.barRadius + 'px',
649 '--bkbg-pb-track-color': a.trackColor,
650 '--bkbg-pb-label-color': a.labelColor,
651 '--bkbg-pb-percentage-color': a.percentageColor,
652 '--bkbg-pb-title-color': a.titleColor,
653 '--bkbg-pb-animation-duration': a.animationDuration + 'ms'
654 };
655 if (_tv) {
656 Object.assign(s, _tv(a.titleTypo || {}, '--bkbg-pb-tt-'));
657 Object.assign(s, _tv(a.labelTypo || {}, '--bkbg-pb-lb-'));
658 Object.assign(s, _tv(a.pctTypo || {}, '--bkbg-pb-pct-'));
659 }
660 return s;
661 })();
662
663 // Serialize items for frontend
664 var itemsData = a.items.map(function (item) {
665 return item.label + '|' + item.percentage + '|' + item.color;
666 }).join(';;');
667
668 // Layout classes
669 var layoutClass = 'bkbg-pb-wrap';
670 layoutClass += ' bkbg-pb-' + a.layoutStyle;
671 if (a.stripes) layoutClass += ' bkbg-pb-striped';
672 if (a.stripesAnimated) layoutClass += ' bkbg-pb-striped-animated';
673 if (a.glowEffect) layoutClass += ' bkbg-pb-glow';
674 if (a.gradient) layoutClass += ' bkbg-pb-gradient';
675
676 // Title element
677 var titleEl = null;
678 if (a.showTitle && a.title) {
679 titleEl = el('div', { className: 'bkbg-pb-title' }, a.title);
680 }
681
682 return el('div', {
683 className: layoutClass,
684 style: wrapStyle,
685 'data-layout': a.layoutStyle,
686 'data-items': itemsData,
687 'data-animate': a.animateOnScroll ? '1' : '0',
688 'data-show-labels': a.showLabels ? '1' : '0',
689 'data-show-percentage': a.showPercentage ? '1' : '0',
690 'data-percentage-position': a.percentagePosition,
691 'data-gradient': a.gradient ? '1' : '0',
692 'data-gradient-angle': a.gradientAngle,
693 'data-glow': a.glowEffect ? '1' : '0'
694 },
695 titleEl,
696 el('div', { className: 'bkbg-pb-list' })
697 );
698 }
699 });
700 }() );
701