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

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

326 lines 15.8 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 __ = wp.i18n.__;
4 var registerBlockType = wp.blocks.registerBlockType;
5 var InspectorControls = wp.blockEditor.InspectorControls;
6 var useBlockProps = wp.blockEditor.useBlockProps;
7 var InnerBlocks = wp.blockEditor.InnerBlocks;
8 var MediaUpload = wp.blockEditor.MediaUpload;
9 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
10 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
11 var PanelBody = wp.components.PanelBody;
12 var ToggleControl = wp.components.ToggleControl;
13 var RangeControl = wp.components.RangeControl;
14 var SelectControl = wp.components.SelectControl;
15 var Button = wp.components.Button;
16
17 var TEMPLATE = [
18 ['core/heading', { level: 2, placeholder: __('Section Heading', 'blockenberg') }],
19 ['core/paragraph', { placeholder: __('Add your content here…', 'blockenberg') }],
20 ];
21
22 var DIR_OPTIONS = [
23 { label: __('Left Low → Right High', 'blockenberg'), value: 'left-low' },
24 { label: __('Left High → Right Low', 'blockenberg'), value: 'left-high' },
25 ];
26
27 /**
28 * Build a clip-path polygon string for a skewed section.
29 * top edge: controlled by skewTop + topAngle + topDirection
30 * bottom edge: controlled by skewBottom + bottomAngle + bottomDirection
31 * Angle is expressed in degrees; we convert to a % offset on the Y axis.
32 */
33 function buildClipPath(a) {
34 // We use percentage-based polygon points.
35 // Y axis: 0% = top, 100% = bottom
36 // We express the skew offset as a percentage of height.
37 // The tangent of the angle times the width gives the height offset, but since
38 // we're working in % and the width ≫ relevant height we use a simplified
39 // linear approach: angle degrees → percent offset (1° ≈ 1.77% for 16:9-ish).
40 var topLeft, topRight, botLeft, botRight;
41 var tOff = a.topAngle; // degrees treated as % shift for simplicity
42 var bOff = a.bottomAngle;
43
44 if (a.skewTop) {
45 if (a.topDirection === 'left-low') {
46 topLeft = tOff + '%';
47 topRight = '0%';
48 } else {
49 topLeft = '0%';
50 topRight = tOff + '%';
51 }
52 } else {
53 topLeft = '0%';
54 topRight = '0%';
55 }
56
57 if (a.skewBottom) {
58 if (a.bottomDirection === 'left-low') {
59 botLeft = '100%';
60 botRight = (100 - bOff) + '%';
61 } else {
62 botLeft = (100 - bOff) + '%';
63 botRight = '100%';
64 }
65 } else {
66 botLeft = '100%';
67 botRight = '100%';
68 }
69
70 return 'polygon(0 ' + topLeft + ', 100% ' + topRight + ', 100% ' + botRight + ', 0 ' + botLeft + ')';
71 }
72
73 function buildBackground(a) {
74 if (a.backgroundImageUrl) {
75 return {
76 backgroundImage: (a.overlayOpacity > 0
77 ? 'linear-gradient(rgba(' + hexToRgb(a.overlayColor) + ',' + (a.overlayOpacity / 100) + '),rgba(' + hexToRgb(a.overlayColor) + ',' + (a.overlayOpacity / 100) + ')),url(' + a.backgroundImageUrl + ')'
78 : 'url(' + a.backgroundImageUrl + ')'),
79 backgroundSize: a.backgroundSize,
80 backgroundPosition: a.backgroundPosition,
81 };
82 }
83 if (a.useGradient) {
84 return { background: 'linear-gradient(' + a.gradientAngle + 'deg,' + a.backgroundColor + ',' + a.gradientEnd + ')' };
85 }
86 return { background: a.backgroundColor };
87 }
88
89 function hexToRgb(hex) {
90 if (!hex || hex.charAt(0) !== '#') return '0,0,0';
91 return parseInt(hex.slice(1, 3), 16) + ',' + parseInt(hex.slice(3, 5), 16) + ',' + parseInt(hex.slice(5, 7), 16);
92 }
93
94 registerBlockType('blockenberg/skewed-section', {
95 edit: function (props) {
96 var a = props.attributes;
97 var set = props.setAttributes;
98
99 var clipPath = buildClipPath(a);
100 var bgStyles = buildBackground(a);
101
102 var extraPadTop = (a.skewTop && a.paddingExtraForSkew) ? (a.topAngle * 6) : 0;
103 var extraPadBottom = (a.skewBottom && a.paddingExtraForSkew) ? (a.bottomAngle * 6) : 0;
104
105 var sectionStyle = Object.assign({}, bgStyles, {
106 position: 'relative',
107 minHeight: a.minHeight + 'px',
108 paddingTop: (a.paddingTop + extraPadTop) + 'px',
109 paddingBottom: (a.paddingBottom + extraPadBottom) + 'px',
110 color: a.textColor,
111 clipPath: clipPath,
112 WebkitClipPath: clipPath,
113 overflow: 'visible',
114 boxSizing: 'border-box',
115 });
116
117 var contentStyle = {
118 position: 'relative',
119 zIndex: 2,
120 maxWidth: a.contentMaxWidth + 'px',
121 margin: '0 auto',
122 padding: '0 24px',
123 textAlign: a.textAlign,
124 color: a.textColor,
125 };
126
127 var blockProps = useBlockProps({ className: 'bksk-wrap', style: sectionStyle });
128
129 return el(wp.element.Fragment, null,
130 el(InspectorControls, null,
131
132 /* Skew Shape */
133 el(PanelBody, { title: __('Skew Shape', 'blockenberg'), initialOpen: true },
134 el(ToggleControl, {
135 label: __('Skew Top Edge', 'blockenberg'),
136 checked: a.skewTop,
137 onChange: function (v) { set({ skewTop: v }); },
138 __nextHasNoMarginBottom: true
139 }),
140 a.skewTop ? el(RangeControl, {
141 label: __('Top Angle (°)', 'blockenberg'),
142 value: a.topAngle,
143 min: 1, max: 15,
144 onChange: function (v) { set({ topAngle: v }); }
145 }) : null,
146 a.skewTop ? el(SelectControl, {
147 label: __('Top Direction', 'blockenberg'),
148 value: a.topDirection,
149 options: DIR_OPTIONS,
150 onChange: function (v) { set({ topDirection: v }); }
151 }) : null,
152 el(ToggleControl, {
153 label: __('Skew Bottom Edge', 'blockenberg'),
154 checked: a.skewBottom,
155 onChange: function (v) { set({ skewBottom: v }); },
156 __nextHasNoMarginBottom: true
157 }),
158 a.skewBottom ? el(RangeControl, {
159 label: __('Bottom Angle (°)', 'blockenberg'),
160 value: a.bottomAngle,
161 min: 1, max: 15,
162 onChange: function (v) { set({ bottomAngle: v }); }
163 }) : null,
164 a.skewBottom ? el(SelectControl, {
165 label: __('Bottom Direction', 'blockenberg'),
166 value: a.bottomDirection,
167 options: DIR_OPTIONS,
168 onChange: function (v) { set({ bottomDirection: v }); }
169 }) : null,
170 el(ToggleControl, {
171 label: __('Add Extra Padding for Skew', 'blockenberg'),
172 checked: a.paddingExtraForSkew,
173 onChange: function (v) { set({ paddingExtraForSkew: v }); },
174 __nextHasNoMarginBottom: true
175 })
176 ),
177
178 /* Background */
179 el(PanelBody, { title: __('Background', 'blockenberg'), initialOpen: false },
180 el(ToggleControl, {
181 label: __('Use Gradient', 'blockenberg'),
182 checked: a.useGradient,
183 onChange: function (v) { set({ useGradient: v }); },
184 __nextHasNoMarginBottom: true
185 }),
186 a.useGradient ? el(RangeControl, {
187 label: __('Gradient Angle (°)', 'blockenberg'),
188 value: a.gradientAngle,
189 min: 0, max: 360,
190 onChange: function (v) { set({ gradientAngle: v }); }
191 }) : null,
192
193 /* Background Image */
194 el('p', { style: { fontWeight: 600, marginBottom: '4px' } }, __('Background Image', 'blockenberg')),
195 el(MediaUploadCheck, null,
196 el(MediaUpload, {
197 onSelect: function (media) { set({ backgroundImageUrl: media.url, backgroundImageId: media.id }); },
198 allowedTypes: ['image'],
199 value: a.backgroundImageId,
200 render: function (ref) {
201 return el(Button, {
202 onClick: ref.open,
203 variant: a.backgroundImageUrl ? 'secondary' : 'primary',
204 style: { marginBottom: '6px' }
205 }, a.backgroundImageUrl ? __('Replace Image', 'blockenberg') : __('Set Background Image', 'blockenberg'));
206 }
207 })
208 ),
209 a.backgroundImageUrl ? el(Button, {
210 variant: 'tertiary', isDestructive: true,
211 onClick: function () { set({ backgroundImageUrl: '', backgroundImageId: 0 }); }
212 }, __('Remove Image', 'blockenberg')) : null,
213 a.backgroundImageUrl ? el(SelectControl, {
214 label: __('Background Size', 'blockenberg'),
215 value: a.backgroundSize,
216 options: [
217 { label: 'Cover', value: 'cover' },
218 { label: 'Contain', value: 'contain' },
219 { label: 'Auto', value: 'auto' },
220 ],
221 onChange: function (v) { set({ backgroundSize: v }); }
222 }) : null,
223 a.backgroundImageUrl ? el(RangeControl, {
224 label: __('Image Overlay Opacity (%)', 'blockenberg'),
225 value: a.overlayOpacity,
226 min: 0, max: 100,
227 onChange: function (v) { set({ overlayOpacity: v }); }
228 }) : null
229 ),
230
231 /* Layout */
232 el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: false },
233 el(RangeControl, {
234 label: __('Min Height (px)', 'blockenberg'),
235 value: a.minHeight,
236 min: 80, max: 1200,
237 onChange: function (v) { set({ minHeight: v }); }
238 }),
239 el(RangeControl, {
240 label: __('Padding Top (px)', 'blockenberg'),
241 value: a.paddingTop,
242 min: 0, max: 300,
243 onChange: function (v) { set({ paddingTop: v }); }
244 }),
245 el(RangeControl, {
246 label: __('Padding Bottom (px)', 'blockenberg'),
247 value: a.paddingBottom,
248 min: 0, max: 300,
249 onChange: function (v) { set({ paddingBottom: v }); }
250 }),
251 el(RangeControl, {
252 label: __('Content Max Width (px)', 'blockenberg'),
253 value: a.contentMaxWidth,
254 min: 200, max: 1600,
255 onChange: function (v) { set({ contentMaxWidth: v }); }
256 }),
257 el(SelectControl, {
258 label: __('Text Align', 'blockenberg'),
259 value: a.textAlign,
260 options: [
261 { label: __('Left', 'blockenberg'), value: 'left' },
262 { label: __('Center', 'blockenberg'), value: 'center' },
263 { label: __('Right', 'blockenberg'), value: 'right' },
264 ],
265 onChange: function (v) { set({ textAlign: v }); }
266 })
267 ),
268
269 /* Colors */
270 el(PanelColorSettings, {
271 title: __('Colors', 'blockenberg'),
272 initialOpen: false,
273 colorSettings: [
274 { value: a.backgroundColor, onChange: function (v) { set({ backgroundColor: v || '#1e40af' }); }, label: __('Background', 'blockenberg') },
275 a.useGradient ? { value: a.gradientEnd, onChange: function (v) { set({ gradientEnd: v || '#7c3aed' }); }, label: __('Gradient End', 'blockenberg') } : null,
276 a.backgroundImageUrl ? { value: a.overlayColor, onChange: function (v) { set({ overlayColor: v || '#1e40af' }); }, label: __('Overlay Color', 'blockenberg') } : null,
277 { value: a.textColor, onChange: function (v) { set({ textColor: v || '#ffffff' }); }, label: __('Text Color', 'blockenberg') },
278 ].filter(Boolean)
279 })
280 ),
281
282 /* Canvas */
283 el('div', blockProps,
284 el('div', { style: contentStyle },
285 el(InnerBlocks, { template: TEMPLATE, __experimentalCaptureToolbars: true })
286 )
287 )
288 );
289 },
290
291 save: function (props) {
292 var a = props.attributes;
293 var blockProps = wp.blockEditor.useBlockProps.save({ className: 'bksk-wrap' });
294
295 return el('div', Object.assign({}, blockProps, {
296 'data-skew-top': String(a.skewTop),
297 'data-skew-bottom': String(a.skewBottom),
298 'data-top-angle': a.topAngle,
299 'data-bottom-angle': a.bottomAngle,
300 'data-top-dir': a.topDirection,
301 'data-bottom-dir': a.bottomDirection,
302 'data-bg': a.backgroundColor,
303 'data-gradient': String(a.useGradient),
304 'data-bg-end': a.gradientEnd,
305 'data-grad-angle': a.gradientAngle,
306 'data-bg-img': a.backgroundImageUrl,
307 'data-bg-size': a.backgroundSize,
308 'data-bg-pos': a.backgroundPosition,
309 'data-overlay-color': a.overlayColor,
310 'data-overlay-opacity': a.overlayOpacity,
311 'data-min-height': a.minHeight,
312 'data-pt': a.paddingTop,
313 'data-pb': a.paddingBottom,
314 'data-extra-pad': String(a.paddingExtraForSkew),
315 'data-max-width': a.contentMaxWidth,
316 'data-text-color': a.textColor,
317 'data-text-align': a.textAlign,
318 }),
319 el('div', { className: 'bksk-content' },
320 el(InnerBlocks.Content, null)
321 )
322 );
323 }
324 });
325 }() );
326