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

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

221 lines 14.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 ( function () {
2 var el = React.createElement;
3 var registerBlockType = wp.blocks.registerBlockType;
4 var useBlockProps = wp.blockEditor.useBlockProps;
5 var InspectorControls = wp.blockEditor.InspectorControls;
6 var MediaUpload = wp.blockEditor.MediaUpload;
7 var PanelBody = wp.components.PanelBody;
8 var RangeControl = wp.components.RangeControl;
9 var SelectControl = wp.components.SelectControl;
10 var TextControl = wp.components.TextControl;
11 var TextareaControl = wp.components.TextareaControl;
12 var ToggleControl = wp.components.ToggleControl;
13 var Button = wp.components.Button;
14 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
15 var useRef = wp.element.useRef;
16 var useEffect = wp.element.useEffect;
17
18 var _tc, _tv;
19 function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
20 function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
21
22 var TAG_OPTS = ['h1','h2','h3','h4','p'].map(function(t){return{value:t,label:t.toUpperCase()};});
23 var BG_OPTS = [{value:'solid',label:'Solid Color'},{value:'gradient',label:'Gradient'},{value:'image',label:'Image'}];
24 var ALIGN_OPTS = [{value:'left',label:'Left'},{value:'center',label:'Center'},{value:'right',label:'Right'}];
25 var TINT_OPTS = [{value:'dark',label:'Dark (shadows)'},{value:'light',label:'Light (halation)'},{value:'colored',label:'Colored (tinted)'}];
26
27 function buildBg(a) {
28 if (a.bgType === 'gradient') return 'linear-gradient('+a.bgAngle+'deg, '+(a.bgColor||'#0a0a0f')+', '+(a.bgColor2||'#1e1b4b')+')';
29 return a.bgColor || '#0a0a0f';
30 }
31
32 registerBlockType('blockenberg/noise-section', {
33 title: 'Noise / Grain Section',
34 icon: 'image-flip-horizontal',
35 category: 'bkbg-effects',
36
37 edit: function (props) {
38 var attr = props.attributes; var setAttr = props.setAttributes;
39 var previewRef = useRef(null);
40 var canvasRef = useRef(null);
41 var rafRef = useRef(null);
42
43 useEffect(function () {
44 var previewEl = previewRef.current;
45 var canvas = canvasRef.current;
46 if (!previewEl || !canvas) return;
47
48 var ctx = canvas.getContext('2d');
49 if (!ctx) return;
50
51 var pixelRatio = window.devicePixelRatio || 1;
52 var fps = Math.max(1, attr.noiseFps || 12);
53 var frameInterval = 1000 / fps;
54 var scale = Math.max(0.5, attr.noiseScale || 1);
55 var opacity = attr.noiseOpacity !== undefined ? attr.noiseOpacity : 0.3;
56 var alpha = Math.max(0, Math.min(255, Math.round(opacity * 255)));
57 var lastFrame = 0;
58
59 var tintR = 0, tintG = 0, tintB = 0;
60 if (attr.noiseTint === 'light') { tintR = 255; tintG = 255; tintB = 220; }
61 else if (attr.noiseTint === 'colored') { tintR = 120; tintG = 80; tintB = 200; }
62
63 function resizeCanvas() {
64 var w = previewEl.offsetWidth || 800;
65 var h = previewEl.offsetHeight || (attr.sectionHeight || 600);
66 canvas.width = Math.ceil(w * pixelRatio / scale);
67 canvas.height = Math.ceil(h * pixelRatio / scale);
68 canvas.style.width = w + 'px';
69 canvas.style.height = h + 'px';
70 }
71
72 function drawNoise(now) {
73 if (now - lastFrame < frameInterval) return;
74 lastFrame = now;
75
76 var w = canvas.width;
77 var h = canvas.height;
78 var imageData = ctx.createImageData(w, h);
79 var data = imageData.data;
80
81 for (var i = 0; i < data.length; i += 4) {
82 var v = Math.random() * 255 | 0;
83 data[i] = tintR || v;
84 data[i + 1] = tintG || v;
85 data[i + 2] = tintB || v;
86 data[i + 3] = alpha;
87 }
88 ctx.putImageData(imageData, 0, 0);
89 }
90
91 function loop(now) {
92 drawNoise(now);
93 rafRef.current = requestAnimationFrame(loop);
94 }
95
96 function stopLoop() {
97 if (rafRef.current) {
98 cancelAnimationFrame(rafRef.current);
99 rafRef.current = null;
100 }
101 }
102
103 resizeCanvas();
104 stopLoop();
105 rafRef.current = requestAnimationFrame(loop);
106 window.addEventListener('resize', resizeCanvas);
107
108 return function () {
109 stopLoop();
110 window.removeEventListener('resize', resizeCanvas);
111 };
112 }, [attr.noiseOpacity, attr.noiseTint, attr.noiseFps, attr.noiseScale, attr.sectionHeight, attr.bgType, attr.bgImage, attr.bgImageOpacity, attr.bgColor, attr.bgColor2, attr.bgAngle]);
113
114 var preview = el('div', {
115 ref: previewRef,
116 style: {
117 position:'relative',
118 minHeight:(attr.sectionHeight||600)+'px',
119 background: buildBg(attr),
120 display:'flex', alignItems:'center', justifyContent: attr.textAlign==='center'?'center':attr.textAlign==='right'?'flex-end':'flex-start',
121 padding:(attr.paddingTop||100)+'px clamp(24px,5vw,80px) '+(attr.paddingBottom||100)+'px',
122 overflow:'hidden',
123 borderRadius:8
124 }
125 },
126 /* Background image layer */
127 attr.bgType === 'image' && attr.bgImage && el('div', {
128 style: { position:'absolute', inset:0, backgroundImage:'url('+attr.bgImage+')', backgroundSize:'cover', backgroundPosition:'center', opacity:attr.bgImageOpacity||0.5, zIndex:0 }
129 }),
130 /* Grain overlay preview (static version) */
131 el('canvas', {
132 ref: canvasRef,
133 style: { position:'absolute', inset:0, zIndex:1, pointerEvents:'none' }
134 }),
135 /* Content */
136 el('div', { className: 'bkbg-ns-content', style: { position:'relative', zIndex:2, maxWidth:(attr.maxWidth||800)+'px', textAlign:attr.textAlign||'center', width:'100%' } },
137 el(attr.tag||'h1', {
138 className: 'bkbg-ns-heading',
139 style: { color:attr.textColor||'#fff', margin:'0 0 24px' }
140 }, attr.heading),
141 attr.subtext && el('p', { className: 'bkbg-ns-subtext', style: { color:attr.subtextColor||'#94a3b8', margin:'0 0 36px' } }, attr.subtext),
142 el('div', { style: { display:'flex', gap:12, flexWrap:'wrap', justifyContent:attr.textAlign==='center'?'center':'flex-start', alignItems:'center' } },
143 attr.showCta && el('a', { href:attr.ctaUrl||'#', style: { background:attr.ctaBg||'#fff', color:attr.ctaColor||'#0a0a0f', padding:'14px 32px', borderRadius:8, fontWeight:700, fontSize:16, textDecoration:'none' } }, attr.ctaLabel||'Get Started'),
144 attr.showCta2 && el('a', { href:attr.ctaUrl2||'#', style: { background:attr.ctaBg2||'transparent', color:attr.ctaColor2||'#fff', border:'1px solid '+(attr.ctaBorder2||'rgba(255,255,255,0.4)'), padding:'14px 32px', borderRadius:8, fontWeight:700, fontSize:16, textDecoration:'none' } }, attr.ctaLabel2||'Learn More')
145 )
146 )
147 );
148
149 var blockProps = useBlockProps((function () {
150 var _tvf = getTypoCssVars();
151 var s = {};
152 if (_tvf) { Object.assign(s, _tvf(attr.headingTypo, '--bkbg-ns-hd-')); Object.assign(s, _tvf(attr.subtextTypo, '--bkbg-ns-st-')); }
153 return { style: s };
154 })());
155
156 return el(React.Fragment, null,
157 el(InspectorControls, null,
158 el(PanelBody, {title:'Content', initialOpen:true},
159 el(TextControl,{__nextHasNoMarginBottom:true,label:'Heading',value:attr.heading,onChange:function(v){setAttr({heading:v});}}),
160 el(SelectControl,{__nextHasNoMarginBottom:true,label:'Heading Tag',value:attr.tag,options:TAG_OPTS,onChange:function(v){setAttr({tag:v});}}),
161 el(TextareaControl,{__nextHasNoMarginBottom:true,label:'Subtext',value:attr.subtext,onChange:function(v){setAttr({subtext:v});}}),
162 el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Primary CTA',checked:attr.showCta,onChange:function(v){setAttr({showCta:v});}}),
163 attr.showCta && el(TextControl,{__nextHasNoMarginBottom:true,label:'CTA Label',value:attr.ctaLabel,onChange:function(v){setAttr({ctaLabel:v});}}),
164 attr.showCta && el(TextControl,{__nextHasNoMarginBottom:true,label:'CTA URL',value:attr.ctaUrl,onChange:function(v){setAttr({ctaUrl:v});}}),
165 el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Secondary CTA',checked:attr.showCta2,onChange:function(v){setAttr({showCta2:v});}}),
166 attr.showCta2 && el(TextControl,{__nextHasNoMarginBottom:true,label:'Secondary Label',value:attr.ctaLabel2,onChange:function(v){setAttr({ctaLabel2:v});}}),
167 attr.showCta2 && el(TextControl,{__nextHasNoMarginBottom:true,label:'Secondary URL',value:attr.ctaUrl2,onChange:function(v){setAttr({ctaUrl2:v});}})
168 ),
169 el(PanelBody, {title:'Background', initialOpen:true},
170 el(SelectControl,{__nextHasNoMarginBottom:true,label:'Background Type',value:attr.bgType,options:BG_OPTS,onChange:function(v){setAttr({bgType:v});}}),
171 attr.bgType === 'image' && el(MediaUpload,{
172 onSelect:function(m){setAttr({bgImage:m.url,bgImageId:m.id});},
173 allowedTypes:['image'],value:attr.bgImageId,
174 render:function(p){ return el(Button,{variant:'secondary',onClick:p.open},attr.bgImage?'Change Image':'Select Image'); }
175 }),
176 attr.bgType === 'image' && attr.bgImage && el(RangeControl,{__nextHasNoMarginBottom:true,label:'Image Opacity',value:attr.bgImageOpacity,min:0,max:1,step:0.05,onChange:function(v){setAttr({bgImageOpacity:v});}}),
177 attr.bgType === 'gradient' && el(RangeControl,{__nextHasNoMarginBottom:true,label:'Gradient Angle',value:attr.bgAngle,min:0,max:360,onChange:function(v){setAttr({bgAngle:v});}})
178 ),
179 el(PanelBody, {title:'Noise Settings', initialOpen:true},
180 el(RangeControl,{__nextHasNoMarginBottom:true,label:'Noise Opacity',value:attr.noiseOpacity !== undefined ? attr.noiseOpacity : 0.3,min:0,max:0.5,step:0.01,onChange:function(v){setAttr({noiseOpacity:v});}}),
181 el(SelectControl,{__nextHasNoMarginBottom:true,label:'Noise Tint',value:attr.noiseTint,options:TINT_OPTS,onChange:function(v){setAttr({noiseTint:v});}}),
182 el(RangeControl,{__nextHasNoMarginBottom:true,label:'FPS (animation speed)',value:attr.noiseFps,min:1,max:60,onChange:function(v){setAttr({noiseFps:v});}}),
183 el(RangeControl,{__nextHasNoMarginBottom:true,label:'Grain Scale',value:attr.noiseScale,min:0.5,max:4,step:0.5,onChange:function(v){setAttr({noiseScale:v});}})
184 ),
185 el(PanelBody, {title:'Typography', initialOpen:false},
186 el(SelectControl,{__nextHasNoMarginBottom:true,label:'Text Align',value:attr.textAlign,options:ALIGN_OPTS,onChange:function(v){setAttr({textAlign:v});}}),
187 getTypoControl() && el(getTypoControl(), { label: 'Heading Typography', value: attr.headingTypo || {}, onChange: function (v) { setAttr({ headingTypo: v }); } }),
188 getTypoControl() && el(getTypoControl(), { label: 'Subtext Typography', value: attr.subtextTypo || {}, onChange: function (v) { setAttr({ subtextTypo: v }); } })
189 ),
190 el(PanelBody, {title:'Layout', initialOpen:false},
191 el(RangeControl,{__nextHasNoMarginBottom:true,label:'Section Height (px)',value:attr.sectionHeight,min:200,max:1200,onChange:function(v){setAttr({sectionHeight:v});}}),
192 el(RangeControl,{__nextHasNoMarginBottom:true,label:'Content Max Width (px)',value:attr.maxWidth,min:300,max:1600,onChange:function(v){setAttr({maxWidth:v});}}),
193 el(RangeControl,{__nextHasNoMarginBottom:true,label:'Padding Top (px)',value:attr.paddingTop,min:0,max:300,onChange:function(v){setAttr({paddingTop:v});}}),
194 el(RangeControl,{__nextHasNoMarginBottom:true,label:'Padding Bottom (px)',value:attr.paddingBottom,min:0,max:300,onChange:function(v){setAttr({paddingBottom:v});}})
195 ),
196 el(PanelColorSettings, {
197 title:'Colors',initialOpen:false,
198 colorSettings:[
199 {value:attr.bgColor, onChange:function(v){setAttr({bgColor: v||'#0a0a0f'});},label:'Background / Gradient From'},
200 attr.bgType==='gradient' && {value:attr.bgColor2,onChange:function(v){setAttr({bgColor2:v||'#1e1b4b'});},label:'Gradient To'},
201 {value:attr.textColor, onChange:function(v){setAttr({textColor: v||'#ffffff'});},label:'Heading'},
202 {value:attr.subtextColor, onChange:function(v){setAttr({subtextColor: v||'#94a3b8'});},label:'Subtext'},
203 attr.showCta && {value:attr.ctaBg, onChange:function(v){setAttr({ctaBg: v||'#fff'});},label:'CTA Bg'},
204 attr.showCta && {value:attr.ctaColor, onChange:function(v){setAttr({ctaColor: v||'#0a0a0f'});},label:'CTA Text'},
205 attr.showCta2 && {value:attr.ctaBg2, onChange:function(v){setAttr({ctaBg2: v||'transparent'});},label:'CTA 2 Bg'},
206 attr.showCta2 && {value:attr.ctaColor2,onChange:function(v){setAttr({ctaColor2:v||'#fff'});},label:'CTA 2 Text'}
207 ].filter(Boolean)
208 })
209 ),
210 el('div', blockProps, preview)
211 );
212 },
213
214 save: function (props) {
215 return el('div', useBlockProps.save(),
216 el('div', { className:'bkbg-ns-app', 'data-opts':JSON.stringify(props.attributes) })
217 );
218 }
219 });
220 }() );
221