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

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

325 lines 19.4 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 useState = wp.element.useState;
5 var __ = wp.i18n.__;
6 var registerBlockType = wp.blocks.registerBlockType;
7 var InspectorControls = wp.blockEditor.InspectorControls;
8 var useBlockProps = wp.blockEditor.useBlockProps;
9 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
10 var PanelBody = wp.components.PanelBody;
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
17 var _tc, _tv;
18 function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
19 function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
20
21 var TAG_OPTIONS = [
22 { label: 'H1', value: 'h1' }, { label: 'H2', value: 'h2' },
23 { label: 'H3', value: 'h3' }, { label: 'H4', value: 'h4' },
24 { label: 'H5', value: 'h5' }, { label: 'H6', value: 'h6' },
25 { label: 'p (paragraph)', value: 'p' },
26 { label: 'div', value: 'div' },
27 ];
28 var ALIGN_OPTIONS = [
29 { label: 'Left', value: 'left' },
30 { label: 'Center', value: 'center' },
31 { label: 'Right', value: 'right' },
32 ];
33 var STYLE_OPTIONS = [
34 { label: 'Normal', value: 'normal' },
35 { label: 'Gradient Text', value: 'gradient' },
36 { label: 'Highlight (bg)', value: 'highlight' },
37 { label: 'Outlined/Stroke', value: 'stroke' },
38 { label: 'Badge Pill', value: 'badge' },
39 { label: 'Monospace/Code', value: 'mono' },
40 { label: 'Ghost (faded)', value: 'ghost' },
41 ];
42 var WEIGHT_OPTIONS = [
43 { label: '300 Light', value: '300' },
44 { label: '400 Normal', value: '400' },
45 { label: '500 Medium', value: '500' },
46 { label: '600 Semibold', value: '600' },
47 { label: '700 Bold', value: '700' },
48 { label: '800 ExtraBold',value: '800' },
49 { label: '900 Black', value: '900' },
50 ];
51 var GRADIENT_DIR_OPTIONS = [
52 { label: 'Left → Right', value: 'to right' },
53 { label: 'Right → Left', value: 'to left' },
54 { label: 'Top → Bottom', value: 'to bottom' },
55 { label: 'Diagonal ↘', value: 'to bottom right' },
56 { label: 'Diagonal ↗', value: 'to top right' },
57 ];
58
59 function updateSeg(arr, idx, field, val) {
60 return arr.map(function (s, i) {
61 if (i !== idx) return s;
62 var p = {}; p[field] = val;
63 return Object.assign({}, s, p);
64 });
65 }
66
67 function segStyle(s, a, effSize) {
68 effSize = effSize || a.fontSize;
69 var base = {
70 fontWeight: s.weight || '700',
71 fontStyle: s.italic ? 'italic' : 'normal',
72 textDecoration: s.underline ? 'underline' : 'none',
73 letterSpacing: s.spacing ? s.spacing + 'em' : undefined,
74 display: 'inline',
75 fontSize: s.scale && s.scale !== 1 ? (effSize * s.scale) + 'px' : undefined,
76 };
77 if (s.style === 'gradient') {
78 return Object.assign({}, base, {
79 backgroundImage: 'linear-gradient(' + (a.gradientDir || 'to right') + ', ' + s.color + ', ' + s.color2 + ')',
80 WebkitBackgroundClip: 'text',
81 WebkitTextFillColor: 'transparent',
82 backgroundClip: 'text',
83 color: 'transparent',
84 });
85 } else if (s.style === 'highlight') {
86 return Object.assign({}, base, {
87 color: s.color,
88 background: s.bg || '#ede9fe',
89 borderRadius: 4 + 'px',
90 padding: '0 4px',
91 display: 'inline',
92 });
93 } else if (s.style === 'stroke') {
94 return Object.assign({}, base, {
95 color: 'transparent',
96 WebkitTextStroke: (a.strokeWidth || 2) + 'px ' + s.color,
97 textStroke: (a.strokeWidth || 2) + 'px ' + s.color,
98 });
99 } else if (s.style === 'badge') {
100 return Object.assign({}, base, {
101 color: s.color,
102 background: s.bg || '#f1f5f9',
103 borderRadius: (a.badgeRadius || 6) + 'px',
104 padding: (a.badgePaddingY || 3) + 'px ' + (a.badgePaddingX || 8) + 'px',
105 display: 'inline-block',
106 verticalAlign: 'middle',
107 });
108 } else if (s.style === 'mono') {
109 return Object.assign({}, base, {
110 color: s.color,
111 fontFamily: "'Courier New', Courier, monospace",
112 background: s.bg || '#f8f8f8',
113 borderRadius: 4 + 'px',
114 padding: '2px 6px',
115 fontSize: '0.88em',
116 });
117 } else if (s.style === 'ghost') {
118 return Object.assign({}, base, {
119 color: s.color,
120 opacity: 0.35,
121 });
122 }
123 // normal
124 return Object.assign({}, base, { color: s.color });
125 }
126
127 function buildPreviewEl(a) {
128 var effSize = (a.headingTypo && a.headingTypo.sizeDesktop) || a.fontSize;
129 var segs = (a.segments || []).map(function (s, i) {
130 return el('span', { key: i, style: segStyle(s, a, effSize) }, s.text);
131 });
132 var hStyle = {
133 textAlign: a.textAlign,
134 maxWidth: a.maxWidth + 'px',
135 margin: '0 auto',
136 paddingTop: a.paddingTop + 'px',
137 paddingBottom: a.paddingBottom + 'px',
138 fontWeight: 'normal',
139 wordBreak: 'break-word',
140 };
141 return el(a.tag || 'h2', { className: 'bkbg-mhd-heading', style: hStyle }, segs);
142 }
143
144 registerBlockType('blockenberg/mixed-heading', {
145 title: __('Mixed Heading', 'blockenberg'),
146 icon: 'editor-spellcheck',
147 category: 'bkbg-content',
148 description: __('Build headings with mixed per-segment styles: gradient, highlight, stroke, badge, mono, and more.', 'blockenberg'),
149
150 deprecated: [{
151 save: function (props) {
152 return el('div', useBlockProps.save(),
153 el('div', { className: 'bkbg-mhd-app', 'data-opts': JSON.stringify(props.attributes) })
154 );
155 }
156 }],
157
158 edit: function (props) {
159 var a = props.attributes;
160 var set = props.setAttributes;
161 var expandedSt = useState(-1);
162 var expanded = expandedSt[0];
163 var setExpanded = expandedSt[1];
164 var blockProps = useBlockProps((function () {
165 var _tvf = getTypoCssVars();
166 var s = { textAlign: a.textAlign };
167 Object.assign(s, _tvf(a.headingTypo, '--bkbg-mhd-hd-'));
168 return { style: s };
169 })());
170
171 return el(Fragment, null,
172 el(InspectorControls, null,
173
174 // Global
175 el(PanelBody, { title: 'Heading Settings', initialOpen: true },
176 el(SelectControl, { label: 'HTML Tag', value: a.tag, options: TAG_OPTIONS, onChange: function (v) { set({ tag: v }); }, __nextHasNoMarginBottom: true }),
177 el('div', { style: { height: 10 } }),
178 el(SelectControl, { label: 'Text Align', value: a.textAlign, options: ALIGN_OPTIONS, onChange: function (v) { set({ textAlign: v }); }, __nextHasNoMarginBottom: true }),
179 el('div', { style: { height: 10 } }),
180 el(RangeControl, { label: 'Max Width (px)', value: a.maxWidth, min: 200, max: 1400, step: 20, onChange: function (v) { set({ maxWidth: v }); }, __nextHasNoMarginBottom: true }),
181 el('div', { style: { height: 10 } }),
182 el(RangeControl, { label: 'Padding Top (px)', value: a.paddingTop, min: 0, max: 120, onChange: function (v) { set({ paddingTop: v }); }, __nextHasNoMarginBottom: true }),
183 el('div', { style: { height: 10 } }),
184 el(RangeControl, { label: 'Padding Bottom (px)', value: a.paddingBottom, min: 0, max: 120, onChange: function (v) { set({ paddingBottom: v }); }, __nextHasNoMarginBottom: true }),
185 el('div', { style: { height: 10 } }),
186 el(SelectControl, { label: 'Gradient Direction', value: a.gradientDir, options: GRADIENT_DIR_OPTIONS, onChange: function (v) { set({ gradientDir: v }); }, __nextHasNoMarginBottom: true }),
187 el('div', { style: { height: 10 } }),
188 el(RangeControl, { label: 'Stroke Width (px)', value: a.strokeWidth, min: 1, max: 8, onChange: function (v) { set({ strokeWidth: v }); }, __nextHasNoMarginBottom: true }),
189 el('div', { style: { height: 10 } }),
190 el(RangeControl, { label: 'Badge Border Radius (px)', value: a.badgeRadius, min: 0, max: 30, onChange: function (v) { set({ badgeRadius: v }); }, __nextHasNoMarginBottom: true })
191 ),
192
193 // Segments
194 el(PanelBody, { title: 'Text Segments (' + a.segments.length + ')', initialOpen: true },
195 el('p', { style: { fontSize: 11, color: '#6b7280', margin: '0 0 10px' } }, 'Add segments to build your heading. Each can have a different style. Use a space-only segment for word gaps.'),
196 a.segments.map(function (s, i) {
197 var isOpen = expanded === i;
198 return el('div', {
199 key: i,
200 style: { border: '1px solid #e5e7eb', borderRadius: 8, marginBottom: 6, background: '#fafafa', overflow: 'hidden' }
201 },
202 // Header row
203 el('div', {
204 style: { display: 'flex', alignItems: 'center', padding: '6px 10px', cursor: 'pointer', gap: 8, borderBottom: isOpen ? '1px solid #e5e7eb' : 'none' },
205 onClick: function () { setExpanded(isOpen ? -1 : i); }
206 },
207 el('span', { style: Object.assign({ fontSize: 13, flex: 1, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }, segStyle(s, a)) }, s.text || '(empty)'),
208 el('span', { style: { fontSize: 10, color: '#9ca3af', flexShrink: 0 } }, s.style),
209 el('span', { style: { fontSize: 12, color: '#9ca3af' } }, isOpen ? '' : '')
210 ),
211 isOpen && el('div', { style: { padding: '10px' } },
212 el(TextControl, { label: 'Text', value: s.text, onChange: function (v) { set({ segments: updateSeg(a.segments, i, 'text', v) }); }, __nextHasNoMarginBottom: true }),
213 el('div', { style: { height: 8 } }),
214 el(SelectControl, { label: 'Style', value: s.style, options: STYLE_OPTIONS, onChange: function (v) { set({ segments: updateSeg(a.segments, i, 'style', v) }); }, __nextHasNoMarginBottom: true }),
215 el('div', { style: { height: 8 } }),
216 el(SelectControl, { label: 'Font Weight', value: s.weight, options: WEIGHT_OPTIONS, onChange: function (v) { set({ segments: updateSeg(a.segments, i, 'weight', v) }); }, __nextHasNoMarginBottom: true }),
217 el('div', { style: { height: 8 } }),
218 el('div', { style: { display: 'flex', gap: 8 } },
219 el('label', { style: { display: 'block', fontSize: 11, fontWeight: 600, color: '#374151' } }, 'Text Color'),
220 el('input', {
221 type: 'color', value: s.color || '#111827',
222 onChange: function (e) { set({ segments: updateSeg(a.segments, i, 'color', e.target.value) }); },
223 style: { flex: 1, height: 32, border: 'none', cursor: 'pointer', borderRadius: 4 }
224 })
225 ),
226 (s.style === 'gradient') && el(Fragment, null,
227 el('div', { style: { height: 8 } }),
228 el('div', { style: { display: 'flex', gap: 8, alignItems: 'center' } },
229 el('label', { style: { fontSize: 11, fontWeight: 600, color: '#374151', whiteSpace: 'nowrap' } }, 'Gradient End'),
230 el('input', {
231 type: 'color', value: s.color2 || '#ec4899',
232 onChange: function (e) { set({ segments: updateSeg(a.segments, i, 'color2', e.target.value) }); },
233 style: { flex: 1, height: 32, border: 'none', cursor: 'pointer', borderRadius: 4 }
234 })
235 )
236 ),
237 (s.style === 'highlight' || s.style === 'badge' || s.style === 'mono') && el(Fragment, null,
238 el('div', { style: { height: 8 } }),
239 el('div', { style: { display: 'flex', gap: 8, alignItems: 'center' } },
240 el('label', { style: { fontSize: 11, fontWeight: 600, color: '#374151', whiteSpace: 'nowrap' } }, 'Background'),
241 el('input', {
242 type: 'color', value: (s.bg && s.bg !== 'transparent') ? s.bg : '#ede9fe',
243 onChange: function (e) { set({ segments: updateSeg(a.segments, i, 'bg', e.target.value) }); },
244 style: { flex: 1, height: 32, border: 'none', cursor: 'pointer', borderRadius: 4 }
245 })
246 )
247 ),
248 el('div', { style: { height: 8 } }),
249 el(ToggleControl, { label: 'Italic', checked: s.italic, onChange: function (v) { set({ segments: updateSeg(a.segments, i, 'italic', v) }); }, __nextHasNoMarginBottom: true }),
250 el('div', { style: { height: 4 } }),
251 el(ToggleControl, { label: 'Underline', checked: s.underline, onChange: function (v) { set({ segments: updateSeg(a.segments, i, 'underline', v) }); }, __nextHasNoMarginBottom: true }),
252 el('div', { style: { height: 8 } }),
253 el(RangeControl, { label: 'Scale (1 = same size)', value: s.scale || 1, min: 0.5, max: 2, step: 0.05, onChange: function (v) { set({ segments: updateSeg(a.segments, i, 'scale', v) }); }, __nextHasNoMarginBottom: true }),
254 el('div', { style: { height: 8 } }),
255 el(RangeControl, { label: 'Letter Spacing (em)', value: s.spacing || 0, min: -0.1, max: 0.3, step: 0.01, onChange: function (v) { set({ segments: updateSeg(a.segments, i, 'spacing', v) }); }, __nextHasNoMarginBottom: true }),
256 el('div', { style: { height: 6 } }),
257 el('div', { style: { display: 'flex', gap: 6, flexWrap: 'wrap' } },
258 el(Button, {
259 variant: 'secondary', size: 'small', __nextHasNoMarginBottom: true,
260 onClick: function () {
261 var cloned = JSON.parse(JSON.stringify(a.segments[i]));
262 var next = a.segments.slice(0, i + 1).concat([cloned]).concat(a.segments.slice(i + 1));
263 set({ segments: next });
264 setExpanded(i + 1);
265 }
266 }, 'Duplicate'),
267 el(Button, {
268 isDestructive: true, variant: 'link', size: 'small', __nextHasNoMarginBottom: true,
269 onClick: function () {
270 set({ segments: a.segments.filter(function (_, idx) { return idx !== i; }) });
271 setExpanded(-1);
272 }
273 }, 'Remove')
274 )
275 )
276 );
277 }),
278 el('div', { style: { height: 6 } }),
279 el('div', { style: { display: 'flex', gap: 6 } },
280 el(Button, {
281 variant: 'secondary', size: 'small', __nextHasNoMarginBottom: true,
282 onClick: function () {
283 var newSeg = { text: 'New text', style: 'normal', color: '#111827', color2: '#6366f1', bg: 'transparent', weight: '700', italic: false, underline: false, scale: 1, spacing: 0 };
284 set({ segments: a.segments.concat([newSeg]) });
285 setExpanded(a.segments.length);
286 }
287 }, '+ Add Segment'),
288 el(Button, {
289 variant: 'tertiary', size: 'small', __nextHasNoMarginBottom: true,
290 onClick: function () {
291 var spacer = { text: ' ', style: 'normal', color: '#111827', color2: '#6366f1', bg: 'transparent', weight: '700', italic: false, underline: false, scale: 1, spacing: 0 };
292 set({ segments: a.segments.concat([spacer]) });
293 }
294 }, '+ Add Space')
295 )
296 ),
297 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
298 el( getTypoControl(), { label: __( 'Heading', 'blockenberg' ), value: a.headingTypo, onChange: function (v) { set({ headingTypo: v }); } })
299 )
300 ),
301
302 // ── Editor Preview ──
303 el('div', blockProps,
304 el('div', { className: 'bkbg-mhd-outer', style: { textAlign: a.textAlign } },
305 buildPreviewEl(a)
306 )
307 )
308 );
309 },
310
311 save: function (props) {
312 var a = props.attributes;
313 var blockProps = useBlockProps.save((function () {
314 var _tvf = getTypoCssVars();
315 var s = {};
316 Object.assign(s, _tvf(a.headingTypo, '--bkbg-mhd-hd-'));
317 return { style: s };
318 })());
319 return el('div', blockProps,
320 el('div', { className: 'bkbg-mhd-app', 'data-opts': JSON.stringify(a) })
321 );
322 }
323 });
324 }() );
325