/** * External dependencies */ import ReactSelect from 'react-select'; /** * Internal dependencies */ import './style.scss'; import SchemeWrapper from '../scheme-wrapper'; import ResponsiveWrapper from '../responsive-wrapper'; import DimensionControl from '../dimension-control'; import CategoriesSelectorControl from '../categories-selector-control'; import TagsSelectorControl from '../tags-selector-control'; import PostsSelectorControl from '../posts-selector-control'; import QueryControl from '../query-control'; import isFieldVisible from '../../utils/is-field-visible'; /** * WordPress dependencies */ const { __, sprintf, } = wp.i18n; const { Component, Fragment, RawHTML, } = wp.element; const { BaseControl, ToggleControl, TextControl, TextareaControl, RangeControl, SelectControl, PanelBody, Notice, DropZone, Button, Toolbar, } = wp.components; const { ColorPalette, MediaPlaceholder, MediaUpload, mediaUpload, } = wp.blockEditor; const { applyFilters, } = wp.hooks; /** * Component */ export default class ComponentFieldsRender extends Component { constructor() { super( ...arguments ); this.getAllFieldsSections = this.getAllFieldsSections.bind( this ); this.getFieldValue = this.getFieldValue.bind( this ); this.updateFieldValue = this.updateFieldValue.bind( this ); this.renderControl = this.renderControl.bind( this ); } /** * Get all available sections. * * @returns {Object} sections. */ getAllFieldsSections() { const { fields = [], } = this.props; const sections = { ...{ '': '' }, ...this.props.sections, }; // check all fields and add section if not defined. fields.forEach( ( field ) => { if ( field.section && typeof sections[ field.section ] === 'undefined' ) { sections[ field.section ] = field.section; } } ); return sections; } /** * Get current field value. If value doesn't exist, use default value. * * @param {Object} fieldData field data. * @param {String} suffix attribute name suffix. * * @returns {Mixed} field value. */ getFieldValue( fieldData, suffix = '' ) { const { attributes = {}, } = this.props; if ( typeof attributes[ fieldData.key + suffix ] !== 'undefined' ) { return attributes[ fieldData.key + suffix ]; } else if ( typeof fieldData[ 'default' + suffix ] !== 'undefined' ) { return fieldData[ 'default' + suffix ]; } return null; } /** * Update current field value. * * @param {Object} fieldData field data. * @param {Mixed} val field value. * @param {String} suffix attribute name suffix. */ updateFieldValue( fieldData, val, suffix = '' ) { const { onChange, } = this.props; onChange( fieldData.key + suffix, val ); } /** * Render control * * @param {Object} fieldData field data. * * @returns {JSX} */ renderControl( fieldData ) { const renderName = `renderControl${ fieldData.type.replace( /(\b\w)|(-.)/g, ( x ) => ( x[1] || x[0] ).toUpperCase() ) }`; // check if render method exist. if (this[renderName]) { return ( {({ schemeSuffix, ComponentSchemeDropdown }) => { return ( {({ responsiveSuffix, ComponentResponsiveDropdown }) => { let fieldSuffix = ''; let newFieldData = { ...fieldData }; // Scheme dropdown. if (canvasSchemes && ('color' === newFieldData.type)) { fieldSuffix += schemeSuffix; newFieldData = { ...newFieldData, label: ( {newFieldData.label || ''} ) }; } // Responsive dropdown. if (newFieldData.responsive) { fieldSuffix += responsiveSuffix; newFieldData = { ...newFieldData, label: ( {newFieldData.label || ''} ) }; } return this[renderName]( newFieldData, this.getFieldValue(newFieldData, fieldSuffix), (val) => { this.updateFieldValue(newFieldData, val, fieldSuffix); } ); }} ) }} ); } // render method does not exist. return ( { sprintf( __( 'Unfortunately, `%s` method doesn\'t exist.' ), renderName ) } ); } /** * Render Separator control * * @param {Object} fieldData field data. * @param {String} val field value. * @param {Object} onChange field on change callback. * * @returns {JSX} */ renderControlSeparator( fieldData, val, onChange ) { return (
); } /** * Render Text control * * @param {Object} fieldData field data. * @param {String} val field value. * @param {Object} onChange field on change callback. * * @returns {JSX} */ renderControlText( fieldData, val, onChange ) { return ( { fieldData.help || '' } } value={ val } onChange={ onChange } /> ); } /** * Render Textarea control * * @param {Object} fieldData field data. * @param {String} val field value. * @param {Object} onChange field on change callback. * * @returns {JSX} */ renderControlTextarea( fieldData, val, onChange ) { return ( ); } /** * Render Toggle control * * @param {Object} fieldData field data. * @param {Boolean} val field value. * @param {Object} onChange field on change callback. * * @returns {JSX} */ renderControlToggle( fieldData, val, onChange ) { return ( ); } /** * Render Toggle List control * * @param {Object} fieldData field data. * @param {Object} val field value. * @param {Object} onChange field on change callback. * * @returns {JSX} */ renderControlToggleList( fieldData, val, onChange ) { return (

{ Object.keys( val ).map( ( valName ) => { return ( { const result = Object.assign( {}, val ); result[ valName ] = ! result[ valName ]; onChange( result ); } } /> ); } ) } ); } /** * Render Dimension control * * @param {Object} fieldData field data. * @param {String} val field value. * @param {Object} onChange field on change callback. * * @returns {JSX} */ renderControlDimension( fieldData, val, onChange ) { return ( ); } /** * Render Number control * * @param {Object} fieldData field data. * @param {Number} val field value. * @param {Object} onChange field on change callback. * * @returns {JSX} */ renderControlNumber( fieldData, val, onChange ) { return ( ); } /** * Render Icon Buttons control * * @param {Object} fieldData field data. * @param {String} val field value. * @param {Object} onChange field on change callback. * * @returns {JSX} */ renderControlIconButtons( fieldData, val, onChange ) { return ( { return { icon: { fieldData.choices[ option ] }, isActive: val === option, onClick() { onChange( val === option ? '' : option ); }, }; } ) } /> ); } /** * Render Select control * * @param {Object} fieldData field data. * @param {String} val field value. * @param {Object} onChange field on change callback. * * @returns {JSX} */ renderControlSelect( fieldData, val, onChange ) { const options = Object.keys( fieldData.choices ).map( ( option ) => { return { label: fieldData.choices[ option ], value: option, }; } ); return ( { onChange( val ); } } /> ); } /** * Render React Select control * * @param {Object} fieldData field data. * @param {String} val field value. * @param {Object} onChange field on change callback. * * @returns {JSX} */ renderControlReactSelect( fieldData, val, onChange ) { const options = Object.keys( fieldData.choices ).map( ( option ) => { return { label: fieldData.choices[ option ], value: option, }; } ); return ( { if ( fieldData.multiple ) { if ( ! Array.isArray( val ) ) { val = []; } // options const result = val.map( ( val ) => { return { value: val, label: fieldData.choices[ val ] || val, }; } ); return result; } return val; } )() } onChange={ ( val ) => { if ( fieldData.multiple ) { if ( val ) { const result = val.map( ( opt ) => { return opt.value; } ); onChange( result ); } else { onChange( [] ); } } else { onChange( val ); } } } /> ); } /** * Render Color Picker control * * @param {Object} fieldData field data. * @param {String} val field value. * @param {Object} onChange field on change callback. * * @returns {JSX} */ renderControlColor( fieldData, val, onChange ) { let result = ( ); if ( fieldData.label || fieldsData.help ) { return ( { result } ); } return result; } /** * Render Image control * * @param {Object} fieldData field data. * @param {Number} val field value. * @param {Object} onChange field on change callback. * * @returns {JSX} */ renderControlImage( fieldData, val = {}, onChange ) { const { id = 0, url = '', } = val; return ( { ! id ? ( { onChange( { id: image.id, url: image.url, } ); } } accept="image/*" allowedTypes={ [ 'image' ] } disableMaxUploadErrorMessages onError={ ( e ) => { console.log( e ); } } /> ) : '' } { url ? ( { mediaUpload( { allowedTypes: [ 'image' ], filesList: files, onFileChange: ( image ) => { onChange( { id: image.id, url: image.url, } ); }, onError( e ) { console.log( e ); }, } ); } } /> { url ? ( ) : '' }

) : '' }
); } /** * Render Gallery control * * @param {Object} fieldData field data. * @param {Number} val field value. * @param {Object} onChange field on change callback. * * @returns {JSX} */ renderControlGallery( fieldData, val = [], onChange ) { const ALLOWED_MEDIA_TYPES = [ 'image' ]; return ( { ! val || ! val.length ? ( { const result = images.map( ( image ) => { return image.id; } ); onChange( result ); } } accept="image/*" allowedTypes={ ALLOWED_MEDIA_TYPES } disableMaxUploadErrorMessages multiple onError={ ( e ) => { // eslint-disable-next-line no-console console.log( e ); } } /> ) : '' } { val && val.length ? ( { const result = images.map( ( image ) => { return image.id; } ); onChange( result ); } } allowedTypes={ ALLOWED_MEDIA_TYPES } multiple gallery value={ val } render={ ( { open } ) => (
{ const currentImages = val || []; mediaUpload( { allowedTypes: ALLOWED_MEDIA_TYPES, filesList: files, onFileChange: ( images ) => { const result = images.map( ( image ) => { return image.id; } ); onChange( currentImages.concat( result ) ); }, onError( e ) { // eslint-disable-next-line no-console console.log( e ); }, } ); } } /> { val ? (
{val.map(imageId => { return ( ) })}
) : '' }
) } /> ) : '' }
); } /** * Render Categories Selector control * * @param {Object} fieldData field data. * @param {String} val field value. * @param {Object} onChange field on change callback. * * @returns {JSX} */ renderControlCategoriesSelector( fieldData, val, onChange ) { return ( ); } /** * Render Tags Selector control * * @param {Object} fieldData field data. * @param {String} val field value. * @param {Object} onChange field on change callback. * * @returns {JSX} */ renderControlTagsSelector( fieldData, val, onChange ) { return ( ); } /** * Render Posts Selector control * * @param {Object} fieldData field data. * @param {String} val field value. * @param {Object} onChange field on change callback. * * @returns {JSX} */ renderControlPostsSelector( fieldData, val, onChange ) { return ( ); } /** * Render Query control * * @param {Object} fieldData field data. * @param {String} val field value. * @param {Object} onChange field on change callback. * * @returns {JSX} */ renderControlQuery( fieldData, val, onChange ) { return ( ); } render() { const { fields = [], attributes, } = this.props; const sections = this.getAllFieldsSections(); return ( { Object.keys( sections ).map( ( sectionName ) => { const sectionTitle = sections[ sectionName ].title; const initialOpen = sections[ sectionName ].open || ( sectionTitle ? false : true ); if ( ! fields || ! fields.length ) { return ''; } const sectionFields = fields .filter( ( fieldData ) => { if ( ! fieldData || ! fieldData.type ) { return false; } // prevent invisible fields, that used only for registering block attributes. if ( 'type-string' === fieldData.type || 'type-number' === fieldData.type || 'type-boolean' === fieldData.type || 'type-array' === fieldData.type ) { return false; } // limit fields for current section only. if ( sectionName && fieldData.section !== sectionName ) { return false; } else if ( ! sectionName && fieldData.section ) { return false; } // check active_callback return isFieldVisible( fieldData, attributes, fields ); }) .map( ( fieldData, i ) => { let fieldKey = `field-${ fieldData.type }-${ i }`; return ( applyFilters( 'canvas.component.fieldsRender.singleField', ( { this.renderControl( fieldData ) } ), { fieldData, props: this.props, } ) ); } ); if ( ! sectionFields || ! sectionFields.length ) { return ''; } return ( { applyFilters( 'canvas.component.fieldsRender', sectionFields, { sectionName, sectionTitle, props: this.props, } ) } ); } ) } ); } }