PluginProbe ʕ •ᴥ•ʔ
FrontBlocks for Gutenberg/GeneratePress / 1.5.2
FrontBlocks for Gutenberg/GeneratePress v1.5.2
1.5.2 1.5.1 1.4.0 1.5.0 trunk 0.2.0 0.2.1 0.2.2 0.2.3 0.2.4 0.2.5 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.1.0 1.2.0 1.2.1 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 ci-artifacts
frontblocks / assets / before-after / frontblocks-before-after.js
frontblocks / assets / before-after Last commit date
frontblocks-before-after-frontend.js 3 months ago frontblocks-before-after-option.jsx 2 months ago frontblocks-before-after.css 2 months ago frontblocks-before-after.js 2 months ago
frontblocks-before-after.js
330 lines
1 "use strict";
2
3 var registerBlockType = wp.blocks.registerBlockType;
4 var _wp$element = wp.element,
5 Fragment = _wp$element.Fragment,
6 useEffect = _wp$element.useEffect,
7 useRef = _wp$element.useRef;
8 var _wp$blockEditor = wp.blockEditor,
9 InspectorControls = _wp$blockEditor.InspectorControls,
10 MediaUpload = _wp$blockEditor.MediaUpload,
11 MediaUploadCheck = _wp$blockEditor.MediaUploadCheck,
12 useBlockProps = _wp$blockEditor.useBlockProps;
13 var _wp$components = wp.components,
14 PanelBody = _wp$components.PanelBody,
15 RangeControl = _wp$components.RangeControl,
16 Button = _wp$components.Button,
17 Placeholder = _wp$components.Placeholder,
18 TextControl = _wp$components.TextControl,
19 ToggleControl = _wp$components.ToggleControl;
20 var __ = wp.i18n.__;
21
22 /**
23 * Edit component for Before After block.
24 */
25 function BeforeAfterEdit(props) {
26 var attributes = props.attributes,
27 setAttributes = props.setAttributes;
28 var beforeImageId = attributes.beforeImageId,
29 beforeImageUrl = attributes.beforeImageUrl,
30 afterImageId = attributes.afterImageId,
31 afterImageUrl = attributes.afterImageUrl,
32 beforeLabel = attributes.beforeLabel,
33 afterLabel = attributes.afterLabel,
34 initialPosition = attributes.initialPosition,
35 blockHeight = attributes.blockHeight,
36 fixedHeight = attributes.fixedHeight;
37 var blockProps = useBlockProps();
38 var hasImages = beforeImageUrl && afterImageUrl;
39 var containerRef = useRef(null);
40
41 // Imperative drag logic — mirrors frontend JS, uses ownerDocument for iframe support.
42 useEffect(function () {
43 if (!containerRef.current || !hasImages) {
44 return;
45 }
46 var block = containerRef.current;
47 var beforeEl = block.querySelector('.frbl-before-after__before');
48 var handle = block.querySelector('.frbl-before-after__handle');
49 if (!beforeEl || !handle) {
50 return;
51 }
52 var dragging = false;
53 var doc = block.ownerDocument;
54 function setPos(pos) {
55 pos = Math.max(0, Math.min(100, pos));
56 beforeEl.style.clipPath = 'inset(0 ' + (100 - pos) + '% 0 0)';
57 handle.style.left = pos + '%';
58 }
59 function getPos(e) {
60 var rect = block.getBoundingClientRect();
61 return (e.clientX - rect.left) / rect.width * 100;
62 }
63 setPos(initialPosition);
64 function onMouseDown(e) {
65 dragging = true;
66 e.preventDefault();
67 e.stopPropagation();
68 }
69 function onMouseMove(e) {
70 if (!dragging) {
71 return;
72 }
73 setPos(getPos(e));
74 }
75 function onMouseUp() {
76 dragging = false;
77 }
78 handle.addEventListener('mousedown', onMouseDown);
79 doc.addEventListener('mousemove', onMouseMove);
80 doc.addEventListener('mouseup', onMouseUp);
81 return function () {
82 handle.removeEventListener('mousedown', onMouseDown);
83 doc.removeEventListener('mousemove', onMouseMove);
84 doc.removeEventListener('mouseup', onMouseUp);
85 };
86 }, [hasImages, beforeImageUrl, afterImageUrl, initialPosition]);
87 return /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement(InspectorControls, null, /*#__PURE__*/React.createElement(PanelBody, {
88 title: __('Before Image', 'frontblocks'),
89 initialOpen: true
90 }, /*#__PURE__*/React.createElement(MediaUploadCheck, null, /*#__PURE__*/React.createElement(MediaUpload, {
91 onSelect: function onSelect(media) {
92 return setAttributes({
93 beforeImageId: media.id,
94 beforeImageUrl: media.url
95 });
96 },
97 allowedTypes: ['image'],
98 value: beforeImageId,
99 render: function render(_ref) {
100 var open = _ref.open;
101 return /*#__PURE__*/React.createElement("div", null, beforeImageUrl && /*#__PURE__*/React.createElement("img", {
102 src: beforeImageUrl,
103 alt: "",
104 style: {
105 width: '100%',
106 marginBottom: '8px',
107 borderRadius: '2px'
108 }
109 }), /*#__PURE__*/React.createElement(Button, {
110 onClick: open,
111 variant: beforeImageUrl ? 'secondary' : 'primary',
112 style: {
113 width: '100%'
114 }
115 }, beforeImageUrl ? __('Replace Before Image', 'frontblocks') : __('Select Before Image', 'frontblocks')), beforeImageUrl && /*#__PURE__*/React.createElement(Button, {
116 onClick: function onClick() {
117 return setAttributes({
118 beforeImageId: undefined,
119 beforeImageUrl: ''
120 });
121 },
122 variant: "link",
123 isDestructive: true,
124 style: {
125 marginTop: '4px',
126 display: 'block'
127 }
128 }, __('Remove', 'frontblocks')));
129 }
130 })), /*#__PURE__*/React.createElement(TextControl, {
131 label: __('Before Label', 'frontblocks'),
132 value: beforeLabel,
133 onChange: function onChange(value) {
134 return setAttributes({
135 beforeLabel: value
136 });
137 },
138 style: {
139 marginTop: '12px'
140 }
141 })), /*#__PURE__*/React.createElement(PanelBody, {
142 title: __('After Image', 'frontblocks'),
143 initialOpen: true
144 }, /*#__PURE__*/React.createElement(MediaUploadCheck, null, /*#__PURE__*/React.createElement(MediaUpload, {
145 onSelect: function onSelect(media) {
146 return setAttributes({
147 afterImageId: media.id,
148 afterImageUrl: media.url
149 });
150 },
151 allowedTypes: ['image'],
152 value: afterImageId,
153 render: function render(_ref2) {
154 var open = _ref2.open;
155 return /*#__PURE__*/React.createElement("div", null, afterImageUrl && /*#__PURE__*/React.createElement("img", {
156 src: afterImageUrl,
157 alt: "",
158 style: {
159 width: '100%',
160 marginBottom: '8px',
161 borderRadius: '2px'
162 }
163 }), /*#__PURE__*/React.createElement(Button, {
164 onClick: open,
165 variant: afterImageUrl ? 'secondary' : 'primary',
166 style: {
167 width: '100%'
168 }
169 }, afterImageUrl ? __('Replace After Image', 'frontblocks') : __('Select After Image', 'frontblocks')), afterImageUrl && /*#__PURE__*/React.createElement(Button, {
170 onClick: function onClick() {
171 return setAttributes({
172 afterImageId: undefined,
173 afterImageUrl: ''
174 });
175 },
176 variant: "link",
177 isDestructive: true,
178 style: {
179 marginTop: '4px',
180 display: 'block'
181 }
182 }, __('Remove', 'frontblocks')));
183 }
184 })), /*#__PURE__*/React.createElement(TextControl, {
185 label: __('After Label', 'frontblocks'),
186 value: afterLabel,
187 onChange: function onChange(value) {
188 return setAttributes({
189 afterLabel: value
190 });
191 },
192 style: {
193 marginTop: '12px'
194 }
195 })), /*#__PURE__*/React.createElement(PanelBody, {
196 title: __('Slider Settings', 'frontblocks'),
197 initialOpen: false
198 }, /*#__PURE__*/React.createElement(RangeControl, {
199 label: __('Initial Handle Position (%)', 'frontblocks'),
200 value: initialPosition,
201 onChange: function onChange(value) {
202 return setAttributes({
203 initialPosition: value
204 });
205 },
206 min: 0,
207 max: 100
208 }), /*#__PURE__*/React.createElement(ToggleControl, {
209 label: __('Fixed Height', 'frontblocks'),
210 checked: fixedHeight,
211 onChange: function onChange(value) {
212 return setAttributes({
213 fixedHeight: value
214 });
215 }
216 }), fixedHeight && /*#__PURE__*/React.createElement(RangeControl, {
217 label: __('Height (px)', 'frontblocks'),
218 value: blockHeight,
219 onChange: function onChange(value) {
220 return setAttributes({
221 blockHeight: value
222 });
223 },
224 min: 100,
225 max: 1000,
226 step: 10
227 }))), /*#__PURE__*/React.createElement("div", blockProps, !hasImages ? /*#__PURE__*/React.createElement(Placeholder, {
228 icon: "image-flip-horizontal",
229 label: __('Before / After', 'frontblocks'),
230 instructions: __('Select a "before" image and an "after" image from the sidebar.', 'frontblocks')
231 }) : /*#__PURE__*/React.createElement("div", {
232 ref: containerRef,
233 className: 'frbl-before-after frbl-before-after--editor' + (fixedHeight ? ' frbl-before-after--fixed-height' : ''),
234 "data-initial-position": initialPosition,
235 style: fixedHeight && blockHeight ? {
236 height: blockHeight + 'px'
237 } : {}
238 }, /*#__PURE__*/React.createElement("div", {
239 className: "frbl-before-after__after"
240 }, /*#__PURE__*/React.createElement("img", {
241 src: afterImageUrl,
242 alt: ""
243 }), afterLabel && /*#__PURE__*/React.createElement("span", {
244 className: "frbl-before-after__label frbl-before-after__label--after"
245 }, afterLabel)), /*#__PURE__*/React.createElement("div", {
246 className: "frbl-before-after__before",
247 style: {
248 clipPath: "inset(0 ".concat(100 - initialPosition, "% 0 0)")
249 }
250 }, /*#__PURE__*/React.createElement("img", {
251 src: beforeImageUrl,
252 alt: ""
253 }), beforeLabel && /*#__PURE__*/React.createElement("span", {
254 className: "frbl-before-after__label frbl-before-after__label--before"
255 }, beforeLabel)), /*#__PURE__*/React.createElement("div", {
256 className: "frbl-before-after__handle",
257 style: {
258 left: "".concat(initialPosition, "%")
259 }
260 }, /*#__PURE__*/React.createElement("span", {
261 className: "frbl-before-after__handle-line"
262 }), /*#__PURE__*/React.createElement("span", {
263 className: "frbl-before-after__handle-thumb"
264 }, /*#__PURE__*/React.createElement("svg", {
265 viewBox: "0 0 20 20",
266 width: "18",
267 height: "18",
268 fill: "currentColor",
269 "aria-hidden": "true"
270 }, /*#__PURE__*/React.createElement("path", {
271 d: "M7 4l-4 6 4 6M13 4l4 6-4 6",
272 stroke: "currentColor",
273 strokeWidth: "2",
274 fill: "none",
275 strokeLinecap: "round",
276 strokeLinejoin: "round"
277 }))), /*#__PURE__*/React.createElement("span", {
278 className: "frbl-before-after__handle-line"
279 })))));
280 }
281
282 // Register the block.
283 registerBlockType('frontblocks/before-after', {
284 title: __('FrontBlocks - Before After', 'frontblocks'),
285 description: __('Compare two images with a draggable before/after slider.', 'frontblocks'),
286 category: 'media',
287 icon: 'image-flip-horizontal',
288 keywords: [__('before', 'frontblocks'), __('after', 'frontblocks'), __('comparison', 'frontblocks'), __('slider', 'frontblocks')],
289 attributes: {
290 beforeImageId: {
291 type: 'integer'
292 },
293 beforeImageUrl: {
294 type: 'string',
295 default: ''
296 },
297 afterImageId: {
298 type: 'integer'
299 },
300 afterImageUrl: {
301 type: 'string',
302 default: ''
303 },
304 beforeLabel: {
305 type: 'string',
306 default: 'Before'
307 },
308 afterLabel: {
309 type: 'string',
310 default: 'After'
311 },
312 initialPosition: {
313 type: 'number',
314 default: 50
315 },
316 fixedHeight: {
317 type: 'boolean',
318 default: false
319 },
320 blockHeight: {
321 type: 'number',
322 default: 400
323 }
324 },
325 edit: BeforeAfterEdit,
326 save: function save() {
327 return null; // Dynamic block, rendered server-side.
328 }
329 });
330