diff options
Diffstat (limited to 'plugins/jetpack/extensions/blocks/tiled-gallery/edit.js')
-rw-r--r-- | plugins/jetpack/extensions/blocks/tiled-gallery/edit.js | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/plugins/jetpack/extensions/blocks/tiled-gallery/edit.js b/plugins/jetpack/extensions/blocks/tiled-gallery/edit.js new file mode 100644 index 00000000..45574252 --- /dev/null +++ b/plugins/jetpack/extensions/blocks/tiled-gallery/edit.js @@ -0,0 +1,289 @@ +/** + * External Dependencies + */ +import { __ } from '@wordpress/i18n'; +import { Component, Fragment } from '@wordpress/element'; +import { filter, get, pick } from 'lodash'; +import { + BlockControls, + BlockIcon, + InspectorControls, + MediaPlaceholder, + MediaUpload, + mediaUpload, +} from '@wordpress/editor'; +import { + DropZone, + FormFileUpload, + IconButton, + PanelBody, + RangeControl, + SelectControl, + Toolbar, + withNotices, +} from '@wordpress/components'; + +/** + * Internal dependencies + */ +import FilterToolbar from './filter-toolbar'; +import Layout from './layout'; +import { ALLOWED_MEDIA_TYPES, LAYOUT_STYLES, MAX_COLUMNS } from './constants'; +import { getActiveStyleName } from '../../shared/block-styles'; +import { icon } from '.'; + +const linkOptions = [ + { value: 'attachment', label: __( 'Attachment Page', 'jetpack' ) }, + { value: 'media', label: __( 'Media File', 'jetpack' ) }, + { value: 'none', label: __( 'None', 'jetpack' ) }, +]; + +// @TODO keep here or move to ./layout ? +function layoutSupportsColumns( layout ) { + return [ 'columns', 'circle', 'square' ].includes( layout ); +} + +export function defaultColumnsNumber( attributes ) { + return Math.min( 3, attributes.images.length ); +} + +export const pickRelevantMediaFiles = image => { + const imageProps = pick( image, [ [ 'alt' ], [ 'id' ], [ 'link' ] ] ); + imageProps.url = + get( image, [ 'sizes', 'large', 'url' ] ) || + get( image, [ 'media_details', 'sizes', 'large', 'source_url' ] ) || + image.url; + return imageProps; +}; + +class TiledGalleryEdit extends Component { + state = { + selectedImage: null, + }; + + static getDerivedStateFromProps( props, state ) { + // Deselect images when deselecting the block + if ( ! props.isSelected && null !== state.selectedImage ) { + return { selectedImage: null }; + } + return null; + } + + setAttributes( attributes ) { + if ( attributes.ids ) { + throw new Error( + 'The "ids" attribute should not be changed directly. It is managed automatically when "images" attribute changes' + ); + } + + if ( attributes.images ) { + attributes = { + ...attributes, + ids: attributes.images.map( ( { id } ) => parseInt( id, 10 ) ), + }; + } + + this.props.setAttributes( attributes ); + } + + addFiles = files => { + const currentImages = this.props.attributes.images || []; + const { noticeOperations } = this.props; + mediaUpload( { + allowedTypes: ALLOWED_MEDIA_TYPES, + filesList: files, + onFileChange: images => { + const imagesNormalized = images.map( image => pickRelevantMediaFiles( image ) ); + this.setAttributes( { images: currentImages.concat( imagesNormalized ) } ); + }, + onError: noticeOperations.createErrorNotice, + } ); + }; + + onRemoveImage = index => () => { + const images = filter( this.props.attributes.images, ( img, i ) => index !== i ); + const { columns } = this.props.attributes; + this.setState( { + selectedImage: null, + } ); + this.setAttributes( { + images, + columns: columns ? Math.min( images.length, columns ) : columns, + } ); + }; + + onSelectImage = index => () => { + if ( this.state.selectedImage !== index ) { + this.setState( { + selectedImage: index, + } ); + } + }; + + onSelectImages = images => { + const { columns } = this.props.attributes; + this.setAttributes( { + columns: columns ? Math.min( images.length, columns ) : columns, + images: images.map( image => pickRelevantMediaFiles( image ) ), + } ); + }; + + setColumnsNumber = value => this.setAttributes( { columns: value } ); + + setImageAttributes = index => attributes => { + const { + attributes: { images }, + } = this.props; + if ( ! images[ index ] ) { + return; + } + this.setAttributes( { + images: [ + ...images.slice( 0, index ), + { ...images[ index ], ...attributes }, + ...images.slice( index + 1 ), + ], + } ); + }; + + setLinkTo = value => this.setAttributes( { linkTo: value } ); + + uploadFromFiles = event => this.addFiles( event.target.files ); + + render() { + const { selectedImage } = this.state; + const { + attributes, + isSelected, + className, + noticeOperations, + noticeUI, + setAttributes, + } = this.props; + const { + align, + columns = defaultColumnsNumber( attributes ), + imageFilter, + images, + linkTo, + } = attributes; + + const dropZone = <DropZone onFilesDrop={ this.addFiles } />; + + const controls = ( + <BlockControls> + { !! images.length && ( + <Fragment> + <Toolbar> + <MediaUpload + onSelect={ this.onSelectImages } + allowedTypes={ ALLOWED_MEDIA_TYPES } + multiple + gallery + value={ images.map( img => img.id ) } + render={ ( { open } ) => ( + <IconButton + className="components-toolbar__control" + label={ __( 'Edit Gallery', 'jetpack' ) } + icon="edit" + onClick={ open } + /> + ) } + /> + </Toolbar> + <FilterToolbar + value={ imageFilter } + onChange={ value => { + setAttributes( { imageFilter: value } ); + this.setState( { selectedImage: null } ); + } } + /> + </Fragment> + ) } + </BlockControls> + ); + + if ( images.length === 0 ) { + return ( + <Fragment> + { controls } + <MediaPlaceholder + icon={ <BlockIcon icon={ icon } /> } + className={ className } + labels={ { + title: __( 'Tiled Gallery', 'jetpack' ), + name: __( 'images', 'jetpack' ), + } } + onSelect={ this.onSelectImages } + accept="image/*" + allowedTypes={ ALLOWED_MEDIA_TYPES } + multiple + notices={ noticeUI } + onError={ noticeOperations.createErrorNotice } + /> + </Fragment> + ); + } + + const layoutStyle = getActiveStyleName( LAYOUT_STYLES, attributes.className ); + + return ( + <Fragment> + { controls } + <InspectorControls> + <PanelBody title={ __( 'Tiled Gallery settings', 'jetpack' ) }> + { layoutSupportsColumns( layoutStyle ) && images.length > 1 && ( + <RangeControl + label={ __( 'Columns', 'jetpack' ) } + value={ columns } + onChange={ this.setColumnsNumber } + min={ 1 } + max={ Math.min( MAX_COLUMNS, images.length ) } + /> + ) } + <SelectControl + label={ __( 'Link To', 'jetpack' ) } + value={ linkTo } + onChange={ this.setLinkTo } + options={ linkOptions } + /> + </PanelBody> + </InspectorControls> + + { noticeUI } + + <Layout + align={ align } + className={ className } + columns={ columns } + imageFilter={ imageFilter } + images={ images } + layoutStyle={ layoutStyle } + linkTo={ linkTo } + onRemoveImage={ this.onRemoveImage } + onSelectImage={ this.onSelectImage } + selectedImage={ isSelected ? selectedImage : null } + setImageAttributes={ this.setImageAttributes } + > + { dropZone } + { isSelected && ( + <div className="tiled-gallery__add-item"> + <FormFileUpload + multiple + isLarge + className="tiled-gallery__add-item-button" + onChange={ this.uploadFromFiles } + accept="image/*" + icon="insert" + > + { __( 'Upload an image', 'jetpack' ) } + </FormFileUpload> + </div> + ) } + </Layout> + </Fragment> + ); + } +} + +export default withNotices( TiledGalleryEdit ); |