PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.14
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.14
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
← All changes | blocks/section/index.js +263 -73 2.0.4 → 2.0.14 View file →
@@ -1,29 +1,23 @@
1 -wp.domReady(function () {
1 +( function () {
2 2 var el = wp.element.createElement;
3 + var useEffect = wp.element.useEffect;
4 + var useState = wp.element.useState;
3 5 var __ = wp.i18n.__;
4 6 var registerBlockType = wp.blocks.registerBlockType;
5 7 var InspectorControls = wp.blockEditor.InspectorControls;
6 8 var BlockControls = wp.blockEditor.BlockControls;
9 + var BlockAlignmentToolbar = wp.blockEditor.BlockAlignmentToolbar;
7 10 var useBlockProps = wp.blockEditor.useBlockProps;
8 11 var InnerBlocks = wp.blockEditor.InnerBlocks;
9 12 var PanelBody = wp.components.PanelBody;
10 13 var SelectControl = wp.components.SelectControl;
14 + var TextControl = wp.components.TextControl;
11 15 var ToolbarGroup = wp.components.ToolbarGroup;
12 16 var ToolbarButton = wp.components.ToolbarButton;
13 17 var ColorPalette = wp.components.ColorPalette;
14 18 var BaseControl = wp.components.BaseControl;
15 19
16 - // Spacing token scale
17 - var spacingOptions = [
18 - { label: __('None', 'blockenberg'), value: 'none' },
19 - { label: __('XS (8px)', 'blockenberg'), value: 'xs' },
20 - { label: __('S (16px)', 'blockenberg'), value: 's' },
21 - { label: __('M (32px)', 'blockenberg'), value: 'm' },
22 - { label: __('L (48px)', 'blockenberg'), value: 'l' },
23 - { label: __('XL (64px)', 'blockenberg'), value: 'xl' }
24 - ];
25 -
26 20 // Background presets
27 21 var backgroundOptions = [
28 22 { label: __('None', 'blockenberg'), value: 'none' },
29 23 { label: __('Light', 'blockenberg'), value: 'light' },
@@ -33,13 +27,62 @@
33 27 ];
34 28
35 29 // Max width options
36 30 var maxWidthOptions = [
37 - { label: '1024px', value: '1024' },
38 - { label: '1200px', value: '1200' },
39 - { label: '1440px', value: '1440' }
31 + { label: '1024px', value: '1024px' },
32 + { label: '1200px', value: '1200px' },
33 + { label: '1440px', value: '1440px' }
40 34 ];
41 35
36 + var spacingUnitOptions = [
37 + { label: 'px', value: 'px' },
38 + { label: '%', value: '%' },
39 + { label: 'em', value: 'em' },
40 + { label: 'rem', value: 'rem' },
41 + { label: 'vw', value: 'vw' },
42 + { label: 'vh', value: 'vh' },
43 + { label: __('Custom', 'blockenberg'), value: 'custom' }
44 + ];
45 +
46 + function cssLength(value) {
47 + if (value === undefined || value === null) return undefined;
48 + var v = String(value).trim();
49 + if (!v) return undefined;
50 + // Back-compat: if stored as a number ("1200"), treat as px.
51 + if (/^\d+(?:\.\d+)?$/.test(v)) return v + 'px';
52 + return v;
53 + }
54 +
55 + function getSpacingSideValue(attrs, type, side) {
56 + var cap = side.charAt(0).toUpperCase() + side.slice(1);
57 + var value = attrs[type + cap];
58 + var unit = attrs[type + cap + 'Unit'] || 'px';
59 + var customUnit = attrs[type + cap + 'CustomUnit'] || '';
60 +
61 + if (value === undefined || value === null) return undefined;
62 + var raw = String(value).trim();
63 + if (!raw) return undefined;
64 +
65 + if (unit === 'custom') {
66 + return customUnit ? raw + customUnit : raw;
67 + }
68 +
69 + return raw + unit;
70 + }
71 +
72 + function getSpacingStyles(attrs) {
73 + return {
74 + paddingTop: getSpacingSideValue(attrs, 'padding', 'top'),
75 + paddingRight: getSpacingSideValue(attrs, 'padding', 'right'),
76 + paddingBottom: getSpacingSideValue(attrs, 'padding', 'bottom'),
77 + paddingLeft: getSpacingSideValue(attrs, 'padding', 'left'),
78 + marginTop: getSpacingSideValue(attrs, 'margin', 'top'),
79 + marginRight: getSpacingSideValue(attrs, 'margin', 'right'),
80 + marginBottom: getSpacingSideValue(attrs, 'margin', 'bottom'),
81 + marginLeft: getSpacingSideValue(attrs, 'margin', 'left')
82 + };
83 + }
84 +
42 85 // Section icon
43 86 var sectionIcon = el('svg', {
44 87 width: 24,
45 88 height: 24,
@@ -54,9 +97,9 @@
54 97
55 98 registerBlockType('blockenberg/section', {
56 99 title: __('Section', 'blockenberg'),
57 100 icon: sectionIcon,
58 - category: 'blockenberg',
101 + category: 'bkbg-layout',
59 102 description: __('A container section for organizing content with rows and columns.', 'blockenberg'),
60 103
61 104 edit: function (props) {
62 105 var attributes = props.attributes;
@@ -62,37 +105,150 @@
62 105 var attributes = props.attributes;
63 106 var setAttributes = props.setAttributes;
64 107 var a = attributes;
65 108
109 + // Back-compat: if existing content used core align, map it to sectionWidth.
110 + // (We keep this local to avoid mutating saved content unless user changes settings.)
111 + var effectiveSectionWidth = a.sectionWidth;
112 + if (!effectiveSectionWidth) {
113 + if (a.align === 'full') effectiveSectionWidth = 'full';
114 + else if (a.align === 'wide') effectiveSectionWidth = 'wide';
115 + else effectiveSectionWidth = 'default';
116 + }
117 +
118 + // Keep core alignment in sync so the editor wrapper can actually change width.
119 + // For "boxed" we intentionally use "wide" to give the block room in the editor,
120 + // while our own max-width controls the final visual width.
121 + var nextAlign = (effectiveSectionWidth === 'full')
122 + ? 'full'
123 + : (effectiveSectionWidth === 'wide' || effectiveSectionWidth === 'boxed')
124 + ? 'wide'
125 + : undefined;
126 +
127 + useEffect(function () {
128 + if (a.align !== nextAlign) {
129 + setAttributes({ align: nextAlign });
130 + }
131 + }, [effectiveSectionWidth]);
132 +
66 133 var blockProps = useBlockProps({
67 134 className: [
68 135 'bkbg-section',
69 - 'bkbg-section--width-' + a.contentWidth,
136 + 'bkbg-section--section-' + effectiveSectionWidth,
137 + 'bkbg-section--content-' + a.contentWidth,
70 138 'bkbg-section--padding-' + a.paddingY,
71 139 'bkbg-section--bg-' + a.background
72 140 ].join(' '),
73 - style: {
74 - '--bkbg-section-max-width': a.boxedMaxWidth + 'px',
141 + style: Object.assign({}, {
142 + '--bkbg-section-section-max-width': cssLength(a.sectionBoxedMaxWidth || a.boxedMaxWidth),
143 + '--bkbg-section-max-width': cssLength(a.boxedMaxWidth),
75 144 '--bkbg-section-custom-bg': a.backgroundColor || undefined
145 + }, getSpacingStyles(a))
146 + });
147 +
148 + function renderSpacingControl(type, label) {
149 + var sides = [
150 + { key: 'Top', label: __('Top', 'blockenberg') },
151 + { key: 'Left', label: __('Left', 'blockenberg') },
152 + { key: 'Bottom', label: __('Bottom', 'blockenberg') },
153 + { key: 'Right', label: __('Right', 'blockenberg') }
154 + ];
155 +
156 + return el('div', { className: 'bkbg-spacing-control' },
157 + el('div', { className: 'bkbg-spacing-control__title' }, label),
158 + el('div', { className: 'bkbg-spacing-control__grid' },
159 + sides.map(function (side) {
160 + var valueKey = type + side.key;
161 + var unitKey = type + side.key + 'Unit';
162 + var customUnitKey = type + side.key + 'CustomUnit';
163 +
164 + return el('div', { className: 'bkbg-spacing-control__item', key: valueKey },
165 + el('label', { className: 'bkbg-spacing-control__label' }, side.label),
166 + el('div', { className: 'bkbg-spacing-control__row' },
167 + el('input', {
168 + type: 'text',
169 + className: 'components-text-control__input',
170 + value: a[valueKey] || '',
171 + placeholder: '0',
172 + onChange: function (e) {
173 + var next = {};
174 + next[valueKey] = e.target.value;
175 + setAttributes(next);
176 + }
177 + }),
178 + el('select', {
179 + className: 'components-select-control__input',
180 + value: a[unitKey] || 'px',
181 + onChange: function (e) {
182 + var next = {};
183 + next[unitKey] = e.target.value;
184 + setAttributes(next);
185 + }
186 + }, spacingUnitOptions.map(function (opt) {
187 + return el('option', { key: opt.value, value: opt.value }, opt.label);
188 + }))
189 + ),
190 + (a[unitKey] || 'px') === 'custom' && el('input', {
191 + type: 'text',
192 + className: 'components-text-control__input bkbg-spacing-control__custom-unit',
193 + value: a[customUnitKey] || '',
194 + placeholder: __('unit, e.g. ch', 'blockenberg'),
195 + onChange: function (e) {
196 + var next = {};
197 + next[customUnitKey] = e.target.value;
198 + setAttributes(next);
199 + }
200 + })
201 + );
202 + })
203 + )
204 + );
205 + }
206 +
207 + // Max width UI modes (preset vs custom)
208 + var presetMaxWidthValues = maxWidthOptions.map(function (o) { return o.value; });
209 + var sectionMaxWidthValue = a.sectionBoxedMaxWidth || a.boxedMaxWidth || '';
210 + var contentMaxWidthValue = a.boxedMaxWidth || '';
211 +
212 + var initialSectionMode = presetMaxWidthValues.indexOf(sectionMaxWidthValue) !== -1 ? 'preset' : 'custom';
213 + var initialContentMode = presetMaxWidthValues.indexOf(contentMaxWidthValue) !== -1 ? 'preset' : 'custom';
214 +
215 + var _sectionModeState = useState(initialSectionMode);
216 + var sectionMaxWidthMode = _sectionModeState[0];
217 + var setSectionMaxWidthMode = _sectionModeState[1];
218 +
219 + var _contentModeState = useState(initialContentMode);
220 + var contentMaxWidthMode = _contentModeState[0];
221 + var setContentMaxWidthMode = _contentModeState[1];
222 +
223 + // If something external changes the value to a non-preset, ensure UI switches to custom.
224 + useEffect(function () {
225 + if (presetMaxWidthValues.indexOf(sectionMaxWidthValue) === -1 && sectionMaxWidthMode !== 'custom') {
226 + setSectionMaxWidthMode('custom');
76 227 }
77 - });
228 + }, [sectionMaxWidthValue]);
78 229
79 - // Toolbar controls
80 - var toolbar = el(BlockControls, {},
81 - el(ToolbarGroup, {},
82 - el(ToolbarButton, {
83 - icon: 'align-full-width',
84 - label: __('Full Width', 'blockenberg'),
85 - isPressed: a.contentWidth === 'full',
86 - onClick: function () { setAttributes({ contentWidth: 'full' }); }
87 - }),
88 - el(ToolbarButton, {
89 - icon: 'align-center',
90 - label: __('Boxed', 'blockenberg'),
91 - isPressed: a.contentWidth === 'boxed',
92 - onClick: function () { setAttributes({ contentWidth: 'boxed' }); }
93 - })
94 - )
230 + useEffect(function () {
231 + if (presetMaxWidthValues.indexOf(contentMaxWidthValue) === -1 && contentMaxWidthMode !== 'custom') {
232 + setContentMaxWidthMode('custom');
233 + }
234 + }, [contentMaxWidthValue]);
235 +
236 + // Toolbar controls - Section alignment + Content width
237 + var toolbarAlignValue = (effectiveSectionWidth === 'full' || effectiveSectionWidth === 'wide')
238 + ? effectiveSectionWidth
239 + : undefined;
240 + var toolbar = el(BlockControls, { group: 'block' },
241 + el(BlockAlignmentToolbar, {
242 + value: toolbarAlignValue,
243 + onChange: function (v) {
244 + // Only wide/full are controlled in the toolbar.
245 + // Clearing alignment goes to "default".
246 + var next = v ? v : 'default';
247 + setAttributes({ sectionWidth: next });
248 + },
249 + controls: ['wide', 'full']
250 + })
95 251 );
96 252
97 253 // Inspector controls
98 254 var inspector = el(InspectorControls, {},
@@ -97,49 +253,81 @@
97 253 // Inspector controls
98 254 var inspector = el(InspectorControls, {},
99 255 el(PanelBody, { title: __('Section Settings', 'blockenberg'), initialOpen: true },
100 256 el(SelectControl, {
257 + label: __('Section Width', 'blockenberg'),
258 + help: __('Controls the width of the section itself (background). Default uses the theme content area. Wide uses the theme\'s wide width (wideSize).', 'blockenberg'),
259 + value: effectiveSectionWidth,
260 + options: [
261 + { label: __('Full Width', 'blockenberg'), value: 'full' },
262 + { label: __('Wide', 'blockenberg'), value: 'wide' },
263 + { label: __('Boxed', 'blockenberg'), value: 'boxed' },
264 + { label: __('Default', 'blockenberg'), value: 'default' }
265 + ],
266 + onChange: function (v) { setAttributes({ sectionWidth: v }); }
267 + }),
268 + effectiveSectionWidth === 'boxed' && el(wp.element.Fragment, {},
269 + el(SelectControl, {
270 + label: __('Section Max Width', 'blockenberg'),
271 + value: sectionMaxWidthMode === 'custom' ? 'custom' : sectionMaxWidthValue,
272 + options: maxWidthOptions.concat([
273 + { label: __('Custom', 'blockenberg'), value: 'custom' }
274 + ]),
275 + onChange: function (v) {
276 + if (v === 'custom') {
277 + setSectionMaxWidthMode('custom');
278 + return;
279 + }
280 + setSectionMaxWidthMode('preset');
281 + setAttributes({ sectionBoxedMaxWidth: v });
282 + }
283 + }),
284 + sectionMaxWidthMode === 'custom' && el(TextControl, {
285 + label: __('Custom Section Max Width', 'blockenberg'),
286 + help: __('Any CSS length, e.g. 1200px, 72rem, 90%, 80vw', 'blockenberg'),
287 + value: sectionMaxWidthValue,
288 + onChange: function (v) {
289 + setSectionMaxWidthMode('custom');
290 + setAttributes({ sectionBoxedMaxWidth: v });
291 + }
292 + })
293 + ),
294 + el(SelectControl, {
101 295 label: __('Content Width', 'blockenberg'),
296 + help: __('Controls the width of the content inside the section.', 'blockenberg'),
102 297 value: a.contentWidth,
103 298 options: [
104 - { label: __('Full Width', 'blockenberg'), value: 'full' },
105 - { label: __('Boxed', 'blockenberg'), value: 'boxed' }
299 + { label: __('Boxed', 'blockenberg'), value: 'boxed' },
300 + { label: __('Full Width', 'blockenberg'), value: 'full' }
106 301 ],
107 302 onChange: function (v) { setAttributes({ contentWidth: v }); }
108 303 }),
109 - a.contentWidth === 'boxed' && el(SelectControl, {
110 - label: __('Max Width', 'blockenberg'),
111 - value: a.boxedMaxWidth,
112 - options: maxWidthOptions,
113 - onChange: function (v) { setAttributes({ boxedMaxWidth: v }); }
114 - }),
115 - el(SelectControl, {
116 - label: __('Vertical Padding', 'blockenberg'),
117 - value: a.paddingY,
118 - options: spacingOptions,
119 - onChange: function (v) { setAttributes({ paddingY: v }); }
120 - })
121 - ),
122 - el(PanelBody, { title: __('Background', 'blockenberg'), initialOpen: false },
123 - el(SelectControl, {
124 - label: __('Background Preset', 'blockenberg'),
125 - value: a.background,
126 - options: backgroundOptions,
127 - onChange: function (v) {
128 - setAttributes({ background: v });
129 - if (v !== 'custom') {
130 - setAttributes({ backgroundColor: '' });
304 + a.contentWidth === 'boxed' && el(wp.element.Fragment, {},
305 + el(SelectControl, {
306 + label: __('Content Max Width', 'blockenberg'),
307 + value: contentMaxWidthMode === 'custom' ? 'custom' : contentMaxWidthValue,
308 + options: maxWidthOptions.concat([
309 + { label: __('Custom', 'blockenberg'), value: 'custom' }
310 + ]),
311 + onChange: function (v) {
312 + if (v === 'custom') {
313 + setContentMaxWidthMode('custom');
314 + return;
315 + }
316 + setContentMaxWidthMode('preset');
317 + setAttributes({ boxedMaxWidth: v });
131 318 }
132 - }
133 - }),
134 - a.background === 'custom' && el(BaseControl, {
135 - label: __('Custom Color', 'blockenberg')
136 - },
137 - el(ColorPalette, {
138 - value: a.backgroundColor,
139 - onChange: function (color) { setAttributes({ backgroundColor: color }); }
319 + }),
320 + contentMaxWidthMode === 'custom' && el(TextControl, {
321 + label: __('Custom Content Max Width', 'blockenberg'),
322 + help: __('Any CSS length, e.g. 1200px, 72rem, 90%, 80vw', 'blockenberg'),
323 + value: contentMaxWidthValue,
324 + onChange: function (v) {
325 + setContentMaxWidthMode('custom');
326 + setAttributes({ boxedMaxWidth: v });
327 + }
140 328 })
141 - )
329 + ),
142 330 )
143 331 );
144 332
145 333 // Inner blocks template - only allow Row blocks
@@ -166,16 +354,18 @@
166 354 var a = props.attributes;
167 355 var blockProps = useBlockProps.save({
168 356 className: [
169 357 'bkbg-section',
170 - 'bkbg-section--width-' + a.contentWidth,
358 + 'bkbg-section--section-' + (a.sectionWidth || (a.align ? (a.align === 'full' ? 'full' : (a.align === 'wide' ? 'wide' : 'default')) : 'full')),
359 + 'bkbg-section--content-' + a.contentWidth,
171 360 'bkbg-section--padding-' + a.paddingY,
172 361 'bkbg-section--bg-' + a.background
173 362 ].join(' '),
174 - style: {
175 - '--bkbg-section-max-width': a.boxedMaxWidth + 'px',
363 + style: Object.assign({}, {
364 + '--bkbg-section-section-max-width': cssLength(a.sectionBoxedMaxWidth || a.boxedMaxWidth),
365 + '--bkbg-section-max-width': cssLength(a.boxedMaxWidth),
176 366 '--bkbg-section-custom-bg': a.backgroundColor || undefined
177 - }
367 + }, getSpacingStyles(a))
178 368 });
179 369
180 370 return el('div', blockProps,
181 371 el('div', { className: 'bkbg-section__inner' },
@@ -183,5 +373,5 @@
183 373 )
184 374 );
185 375 }
186 376 });
187 -});
377 +}() );