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 / styled-text-builder / index.js

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

397 lines 19.6 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 useBlockProps = wp.blockEditor.useBlockProps;
9 var PanelBody = wp.components.PanelBody;
10 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
11 var ToggleControl = wp.components.ToggleControl;
12 var RangeControl = wp.components.RangeControl;
13 var SelectControl = wp.components.SelectControl;
14 var TextControl = wp.components.TextControl;
15 var Button = wp.components.Button;
16 var ColorIndicator = wp.components.ColorIndicator;
17
18 var _tc; function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
19 var _tv; function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
20
21 function genId() {
22 return 'stb_' + Math.random().toString(36).substr(2, 9);
23 }
24
25 // Compute inline style for a segment (editor + save)
26 function getSegmentStyle(seg, isEditor) {
27 var style = {};
28
29 // Preset base styles (overridden by custom settings)
30 switch (seg.preset) {
31 case 'highlight-yellow':
32 style.background = '#fef9c3';
33 style.borderRadius = '3px';
34 style.padding = '1px 4px';
35 break;
36 case 'highlight-green':
37 style.background = '#dcfce7';
38 style.borderRadius = '3px';
39 style.padding = '1px 4px';
40 break;
41 case 'highlight-pink':
42 style.background = '#fce7f3';
43 style.borderRadius = '3px';
44 style.padding = '1px 4px';
45 break;
46 case 'highlight-blue':
47 style.background = '#dbeafe';
48 style.borderRadius = '3px';
49 style.padding = '1px 4px';
50 break;
51 case 'badge':
52 style.display = 'inline-flex';
53 style.alignItems = 'center';
54 style.borderRadius = '50px';
55 style.padding = '3px 10px';
56 style.fontSize = '85%';
57 style.fontWeight = '600';
58 style.lineHeight = '1.4';
59 style.background = seg.bgColor || '#2563eb';
60 style.color = seg.color || '#ffffff';
61 break;
62 case 'code':
63 style.fontFamily = 'ui-monospace, SFMono-Regular, "Cascadia Code", Consolas, monospace';
64 style.background = '#f1f5f9';
65 style.color = '#dc2626';
66 style.borderRadius = '4px';
67 style.padding = '1px 6px';
68 style.fontSize = '88%';
69 break;
70 case 'glow':
71 style.textShadow = '0 0 8px ' + (seg.color || '#a78bfa') + ', 0 0 16px ' + (seg.color || '#a78bfa');
72 break;
73 case 'underline-fancy':
74 style.textDecoration = 'underline';
75 style.textDecorationColor = seg.color || '#2563eb';
76 style.textDecorationThickness = '2px';
77 style.textUnderlineOffset = '3px';
78 break;
79 default:
80 break;
81 }
82
83 // Custom overrides (non-badge presets)
84 if (seg.preset !== 'badge') {
85 if (seg.color) style.color = seg.color;
86 if (seg.bgColor) style.background = seg.bgColor;
87 }
88
89 // Shared overrides
90 if (seg.bold) style.fontWeight = '700';
91 if (seg.italic) style.fontStyle = 'italic';
92 if (seg.underline) style.textDecoration = 'underline';
93
94 if (seg.customSize > 0) style.fontSize = seg.customSize + 'px';
95
96 if (seg.borderRadius > 0 && seg.preset === 'none') {
97 style.borderRadius = seg.borderRadius + 'px';
98 }
99 if (seg.px > 0 && seg.preset === 'none') {
100 style.paddingLeft = seg.px + 'px';
101 style.paddingRight = seg.px + 'px';
102 }
103 if (seg.py > 0 && seg.preset === 'none') {
104 style.paddingTop = seg.py + 'px';
105 style.paddingBottom = seg.py + 'px';
106 }
107
108 return style;
109 }
110
111 var PRESETS = [
112 { label: __('None (plain)', 'blockenberg'), value: 'none' },
113 { label: __('Highlight — Yellow', 'blockenberg'), value: 'highlight-yellow' },
114 { label: __('Highlight — Green', 'blockenberg'), value: 'highlight-green' },
115 { label: __('Highlight — Pink', 'blockenberg'), value: 'highlight-pink' },
116 { label: __('Highlight — Blue', 'blockenberg'), value: 'highlight-blue' },
117 { label: __('Badge / Pill', 'blockenberg'), value: 'badge' },
118 { label: __('Code Span', 'blockenberg'), value: 'code' },
119 { label: __('Gradient Text', 'blockenberg'), value: 'gradient' },
120 { label: __('Glow Text', 'blockenberg'), value: 'glow' },
121 { label: __('Underline (styled)', 'blockenberg'), value: 'underline-fancy' }
122 ];
123
124 registerBlockType('blockenberg/styled-text-builder', {
125 title: __('Styled Text Builder', 'blockenberg'),
126 icon: 'editor-textcolor',
127 category: 'bkbg-content',
128 description: __('Build a paragraph with mixed inline text styles — highlights, badges, gradients and more.', 'blockenberg'),
129
130 edit: function (props) {
131 var a = props.attributes;
132 var setAttributes = props.setAttributes;
133 var activeState = useState(null);
134 var activeId = activeState[0];
135 var setActiveId = activeState[1];
136
137 var activeSeg = activeId ? a.segments.find(function (s) { return s.id === activeId; }) : null;
138
139 function updateSeg(id, field, value) {
140 setAttributes({
141 segments: a.segments.map(function (s) {
142 if (s.id !== id) return s;
143 var u = Object.assign({}, s);
144 u[field] = value;
145 return u;
146 })
147 });
148 }
149
150 function addSeg() {
151 var newSeg = { id: genId(), text: ' New Text', preset: 'none', bold: false, italic: false, underline: false, color: '', bgColor: '', customSize: 0, borderRadius: 0, px: 0, py: 0 };
152 setAttributes({ segments: a.segments.concat([newSeg]) });
153 setActiveId(newSeg.id);
154 }
155
156 function removeSeg(id) {
157 if (a.segments.length <= 1) return;
158 setAttributes({ segments: a.segments.filter(function (s) { return s.id !== id; }) });
159 if (activeId === id) setActiveId(null);
160 }
161
162 function moveSeg(index, dir) {
163 var segs = a.segments.slice();
164 var target = index + dir;
165 if (target < 0 || target >= segs.length) return;
166 var tmp = segs[index];
167 segs[index] = segs[target];
168 segs[target] = tmp;
169 setAttributes({ segments: segs });
170 }
171
172 var alignOptions = [
173 { label: __('Left', 'blockenberg'), value: 'left' },
174 { label: __('Center', 'blockenberg'), value: 'center' },
175 { label: __('Right', 'blockenberg'), value: 'right' }
176 ];
177
178 // Inspector
179 var inspector = el(InspectorControls, {},
180 // Active segment editor
181 el(PanelBody, { title: activeSeg ? '' + __('Edit Segment', 'blockenberg') : __('Select a Segment', 'blockenberg'), initialOpen: true },
182 !activeSeg && el('p', { style: { fontSize: '13px', color: '#64748b', margin: '0 0 4px' } },
183 __('Click on any text segment in the block to edit it, or pick one from the list below.', 'blockenberg')
184 ),
185 activeSeg && el(Fragment, {},
186 el(TextControl, {
187 label: __('Text', 'blockenberg'),
188 value: activeSeg.text,
189 onChange: function (v) { updateSeg(activeId, 'text', v); },
190 __nextHasNoMarginBottom: true
191 }),
192 el('div', { style: { marginTop: '8px' } },
193 el(SelectControl, {
194 label: __('Style Preset', 'blockenberg'),
195 value: activeSeg.preset,
196 options: PRESETS,
197 onChange: function (v) { updateSeg(activeId, 'preset', v); }
198 })
199 ),
200 el('div', { style: { marginTop: '4px' } },
201 el(ToggleControl, { label: __('Bold', 'blockenberg'), checked: activeSeg.bold, onChange: function (v) { updateSeg(activeId, 'bold', v); }, __nextHasNoMarginBottom: true })
202 ),
203 el(ToggleControl, { label: __('Italic', 'blockenberg'), checked: activeSeg.italic, onChange: function (v) { updateSeg(activeId, 'italic', v); }, __nextHasNoMarginBottom: true }),
204 el(ToggleControl, { label: __('Underline', 'blockenberg'), checked: activeSeg.underline, onChange: function (v) { updateSeg(activeId, 'underline', v); }, __nextHasNoMarginBottom: true }),
205 el('div', { style: { marginTop: '8px' } },
206 el(RangeControl, {
207 label: __('Custom Font Size (0 = inherit)', 'blockenberg'),
208 value: activeSeg.customSize,
209 min: 0, max: 64, step: 1,
210 onChange: function (v) { updateSeg(activeId, 'customSize', v); }
211 })
212 ),
213 (activeSeg.preset === 'none' || activeSeg.preset === 'badge' || activeSeg.preset === 'glow') && el(Fragment, {},
214 el(PanelColorSettings, {
215 title: __('Segment Colors', 'blockenberg'),
216 initialOpen: false,
217 colorSettings: [
218 { value: activeSeg.color, onChange: function (c) { updateSeg(activeId, 'color', c || ''); }, label: __('Text Color', 'blockenberg') },
219 { value: activeSeg.bgColor, onChange: function (c) { updateSeg(activeId, 'bgColor', c || ''); }, label: __('Background', 'blockenberg') }
220 ]
221 })
222 ),
223 activeSeg.preset === 'none' && el(Fragment, {},
224 el(RangeControl, {
225 label: __('Horizontal Padding', 'blockenberg'),
226 value: activeSeg.px, min: 0, max: 30,
227 onChange: function (v) { updateSeg(activeId, 'px', v); }
228 }),
229 el(RangeControl, {
230 label: __('Vertical Padding', 'blockenberg'),
231 value: activeSeg.py, min: 0, max: 20,
232 onChange: function (v) { updateSeg(activeId, 'py', v); }
233 }),
234 el(RangeControl, {
235 label: __('Border Radius', 'blockenberg'),
236 value: activeSeg.borderRadius, min: 0, max: 50,
237 onChange: function (v) { updateSeg(activeId, 'borderRadius', v); }
238 })
239 ),
240 el('div', { style: { marginTop: '12px' } },
241 el(Button, {
242 variant: 'tertiary',
243 isDestructive: true,
244 onClick: function () { removeSeg(activeId); },
245 disabled: a.segments.length <= 1,
246 style: { width: '100%', justifyContent: 'center' }
247 }, __('Remove This Segment', 'blockenberg'))
248 )
249 )
250 ),
251
252 el(PanelBody, { title: __('All Segments', 'blockenberg'), initialOpen: false },
253 a.segments.map(function (seg, idx) {
254 var isActive = seg.id === activeId;
255 return el('div', {
256 key: seg.id,
257 style: {
258 display: 'flex', alignItems: 'center', gap: '6px',
259 marginBottom: '6px', padding: '6px 8px',
260 background: isActive ? '#ede9fe' : '#f8fafc',
261 border: '1px solid ' + (isActive ? '#c4b5fd' : '#e2e8f0'),
262 borderRadius: '6px', cursor: 'pointer'
263 },
264 onClick: function () { setActiveId(isActive ? null : seg.id); }
265 },
266 el('span', { style: { flex: 1, fontSize: '12px', overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis' } },
267 el('strong', {}, seg.preset !== 'none' ? '[' + seg.preset + '] ' : ''),
268 seg.text
269 ),
270 el('div', { style: { display: 'flex', gap: '2px' } },
271 el(Button, { isSmall: true, variant: 'tertiary', onClick: function (e) { e.stopPropagation(); moveSeg(idx, -1); }, disabled: idx === 0 }, ''),
272 el(Button, { isSmall: true, variant: 'tertiary', onClick: function (e) { e.stopPropagation(); moveSeg(idx, 1); }, disabled: idx === a.segments.length - 1 }, '')
273 )
274 );
275 }),
276 el('div', { style: { marginTop: '8px' } },
277 el(Button, { variant: 'secondary', onClick: addSeg, style: { width: '100%', justifyContent: 'center' } },
278 '+ ' + __('Add Segment', 'blockenberg')
279 )
280 )
281 ),
282
283 el(PanelBody, { title: __('Container', 'blockenberg'), initialOpen: false },
284 el(SelectControl, {
285 label: __('Text Align', 'blockenberg'),
286 value: a.textAlign,
287 options: alignOptions,
288 onChange: function (v) { setAttributes({ textAlign: v }); }
289 }),
290 el(RangeControl, {
291 label: __('Max Width (0 = full)', 'blockenberg'),
292 value: a.maxWidth, min: 0, max: 1200, step: 20,
293 onChange: function (v) { setAttributes({ maxWidth: v }); }
294 }),
295 el(RangeControl, {
296 label: __('Container Padding', 'blockenberg'),
297 value: a.containerPadding, min: 0, max: 60,
298 onChange: function (v) { setAttributes({ containerPadding: v }); }
299 }),
300 el(RangeControl, {
301 label: __('Container Border Radius', 'blockenberg'),
302 value: a.containerRadius, min: 0, max: 30,
303 onChange: function (v) { setAttributes({ containerRadius: v }); }
304 })
305 ),
306 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
307 getTypoControl()({ label: __('Paragraph Text', 'blockenberg'), value: a.textTypo, onChange: function (v) { setAttributes({ textTypo: v }); } })
308 ),
309
310 el(PanelColorSettings, {
311 title: __('Container Colors', 'blockenberg'),
312 initialOpen: false,
313 colorSettings: [
314 { value: a.textColor, onChange: function (c) { setAttributes({ textColor: c || '' }); }, label: __('Base Text Color', 'blockenberg') }
315 ]
316 })
317 );
318
319 var paraStyle = {
320 textAlign: a.textAlign,
321 color: a.textColor || '#1e293b',
322 margin: 0,
323 padding: a.containerPadding > 0 ? a.containerPadding + 'px' : undefined,
324 borderRadius: a.containerRadius > 0 ? a.containerRadius + 'px' : undefined
325 };
326 if (a.maxWidth > 0) paraStyle.maxWidth = a.maxWidth + 'px';
327
328 var blockProps = useBlockProps((function () {
329 var _tvf = getTypoCssVars();
330 var s = {};
331 Object.assign(s, _tvf(a.textTypo, '--bkstb-tx-'));
332 return { className: 'bkbg-editor-wrap', 'data-block-label': 'Styled Text Builder', style: s };
333 })());
334
335 return el('div', blockProps,
336 inspector,
337 el('p', { className: 'bkbg-stb-paragraph', style: paraStyle },
338 a.segments.map(function (seg) {
339 var isActive = seg.id === activeId;
340 var segClass = 'bkbg-stb-seg';
341 if (seg.preset && seg.preset !== 'none') segClass += ' bkbg-stb-preset--' + seg.preset;
342 if (isActive) segClass += ' bkbg-stb-is-active';
343
344 var style = getSegmentStyle(seg, true);
345 if (isActive) {
346 style.outline = '2px dashed #6c3fb5';
347 style.outlineOffset = '2px';
348 }
349
350 return el('span', {
351 key: seg.id,
352 className: segClass,
353 style: style,
354 title: __('Click to edit', 'blockenberg'),
355 onClick: function (e) {
356 e.stopPropagation();
357 setActiveId(isActive ? null : seg.id);
358 }
359 }, seg.text);
360 })
361 ),
362 // Add segment shortcut below
363 el('div', { style: { marginTop: '10px', textAlign: 'center' } },
364 el(Button, {
365 variant: 'tertiary',
366 onClick: addSeg,
367 style: { fontSize: '12px', color: '#6c3fb5' }
368 }, '+ ' + __('Add Text Segment', 'blockenberg'))
369 )
370 );
371 },
372
373 save: function (props) {
374 var a = props.attributes;
375 var paraStyle = {
376 textAlign: a.textAlign,
377 color: a.textColor || undefined,
378 margin: 0
379 };
380 if (a.containerPadding > 0) paraStyle.padding = a.containerPadding + 'px';
381 if (a.containerRadius > 0) paraStyle.borderRadius = a.containerRadius + 'px';
382 if (a.maxWidth > 0) paraStyle.maxWidth = a.maxWidth + 'px';
383 var _tvf = window.bkbgTypoCssVars || function () { return {}; };
384 Object.assign(paraStyle, _tvf(a.textTypo, '--bkstb-tx-'));
385
386 return el('p', { className: 'bkbg-stb-paragraph', style: paraStyle },
387 a.segments.map(function (seg, idx) {
388 var segClass = 'bkbg-stb-seg';
389 if (seg.preset && seg.preset !== 'none') segClass += ' bkbg-stb-preset--' + seg.preset;
390
391 return el('span', { key: idx, className: segClass, style: getSegmentStyle(seg, false) }, seg.text);
392 })
393 );
394 }
395 });
396 }() );
397