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 / hovering-image-stack / index.js

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

264 lines 13.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 __ = wp.i18n.__;
4 var registerBlockType = wp.blocks.registerBlockType;
5 var useBlockProps = wp.blockEditor.useBlockProps;
6 var InspectorControls = wp.blockEditor.InspectorControls;
7 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
8 var MediaUpload = wp.blockEditor.MediaUpload;
9 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
10 var PanelBody = wp.components.PanelBody;
11 var RangeControl = wp.components.RangeControl;
12 var ToggleControl = wp.components.ToggleControl;
13 var SelectControl = wp.components.SelectControl;
14 var TextControl = wp.components.TextControl;
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 PLACEHOLDER = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjI4MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZTJlOGYwIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZvbnQtc2l6ZT0iMTQiIGZpbGw9IiM5NGEzYjgiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGR5PSIuM2VtIj5JbWFnZTwvdGV4dD48L3N2Zz4=';
22
23 function getStackedStyle(index, total, a, hovered) {
24 /* Fan stacking — each card rotated and offset slightly */
25 var mid = (total - 1) / 2;
26 var angle = (index - mid) * a.spreadAngle;
27 var tx = (index - mid) * 18;
28 var ty = Math.abs(index - mid) * 4;
29 var scale = 1 - Math.abs(index - mid) * 0.03;
30 var z = index;
31 if (hovered) {
32 /* spread cards wide */
33 tx = (index - mid) * a.spreadDist;
34 angle = 0;
35 ty = 0;
36 scale = 1;
37 z = total - index;
38 }
39 return {
40 transform: 'translateX(' + tx + 'px) translateY(' + ty + 'px) rotate(' + angle + 'deg) scale(' + scale + ')',
41 zIndex: z,
42 transition: 'transform 0.45s cubic-bezier(.34,1.56,.64,1), box-shadow 0.3s ease',
43 flexShrink: 0
44 };
45 }
46
47 registerBlockType('blockenberg/hovering-image-stack', {
48 title: __('Hovering Image Stack'),
49 icon: 'format-gallery',
50 category: 'bkbg-effects',
51
52 edit: function (props) {
53 var attr = props.attributes;
54 var setAttr = props.setAttributes;
55 var useState = wp.element.useState;
56 var hovered = useState(false);
57 var isHovered = hovered[0];
58 var setHovered = hovered[1];
59 var bp = useBlockProps({ className: 'bkbg-his-wrap' });
60
61 function updateImage(idx, field, val) {
62 var imgs = attr.images.map(function (img, i) {
63 return i === idx ? Object.assign({}, img, { [field]: val }) : img;
64 });
65 setAttr({ images: imgs });
66 }
67 function addImage() {
68 setAttr({ images: attr.images.concat([{ url: '', alt: 'Image', caption: '' }]) });
69 }
70 function removeImage(idx) {
71 if (attr.images.length <= 2) return;
72 setAttr({ images: attr.images.filter(function (_, i) { return i !== idx; }) });
73 }
74
75 var total = attr.images.length;
76
77 var inspector = el(InspectorControls, {},
78 el(PanelBody, { title: __('Images'), initialOpen: true },
79 attr.images.map(function (img, idx) {
80 return el(PanelBody, { key: idx, title: 'Image ' + (idx + 1), initialOpen: false },
81 el(MediaUploadCheck, {},
82 el(MediaUpload, {
83 onSelect: function (media) {
84 updateImage(idx, 'url', media.url);
85 updateImage(idx, 'alt', media.alt || '');
86 },
87 allowedTypes: ['image'],
88 value: img.url,
89 render: function (ref) {
90 return el(Button, {
91 variant: img.url ? 'secondary' : 'primary',
92 onClick: ref.open
93 }, img.url ? __('Change Image') : __('Choose Image'));
94 }
95 })
96 ),
97 img.url && el('img', {
98 src: img.url, alt: img.alt || '',
99 style: { maxWidth: '100%', maxHeight: '80px', objectFit: 'cover',
100 borderRadius: '4px', marginTop: '8px', display: 'block' }
101 }),
102 el(TextControl, { label: __('Alt text'), value: img.alt || '',
103 onChange: function (v) { updateImage(idx, 'alt', v); },
104 __nextHasNoMarginBottom: true }),
105 el(TextControl, { label: __('Caption'), value: img.caption || '',
106 onChange: function (v) { updateImage(idx, 'caption', v); },
107 __nextHasNoMarginBottom: true }),
108 attr.images.length > 2 && el(Button, {
109 isDestructive: true, variant: 'secondary',
110 onClick: function () { removeImage(idx); },
111 style: { marginTop: '8px' }
112 }, __('Remove'))
113 );
114 }),
115 el(Button, { variant: 'primary', onClick: addImage,
116 style: { marginTop: '8px', width: '100%' }
117 }, __('+ Add Image'))
118 ),
119
120 el(PanelBody, { title: __('Layout'), initialOpen: false },
121 el(SelectControl, {
122 label: __('Spread Mode'),
123 value: attr.spreadMode,
124 options: [
125 { label: __('Fan'), value: 'fan' },
126 { label: __('Cascade Down'), value: 'cascade' },
127 { label: __('Horizontal'), value: 'horizontal' }
128 ],
129 onChange: function (v) { setAttr({ spreadMode: v }); },
130 __nextHasNoMarginBottom: true
131 }),
132 el(RangeControl, { label: __('Fan Angle (°)'), value: attr.spreadAngle,
133 min: 0, max: 45,
134 onChange: function (v) { setAttr({ spreadAngle: v }); },
135 __nextHasNoMarginBottom: true }),
136 el(RangeControl, { label: __('Spread Distance (px)'), value: attr.spreadDist,
137 min: 20, max: 200,
138 onChange: function (v) { setAttr({ spreadDist: v }); },
139 __nextHasNoMarginBottom: true }),
140 el(RangeControl, { label: __('Image Width (px)'), value: attr.imageWidth,
141 min: 100, max: 400,
142 onChange: function (v) { setAttr({ imageWidth: v }); },
143 __nextHasNoMarginBottom: true }),
144 el(RangeControl, { label: __('Image Height (px)'), value: attr.imageHeight,
145 min: 100, max: 600,
146 onChange: function (v) { setAttr({ imageHeight: v }); },
147 __nextHasNoMarginBottom: true }),
148 el(RangeControl, { label: __('Border Radius (px)'), value: attr.imageRadius,
149 min: 0, max: 40,
150 onChange: function (v) { setAttr({ imageRadius: v }); },
151 __nextHasNoMarginBottom: true }),
152 el(RangeControl, { label: __('Border Width (px)'), value: attr.borderWidth,
153 min: 0, max: 10,
154 onChange: function (v) { setAttr({ borderWidth: v }); },
155 __nextHasNoMarginBottom: true }),
156 el(RangeControl, { label: __('Shadow Strength (%)'), value: attr.shadowStrength,
157 min: 0, max: 60,
158 onChange: function (v) { setAttr({ shadowStrength: v }); },
159 __nextHasNoMarginBottom: true }),
160 el(ToggleControl, { label: __('Show Captions on Hover'), checked: attr.showCaption,
161 onChange: function (v) { setAttr({ showCaption: v }); },
162 __nextHasNoMarginBottom: true })
163 ),
164
165 el(PanelColorSettings, {
166 title: __('Colors'), initialOpen: false,
167 colorSettings: [
168 { label: __('Background'), value: attr.bgColor,
169 onChange: function (v) { setAttr({ bgColor: v || 'transparent' }); } },
170 { label: __('Image Border'), value: attr.borderColor,
171 onChange: function (v) { setAttr({ borderColor: v || '#ffffff' }); } },
172 { label: __('Caption Text'), value: attr.captionColor,
173 onChange: function (v) { setAttr({ captionColor: v || '#0f172a' }); } },
174 { label: __('Caption BG'), value: attr.captionBg,
175 onChange: function (v) { setAttr({ captionBg: v || '#ffffff' }); } }
176 ]
177 }),
178 el(PanelBody, { title: __('Typography'), initialOpen: false },
179 getTypographyControl() && el(getTypographyControl(), {
180 label: __('Caption'),
181 typo: attr.captionTypo || {},
182 onChange: function(v){ setAttr({ captionTypo: v }); }
183 })
184 )
185 );
186
187 var shadowAlpha = (attr.shadowStrength / 100).toFixed(2);
188
189 /* Preview — show stacked */
190 var stackEl = el('div', {
191 className: 'bkbg-his-stack',
192 style: { position: 'relative', display: 'inline-flex', alignItems: 'center',
193 height: attr.imageHeight + 'px',
194 minWidth: attr.imageWidth + total * 20 + 'px' },
195 onMouseEnter: function () { setHovered(true); },
196 onMouseLeave: function () { setHovered(false); }
197 },
198 attr.images.map(function (img, i) {
199 var st = getStackedStyle(i, total, attr, isHovered);
200 return el('div', {
201 key: i, className: 'bkbg-his-card',
202 style: Object.assign({}, st, {
203 position: 'absolute',
204 width: attr.imageWidth + 'px',
205 height: attr.imageHeight + 'px',
206 borderRadius: attr.imageRadius + 'px',
207 border: attr.borderWidth + 'px solid ' + (attr.borderColor || '#ffffff'),
208 boxShadow: '0 8px 32px rgba(0,0,0,' + shadowAlpha + ')',
209 overflow: 'hidden'
210 })
211 },
212 el('img', {
213 src: img.url || PLACEHOLDER,
214 alt: img.alt || '',
215 style: { width: '100%', height: '100%', objectFit: 'cover', display: 'block' }
216 })
217 );
218 })
219 );
220
221 return el(wp.element.Fragment, {}, inspector,
222 el('div', bp,
223 el('div', {
224 style: {
225 background: attr.bgColor || 'transparent',
226 padding: '40px 16px',
227 display: 'flex',
228 justifyContent: 'center',
229 alignItems: 'center',
230 minHeight: attr.imageHeight + 80 + 'px'
231 }
232 }, stackEl)
233 )
234 );
235 },
236
237 save: function (props) {
238 var attr = props.attributes;
239 var bp = useBlockProps.save();
240 var opts = {
241 images: attr.images,
242 spreadMode: attr.spreadMode,
243 spreadAngle: attr.spreadAngle,
244 spreadDist: attr.spreadDist,
245 imageWidth: attr.imageWidth,
246 imageHeight: attr.imageHeight,
247 imageRadius: attr.imageRadius,
248 showCaption: attr.showCaption,
249 captionSize: attr.captionSize,
250 shadowStrength: attr.shadowStrength,
251 borderWidth: attr.borderWidth,
252 borderColor: attr.borderColor,
253 bgColor: attr.bgColor,
254 captionColor: attr.captionColor,
255 captionBg: attr.captionBg,
256 captionTypo: attr.captionTypo
257 };
258 return el('div', bp,
259 el('div', { className: 'bkbg-his-app', 'data-opts': JSON.stringify(opts) })
260 );
261 }
262 });
263 }() );
264