PluginProbe
Block Animations, Motion & Scroll Effects – Ghost Kit / 3.3.3
Block Animations, Motion & Scroll Effects – Ghost Kit v3.3.3
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.3.3, at gutenberg/blocks/image-compare/edit.js

720 lines 18.1 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 />
224 <ToggleGroupControl
225 label={__('Direction', 'ghostkit')}
226 onChange={(val) =>
227 setAttributes({ direction: val })
228 }
229 value={direction || ''}
230 isBlock
231 >
232 <ToggleGroupControlOption
233 value=""
234 label={__('Horizontal', 'ghostkit')}
235 />
236 <ToggleGroupControlOption
237 value="vertical"
238 label={__('Vertical', 'ghostkit')}
239 />
240 </ToggleGroupControl>
241 <ToggleGroupControl
242 label={__('Trigger', 'ghostkit')}
243 onChange={(val) => setAttributes({ trigger: val })}
244 value={trigger || ''}
245 isBlock
246 >
247 <ToggleGroupControlOption
248 value=""
249 label={__('Click', 'ghostkit')}
250 />
251 <ToggleGroupControlOption
252 value="hover"
253 label={__('Hover', 'ghostkit')}
254 />
255 </ToggleGroupControl>
256 </PanelBody>
257 ) : null}
258
259 <PanelBody title={__('Labels', 'ghostkit')}>
260 <ToggleControl
261 label={__('Show Labels', 'ghostkit')}
262 checked={!!showLabels}
263 onChange={(value) =>
264 setAttributes({ showLabels: value })
265 }
266 />
267 {showLabels && (
268 <BaseControl
269 id={baseControlLabel}
270 label={baseControlLabel}
271 >
272 <div>
273 <Toolbar
274 label={
275 direction === 'vertical'
276 ? __('Horizontal Align', 'ghostkit')
277 : __('Vertical Align', 'ghostkit')
278 }
279 >
280 <ToolbarButton
281 icon={iconStart}
282 title={__('Start', 'ghostkit')}
283 onClick={() =>
284 setAttributes({
285 labelAlign: 'start',
286 })
287 }
288 isActive={labelAlign === 'start'}
289 />
290 <ToolbarButton
291 icon={iconCenter}
292 title={__('Center', 'ghostkit')}
293 onClick={() =>
294 setAttributes({
295 labelAlign: 'center',
296 })
297 }
298 isActive={labelAlign === 'center'}
299 />
300 <ToolbarButton
301 icon={iconEnd}
302 title={__('End', 'ghostkit')}
303 onClick={() =>
304 setAttributes({ labelAlign: 'end' })
305 }
306 isActive={labelAlign === 'end'}
307 />
308 </Toolbar>
309 </div>
310 </BaseControl>
311 )}
312 </PanelBody>
313
314 <PanelBody title={__('Before Image Settings', 'ghostkit')}>
315 {!beforeId ? (
316 <MediaUpload
317 onSelect={(media) => {
318 updateImageData('before', media);
319 }}
320 allowedTypes={['image']}
321 value={beforeId}
322 render={({ open }) => (
323 <Button onClick={open} isPrimary>
324 {__('Select Image', 'ghostkit')}
325 </Button>
326 )}
327 />
328 ) : null}
329
330 {beforeId ? (
331 <>
332 <MediaUpload
333 onSelect={(media) => {
334 updateImageData('before', media);
335 }}
336 allowedTypes={['image']}
337 value={beforeId}
338 render={({ open }) => (
339 <BaseControl
340 help={__(
341 'Click the image to edit or update',
342 'ghostkit'
343 )}
344 >
345 {/* eslint-disable-next-line jsx-a11y/control-has-associated-label, jsx-a11y/anchor-is-valid */}
346 <a
347 href="#"
348 onClick={open}
349 className="ghostkit-gutenberg-media-upload"
350 style={{ display: 'block' }}
351 >
352 <img
353 src={beforeUrl}
354 alt={beforeAlt}
355 width={beforeWidth}
356 height={beforeHeight}
357 />
358 </a>
359 </BaseControl>
360 )}
361 />
362 <div style={{ marginTop: -20 }} />
363 <Button
364 isLink
365 onClick={(e) => {
366 setAttributes({
367 beforeId: '',
368 beforeUrl: '',
369 beforeAlt: '',
370 beforeWidth: '',
371 beforeHeight: '',
372 });
373 e.preventDefault();
374 }}
375 className="button button-secondary"
376 >
377 {__('Remove Image', 'ghostkit')}
378 </Button>
379 <div style={{ marginBottom: 13 }} />
380 {editorSettings && editorSettings.imageSizes ? (
381 <SelectControl
382 label={__('Resolution', 'ghostkit')}
383 help={__(
384 'Select the size of the source image.',
385 'ghostkit'
386 )}
387 value={beforeSizeSlug || DEFAULT_SIZE_SLUG}
388 onChange={(val) => {
389 updateImageData(
390 'before',
391 beforeImage,
392 val
393 );
394 }}
395 options={editorSettings.imageSizes.map(
396 (imgSize) => ({
397 value: imgSize.slug,
398 label: imgSize.name,
399 })
400 )}
401 />
402 ) : null}
403 <TextareaControl
404 label={__('Alt text (alternative text)')}
405 value={beforeAlt}
406 onChange={(val) =>
407 setAttributes({ beforeAlt: val })
408 }
409 help={
410 <>
411 <ExternalLink href="https://www.w3.org/WAI/tutorials/images/decision-tree">
412 {__(
413 'Describe the purpose of the image',
414 'ghostkit'
415 )}
416 </ExternalLink>
417 {__(
418 'Leave empty if the image is purely decorative.',
419 'ghostkit'
420 )}
421 </>
422 }
423 />
424 </>
425 ) : null}
426 </PanelBody>
427 <PanelBody title={__('After Image Settings', 'ghostkit')}>
428 {!afterId ? (
429 <MediaUpload
430 onSelect={(media) => {
431 updateImageData('after', media);
432 }}
433 allowedTypes={['image']}
434 value={afterId}
435 render={({ open }) => (
436 <Button onClick={open} isPrimary>
437 {__('Select Image', 'ghostkit')}
438 </Button>
439 )}
440 />
441 ) : null}
442
443 {afterId ? (
444 <>
445 <MediaUpload
446 onSelect={(media) => {
447 updateImageData('after', media);
448 }}
449 allowedTypes={['image']}
450 value={afterId}
451 render={({ open }) => (
452 <BaseControl
453 help={__(
454 'Click the image to edit or update',
455 'ghostkit'
456 )}
457 >
458 {/* eslint-disable-next-line jsx-a11y/control-has-associated-label, jsx-a11y/anchor-is-valid */}
459 <a
460 href="#"
461 onClick={open}
462 className="ghostkit-gutenberg-media-upload"
463 style={{ display: 'block' }}
464 >
465 <img
466 src={afterUrl}
467 alt={afterAlt}
468 width={afterWidth}
469 height={afterHeight}
470 />
471 </a>
472 </BaseControl>
473 )}
474 />
475 <div style={{ marginTop: -20 }} />
476 <Button
477 isLink
478 onClick={(e) => {
479 setAttributes({
480 afterId: '',
481 afterUrl: '',
482 afterAlt: '',
483 afterWidth: '',
484 afterHeight: '',
485 });
486 e.preventDefault();
487 }}
488 className="button button-secondary"
489 >
490 {__('Remove Image', 'ghostkit')}
491 </Button>
492 <div style={{ marginBottom: 13 }} />
493 {editorSettings && editorSettings.imageSizes ? (
494 <SelectControl
495 label={__('Resolution', 'ghostkit')}
496 help={__(
497 'Select the size of the source image.',
498 'ghostkit'
499 )}
500 value={afterSizeSlug || DEFAULT_SIZE_SLUG}
501 onChange={(val) => {
502 updateImageData(
503 'after',
504 afterImage,
505 val
506 );
507 }}
508 options={editorSettings.imageSizes.map(
509 (imgSize) => ({
510 value: imgSize.slug,
511 label: imgSize.name,
512 })
513 )}
514 />
515 ) : null}
516 <TextareaControl
517 label={__('Alt text (alternative text)')}
518 value={afterAlt}
519 onChange={(val) =>
520 setAttributes({ afterAlt: val })
521 }
522 help={
523 <>
524 <ExternalLink href="https://www.w3.org/WAI/tutorials/images/decision-tree">
525 {__(
526 'Describe the purpose of the image',
527 'ghostkit'
528 )}
529 </ExternalLink>
530 {__(
531 'Leave empty if the image is purely decorative.',
532 'ghostkit'
533 )}
534 </>
535 }
536 />
537 </>
538 ) : null}
539 </PanelBody>
540 </InspectorControls>
541
542 <InspectorControls group="styles">
543 <PanelBody title={__('Color', 'ghostkit')}>
544 <ColorPicker
545 label={__('Divider', 'ghostkit')}
546 value={colorDivider}
547 onChange={(val) => setAttributes({ colorDivider: val })}
548 alpha
549 />
550 <ColorPicker
551 label={__('Divider Icon', 'ghostkit')}
552 value={colorDividerIcon}
553 onChange={(val) =>
554 setAttributes({ colorDividerIcon: val })
555 }
556 alpha
557 />
558 </PanelBody>
559 </InspectorControls>
560
561 {!beforeUrl || !afterUrl ? (
562 <div {...blockProps}>
563 <Placeholder
564 className="ghostkit-image-compare-placeholder"
565 icon={getIcon('block-image-compare')}
566 label={__('Image Compare', 'ghostkit')}
567 instructions={__(
568 'Select images to compare',
569 'ghostkit'
570 )}
571 >
572 {getImgTag('before') ? (
573 <div className="components-placeholder">
574 {getImgTag('before')}
575 </div>
576 ) : (
577 <MediaPlaceholder
578 icon="format-image"
579 labels={{
580 title: __('Image Before', 'ghostkit'),
581 name: __('image', 'ghostkit'),
582 }}
583 onSelect={(image) => {
584 updateImageData('before', image);
585 }}
586 accept="image/*"
587 allowedTypes={ALLOWED_MEDIA_TYPES}
588 disableMaxUploadErrorMessages
589 onError={onUploadError}
590 />
591 )}
592 {getImgTag('after') ? (
593 <div className="components-placeholder">
594 {getImgTag('after')}
595 </div>
596 ) : (
597 <MediaPlaceholder
598 icon="format-image"
599 labels={{
600 title: __('Image After', 'ghostkit'),
601 name: __('image', 'ghostkit'),
602 }}
603 value={
604 afterUrl
605 ? {
606 src: afterUrl,
607 }
608 : false
609 }
610 onSelect={(image) => {
611 updateImageData('after', image);
612 }}
613 accept="image/*"
614 allowedTypes={ALLOWED_MEDIA_TYPES}
615 disableMaxUploadErrorMessages
616 onError={onUploadError}
617 />
618 )}
619 </Placeholder>
620 </div>
621 ) : (
622 <figure {...blockProps}>
623 <div className="ghostkit-image-compare-images">
624 <div className="ghostkit-image-compare-image-before">
625 {getImgTag('before')}
626 {showLabels &&
627 (!RichText.isEmpty(labelBeforeText) ||
628 isSelected) ? (
629 <div className="ghostkit-image-compare-image-label ghostkit-image-compare-image-before-label">
630 <RichText
631 inlineToolbar
632 tagName="div"
633 onChange={(val) =>
634 setAttributes({
635 labelBeforeText: val,
636 })
637 }
638 value={labelBeforeText}
639 placeholder={__(
640 'Before label…',
641 'ghostkit'
642 )}
643 />
644 </div>
645 ) : null}
646 </div>
647 <div className="ghostkit-image-compare-image-after">
648 {getImgTag('after')}
649 {showLabels &&
650 (!RichText.isEmpty(labelAfterText) ||
651 isSelected) ? (
652 <div className="ghostkit-image-compare-image-label ghostkit-image-compare-image-after-label">
653 <RichText
654 inlineToolbar
655 tagName="div"
656 onChange={(val) =>
657 setAttributes({
658 labelAfterText: val,
659 })
660 }
661 value={labelAfterText}
662 placeholder={__(
663 'After label…',
664 'ghostkit'
665 )}
666 />
667 </div>
668 ) : null}
669 </div>
670 <div className="ghostkit-image-compare-images-divider">
671 <div className="ghostkit-image-compare-images-divider-button-arrow-left">
672 <svg
673 className="ghostkit-svg-icon"
674 width="24"
675 height="24"
676 viewBox="0 0 24 24"
677 fill="none"
678 xmlns="http://www.w3.org/2000/svg"
679 >
680 <path
681 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"
682 fill="currentColor"
683 />
684 </svg>
685 </div>
686 <div className="ghostkit-image-compare-images-divider-button-arrow-right">
687 <svg
688 className="ghostkit-svg-icon"
689 width="24"
690 height="24"
691 viewBox="0 0 24 24"
692 fill="none"
693 xmlns="http://www.w3.org/2000/svg"
694 >
695 <path
696 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"
697 fill="currentColor"
698 />
699 </svg>
700 </div>
701 </div>
702 </div>
703 {(!RichText.isEmpty(caption) || isSelected) && (
704 <RichText
705 inlineToolbar
706 className="ghostkit-image-compare-caption"
707 onChange={(value) =>
708 setAttributes({ caption: value })
709 }
710 placeholder={__('Write caption…', 'jetpack')}
711 tagName="figcaption"
712 value={caption}
713 />
714 )}
715 </figure>
716 )}
717 </>
718 );
719 }
720