PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.11
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.11
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 / image-filter / index.js

index.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.11, at blocks/image-filter/index.js

271 lines 17.5 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 useBlockProps = wp.blockEditor.useBlockProps;
5 var InspectorControls = wp.blockEditor.InspectorControls;
6 var MediaUpload = wp.blockEditor.MediaUpload;
7 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
8 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
9 var PanelBody = wp.components.PanelBody;
10 var TextControl = wp.components.TextControl;
11 var TextareaControl = wp.components.TextareaControl;
12 var ToggleControl = wp.components.ToggleControl;
13 var SelectControl = wp.components.SelectControl;
14 var RangeControl = wp.components.RangeControl;
15 var Button = wp.components.Button;
16
17 var _TypographyControl, _typoCssVars;
18 function getTypographyControl() { return _TypographyControl || (_TypographyControl = window.bkbgTypographyControl); }
19 function getTypoCssVars() { return _typoCssVars || (_typoCssVars = window.bkbgTypoCssVars); }
20
21 var PRESETS = [
22 { label: 'None (Custom)', value: 'none' },
23 { label: 'Vivid', value: 'vivid' },
24 { label: 'Dramatic', value: 'dramatic' },
25 { label: 'Vintage', value: 'vintage' },
26 { label: 'Noir', value: 'noir' },
27 { label: 'Warm Glow', value: 'warm' },
28 { label: 'Cool Ice', value: 'cool' },
29 { label: 'Faded', value: 'faded' },
30 { label: 'Cinema', value: 'cinema' },
31 { label: 'Matte', value: 'matte' },
32 { label: 'Duotone', value: 'duotone' },
33 { label: 'High Key', value: 'highkey' },
34 { label: 'Low Key', value: 'lowkey' },
35 { label: 'Neon Pop', value: 'neonpop' }
36 ];
37
38 var PRESET_VALUES = {
39 none: { brightness: 100, contrast: 100, saturation: 100, hueRotate: 0, blur: 0, sepia: 0, grayscale: 0, invert: 0, opacity: 100 },
40 vivid: { brightness: 105, contrast: 120, saturation: 150, hueRotate: 0, blur: 0, sepia: 0, grayscale: 0, invert: 0, opacity: 100 },
41 dramatic: { brightness: 90, contrast: 140, saturation: 110, hueRotate: 0, blur: 0, sepia: 0, grayscale: 0, invert: 0, opacity: 100 },
42 vintage: { brightness: 105, contrast: 95, saturation: 75, hueRotate: 0, blur: 0, sepia: 40, grayscale: 15, invert: 0, opacity: 95 },
43 noir: { brightness: 100, contrast: 125, saturation: 0, hueRotate: 0, blur: 0, sepia: 0, grayscale: 100,invert: 0, opacity: 100 },
44 warm: { brightness: 108, contrast: 100, saturation: 110, hueRotate: 20, blur: 0, sepia: 20, grayscale: 0, invert: 0, opacity: 100 },
45 cool: { brightness: 103, contrast: 105, saturation: 95, hueRotate: -30, blur: 0, sepia: 0, grayscale: 10, invert: 0, opacity: 100 },
46 faded: { brightness: 112, contrast: 78, saturation: 60, hueRotate: 0, blur: 0, sepia: 15, grayscale: 10, invert: 0, opacity: 90 },
47 cinema: { brightness: 88, contrast: 130, saturation: 85, hueRotate: 0, blur: 0, sepia: 8, grayscale: 0, invert: 0, opacity: 100 },
48 matte: { brightness: 110, contrast: 85, saturation: 70, hueRotate: 0, blur: 0, sepia: 10, grayscale: 5, invert: 0, opacity: 88 },
49 duotone: { brightness: 100, contrast: 120, saturation: 0, hueRotate: 0, blur: 0, sepia: 60, grayscale: 40, invert: 0, opacity: 100 },
50 highkey: { brightness: 145, contrast: 80, saturation: 80, hueRotate: 0, blur: 0, sepia: 0, grayscale: 0, invert: 0, opacity: 100 },
51 lowkey: { brightness: 55, contrast: 130, saturation: 100, hueRotate: 0, blur: 0, sepia: 0, grayscale: 0, invert: 0, opacity: 100 },
52 neonpop: { brightness: 110, contrast: 130, saturation: 200, hueRotate: 45, blur: 0, sepia: 0, grayscale: 0, invert: 0, opacity: 100 }
53 };
54
55 var BLEND_MODES = [
56 'normal','multiply','screen','overlay','darken','lighten',
57 'color-dodge','color-burn','hard-light','soft-light','difference','exclusion','hue','saturation','color','luminosity'
58 ].map(function (v) { return { label: v, value: v }; });
59
60 var FIT_OPTIONS = [
61 { label: 'Cover', value: 'cover' },
62 { label: 'Contain', value: 'contain' },
63 { label: 'Fill', value: 'fill' },
64 { label: 'Original', value: 'none' }
65 ];
66
67 function buildFilterString(attr) {
68 var parts = [];
69 if (attr.brightness !== 100) parts.push('brightness(' + attr.brightness + '%)');
70 if (attr.contrast !== 100) parts.push('contrast(' + attr.contrast + '%)');
71 if (attr.saturation !== 100) parts.push('saturate(' + attr.saturation + '%)');
72 if (attr.hueRotate !== 0) parts.push('hue-rotate(' + attr.hueRotate + 'deg)');
73 if (attr.blur !== 0) parts.push('blur(' + attr.blur + 'px)');
74 if (attr.sepia !== 0) parts.push('sepia(' + attr.sepia + '%)');
75 if (attr.grayscale !== 0) parts.push('grayscale(' + attr.grayscale + '%)');
76 if (attr.invert !== 0) parts.push('invert(' + attr.invert + '%)');
77 if (attr.opacity !== 100) parts.push('opacity(' + attr.opacity + '%)');
78 return parts.length ? parts.join(' ') : 'none';
79 }
80
81 // Editor image placeholder / live preview
82 function ImagePreview(props) {
83 var attr = props.attr;
84 var onSelect = props.onSelect;
85 var filter = buildFilterString(attr);
86
87 var wrapStyle = {
88 position: 'relative',
89 borderRadius: attr.borderRadius + 'px',
90 overflow: 'hidden',
91 maxWidth: attr.maxWidth ? attr.maxWidth + 'px' : '100%',
92 margin: '0 auto',
93 borderColor: attr.borderColor || undefined,
94 borderWidth: attr.borderColor ? '2px' : undefined,
95 borderStyle: attr.borderColor ? 'solid' : undefined
96 };
97
98 if (!attr.imageUrl) {
99 return el(MediaUploadCheck, {},
100 el(MediaUpload, {
101 onSelect: onSelect,
102 allowedTypes: ['image'],
103 value: attr.imageId,
104 render: function (ref) {
105 return el('div', {
106 style: {
107 border: '2px dashed #ccc', borderRadius: '8px', padding: '40px',
108 textAlign: 'center', cursor: 'pointer', background: '#fafafa'
109 },
110 onClick: ref.open
111 },
112 el('div', { style: { fontSize: '40px', marginBottom: '8px' } }, '🖼️'),
113 el('p', { style: { margin: 0, color: '#888' } }, __('Click to upload an image', 'blockenberg'))
114 );
115 }
116 })
117 );
118 }
119
120 return el('div', { style: wrapStyle },
121 el('img', {
122 src: attr.imageUrl,
123 alt: attr.imageAlt,
124 style: {
125 display: 'block', width: '100%',
126 height: attr.imageHeight ? attr.imageHeight + 'px' : 'auto',
127 objectFit: attr.objectFit,
128 filter: filter,
129 mixBlendMode: attr.blendMode !== 'normal' ? attr.blendMode : undefined,
130 transition: 'filter ' + attr.transitionMs + 'ms',
131 borderRadius: attr.borderRadius + 'px'
132 }
133 }),
134 attr.overlayColor && attr.overlayOpacity > 0 && el('div', {
135 style: {
136 position: 'absolute', inset: 0,
137 background: attr.overlayColor,
138 opacity: attr.overlayOpacity / 100,
139 borderRadius: attr.borderRadius + 'px',
140 pointerEvents: 'none'
141 }
142 }),
143 attr.caption && el('p', {
144 style: {
145 margin: 0, padding: '8px 12px', textAlign: 'center', fontSize: '14px',
146 color: attr.captionColor,
147 background: attr.captionBg || undefined
148 }
149 }, attr.caption)
150 );
151 }
152
153 wp.blocks.registerBlockType('blockenberg/image-filter', {
154 title: 'Image Filter',
155 icon: 'format-image',
156 category: 'bkbg-media',
157 edit: function (props) {
158 var attr = props.attributes;
159 var setAttr = props.setAttributes;
160 var blockProps = useBlockProps({ style: (function () {
161 var s = { fontFamily: 'inherit' };
162 var fn = getTypoCssVars();
163 if (fn) { Object.assign(s, fn(attr.captionTypo, '--bkbg-if-cp-')); }
164 return s;
165 })() });
166
167 function onSelectImage(media) {
168 setAttr({ imageUrl: media.url, imageId: media.id, imageAlt: media.alt || '' });
169 }
170
171 function applyPreset(v) {
172 var p = PRESET_VALUES[v];
173 if (p) setAttr(Object.assign({}, p, { preset: v }));
174 else setAttr({ preset: v });
175 }
176
177 return el('div', blockProps,
178 el(InspectorControls, {},
179 el(PanelBody, { title: __('Image', 'blockenberg'), initialOpen: true },
180 attr.imageUrl && el(MediaUploadCheck, {},
181 el(MediaUpload, {
182 onSelect: onSelectImage, allowedTypes: ['image'], value: attr.imageId,
183 render: function (ref) {
184 return el(Button, {
185 __nextHasNoMarginBottom: true,
186 isSecondary: true,
187 onClick: ref.open,
188 style: { marginBottom: '8px', display: 'block' }
189 }, __('Replace Image', 'blockenberg'));
190 }
191 })
192 ),
193 el(TextControl, {
194 __nextHasNoMarginBottom: true,
195 label: __('Alt Text', 'blockenberg'),
196 value: attr.imageAlt,
197 onChange: function (v) { setAttr({ imageAlt: v }); }
198 }),
199 el(TextareaControl, {
200 __nextHasNoMarginBottom: true,
201 label: __('Caption', 'blockenberg'),
202 value: attr.caption,
203 onChange: function (v) { setAttr({ caption: v }); }
204 })
205 ),
206 el(PanelBody, { title: __('Filter Preset', 'blockenberg'), initialOpen: false },
207 el(SelectControl, {
208 __nextHasNoMarginBottom: true,
209 label: __('Quick Preset', 'blockenberg'),
210 value: attr.preset,
211 options: PRESETS,
212 onChange: applyPreset
213 })
214 ),
215 el(PanelBody, { title: __('Custom Filter Sliders', 'blockenberg'), initialOpen: false },
216 el(RangeControl, { __nextHasNoMarginBottom: true, label: __('Brightness (%)', 'blockenberg'), value: attr.brightness, min: 0, max: 300, onChange: function (v) { setAttr({ brightness: v, preset: 'none' }); } }),
217 el(RangeControl, { __nextHasNoMarginBottom: true, label: __('Contrast (%)', 'blockenberg'), value: attr.contrast, min: 0, max: 300, onChange: function (v) { setAttr({ contrast: v, preset: 'none' }); } }),
218 el(RangeControl, { __nextHasNoMarginBottom: true, label: __('Saturation (%)', 'blockenberg'), value: attr.saturation, min: 0, max: 400, onChange: function (v) { setAttr({ saturation: v, preset: 'none' }); } }),
219 el(RangeControl, { __nextHasNoMarginBottom: true, label: __('Hue Rotate (°)', 'blockenberg'), value: attr.hueRotate, min: -180, max: 180, onChange: function (v) { setAttr({ hueRotate: v, preset: 'none' }); } }),
220 el(RangeControl, { __nextHasNoMarginBottom: true, label: __('Blur (px)', 'blockenberg'), value: attr.blur, min: 0, max: 20, onChange: function (v) { setAttr({ blur: v, preset: 'none' }); } }),
221 el(RangeControl, { __nextHasNoMarginBottom: true, label: __('Sepia (%)', 'blockenberg'), value: attr.sepia, min: 0, max: 100, onChange: function (v) { setAttr({ sepia: v, preset: 'none' }); } }),
222 el(RangeControl, { __nextHasNoMarginBottom: true, label: __('Grayscale (%)', 'blockenberg'), value: attr.grayscale, min: 0, max: 100, onChange: function (v) { setAttr({ grayscale: v, preset: 'none' }); } }),
223 el(RangeControl, { __nextHasNoMarginBottom: true, label: __('Invert (%)', 'blockenberg'), value: attr.invert, min: 0, max: 100, onChange: function (v) { setAttr({ invert: v, preset: 'none' }); } }),
224 el(RangeControl, { __nextHasNoMarginBottom: true, label: __('Opacity (%)', 'blockenberg'), value: attr.opacity, min: 0, max: 100, onChange: function (v) { setAttr({ opacity: v, preset: 'none' }); } })
225 ),
226 el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: false },
227 el(RangeControl, { __nextHasNoMarginBottom: true, label: __('Max Width (0 = full)', 'blockenberg'), value: attr.maxWidth, min: 0, max: 1600, step: 20, onChange: function (v) { setAttr({ maxWidth: v }); } }),
228 el(RangeControl, { __nextHasNoMarginBottom: true, label: __('Image Height (0 = auto)', 'blockenberg'), value: attr.imageHeight, min: 0, max: 1000, step: 10, onChange: function (v) { setAttr({ imageHeight: v }); } }),
229 el(RangeControl, { __nextHasNoMarginBottom: true, label: __('Border Radius (px)', 'blockenberg'), value: attr.borderRadius, min: 0, max: 50, onChange: function (v) { setAttr({ borderRadius: v }); } }),
230 el(SelectControl, { __nextHasNoMarginBottom: true, label: __('Object Fit', 'blockenberg'), value: attr.objectFit, options: FIT_OPTIONS, onChange: function (v) { setAttr({ objectFit: v }); } }),
231 el(SelectControl, { __nextHasNoMarginBottom: true, label: __('Blend Mode', 'blockenberg'), value: attr.blendMode, options: BLEND_MODES, onChange: function (v) { setAttr({ blendMode: v }); } }),
232 el(RangeControl, { __nextHasNoMarginBottom: true, label: __('Transition (ms)', 'blockenberg'), value: attr.transitionMs, min: 0, max: 2000, step: 50, onChange: function (v) { setAttr({ transitionMs: v }); } })
233 ),
234 el(PanelBody, { title: __('Overlay', 'blockenberg'), initialOpen: false },
235 el(RangeControl, { __nextHasNoMarginBottom: true, label: __('Overlay Opacity (%)', 'blockenberg'), value: attr.overlayOpacity, min: 0, max: 100, onChange: function (v) { setAttr({ overlayOpacity: v }); } })
236 ),
237 el(PanelBody, { title: __('Frontend Controls', 'blockenberg'), initialOpen: false },
238 el(ToggleControl, { __nextHasNoMarginBottom: true, label: __('Show Interactive Sliders', 'blockenberg'), help: __('Let visitors adjust the filter sliders on the front end', 'blockenberg'), checked: attr.showControls, onChange: function (v) { setAttr({ showControls: v }); } }),
239 attr.showControls && el(ToggleControl, { __nextHasNoMarginBottom: true, label: __('Show Reset Button', 'blockenberg'), checked: attr.showReset, onChange: function (v) { setAttr({ showReset: v }); } }),
240 attr.showControls && el(ToggleControl, { __nextHasNoMarginBottom: true, label: __('Show Preset Selector', 'blockenberg'), checked: attr.showPresets, onChange: function (v) { setAttr({ showPresets: v }); } })
241 ),
242 el(PanelColorSettings, {
243 title: __('Colors', 'blockenberg'),
244 initialOpen: false,
245 colorSettings: [
246 { label: __('Overlay Color', 'blockenberg'), value: attr.overlayColor, onChange: function (v) { setAttr({ overlayColor: v || '' }); } },
247 { label: __('Caption Color', 'blockenberg'), value: attr.captionColor, onChange: function (v) { setAttr({ captionColor: v || '' }); } },
248 { label: __('Caption Background', 'blockenberg'), value: attr.captionBg, onChange: function (v) { setAttr({ captionBg: v || '' }); } },
249 { label: __('Border Color', 'blockenberg'), value: attr.borderColor, onChange: function (v) { setAttr({ borderColor: v || '' }); } }
250 ]
251 }),
252 el(PanelBody, { title: 'Typography', initialOpen: false },
253 getTypographyControl() && el(getTypographyControl(), { label: __('Caption', 'blockenberg'), value: attr.captionTypo, onChange: function (v) { setAttr({ captionTypo: v }); } })
254 ),
255
256 ),
257 el(ImagePreview, { attr: attr, onSelect: function (media) { setAttr({ imageUrl: media.url, imageId: media.id, imageAlt: media.alt || '' }); } })
258 );
259 },
260 save: function (props) {
261 var attr = props.attributes;
262 return el('div', useBlockProps.save(),
263 el('div', {
264 className: 'bkbg-if-app',
265 'data-opts': JSON.stringify(attr)
266 })
267 );
268 }
269 });
270 }() );
271