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

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

672 lines 34.5 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 useState = wp.element.useState;
5 var useEffect = wp.element.useEffect;
6 var __ = wp.i18n.__;
7 var registerBlockType = wp.blocks.registerBlockType;
8 var InspectorControls = wp.blockEditor.InspectorControls;
9 var useBlockProps = wp.blockEditor.useBlockProps;
10 var RichText = wp.blockEditor.RichText;
11 var PanelBody = wp.components.PanelBody;
12 var ColorPicker = wp.components.ColorPicker;
13 var Popover = wp.components.Popover;
14 var Button = wp.components.Button;
15 var ToggleControl = wp.components.ToggleControl;
16 var RangeControl = wp.components.RangeControl;
17 var SelectControl = wp.components.SelectControl;
18
19 // ── SVG icon paths (24×24 viewBox) ──────────────────────────────────────────
20 var ICONS = {
21 star: 'M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z',
22 heart: 'M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z',
23 diamond: 'M12 2l9 10-9 10L3 12z',
24 bolt: 'M13 2L4.5 13.5H11L10 22l9.5-12H13z',
25 moon: 'M12 3a9 9 0 0 0 0 18 9 9 0 0 0 7.13-14.47A7 7 0 0 1 12 3z',
26 star8: 'M12 2l2.4 7.4H22l-6.2 4.5 2.4 7.4L12 17l-6.2 4.3 2.4-7.4L2 9.4h7.6z',
27 cross: 'M19 11H13V5h-2v6H5v2h6v6h2v-6h6z',
28 leaf: 'M17 8C8 10 5.9 16.17 3.82 21.34L5.71 22 7 19.46c.61.34 1.3.54 2 .54 9 0 12-8 12-8s-3.5.5-5.5-2.5S17 8 17 8z',
29 snowflake: 'M22 11h-4.17l3.24-3.24-1.41-1.42L15 11h-2V9l4.66-4.66-1.42-1.41L13 6.17V2h-2v4.17L7.76 2.93 6.34 4.34 11 9v2H9L4.34 6.34 2.93 7.76 6.17 11H2v2h4.17l-3.24 3.24 1.41 1.42L9 13h2v2l-4.66 4.66 1.42 1.41L11 17.83V22h2v-4.17l3.24 3.24 1.42-1.41L13 15v-2h2l4.66 4.66 1.41-1.42L17.83 13H22z',
30 circle: null // handled separately with <circle> element
31 };
32
33 // ── Generate unique block ID ─────────────────────────────────────────────────
34 function generateId() {
35 return 'sep' + Math.random().toString(36).substr(2, 8);
36 }
37
38 // ── Build icon SVG element ───────────────────────────────────────────────────
39 function buildIconSVG(iconName, iconColor, iconSize) {
40 var isCircle = iconName === 'circle';
41 var shape = isCircle
42 ? el('circle', { cx: '12', cy: '12', r: '10', fill: iconColor })
43 : el('path', { d: ICONS[iconName] || ICONS.star, fill: iconColor });
44 return el('svg', {
45 viewBox: '0 0 24 24',
46 xmlns: 'http://www.w3.org/2000/svg',
47 width: iconSize,
48 height: iconSize,
49 'aria-hidden': 'true',
50 style: { display: 'block', flexShrink: 0 }
51 }, shape);
52 }
53
54 // ── Build wave path string ───────────────────────────────────────────────────
55 function buildWavePath(svgH) {
56 var mid = svgH / 2;
57 var amp = Math.max(1, mid - 2);
58 return 'M0,' + mid
59 + ' C100,' + (mid - amp) + ' 200,' + (mid + amp) + ' 300,' + mid
60 + ' C400,' + (mid - amp) + ' 500,' + (mid + amp) + ' 600,' + mid
61 + ' C700,' + (mid - amp) + ' 800,' + (mid + amp) + ' 900,' + mid
62 + ' C1000,' + (mid - amp) + ' 1100,' + (mid + amp) + ' 1200,' + mid;
63 }
64
65 // ── Build zigzag path string ─────────────────────────────────────────────────
66 function buildZigzagPath(svgH) {
67 var mid = svgH / 2;
68 var amp = Math.max(1, mid - 2);
69 var d = 'M0,' + mid;
70 for (var i = 1; i <= 20; i++) {
71 d += ' L' + (i * 60) + ',' + (i % 2 === 1 ? (mid - amp) : (mid + amp));
72 }
73 d += ' L1200,' + mid;
74 return d;
75 }
76
77 // ── Scallop: repeated arcs pointing up (fish-scale / petal pattern) ──────────
78 function buildScallopPath(svgH) {
79 var d = 'M0,' + svgH;
80 for (var i = 0; i < 20; i++) {
81 var x1 = i * 60 + 30;
82 var x2 = (i + 1) * 60;
83 d += ' Q' + x1 + ',0 ' + x2 + ',' + svgH;
84 }
85 return d;
86 }
87
88 // ── Arrows: open right-pointing chevrons >>>> ────────────────────────────────
89 function buildArrowsPath(svgH) {
90 var mid = svgH / 2;
91 var amp = Math.max(2, mid - 2);
92 // Each chevron: 24px wide, 55px pitch → 22 chevrons across 1200px
93 var d = '';
94 for (var i = 0; i < 22; i++) {
95 var x = i * 55 + 3;
96 d += (d ? ' ' : '') + 'M' + x + ',' + (mid - amp)
97 + ' L' + (x + 24) + ',' + mid
98 + ' L' + x + ',' + (mid + amp);
99 }
100 return d;
101 }
102
103 // ── Brush: organic calligraphy-style stroke ───────────────────────────────────
104 function buildBrushPath(svgH) {
105 var mid = svgH / 2;
106 var amp = mid * 0.85;
107 return 'M0,' + mid
108 + ' C60,' + (mid - amp * 0.9) + ' 110,' + (mid + amp * 0.5) + ' 170,' + (mid - amp * 0.15)
109 + ' C230,' + (mid + amp * 0.8) + ' 280,' + (mid - amp * 0.6) + ' 340,' + (mid + amp * 0.3)
110 + ' C400,' + (mid - amp * 0.4) + ' 450,' + (mid + amp * 0.75) + ' 510,' + (mid - amp * 0.1)
111 + ' C570,' + (mid - amp * 0.7) + ' 620,' + (mid + amp * 0.55) + ' 680,' + (mid + amp * 0.2)
112 + ' C740,' + (mid - amp * 0.85)+ ' 790,' + (mid - amp * 0.3) + ' 850,' + (mid + amp * 0.65)
113 + ' C910,' + (mid + amp * 0.4) + ' 960,' + (mid - amp * 0.8) + ' 1020,'+ (mid - amp * 0.05)
114 + ' C1080,'+ (mid + amp * 0.6) + ' 1130,'+ (mid - amp * 0.45) + ' 1200,'+ mid;
115 }
116
117 // ── Heartbeat / EKG ──────────────────────────────────────────────────────────
118 function buildHeartbeatPath(svgH) {
119 var mid = svgH / 2;
120 var amp = Math.max(3, svgH * 0.46);
121 // Period = 200px, 6 full cycles in 1200px
122 var d = 'M0,' + mid;
123 for (var i = 0; i < 6; i++) {
124 var o = i * 200; // offset
125 d += ' L'+(o+25)+','+mid // flat
126 + ' L'+(o+35)+','+(mid - amp*0.28) // P-wave up
127 + ' L'+(o+45)+','+mid // baseline
128 + ' L'+(o+58)+','+(mid + amp*0.18) // Q dip
129 + ' L'+(o+68)+','+(mid - amp) // R spike (tall)
130 + ' L'+(o+80)+','+(mid + amp*0.55) // S dip below
131 + ' L'+(o+95)+','+mid // baseline
132 + ' L'+(o+110)+','+(mid - amp*0.22) // T-wave bump
133 + ' L'+(o+130)+','+mid; // flat exit
134 }
135 d += ' L1200,' + mid;
136 return d;
137 }
138
139 // ── Build one line segment ───────────────────────────────────────────────────
140 // side: 'l' | 'r' | 'full'
141 // flexValue: CSS flex shorthand string
142 var SVG_TYPES = ['wave', 'zigzag', 'scallop', 'arrows', 'brush', 'heartbeat'];
143
144 function buildLineSeg(a, side, flexValue) {
145 var type = a.dividerType;
146 var segStyle = { flex: flexValue || '1', minWidth: 0 };
147 var isSvgType = SVG_TYPES.indexOf(type) !== -1;
148
149 var svgEl = null;
150 if (isSvgType) {
151 var d;
152 if (type === 'wave') { d = buildWavePath(a.svgHeight); }
153 else if (type === 'zigzag') { d = buildZigzagPath(a.svgHeight); }
154 else if (type === 'scallop') { d = buildScallopPath(a.svgHeight); }
155 else if (type === 'arrows') { d = buildArrowsPath(a.svgHeight); }
156 else if (type === 'brush') { d = buildBrushPath(a.svgHeight); }
157 else { d = buildHeartbeatPath(a.svgHeight); }
158 svgEl = el('svg', {
159 viewBox: '0 0 1200 ' + a.svgHeight,
160 preserveAspectRatio: 'none',
161 xmlns: 'http://www.w3.org/2000/svg',
162 width: '100%',
163 height: a.svgHeight,
164 style: { display: 'block', overflow: 'visible' }
165 }, el('path', {
166 d: d,
167 fill: 'none',
168 stroke: a.lineColor,
169 strokeWidth: a.lineThickness,
170 strokeLinecap: 'round',
171 strokeLinejoin: 'round'
172 }));
173 }
174
175 if (isSvgType) {
176 Object.assign(segStyle, { display: 'block', overflow: 'hidden' });
177 }
178
179 return el('span', {
180 key: 'seg-' + side,
181 className: 'bkbg-div-seg bkbg-div-seg--' + side,
182 'aria-hidden': 'true',
183 style: segStyle
184 }, svgEl);
185 }
186
187 // ── Build middle element (icon or text) ────────────────────────────────────
188 // In edit mode, text uses RichText; in save mode, caller passes a pre-built element.
189 function buildIconMiddle(a) {
190 var iconSvg = buildIconSVG(a.iconName, a.iconColor, a.iconSize);
191 var hasBg = a.iconBgShape !== 'none';
192
193 if (!hasBg) {
194 return el('span', {
195 className: 'bkbg-div-mid-icon',
196 'aria-hidden': 'true'
197 }, iconSvg);
198 }
199
200 var radius = a.iconBgShape === 'circle' ? '50%' : '8px';
201 return el('span', {
202 className: 'bkbg-div-mid-icon bkbg-div-mid-icon--bg',
203 'aria-hidden': 'true',
204 style: {
205 display: 'inline-flex',
206 alignItems: 'center',
207 justifyContent: 'center',
208 width: a.iconBgSize + 'px',
209 height: a.iconBgSize + 'px',
210 borderRadius: radius,
211 background: a.iconBgColor,
212 flexShrink: 0
213 }
214 }, iconSvg);
215 }
216
217 // ── CSS vars for wrapper ─────────────────────────────────────────────────────
218 function buildWrapStyle(a) {
219 return {
220 '--bkbg-div-color': a.lineColor,
221 '--bkbg-div-thickness': a.lineThickness + 'px',
222 '--bkbg-div-svgh': a.svgHeight + 'px',
223 '--bkbg-div-dot-spacing': a.dotSpacing + 'px',
224 '--bkbg-div-gap': a.gapAroundMiddle + 'px',
225 '--bkbg-div-width': a.widthPercent + '%',
226 '--bkbg-div-mt': a.marginTop + 'px',
227 '--bkbg-div-mb': a.marginBottom + 'px',
228 '--bkbg-div-text-size': a.textSize + 'px',
229 '--bkbg-div-text-weight': a.textWeight,
230 '--bkbg-div-text-color': a.textColor,
231 '--bkbg-div-text-bg': a.textBgColor,
232 '--bkbg-div-text-ph': a.textPaddingH + 'px',
233 '--bkbg-div-text-pv': a.textPaddingV + 'px',
234 '--bkbg-div-text-radius': a.textRadius + 'px'
235 };
236 }
237
238 registerBlockType('blockenberg/separator', {
239 title: __('Separator / Divider', 'blockenberg'),
240 icon: 'minus',
241 category: 'blockenberg',
242 description: __('Decorative divider: line, wave, zigzag or dot pattern — optionally with an icon or text in the centre.', 'blockenberg'),
243
244 edit: function (props) {
245 var a = props.attributes;
246 var setAttributes = props.setAttributes;
247
248 // ── Generate blockId on first mount ──────────────────────────────
249 useEffect(function () {
250 if (!a.blockId) { setAttributes({ blockId: generateId() }); }
251 }, []);
252
253 // ── Color picker state (one open at a time) ───────────────────────
254 var openColorKeyState = useState(null);
255 var openColorKey = openColorKeyState[0];
256 var setOpenColorKey = openColorKeyState[1];
257
258 // ── Alpha-enabled color control ───────────────────────────────────
259 function renderColorControl(key, label, value, onChange) {
260 var isOpen = openColorKey === key;
261 return el('div', { key: key, style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '4px 0', gap: '8px' } },
262 el('span', { style: { fontSize: '12px', color: '#1e1e1e', flex: 1, lineHeight: 1.4 } }, label),
263 el('div', { style: { position: 'relative', flexShrink: 0 } },
264 el('button', {
265 type: 'button',
266 title: value || 'none',
267 onClick: function () { setOpenColorKey(isOpen ? null : key); },
268 style: {
269 width: '28px', height: '28px', borderRadius: '4px',
270 border: isOpen ? '2px solid #007cba' : '2px solid #ddd',
271 cursor: 'pointer', padding: 0, display: 'block',
272 background: value || '#ffffff', flexShrink: 0
273 }
274 }),
275 isOpen && el(Popover, {
276 position: 'bottom left',
277 onClose: function () { setOpenColorKey(null); }
278 },
279 el('div', {
280 style: { padding: '8px' },
281 onMouseDown: function (e) { e.stopPropagation(); }
282 },
283 el('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '6px' } },
284 el('strong', { style: { fontSize: '12px' } }, label),
285 el(Button, { icon: 'no-alt', isSmall: true, onClick: function () { setOpenColorKey(null); } })
286 ),
287 el(ColorPicker, {
288 color: value,
289 enableAlpha: true,
290 onChange: function (c) { onChange(c); }
291 })
292 )
293 )
294 )
295 );
296 }
297
298 // ── Option lists ──────────────────────────────────────────────────
299 var dividerTypeOptions = [
300 { label: __('Line', 'blockenberg'), value: 'line' },
301 { label: __('Dashed', 'blockenberg'), value: 'dashed' },
302 { label: __('Dotted', 'blockenberg'), value: 'dotted' },
303 { label: __('Double', 'blockenberg'), value: 'double' },
304 { label: __('Triple', 'blockenberg'), value: 'triple' },
305 { label: __('Gradient', 'blockenberg'), value: 'gradient' },
306 { label: __('Dots', 'blockenberg'), value: 'dots' },
307 { label: __('Wave', 'blockenberg'), value: 'wave' },
308 { label: __('Zigzag', 'blockenberg'), value: 'zigzag' },
309 { label: __('Scallop', 'blockenberg'), value: 'scallop' },
310 { label: __('Arrows', 'blockenberg'), value: 'arrows' },
311 { label: __('Brush', 'blockenberg'), value: 'brush' },
312 { label: __('Heartbeat', 'blockenberg'), value: 'heartbeat' }
313 ];
314
315 var middleTypeOptions = [
316 { label: __('None', 'blockenberg'), value: 'none' },
317 { label: __('Icon', 'blockenberg'), value: 'icon' },
318 { label: __('Text', 'blockenberg'), value: 'text' }
319 ];
320
321 var middlePosOptions = [
322 { label: __('Left', 'blockenberg'), value: 'left' },
323 { label: __('Center', 'blockenberg'), value: 'center' },
324 { label: __('Right', 'blockenberg'), value: 'right' }
325 ];
326
327 var iconOptions = [
328 { label: __('Star', 'blockenberg'), value: 'star' },
329 { label: __('Heart', 'blockenberg'), value: 'heart' },
330 { label: __('Diamond', 'blockenberg'), value: 'diamond' },
331 { label: __('Bolt', 'blockenberg'), value: 'bolt' },
332 { label: __('Moon', 'blockenberg'), value: 'moon' },
333 { label: __('8-Point Star', 'blockenberg'), value: 'star8' },
334 { label: __('Cross (+)', 'blockenberg'), value: 'cross' },
335 { label: __('Leaf', 'blockenberg'), value: 'leaf' },
336 { label: __('Snowflake', 'blockenberg'), value: 'snowflake' },
337 { label: __('Circle', 'blockenberg'), value: 'circle' }
338 ];
339
340 var iconBgShapeOptions = [
341 { label: __('None', 'blockenberg'), value: 'none' },
342 { label: __('Circle', 'blockenberg'), value: 'circle' },
343 { label: __('Square', 'blockenberg'), value: 'square' }
344 ];
345
346 var fontWeightOptions = [
347 { label: '300 — Light', value: 300 },
348 { label: '400 — Regular', value: 400 },
349 { label: '500 — Medium', value: 500 },
350 { label: '600 — Semi Bold', value: 600 },
351 { label: '700 — Bold', value: 700 },
352 { label: '800 — Extra Bold', value: 800 }
353 ];
354
355 var isSvgType = SVG_TYPES.indexOf(a.dividerType) !== -1;
356 var isDotsType = a.dividerType === 'dots';
357 var hasMiddle = a.middleType !== 'none';
358
359 // ── Inspector ─────────────────────────────────────────────────────
360 var inspector = el(InspectorControls, {},
361
362 // ── Divider ───────────────────────────────────────────────────
363 el(PanelBody, { title: __('Divider', 'blockenberg'), initialOpen: true },
364 el(SelectControl, {
365 label: __('Type', 'blockenberg'),
366 value: a.dividerType,
367 options: dividerTypeOptions,
368 onChange: function (v) { setAttributes({ dividerType: v }); }
369 }),
370 el(RangeControl, {
371 label: isSvgType
372 ? __('Stroke Width (px)', 'blockenberg')
373 : (isDotsType ? __('Dot Radius (px)', 'blockenberg') : __('Thickness (px)', 'blockenberg')),
374 value: a.lineThickness,
375 min: 1,
376 max: isSvgType ? 8 : 12,
377 onChange: function (v) { setAttributes({ lineThickness: v }); }
378 }),
379 (isSvgType || isDotsType) && el(RangeControl, {
380 label: isSvgType
381 ? __('Height (px)', 'blockenberg')
382 : __('Row Height (px)', 'blockenberg'),
383 value: a.svgHeight,
384 min: 8,
385 max: 80,
386 onChange: function (v) { setAttributes({ svgHeight: v }); }
387 }),
388 isDotsType && el(RangeControl, {
389 label: __('Dot Spacing (px)', 'blockenberg'),
390 value: a.dotSpacing,
391 min: 4,
392 max: 60,
393 onChange: function (v) { setAttributes({ dotSpacing: v }); }
394 })
395 ),
396
397 // ── Layout ────────────────────────────────────────────────────
398 el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: false },
399 el(RangeControl, {
400 label: __('Width (%)', 'blockenberg'),
401 value: a.widthPercent,
402 min: 10,
403 max: 100,
404 onChange: function (v) { setAttributes({ widthPercent: v }); }
405 }),
406 el(RangeControl, {
407 label: __('Margin Top (px)', 'blockenberg'),
408 value: a.marginTop,
409 min: 0,
410 max: 120,
411 onChange: function (v) { setAttributes({ marginTop: v }); }
412 }),
413 el(RangeControl, {
414 label: __('Margin Bottom (px)', 'blockenberg'),
415 value: a.marginBottom,
416 min: 0,
417 max: 120,
418 onChange: function (v) { setAttributes({ marginBottom: v }); }
419 })
420 ),
421
422 // ── Centrepiece ───────────────────────────────────────────────
423 el(PanelBody, { title: __('Centrepiece', 'blockenberg'), initialOpen: false },
424 el(SelectControl, {
425 label: __('Centrepiece', 'blockenberg'),
426 value: a.middleType,
427 options: middleTypeOptions,
428 onChange: function (v) { setAttributes({ middleType: v }); }
429 }),
430
431 hasMiddle && el(Fragment, {},
432 el(SelectControl, {
433 label: __('Position', 'blockenberg'),
434 value: a.middlePosition,
435 options: middlePosOptions,
436 onChange: function (v) { setAttributes({ middlePosition: v }); }
437 }),
438 el(RangeControl, {
439 label: __('Gap around centrepiece (px)', 'blockenberg'),
440 value: a.gapAroundMiddle,
441 min: 0,
442 max: 60,
443 onChange: function (v) { setAttributes({ gapAroundMiddle: v }); }
444 }),
445
446 // Icon-specific controls
447 a.middleType === 'icon' && el(Fragment, {},
448 el('hr', {}),
449 el('p', { style: { margin: '4px 0 6px', fontWeight: 600, fontSize: '11px', textTransform: 'uppercase', color: '#888' } },
450 __('Icon', 'blockenberg')
451 ),
452 el(SelectControl, {
453 label: __('Icon', 'blockenberg'),
454 value: a.iconName,
455 options: iconOptions,
456 onChange: function (v) { setAttributes({ iconName: v }); }
457 }),
458 el(RangeControl, {
459 label: __('Icon Size (px)', 'blockenberg'),
460 value: a.iconSize,
461 min: 12,
462 max: 80,
463 onChange: function (v) { setAttributes({ iconSize: v }); }
464 }),
465 el('hr', {}),
466 el('p', { style: { margin: '4px 0 6px', fontWeight: 600, fontSize: '11px', textTransform: 'uppercase', color: '#888' } },
467 __('Icon Background', 'blockenberg')
468 ),
469 el(SelectControl, {
470 label: __('Shape', 'blockenberg'),
471 value: a.iconBgShape,
472 options: iconBgShapeOptions,
473 onChange: function (v) { setAttributes({ iconBgShape: v }); }
474 }),
475 a.iconBgShape !== 'none' && el(RangeControl, {
476 label: __('Background Size (px)', 'blockenberg'),
477 value: a.iconBgSize,
478 min: a.iconSize,
479 max: 120,
480 onChange: function (v) { setAttributes({ iconBgSize: v }); }
481 })
482 ),
483
484 // Text-specific controls
485 a.middleType === 'text' && el(Fragment, {},
486 el('hr', {}),
487 el('p', { style: { margin: '4px 0 6px', fontWeight: 600, fontSize: '11px', textTransform: 'uppercase', color: '#888' } },
488 __('Text Style', 'blockenberg')
489 ),
490 el(RangeControl, {
491 label: __('Font Size (px)', 'blockenberg'),
492 value: a.textSize,
493 min: 10,
494 max: 48,
495 onChange: function (v) { setAttributes({ textSize: v }); }
496 }),
497 el(SelectControl, {
498 label: __('Font Weight', 'blockenberg'),
499 value: a.textWeight,
500 options: fontWeightOptions,
501 onChange: function (v) { setAttributes({ textWeight: parseInt(v, 10) }); }
502 }),
503 el(RangeControl, {
504 label: __('Padding Horizontal (px)', 'blockenberg'),
505 value: a.textPaddingH,
506 min: 0,
507 max: 48,
508 onChange: function (v) { setAttributes({ textPaddingH: v }); }
509 }),
510 el(RangeControl, {
511 label: __('Padding Vertical (px)', 'blockenberg'),
512 value: a.textPaddingV,
513 min: 0,
514 max: 32,
515 onChange: function (v) { setAttributes({ textPaddingV: v }); }
516 }),
517 el(RangeControl, {
518 label: __('Border Radius (px)', 'blockenberg'),
519 value: a.textRadius,
520 min: 0,
521 max: 50,
522 onChange: function (v) { setAttributes({ textRadius: v }); }
523 })
524 )
525 )
526 ),
527
528 // ── Colors ────────────────────────────────────────────────────
529 el(PanelBody, { title: __('Colors', 'blockenberg'), initialOpen: false },
530 el('p', { style: { margin: '4px 0 6px', fontWeight: 600, fontSize: '11px', textTransform: 'uppercase', color: '#888' } },
531 __('Line', 'blockenberg')
532 ),
533 renderColorControl('lineColor', __('Line / stroke color', 'blockenberg'), a.lineColor,
534 function (c) { setAttributes({ lineColor: c }); }
535 ),
536
537 a.middleType === 'icon' && el(Fragment, {},
538 el('hr', {}),
539 el('p', { style: { margin: '4px 0 6px', fontWeight: 600, fontSize: '11px', textTransform: 'uppercase', color: '#888' } },
540 __('Icon', 'blockenberg')
541 ),
542 renderColorControl('iconColor', __('Icon fill', 'blockenberg'), a.iconColor,
543 function (c) { setAttributes({ iconColor: c }); }
544 ),
545 a.iconBgShape !== 'none' && renderColorControl('iconBgColor', __('Icon background', 'blockenberg'), a.iconBgColor,
546 function (c) { setAttributes({ iconBgColor: c }); }
547 )
548 ),
549
550 a.middleType === 'text' && el(Fragment, {},
551 el('hr', {}),
552 el('p', { style: { margin: '4px 0 6px', fontWeight: 600, fontSize: '11px', textTransform: 'uppercase', color: '#888' } },
553 __('Text', 'blockenberg')
554 ),
555 renderColorControl('textColor', __('Text color', 'blockenberg'), a.textColor,
556 function (c) { setAttributes({ textColor: c }); }
557 ),
558 renderColorControl('textBgColor', __('Text background', 'blockenberg'), a.textBgColor,
559 function (c) { setAttributes({ textBgColor: c }); }
560 )
561 )
562 )
563 );
564
565 // ── Build line segments ───────────────────────────────────────────
566 var segLFlex = !hasMiddle ? '1'
567 : a.middlePosition === 'left' ? '0 0 20px'
568 : '1';
569 var segRFlex = !hasMiddle ? '0 0 0px'
570 : a.middlePosition === 'right' ? '0 0 20px'
571 : '1';
572
573 // When no middle: single full-width segment
574 var segments;
575 if (!hasMiddle) {
576 segments = [buildLineSeg(a, 'full', '1')];
577 } else {
578 var leftSeg = buildLineSeg(a, 'l', segLFlex);
579 var rightSeg = buildLineSeg(a, 'r', segRFlex);
580
581 var middleEl;
582 if (a.middleType === 'icon') {
583 middleEl = el('span', { key: 'middle', className: 'bkbg-div-middle' },
584 buildIconMiddle(a)
585 );
586 } else {
587 // text — RichText in edit
588 middleEl = el('span', { key: 'middle', className: 'bkbg-div-middle' },
589 el(RichText, {
590 tagName: 'span',
591 className: 'bkbg-div-mid-text',
592 value: a.middleText,
593 onChange: function (v) { setAttributes({ middleText: v }); },
594 placeholder: __('Text…', 'blockenberg'),
595 allowedFormats: ['core/bold', 'core/italic', 'core/text-color']
596 })
597 );
598 }
599 segments = [leftSeg, middleEl, rightSeg];
600 }
601
602 // ── Edit render ───────────────────────────────────────────────────
603 var blockProps = useBlockProps({
604 className: 'bkbg-editor-wrap',
605 'data-block-label': 'Separator'
606 });
607
608 return el('div', blockProps,
609 inspector,
610 el('div', {
611 className: 'bkbg-div-outer',
612 style: { marginTop: a.marginTop + 'px', marginBottom: a.marginBottom + 'px' }
613 },
614 el('div', Object.assign(
615 { className: 'bkbg-div-wrap', style: buildWrapStyle(a) },
616 { 'data-type': a.dividerType, 'data-middle': a.middleType }
617 ), segments)
618 )
619 );
620 },
621
622 // ── Save ─────────────────────────────────────────────────────────────────
623 save: function (props) {
624 var a = props.attributes;
625 var RichTextContent = wp.blockEditor.RichText.Content;
626
627 var hasMiddle = a.middleType !== 'none';
628 var segLFlex = !hasMiddle ? '1'
629 : a.middlePosition === 'left' ? '0 0 20px'
630 : '1';
631 var segRFlex = !hasMiddle ? '0 0 0px'
632 : a.middlePosition === 'right' ? '0 0 20px'
633 : '1';
634
635 var segments;
636 if (!hasMiddle) {
637 segments = [buildLineSeg(a, 'full', '1')];
638 } else {
639 var leftSeg = buildLineSeg(a, 'l', segLFlex);
640 var rightSeg = buildLineSeg(a, 'r', segRFlex);
641
642 var middleEl;
643 if (a.middleType === 'icon') {
644 middleEl = el('span', { key: 'middle', className: 'bkbg-div-middle' },
645 buildIconMiddle(a)
646 );
647 } else {
648 // text: use RichText.Content
649 middleEl = el('span', { key: 'middle', className: 'bkbg-div-middle' },
650 el(RichTextContent, {
651 tagName: 'span',
652 className: 'bkbg-div-mid-text',
653 value: a.middleText
654 })
655 );
656 }
657 segments = [leftSeg, middleEl, rightSeg];
658 }
659
660 return el('div', {
661 className: 'bkbg-div-outer',
662 style: { marginTop: a.marginTop + 'px', marginBottom: a.marginBottom + 'px' }
663 },
664 el('div', Object.assign(
665 { className: 'bkbg-div-wrap', style: buildWrapStyle(a) },
666 { 'data-type': a.dividerType, 'data-middle': a.middleType }
667 ), segments)
668 );
669 }
670 });
671 });
672