PluginProbe
Block Animations, Motion & Scroll Effects – Ghost Kit / 3.4.1
Block Animations, Motion & Scroll Effects – Ghost Kit v3.4.1
3.7.2 3.7.1 3.7.0 3.6.1 trunk 1.6.3 2.25.0 3.3.0 3.3.1 3.3.2 3.3.3 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6 3.5.0 3.5.1 3.6.0
ghostkit / gutenberg / blocks / image-compare / edit.js

edit.js in Block Animations, Motion & Scroll Effects – Ghost Kit 3.4.1, at gutenberg/blocks/image-compare/edit.js

736 lines 18.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import classnames from 'classnames/dedupe';
2
3 import {
4 BlockControls,
5 InspectorControls,
6 MediaPlaceholder,
7 MediaUpload,
8 RichText,
9 useBlockProps,
10 } from '@wordpress/block-editor';
11 import {
12 __experimentalToggleGroupControl as ToggleGroupControl,
13 __experimentalToggleGroupControlOption as ToggleGroupControlOption,
14 BaseControl,
15 Button,
16 ExternalLink,
17 PanelBody,
18 Placeholder,
19 SelectControl,
20 TextareaControl,
21 ToggleControl,
22 Toolbar,
23 ToolbarButton,
24 ToolbarGroup,
25 } from '@wordpress/components';
26 import { useSelect } from '@wordpress/data';
27 import { __ } from '@wordpress/i18n';
28
29 import ColorPicker from '../../components/color-picker';
30 import RangeControl from '../../components/range-control';
31 import getIcon from '../../utils/get-icon';
32
33 const ALLOWED_MEDIA_TYPES = ['image'];
34 const DEFAULT_SIZE_SLUG = 'large';
35
36 /**
37 * Block Edit Class.
38 *
39 * @param props
40 */
41 export default function BlockEdit(props) {
42 const { attributes, isSelected, setAttributes } = props;
43 let { className } = props;
44
45 const {
46 position,
47 direction,
48 trigger,
49 caption,
50
51 showLabels,
52 labelBeforeText,
53 labelAfterText,
54 labelAlign,
55
56 beforeId,
57 beforeUrl,
58 beforeAlt,
59 beforeWidth,
60 beforeHeight,
61 beforeSizeSlug,
62
63 afterId,
64 afterUrl,
65 afterAlt,
66 afterWidth,
67 afterHeight,
68 afterSizeSlug,
69
70 colorDivider,
71 colorDividerIcon,
72 } = attributes;
73
74 const { editorSettings, beforeImage, afterImage } = useSelect((select) => {
75 const { getSettings } = select('core/block-editor');
76 const { getMedia } = select('core');
77
78 return {
79 editorSettings: getSettings(),
80 beforeImage: beforeId && isSelected ? getMedia(beforeId) : null,
81 afterImage: afterId && isSelected ? getMedia(afterId) : null,
82 };
83 });
84
85 const onUploadError = (message) => {
86 const { noticeOperations } = props;
87 noticeOperations.removeAllNotices();
88 noticeOperations.createErrorNotice(message);
89 };
90
91 const getImgTag = (type = 'before') => {
92 return attributes[`${type}Url`] ? (
93 <img
94 src={attributes[`${type}Url`]}
95 alt={attributes[`${type}Alt`]}
96 className={
97 attributes[`${type}Id`]
98 ? `wp-image-${attributes[`${type}Id`]}`
99 : null
100 }
101 width={attributes[`${type}Width`]}
102 height={attributes[`${type}Height`]}
103 />
104 ) : null;
105 };
106
107 const updateImageData = (
108 type = 'before',
109 imageData = {},
110 imageSize = false
111 ) => {
112 imageSize =
113 imageSize || attributes[`${type}SizeSlug`] || DEFAULT_SIZE_SLUG;
114
115 // Prepare full image data.
116 const result = {
117 [`${type}SizeSlug`]: imageSize,
118 [`${type}Id`]: imageData.id,
119 [`${type}Url`]: imageData.url || imageData.source_url,
120 [`${type}Alt`]: imageData.alt || imageData.alt_text,
121 [`${type}Width`]:
122 imageData.width ||
123 (imageData.media_details && imageData.media_details.width
124 ? imageData.media_details.width
125 : undefined),
126 [`${type}Height`]:
127 imageData.height ||
128 (imageData.media_details && imageData.media_details.height
129 ? imageData.media_details.height
130 : undefined),
131 };
132
133 let sizes = imageData.sizes && imageData.sizes[imageSize];
134
135 if (
136 !sizes &&
137 imageData.media_details &&
138 imageData.media_details.sizes &&
139 imageData.media_details.sizes[imageSize]
140 ) {
141 sizes = imageData.media_details.sizes[imageSize];
142 }
143
144 // Prepare image data for selected size.
145 if (sizes) {
146 if (sizes.url) {
147 result[`${type}Url`] = sizes.url;
148 }
149 if (sizes.source_url) {
150 result[`${type}Url`] = sizes.source_url;
151 }
152 if (sizes.width) {
153 result[`${type}Width`] = sizes.width;
154 }
155 if (sizes.height) {
156 result[`${type}Height`] = sizes.height;
157 }
158 }
159
160 setAttributes(result);
161 };
162
163 const iconStart =
164 direction === 'vertical'
165 ? getIcon('icon-horizontal-start')
166 : getIcon('icon-vertical-top');
167 const iconCenter =
168 direction === 'vertical'
169 ? getIcon('icon-horizontal-center')
170 : getIcon('icon-vertical-center');
171 const iconEnd =
172 direction === 'vertical'
173 ? getIcon('icon-horizontal-end')
174 : getIcon('icon-vertical-bottom');
175
176 className = classnames(
177 'ghostkit-image-compare',
178 direction === 'vertical' ? 'ghostkit-image-compare-vertical' : false,
179 showLabels && labelAlign
180 ? `ghostkit-image-compare-labels-align-${labelAlign}`
181 : false,
182 className
183 );
184
185 const blockProps = useBlockProps({ className });
186
187 const baseControlLabel =
188 direction === 'vertical'
189 ? __('Horizontal Align', 'ghostkit')
190 : __('Vertical Align', 'ghostkit');
191
192 return (
193 <>
194 {beforeUrl && afterUrl ? (
195 <BlockControls>
196 <ToolbarGroup>
197 <ToolbarButton
198 icon={getIcon('icon-flip-vertical')}
199 title={__('Vertical', 'ghostkit')}
200 onClick={() =>
201 setAttributes({
202 direction:
203 direction === 'vertical'
204 ? ''
205 : 'vertical',
206 })
207 }
208 isActive={direction === 'vertical'}
209 />
210 </ToolbarGroup>
211 </BlockControls>
212 ) : null}
213
214 <InspectorControls>
215 {beforeUrl && afterUrl ? (
216 <PanelBody title={__('General', 'ghostkit')}>
217 <RangeControl
218 label={__('Start Position', 'ghostkit')}
219 value={position}
220 min={0}
221 max={100}
222 onChange={(val) => setAttributes({ position: val })}
223 __next40pxDefaultSize
224 __nextHasNoMarginBottom
225 />
226 <ToggleGroupControl
227 label={__('Direction', 'ghostkit')}
228 onChange={(val) =>
229 setAttributes({ direction: val })
230 }
231 value={direction || ''}
232 isBlock
233 __next40pxDefaultSize
234 __nextHasNoMarginBottom
235 >
236 <ToggleGroupControlOption
237 value=""
238 label={__('Horizontal', 'ghostkit')}
239 />
240 <ToggleGroupControlOption
241 value="vertical"
242 label={__('Vertical', 'ghostkit')}
243 />
244 </ToggleGroupControl>
245 <ToggleGroupControl
246 label={__('Trigger', 'ghostkit')}
247 onChange={(val) => setAttributes({ trigger: val })}
248 value={trigger || ''}
249 isBlock
250 __next40pxDefaultSize
251 __nextHasNoMarginBottom
252 >
253 <ToggleGroupControlOption
254 value=""
255 label={__('Click', 'ghostkit')}
256 />
257 <ToggleGroupControlOption
258 value="hover"
259 label={__('Hover', 'ghostkit')}
260 />
261 </ToggleGroupControl>
262 </PanelBody>
263 ) : null}
264
265 <PanelBody title={__('Labels', 'ghostkit')}>
266 <ToggleControl
267 label={__('Show Labels', 'ghostkit')}
268 checked={!!showLabels}
269 onChange={(value) =>
270 setAttributes({ showLabels: value })
271 }
272 __nextHasNoMarginBottom
273 />
274 {showLabels && (
275 <BaseControl
276 id={baseControlLabel}
277 label={baseControlLabel}
278 __nextHasNoMarginBottom
279 >
280 <div>
281 <Toolbar
282 label={
283 direction === 'vertical'
284 ? __('Horizontal Align', 'ghostkit')
285 : __('Vertical Align', 'ghostkit')
286 }
287 >
288 <ToolbarButton
289 icon={iconStart}
290 title={__('Start', 'ghostkit')}
291 onClick={() =>
292 setAttributes({
293 labelAlign: 'start',
294 })
295 }
296 isActive={labelAlign === 'start'}
297 />
298 <ToolbarButton
299 icon={iconCenter}
300 title={__('Center', 'ghostkit')}
301 onClick={() =>
302 setAttributes({
303 labelAlign: 'center',
304 })
305 }
306 isActive={labelAlign === 'center'}
307 />
308 <ToolbarButton
309 icon={iconEnd}
310 title={__('End', 'ghostkit')}
311 onClick={() =>
312 setAttributes({ labelAlign: 'end' })
313 }
314 isActive={labelAlign === 'end'}
315 />
316 </Toolbar>
317 </div>
318 </BaseControl>
319 )}
320 </PanelBody>
321
322 <PanelBody title={__('Before Image Settings', 'ghostkit')}>
323 {!beforeId ? (
324 <MediaUpload
325 onSelect={(media) => {
326 updateImageData('before', media);
327 }}
328 allowedTypes={['image']}
329 value={beforeId}
330 render={({ open }) => (
331 <Button onClick={open} variant="primary">
332 {__('Select Image', 'ghostkit')}
333 </Button>
334 )}
335 />
336 ) : null}
337
338 {beforeId ? (
339 <>
340 <MediaUpload
341 onSelect={(media) => {
342 updateImageData('before', media);
343 }}
344 allowedTypes={['image']}
345 value={beforeId}
346 render={({ open }) => (
347 <BaseControl
348 help={__(
349 'Click the image to edit or update',
350 'ghostkit'
351 )}
352 __nextHasNoMarginBottom
353 >
354 {/* eslint-disable-next-line jsx-a11y/control-has-associated-label, jsx-a11y/anchor-is-valid */}
355 <a
356 href="#"
357 onClick={open}
358 className="ghostkit-gutenberg-media-upload"
359 style={{ display: 'block' }}
360 >
361 <img
362 src={beforeUrl}
363 alt={beforeAlt}
364 width={beforeWidth}
365 height={beforeHeight}
366 />
367 </a>
368 </BaseControl>
369 )}
370 />
371 <div style={{ marginTop: -20 }} />
372 <Button
373 isLink
374 onClick={(e) => {
375 setAttributes({
376 beforeId: '',
377 beforeUrl: '',
378 beforeAlt: '',
379 beforeWidth: '',
380 beforeHeight: '',
381 });
382 e.preventDefault();
383 }}
384 className="button button-secondary"
385 >
386 {__('Remove Image', 'ghostkit')}
387 </Button>
388 <div style={{ marginBottom: 13 }} />
389 {editorSettings && editorSettings.imageSizes ? (
390 <SelectControl
391 label={__('Resolution', 'ghostkit')}
392 help={__(
393 'Select the size of the source image.',
394 'ghostkit'
395 )}
396 value={beforeSizeSlug || DEFAULT_SIZE_SLUG}
397 onChange={(val) => {
398 updateImageData(
399 'before',
400 beforeImage,
401 val
402 );
403 }}
404 options={editorSettings.imageSizes.map(
405 (imgSize) => ({
406 value: imgSize.slug,
407 label: imgSize.name,
408 })
409 )}
410 __next40pxDefaultSize
411 __nextHasNoMarginBottom
412 />
413 ) : null}
414 <TextareaControl
415 label={__('Alt text (alternative text)')}
416 value={beforeAlt}
417 onChange={(val) =>
418 setAttributes({ beforeAlt: val })
419 }
420 help={
421 <>
422 <ExternalLink href="https://www.w3.org/WAI/tutorials/images/decision-tree">
423 {__(
424 'Describe the purpose of the image',
425 'ghostkit'
426 )}
427 </ExternalLink>
428 {__(
429 'Leave empty if the image is purely decorative.',
430 'ghostkit'
431 )}
432 </>
433 }
434 __nextHasNoMarginBottom
435 />
436 </>
437 ) : null}
438 </PanelBody>
439 <PanelBody title={__('After Image Settings', 'ghostkit')}>
440 {!afterId ? (
441 <MediaUpload
442 onSelect={(media) => {
443 updateImageData('after', media);
444 }}
445 allowedTypes={['image']}
446 value={afterId}
447 render={({ open }) => (
448 <Button onClick={open} variant="primary">
449 {__('Select Image', 'ghostkit')}
450 </Button>
451 )}
452 />
453 ) : null}
454
455 {afterId ? (
456 <>
457 <MediaUpload
458 onSelect={(media) => {
459 updateImageData('after', media);
460 }}
461 allowedTypes={['image']}
462 value={afterId}
463 render={({ open }) => (
464 <BaseControl
465 help={__(
466 'Click the image to edit or update',
467 'ghostkit'
468 )}
469 __nextHasNoMarginBottom
470 >
471 {/* eslint-disable-next-line jsx-a11y/control-has-associated-label, jsx-a11y/anchor-is-valid */}
472 <a
473 href="#"
474 onClick={open}
475 className="ghostkit-gutenberg-media-upload"
476 style={{ display: 'block' }}
477 >
478 <img
479 src={afterUrl}
480 alt={afterAlt}
481 width={afterWidth}
482 height={afterHeight}
483 />
484 </a>
485 </BaseControl>
486 )}
487 />
488 <div style={{ marginTop: -20 }} />
489 <Button
490 isLink
491 onClick={(e) => {
492 setAttributes({
493 afterId: '',
494 afterUrl: '',
495 afterAlt: '',
496 afterWidth: '',
497 afterHeight: '',
498 });
499 e.preventDefault();
500 }}
501 className="button button-secondary"
502 >
503 {__('Remove Image', 'ghostkit')}
504 </Button>
505 <div style={{ marginBottom: 13 }} />
506 {editorSettings && editorSettings.imageSizes ? (
507 <SelectControl
508 label={__('Resolution', 'ghostkit')}
509 help={__(
510 'Select the size of the source image.',
511 'ghostkit'
512 )}
513 value={afterSizeSlug || DEFAULT_SIZE_SLUG}
514 onChange={(val) => {
515 updateImageData(
516 'after',
517 afterImage,
518 val
519 );
520 }}
521 options={editorSettings.imageSizes.map(
522 (imgSize) => ({
523 value: imgSize.slug,
524 label: imgSize.name,
525 })
526 )}
527 __next40pxDefaultSize
528 __nextHasNoMarginBottom
529 />
530 ) : null}
531 <TextareaControl
532 label={__('Alt text (alternative text)')}
533 value={afterAlt}
534 onChange={(val) =>
535 setAttributes({ afterAlt: val })
536 }
537 help={
538 <>
539 <ExternalLink href="https://www.w3.org/WAI/tutorials/images/decision-tree">
540 {__(
541 'Describe the purpose of the image',
542 'ghostkit'
543 )}
544 </ExternalLink>
545 {__(
546 'Leave empty if the image is purely decorative.',
547 'ghostkit'
548 )}
549 </>
550 }
551 __nextHasNoMarginBottom
552 />
553 </>
554 ) : null}
555 </PanelBody>
556 </InspectorControls>
557
558 <InspectorControls group="styles">
559 <PanelBody title={__('Color', 'ghostkit')}>
560 <ColorPicker
561 label={__('Divider', 'ghostkit')}
562 value={colorDivider}
563 onChange={(val) => setAttributes({ colorDivider: val })}
564 alpha
565 />
566 <ColorPicker
567 label={__('Divider Icon', 'ghostkit')}
568 value={colorDividerIcon}
569 onChange={(val) =>
570 setAttributes({ colorDividerIcon: val })
571 }
572 alpha
573 />
574 </PanelBody>
575 </InspectorControls>
576
577 {!beforeUrl || !afterUrl ? (
578 <div {...blockProps}>
579 <Placeholder
580 className="ghostkit-image-compare-placeholder"
581 icon={getIcon('block-image-compare')}
582 label={__('Image Compare', 'ghostkit')}
583 instructions={__(
584 'Select images to compare',
585 'ghostkit'
586 )}
587 >
588 {getImgTag('before') ? (
589 <div className="components-placeholder">
590 {getImgTag('before')}
591 </div>
592 ) : (
593 <MediaPlaceholder
594 icon="format-image"
595 labels={{
596 title: __('Image Before', 'ghostkit'),
597 name: __('image', 'ghostkit'),
598 }}
599 onSelect={(image) => {
600 updateImageData('before', image);
601 }}
602 accept="image/*"
603 allowedTypes={ALLOWED_MEDIA_TYPES}
604 disableMaxUploadErrorMessages
605 onError={onUploadError}
606 />
607 )}
608 {getImgTag('after') ? (
609 <div className="components-placeholder">
610 {getImgTag('after')}
611 </div>
612 ) : (
613 <MediaPlaceholder
614 icon="format-image"
615 labels={{
616 title: __('Image After', 'ghostkit'),
617 name: __('image', 'ghostkit'),
618 }}
619 value={
620 afterUrl
621 ? {
622 src: afterUrl,
623 }
624 : false
625 }
626 onSelect={(image) => {
627 updateImageData('after', image);
628 }}
629 accept="image/*"
630 allowedTypes={ALLOWED_MEDIA_TYPES}
631 disableMaxUploadErrorMessages
632 onError={onUploadError}
633 />
634 )}
635 </Placeholder>
636 </div>
637 ) : (
638 <figure {...blockProps}>
639 <div className="ghostkit-image-compare-images">
640 <div className="ghostkit-image-compare-image-before">
641 {getImgTag('before')}
642 {showLabels &&
643 (!RichText.isEmpty(labelBeforeText) ||
644 isSelected) ? (
645 <div className="ghostkit-image-compare-image-label ghostkit-image-compare-image-before-label">
646 <RichText
647 inlineToolbar
648 tagName="div"
649 onChange={(val) =>
650 setAttributes({
651 labelBeforeText: val,
652 })
653 }
654 value={labelBeforeText}
655 placeholder={__(
656 'Before label…',
657 'ghostkit'
658 )}
659 />
660 </div>
661 ) : null}
662 </div>
663 <div className="ghostkit-image-compare-image-after">
664 {getImgTag('after')}
665 {showLabels &&
666 (!RichText.isEmpty(labelAfterText) ||
667 isSelected) ? (
668 <div className="ghostkit-image-compare-image-label ghostkit-image-compare-image-after-label">
669 <RichText
670 inlineToolbar
671 tagName="div"
672 onChange={(val) =>
673 setAttributes({
674 labelAfterText: val,
675 })
676 }
677 value={labelAfterText}
678 placeholder={__(
679 'After label…',
680 'ghostkit'
681 )}
682 />
683 </div>
684 ) : null}
685 </div>
686 <div className="ghostkit-image-compare-images-divider">
687 <div className="ghostkit-image-compare-images-divider-button-arrow-left">
688 <svg
689 className="ghostkit-svg-icon"
690 width="24"
691 height="24"
692 viewBox="0 0 24 24"
693 fill="none"
694 xmlns="http://www.w3.org/2000/svg"
695 >
696 <path
697 d="M14.7803 17.7803C14.4874 18.0732 14.0126 18.0732 13.7197 17.7803L8.4697 12.5303C8.1768 12.2374 8.1768 11.7626 8.4697 11.4697L13.7197 6.21967C14.0126 5.92678 14.4874 5.92678 14.7803 6.21967C15.0732 6.51256 15.0732 6.98744 14.7803 7.28033L10.0607 12L14.7803 16.7197C15.0732 17.0126 15.0732 17.4874 14.7803 17.7803Z"
698 fill="currentColor"
699 />
700 </svg>
701 </div>
702 <div className="ghostkit-image-compare-images-divider-button-arrow-right">
703 <svg
704 className="ghostkit-svg-icon"
705 width="24"
706 height="24"
707 viewBox="0 0 24 24"
708 fill="none"
709 xmlns="http://www.w3.org/2000/svg"
710 >
711 <path
712 d="M9.21967 6.2197C9.51256 5.9268 9.98744 5.9268 10.2803 6.2197L15.5303 11.4697C15.8232 11.7626 15.8232 12.2374 15.5303 12.5303L10.2803 17.7803C9.98744 18.0732 9.51256 18.0732 9.21967 17.7803C8.92678 17.4874 8.92678 17.0126 9.21967 16.7197L13.9393 12L9.21967 7.2803C8.92678 6.9874 8.92678 6.5126 9.21967 6.2197Z"
713 fill="currentColor"
714 />
715 </svg>
716 </div>
717 </div>
718 </div>
719 {(!RichText.isEmpty(caption) || isSelected) && (
720 <RichText
721 inlineToolbar
722 className="ghostkit-image-compare-caption"
723 onChange={(value) =>
724 setAttributes({ caption: value })
725 }
726 placeholder={__('Write caption…', 'jetpack')}
727 tagName="figcaption"
728 value={caption}
729 />
730 )}
731 </figure>
732 )}
733 </>
734 );
735 }
736