/**
 * Custom Fields — native Gutenberg document sidebar.
 */
( function ( wp, config ) {
	'use strict';

	if ( ! wp || ! wp.plugins || ! wp.element || ! wp.data || ! wp.components || ! wp.i18n || ! config ) {
		return;
	}

	var createElement = wp.element.createElement;
	var Fragment = wp.element.Fragment;
	var useState = wp.element.useState;
	var useEffect = wp.element.useEffect;
	var useMemo = wp.element.useMemo;
	var PluginDocumentSettingPanel = wp.editor && wp.editor.PluginDocumentSettingPanel
		? wp.editor.PluginDocumentSettingPanel
		: wp.editPost && wp.editPost.PluginDocumentSettingPanel;
	var useSelect = wp.data.useSelect;
	var useDispatch = wp.data.useDispatch;
	var __ = wp.i18n.__;
	var TextControl = wp.components.TextControl;
	var TextareaControl = wp.components.TextareaControl;
	var ToggleControl = wp.components.ToggleControl;
	var SelectControl = wp.components.SelectControl;
	var CheckboxControl = wp.components.CheckboxControl;
	var RadioControl = wp.components.RadioControl;
	var Button = wp.components.Button;
	var Notice = wp.components.Notice;
	var BaseControl = wp.components.BaseControl;
	var ComboboxControl = wp.components.ComboboxControl;
	var RawHTML = wp.element.RawHTML;
	var useRef = wp.element.useRef;

	if ( ! PluginDocumentSettingPanel ) {
		return;
	}

	var strings = config.strings || {};
	var initialSchema = config.schema || { groups: [] };

	function usePostMeta() {
		var meta = useSelect( function ( select ) {
			var editor = select( 'core/editor' );
			return editor ? editor.getEditedPostAttribute( 'meta' ) || {} : {};
		}, [] );

		var editPost = useDispatch( 'core/editor' ).editPost;

		function setMetaKey( key, value ) {
			editPost( {
				meta: Object.assign( {}, meta, ( function () {
					var patch = {};
					patch[ key ] = value;
					return patch;
				}() ) )
			} );
		}

		return { meta: meta, setMetaKey: setMetaKey };
	}

	function getMetaValue( meta, field ) {
		if ( Object.prototype.hasOwnProperty.call( meta, field.metaKey ) ) {
			return meta[ field.metaKey ];
		}
		return field.value;
	}

	function stringifyValue( value ) {
		if ( Array.isArray( value ) ) {
			return value.map( function ( item ) {
				return String( item == null ? '' : item ).trim();
			} ).filter( Boolean );
		}
		if ( value && typeof value === 'object' ) {
			return JSON.stringify( value );
		}
		if ( value === true || value === '1' ) {
			return '1';
		}
		if ( value === false || value === '0' ) {
			return '0';
		}
		return value == null ? '' : String( value );
	}

	function escapeHtml( value ) {
		return String( value == null ? '' : value )
			.replace( /&/g, '&amp;' )
			.replace( /</g, '&lt;' )
			.replace( />/g, '&gt;' )
			.replace( /"/g, '&quot;' )
			.replace( /'/g, '&#039;' );
	}

	function formatMessageHtml( field, fallback ) {
		var message = field.message || fallback || '';
		var html = field.messageEscapeHtml ? escapeHtml( message ) : message;
		var newLines = field.messageNewLines || '';

		if ( newLines === 'wpautop' ) {
			html = html
				.split( /\n\s*\n/ )
				.map( function ( paragraph ) {
					return '<p>' + paragraph.replace( /\n/g, '<br />' ) + '</p>';
				} )
				.join( '' );
		} else if ( newLines === 'br' ) {
			html = html.replace( /\n/g, '<br />' );
		}

		return html;
	}

	function compare( actual, operator, expected ) {
		expected = String( expected || '' ).trim();

		if ( Array.isArray( actual ) ) {
			var items = actual.map( function ( item ) {
				return String( item == null ? '' : item ).trim();
			} ).filter( Boolean );

			switch ( operator ) {
				case '!=':
					return items.indexOf( expected ) === -1;
				case 'contains':
					return expected !== '' && items.indexOf( expected ) !== -1;
				case 'not_contains':
					return expected === '' || items.indexOf( expected ) === -1;
				case 'empty':
					return items.length === 0;
				case 'not_empty':
					return items.length > 0;
				case '==':
					return items.indexOf( expected ) !== -1;
				default:
					actual = items.join( ',' );
					break;
			}
		}

		actual = String( actual == null ? '' : actual ).trim();
		var a = parseFloat( actual );
		var b = parseFloat( expected );
		var numeric = ! isNaN( a ) && ! isNaN( b );
		var actualItems = actual.split( ',' ).map( function ( item ) {
			return item.trim();
		} );

		function containsExpected() {
			if ( expected === '' ) {
				return false;
			}

			return actualItems.length > 1
				? actualItems.indexOf( String( expected ) ) !== -1
				: String( actual ).indexOf( expected ) !== -1;
		}

		switch ( operator ) {
			case '!=':
				return actual !== expected;
			case '>':
				return numeric && a > b;
			case '>=':
				return numeric && a >= b;
			case '<':
				return numeric && a < b;
			case '<=':
				return numeric && a <= b;
			case 'contains':
				return containsExpected();
			case 'not_contains':
				return expected === '' || ! containsExpected();
			case 'empty':
				return actual === '' || actual === '0';
			case 'not_empty':
				return actual !== '' && actual !== '0';
			case '==':
			default:
				return actual === expected;
		}
	}

	function matchesConditional( conditional, values ) {
		if ( ! conditional || ! conditional.enabled || ! conditional.rules || ! conditional.rules.length ) {
			return true;
		}
		var results = conditional.rules.map( function ( rule ) {
			var actual = Object.prototype.hasOwnProperty.call( values, rule.field ) ? values[ rule.field ] : '';
			return compare( actual, rule.operator || '==', rule.value || '' );
		} );
		return conditional.match === 'any'
			? results.indexOf( true ) !== -1
			: results.indexOf( false ) === -1;
	}

	function buildFieldValues( group, meta ) {
		var values = {};
		( group.fields || [] ).forEach( function ( field ) {
			if ( ! field.key ) {
				return;
			}
			values[ field.key ] = stringifyValue( getMetaValue( meta, field ) );
		} );
		return values;
	}

	function openMediaFrame( args, onSelect ) {
		if ( ! wp.media ) {
			return;
		}
		var frame = wp.media(
			Object.assign(
				{
					title: strings.selectImage || 'Select',
					button: { text: strings.selectImage || 'Select' },
					multiple: false
				},
				args
			)
		);
		frame.on( 'select', function () {
			var selection = frame.state().get( 'selection' );
			onSelect( selection );
		} );
		frame.open();
	}

	function normalizeMediaMimeTypes( raw ) {
		var extensionMap = {
			jpg: 'image/jpeg',
			jpeg: 'image/jpeg',
			png: 'image/png',
			gif: 'image/gif',
			webp: 'image/webp',
			svg: 'image/svg+xml',
			pdf: 'application/pdf',
			txt: 'text/plain',
			csv: 'text/csv',
			doc: 'application/msword',
			docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
			xls: 'application/vnd.ms-excel',
			xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
			zip: 'application/zip',
			mp3: 'audio/mpeg',
			wav: 'audio/wav',
			mp4: 'video/mp4',
			mov: 'video/quicktime'
		};

		return String( raw || '' )
			.split( /[\s,]+/ )
			.map( function ( item ) {
				item = item.trim().toLowerCase().replace( /^\./, '' );
				return extensionMap[ item ] || item;
			} )
			.filter( function ( item ) {
				return item && ( item.indexOf( '/' ) !== -1 || item === 'image' || item === 'audio' || item === 'video' );
			} );
	}

	function imageMimeTypesOnly( types ) {
		var filtered = types.filter( function ( item ) {
			return item === 'image' || item.indexOf( 'image/' ) === 0;
		} );
		return filtered.length ? filtered : [ 'image' ];
	}

	function mapRelOptions( options ) {
		return ( options || [] ).map( function ( option ) {
			return { value: String( option.value ), label: option.label };
		} );
	}

	function searchRelational( field, query, include, runtimeFilters ) {
		if ( ! config.searchUrl || ! config.restNonce ) {
			return Promise.resolve( [] );
		}

		var type = field.type;
		var endpoint = '';
		var params = new URLSearchParams();
		params.set( 's', query );
		params.set( 'group_key', field.groupKey || '' );
		params.set( 'field_key', field.key || field.name || '' );
		if ( include ) {
			params.set( 'include', include );
		}

		if ( type === 'post_object' || type === 'relationship' || type === 'page_link' ) {
			endpoint = 'posts';
			params.set( 'post_types', ( field.postTypes || [] ).join( ',' ) );
			params.set( 'post_statuses', ( field.postStatuses || [] ).join( ',' ) );
			if ( type === 'relationship' && runtimeFilters ) {
				if ( runtimeFilters.postType ) {
					params.set( 'filter_post_type', runtimeFilters.postType );
				}
				if ( runtimeFilters.taxonomy ) {
					params.set( 'filter_taxonomy', runtimeFilters.taxonomy );
				}
			}
		} else if ( type === 'term' ) {
			endpoint = 'terms';
			params.set( 'taxonomy', field.taxonomy || '' );
		} else if ( type === 'user' ) {
			endpoint = 'users';
		} else {
			return Promise.resolve( [] );
		}

		return window.fetch( config.searchUrl + endpoint + '?' + params.toString(), {
			headers: { 'X-WP-Nonce': config.restNonce }
		} )
			.then( function ( response ) {
				return response.json();
			} )
			.then( function ( data ) {
				return mapRelOptions( data.results || [] );
			} )
			.catch( function () {
				return [];
			} );
	}

	function createTerm( field, termName, parent ) {
		if ( ! config.createTermUrl || ! config.restNonce ) {
			return Promise.reject( new Error( 'Term creation is not available.' ) );
		}

		var body = new URLSearchParams();
		body.set( 'group_key', field.groupKey || '' );
		body.set( 'field_key', field.key || field.name || '' );
		body.set( 'term_name', termName );
		body.set( 'term_parent', parent || '0' );

		return window.fetch( config.createTermUrl, {
			method: 'POST',
			headers: {
				'X-WP-Nonce': config.restNonce,
				'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
			},
			body: body.toString()
		} )
			.then( function ( response ) {
				return response.json().then( function ( data ) {
					if ( ! response.ok ) {
						throw new Error( data.message || 'Could not add term.' );
					}
					return data.term;
				} );
			} );
	}

	function RelationalField( props ) {
		var field = props.field;
		var value = props.value;
		var setMetaKey = props.setMetaKey;
		var label = props.label;
		var help = props.help;

		var initialOptions = useMemo( function () {
			var opts = mapRelOptions( field.options );
			return [ { label: '—', value: '' } ].concat(
				opts.filter( function ( option ) {
					return option.value !== '';
				} )
			);
		}, [ field.options ] );

		var optionsState = useState( initialOptions );
		var options = optionsState[0];
		var setOptions = optionsState[1];
		var filterState = useState( '' );
		var filter = filterState[0];
		var setFilter = filterState[1];
		var filterPostTypeState = useState( '' );
		var filterPostType = filterPostTypeState[0];
		var setFilterPostType = filterPostTypeState[1];
		var filterTaxonomyState = useState( '' );
		var filterTaxonomy = filterTaxonomyState[0];
		var setFilterTaxonomy = filterTaxonomyState[1];
		var newTermState = useState( '' );
		var newTerm = newTermState[0];
		var setNewTerm = newTermState[1];
		var parentState = useState( '0' );
		var parent = parentState[0];
		var setParent = parentState[1];
		var parentOptionsState = useState( field.parentTerms || [] );
		var parentOptions = parentOptionsState[0];
		var setParentOptions = parentOptionsState[1];
		var createStatusState = useState( '' );
		var createStatus = createStatusState[0];
		var setCreateStatus = createStatusState[1];
		var creatingState = useState( false );
		var creating = creatingState[0];
		var setCreating = creatingState[1];

		useEffect( function () {
			setOptions( initialOptions );
		}, [ initialOptions, setOptions ] );

		useEffect( function () {
			setParentOptions( field.parentTerms || [] );
		}, [ field.parentTerms, setParentOptions ] );

		useEffect( function () {
			var timer = window.setTimeout( function () {
				var include = Array.isArray( value ) ? value.join( ',' ) : ( value ? String( value ) : '' );
				searchRelational( field, filter, include, {
					postType: filterPostType,
					taxonomy: filterTaxonomy
				} ).then( function ( results ) {
					setOptions( [ { label: '—', value: '' } ].concat( results ) );
				} );
			}, 300 );

			return function () {
				window.clearTimeout( timer );
			};
		}, [ filter, filterPostType, filterTaxonomy, field, value, setOptions ] );

		var relationshipFilterControls = field.type === 'relationship'
			? createElement(
				'div',
				{ className: 'wpps-cf-sidebar-relationship-filters' },
				( field.relationshipFilters || [] ).indexOf( 'post_type' ) !== -1 && ( field.relationshipPostTypeOptions || [] ).length
					? createElement( SelectControl, {
						label: __( 'Post type', 'wppowersuite-lite' ),
						value: filterPostType,
						options: [ { label: __( 'Any post type', 'wppowersuite-lite' ), value: '' } ].concat( field.relationshipPostTypeOptions || [] ),
						onChange: setFilterPostType
					} )
					: null,
				( field.relationshipFilters || [] ).indexOf( 'taxonomy' ) !== -1 && ( field.relationshipTaxonomyOptions || [] ).length
					? createElement( SelectControl, {
						label: __( 'Taxonomy', 'wppowersuite-lite' ),
						value: filterTaxonomy,
						options: [ { label: __( 'All terms', 'wppowersuite-lite' ), value: '' } ].concat( field.relationshipTaxonomyOptions || [] ),
						onChange: setFilterTaxonomy
					} )
					: null
			)
			: null;

		var createTermControl = field.type === 'term' && field.addTerms
			? createElement(
				'div',
				{ className: 'wpps-cf-sidebar-term-create' },
				createElement( TextControl, {
					label: __( 'New term', 'wppowersuite-lite' ),
					value: newTerm,
					onChange: setNewTerm,
					disabled: creating
				} ),
				field.hierarchical
					? createElement( SelectControl, {
						label: __( 'Parent', 'wppowersuite-lite' ),
						value: parent,
						options: [ { label: __( 'No parent', 'wppowersuite-lite' ), value: '0' } ].concat( parentOptions ),
						onChange: setParent,
						disabled: creating
					} )
					: null,
				createElement(
					Button,
					{
						variant: 'secondary',
						isBusy: creating,
						disabled: creating || ! newTerm.trim(),
						onClick: function () {
							var termName = newTerm.trim();
							if ( ! termName ) {
								return;
							}
							setCreating( true );
							setCreateStatus( '' );
							createTerm( field, termName, parent )
								.then( function ( term ) {
									if ( term && term.value ) {
										setOptions( options.concat( mapRelOptions( [ term ] ) ) );
										if ( field.hierarchical ) {
											setParentOptions( parentOptions.concat( mapRelOptions( [ term ] ) ) );
											setParent( '0' );
										}
										if ( field.multiple ) {
											var current = Array.isArray( value ) ? value.map( String ) : ( value ? [ String( value ) ] : [] );
											if ( current.indexOf( String( term.value ) ) === -1 ) {
												current.push( String( term.value ) );
											}
											setMetaKey( field.metaKey, current );
										} else {
											setMetaKey( field.metaKey, parseInt( term.value, 10 ) );
										}
										setNewTerm( '' );
										setCreateStatus( 'Term added.' );
									}
								} )
								.catch( function ( error ) {
									setCreateStatus( error.message || 'Could not add term.' );
								} )
								.finally( function () {
									setCreating( false );
								} );
						}
					},
					__( 'Add term', 'wppowersuite-lite' )
				),
				createStatus
					? createElement( Notice, { status: 'info', isDismissible: false }, createStatus )
					: null
			)
			: null;

		if ( field.multiple ) {
			var selectedValues = Array.isArray( value ) ? value.map( String ) : ( value ? [ String( value ) ] : [] );
			var optionRows = options.filter( function ( option ) {
				return option.value !== '';
			} );

			return createElement(
				Fragment,
				null,
				createElement(
					BaseControl,
					{ label: label, help: help },
					createElement( TextControl, {
						value: filter,
						onChange: setFilter,
						placeholder: strings.search || __( 'Search...', 'wppowersuite-lite' )
					} ),
					relationshipFilterControls,
					optionRows.length
						? optionRows.map( function ( option ) {
							return createElement( CheckboxControl, {
								key: option.value,
								label: option.label,
								checked: selectedValues.indexOf( String( option.value ) ) !== -1,
								onChange: function ( checked ) {
									var next = selectedValues.slice();
									var optionValue = String( option.value );
									if ( checked ) {
										if ( next.indexOf( optionValue ) === -1 ) {
											next.push( optionValue );
										}
									} else {
										next = next.filter( function ( item ) {
											return item !== optionValue;
										} );
									}
									setMetaKey( field.metaKey, next );
								}
							} );
						} )
						: createElement( 'p', { className: 'description' }, __( 'No matches found', 'wppowersuite-lite' ) )
				),
				createTermControl
			);
		}

		return createElement(
			Fragment,
			null,
			createElement( ComboboxControl, {
				label: label,
				help: help,
				value: value == null || value === '' ? null : String( value ),
				options: options,
				onChange: function ( next ) {
					setMetaKey( field.metaKey, field.type === 'page_link' ? ( next || '' ) : ( next ? parseInt( next, 10 ) : 0 ) );
				},
				onFilterValueChange: function ( next ) {
					setFilter( next );
				},
				allowReset: true
			} ),
			createTermControl
		);
	}

	function OembedField( props ) {
		var field = props.field;
		var value = props.value;
		var setMetaKey = props.setMetaKey;
		var label = props.label;
		var help = props.help;
		var previewState = useState( '' );
		var preview = previewState[0];
		var setPreview = previewState[1];
		var loadingState = useState( false );
		var loading = loadingState[0];
		var setLoading = loadingState[1];

		useEffect( function () {
			var url = value == null ? '' : String( value ).trim();
			if ( ! url || ! config.oembedUrl ) {
				setPreview( '' );
				setLoading( false );
				return;
			}

			setLoading( true );
			var params = new URLSearchParams( {
				url: url,
				maxwidth: field.oembedWidth ? String( field.oembedWidth ) : '640'
			} );
			if ( field.oembedHeight ) {
				params.set( 'maxheight', String( field.oembedHeight ) );
			}
			window.fetch( config.oembedUrl + '?' + params.toString() )
				.then( function ( response ) {
					return response.json();
				} )
				.then( function ( data ) {
					setPreview( data && data.html ? data.html : '' );
				} )
				.catch( function () {
					setPreview( '' );
				} )
				.finally( function () {
					setLoading( false );
				} );
		}, [ value, field.oembedWidth, field.oembedHeight, setLoading, setPreview ] );

		return createElement(
			Fragment,
			null,
			createElement( TextControl, {
				label: label,
				help: help,
				type: 'url',
				value: value == null ? '' : String( value ),
				onChange: function ( next ) {
					setMetaKey( field.metaKey, next );
				}
			} ),
			loading
				? createElement(
					'p',
					{ className: 'wpps-cf-gutenberg-oembed-preview is-loading description' },
					strings.oembedLoading || 'Loading preview…'
				)
				: preview
					? createElement( RawHTML, { className: 'wpps-cf-gutenberg-oembed-preview' }, preview )
					: null
		);
	}

	function ColorField( props ) {
		var field = props.field;
		var value = props.value;
		var setMetaKey = props.setMetaKey;
		var label = props.label;
		var help = props.help;
		var inputRef = useRef( null );

		useEffect( function () {
			var $input = window.jQuery && inputRef.current ? window.jQuery( inputRef.current ) : null;
			if ( ! $input || ! $input.length || typeof $input.wpColorPicker !== 'function' ) {
				return;
			}

			if ( $input.hasClass( 'wp-color-picker' ) ) {
				return;
			}

			var palettes = String( field.colorPalettes || '' ).split( ',' ).map( function ( color ) {
				return color.trim();
			} ).filter( Boolean );
			var pickerOptions = {
				defaultColor: '',
				change: function ( event, ui ) {
					setMetaKey( field.metaKey, ui.color.toString() );
				},
				clear: function () {
					setMetaKey( field.metaKey, '' );
				}
			};
			if ( palettes.length ) {
				pickerOptions.palettes = palettes;
			}
			if ( field.colorAlpha ) {
				pickerOptions.alpha = true;
			}

			$input.wpColorPicker( pickerOptions );
			if ( field.colorShowWheel === false ) {
				$input.closest( '.wp-picker-container' ).addClass( 'cf-color-picker--no-wheel' );
			}

			return function () {
				if ( $input.hasClass( 'wp-color-picker' ) ) {
					try {
						$input.wpColorPicker( 'destroy' );
					} catch ( error ) {
						// Ignore teardown errors when the panel unmounts quickly.
					}
				}
			};
		}, [ field.metaKey, field.colorPalettes, field.colorAlpha, field.colorShowWheel, setMetaKey ] );

		useEffect( function () {
			var $input = window.jQuery && inputRef.current ? window.jQuery( inputRef.current ) : null;
			if ( ! $input || ! $input.hasClass( 'wp-color-picker' ) ) {
				return;
			}

			var current = $input.wpColorPicker( 'color' );
			if ( String( current ) !== String( value || '' ) ) {
				$input.wpColorPicker( 'color', value || '' );
			}
		}, [ value ] );

		return createElement(
			BaseControl,
			{ label: label, help: help, className: 'wpps-cf-gutenberg-color' },
			createElement( 'input', {
				ref: inputRef,
				type: 'text',
				className: 'cf-color-field',
				defaultValue: value == null ? '' : String( value )
			} )
		);
	}

	function GalleryField( props ) {
		var field = props.field;
		var value = props.value;
		var setMetaKey = props.setMetaKey;
		var label = props.label;
		var help = props.help;
		var mimeTypes = imageMimeTypesOnly( normalizeMediaMimeTypes( field.mimeTypes ) );
		var ids = Array.isArray( value )
			? value.map( function ( id ) {
				return parseInt( id, 10 );
			} ).filter( Boolean )
			: [];

		var attachments = useSelect( function ( select ) {
			var core = select( 'core' );
			if ( ! core || typeof core.getMedia !== 'function' ) {
				return [];
			}

			return ids.map( function ( id ) {
				return core.getMedia( id );
			} ).filter( Boolean );
		}, [ ids.join( ',' ) ] );

		return createElement(
			BaseControl,
			{ label: label, help: help, className: 'wpps-cf-gutenberg-gallery' },
			attachments.length
				? createElement(
					'div',
					{ className: 'wpps-cf-gutenberg-gallery__grid' },
					attachments.map( function ( attachment ) {
						var thumb = attachment.media_details
							&& attachment.media_details.sizes
							&& attachment.media_details.sizes.thumbnail
							? attachment.media_details.sizes.thumbnail.source_url
							: attachment.source_url;

						return createElement(
							'div',
							{ key: attachment.id, className: 'wpps-cf-gutenberg-gallery__item' },
							createElement( 'img', { src: thumb, alt: attachment.alt_text || '' } ),
							createElement(
								Button,
								{
									variant: 'link',
									isDestructive: true,
									onClick: function () {
										setMetaKey(
											field.metaKey,
											ids.filter( function ( item ) {
												return item !== attachment.id;
											} )
										);
									}
								},
								strings.remove || 'Remove'
							)
						);
					} )
				)
				: createElement( 'p', { className: 'description' }, '—' ),
			createElement(
				Button,
				{
					variant: 'secondary',
					onClick: function () {
						openMediaFrame(
							{ multiple: true, library: { type: mimeTypes.length ? mimeTypes : 'image' } },
							function ( selection ) {
								var next = ids.slice();
								selection.each( function ( item ) {
									var id = item.get( 'id' );
									if ( next.indexOf( id ) === -1 ) {
										next.push( id );
									}
								} );
								setMetaKey( field.metaKey, next );
							}
						);
					}
				},
				strings.selectImage || 'Select images'
			),
			ids.length
				? createElement(
					Button,
					{
						variant: 'link',
						isDestructive: true,
						onClick: function () {
							setMetaKey( field.metaKey, [] );
						}
					},
					strings.clearGallery || 'Clear all'
				)
				: null
		);
	}

	function iconChoices() {
		return [
			{ value: '', label: __( 'No icon', 'wppowersuite-lite' ) },
			{ value: 'dashicons-admin-post', label: __( 'Post', 'wppowersuite-lite' ) },
			{ value: 'dashicons-admin-page', label: __( 'Page', 'wppowersuite-lite' ) },
			{ value: 'dashicons-admin-media', label: __( 'Media', 'wppowersuite-lite' ) },
			{ value: 'dashicons-admin-links', label: __( 'Link', 'wppowersuite-lite' ) },
			{ value: 'dashicons-admin-users', label: __( 'Users', 'wppowersuite-lite' ) },
			{ value: 'dashicons-admin-settings', label: __( 'Settings', 'wppowersuite-lite' ) },
			{ value: 'dashicons-art', label: __( 'Art', 'wppowersuite-lite' ) },
			{ value: 'dashicons-awards', label: __( 'Award', 'wppowersuite-lite' ) },
			{ value: 'dashicons-book', label: __( 'Book', 'wppowersuite-lite' ) },
			{ value: 'dashicons-calendar', label: __( 'Calendar', 'wppowersuite-lite' ) },
			{ value: 'dashicons-camera', label: __( 'Camera', 'wppowersuite-lite' ) },
			{ value: 'dashicons-cart', label: __( 'Cart', 'wppowersuite-lite' ) },
			{ value: 'dashicons-chart-bar', label: __( 'Chart', 'wppowersuite-lite' ) },
			{ value: 'dashicons-email-alt', label: __( 'Email', 'wppowersuite-lite' ) },
			{ value: 'dashicons-heart', label: __( 'Heart', 'wppowersuite-lite' ) },
			{ value: 'dashicons-location', label: __( 'Location', 'wppowersuite-lite' ) },
			{ value: 'dashicons-marker', label: __( 'Marker', 'wppowersuite-lite' ) },
			{ value: 'dashicons-phone', label: __( 'Phone', 'wppowersuite-lite' ) },
			{ value: 'dashicons-star-filled', label: __( 'Star', 'wppowersuite-lite' ) },
			{ value: 'dashicons-store', label: __( 'Store', 'wppowersuite-lite' ) },
			{ value: 'dashicons-tag', label: __( 'Tag', 'wppowersuite-lite' ) },
			{ value: 'dashicons-video-alt3', label: __( 'Video', 'wppowersuite-lite' ) },
			{ value: 'dashicons-visibility', label: __( 'Visibility', 'wppowersuite-lite' ) }
		];
	}

	function IconField( props ) {
		var field = props.field;
		var value = props.value == null ? '' : String( props.value );
		var setMetaKey = props.setMetaKey;
		var tabs = Array.isArray( field.iconTabs ) && field.iconTabs.length ? field.iconTabs : [ 'dashicons' ];
		var activeDefault = /^https?:\/\//i.test( value ) && tabs.indexOf( 'url' ) !== -1 ? 'url' : tabs[0];
		var activeTabState = useState( activeDefault );
		var activeTab = activeTabState[0];
		var setActiveTab = activeTabState[1];
		var isUrl = /^https?:\/\//i.test( value );

		return createElement(
			BaseControl,
			{ label: props.label, help: props.help },
			createElement(
				'div',
				{ className: 'wpps-cf-gutenberg-icon' },
				tabs.length > 1
					? createElement(
						'div',
						{ className: 'wpps-cf-gutenberg-icon__tabs' },
						tabs.map( function ( tab ) {
							var label = tab === 'media_library' ? __( 'Media', 'wppowersuite-lite' ) : ( tab === 'url' ? 'URL' : __( 'Dashicons', 'wppowersuite-lite' ) );
							return createElement(
								Button,
								{
									key: tab,
									variant: activeTab === tab ? 'primary' : 'secondary',
									onClick: function () {
										setActiveTab( tab );
									}
								},
								label
							);
						} )
					)
					: null,
				activeTab === 'dashicons'
					? createElement( SelectControl, {
						label: __( 'Dashicon', 'wppowersuite-lite' ),
						value: isUrl ? '' : value,
						options: iconChoices(),
						onChange: function ( next ) {
							setMetaKey( field.metaKey, next );
						}
					} )
					: null,
				activeTab === 'media_library'
					? createElement(
						Button,
						{
							variant: 'secondary',
							onClick: function () {
								openMediaFrame(
									{ title: __( 'Select icon', 'wppowersuite-lite' ), library: { type: 'image' } },
									function ( selection ) {
										var item = selection.first();
										if ( item ) {
											setMetaKey( field.metaKey, item.get( 'url' ) || '' );
										}
									}
								);
							}
						},
						__( 'Select icon', 'wppowersuite-lite' )
					)
					: null,
				activeTab === 'url'
					? createElement( TextControl, {
						label: __( 'Icon URL', 'wppowersuite-lite' ),
						type: 'url',
						value: isUrl ? value : '',
						onChange: function ( next ) {
							setMetaKey( field.metaKey, next );
						}
					} )
					: null,
				createElement(
					'div',
					{ className: 'wpps-cf-gutenberg-icon__preview' },
					isUrl
						? createElement( 'img', { src: value, alt: '' } )
						: createElement( 'span', { className: 'dashicons ' + ( value || 'dashicons-marker' ), 'aria-hidden': true } )
				)
			)
		);
	}

	function AffixedTextControl( props ) {
		var field = props.field;
		var prepend = field.prepend || '';
		var append = field.append || '';
		var hasAffix = prepend !== '' || append !== '';

		if ( ! hasAffix ) {
			return createElement( TextControl, props.controlProps );
		}

		return createElement(
			BaseControl,
			{
				label: props.controlProps.label,
				help: props.controlProps.help,
				className: 'wpps-cf-gutenberg-affixed-control'
			},
			createElement(
				'div',
				{ className: 'wpps-cf-gutenberg-affixed-control__row' },
				prepend !== ''
					? createElement( 'span', { className: 'wpps-cf-gutenberg-affixed-control__addon' }, prepend )
					: null,
				createElement( TextControl, Object.assign( {}, props.controlProps, { label: undefined, help: undefined } ) ),
				append !== ''
					? createElement( 'span', { className: 'wpps-cf-gutenberg-affixed-control__addon' }, append )
					: null
			)
		);
	}

	function CurrentDefaultTextControl( props ) {
		return createElement( AffixedTextControl, {
			field: props.field,
			controlProps: props.controlProps
		} );
	}

	function currentDefaultValue( type ) {
		var now = new Date();
		var pad = function ( part ) {
			return String( part ).padStart( 2, '0' );
		};

		if ( type === 'date' ) {
			return now.getFullYear() + '-' + pad( now.getMonth() + 1 ) + '-' + pad( now.getDate() );
		}

		if ( type === 'time' ) {
			return pad( now.getHours() ) + ':' + pad( now.getMinutes() );
		}

		if ( type === 'datetime' ) {
			return now.getFullYear() + '-' + pad( now.getMonth() + 1 ) + '-' + pad( now.getDate() ) + 'T' + pad( now.getHours() ) + ':' + pad( now.getMinutes() );
		}

		return '';
	}

	function parseDateByFormat( value, format ) {
		var text = value == null ? '' : String( value ).trim();
		var tokens = [];
		var pattern = '';
		var i;
		var tokenPatterns = {
			Y: '(\\d{4})',
			m: '(\\d{2})',
			d: '(\\d{2})',
			n: '(\\d{1,2})',
			j: '(\\d{1,2})'
		};
		var values = {};
		var match;
		var date;

		if ( ! text || ! format ) {
			return null;
		}

		for ( i = 0; i < format.length; i++ ) {
			if ( tokenPatterns[ format[ i ] ] ) {
				tokens.push( format[ i ] );
				pattern += tokenPatterns[ format[ i ] ];
			} else {
				pattern += format[ i ].replace( /[-/\\^$*+?.()|[\]{}]/g, '\\$&' );
			}
		}

		match = new RegExp( '^' + pattern + '$' ).exec( text );
		if ( ! match ) {
			return null;
		}

		tokens.forEach( function ( token, index ) {
			values[ token ] = Number( match[ index + 1 ] );
		} );

		date = new Date( values.Y || 0, ( values.m || values.n || 1 ) - 1, values.d || values.j || 1 );
		if (
			date.getFullYear() !== ( values.Y || 0 ) ||
			date.getMonth() !== ( values.m || values.n || 1 ) - 1 ||
			date.getDate() !== ( values.d || values.j || 1 )
		) {
			return null;
		}

		return date;
	}

	function formatDateByFormat( date, format ) {
		var pad = function ( part ) {
			return String( part ).padStart( 2, '0' );
		};
		var replacements = {
			Y: String( date.getFullYear() ),
			m: pad( date.getMonth() + 1 ),
			d: pad( date.getDate() ),
			n: String( date.getMonth() + 1 ),
			j: String( date.getDate() )
		};

		return String( format || 'Y-m-d' ).replace( /[Ymdnj]/g, function ( token ) {
			return replacements[ token ] || token;
		} );
	}

	function normalizeDateInputValue( field, value ) {
		var text = value == null ? '' : String( value ).trim();
		var saveFormat = field.saveFormat || '';
		var date;

		if ( ! text ) {
			return '';
		}

		date = saveFormat ? parseDateByFormat( text, saveFormat ) : null;
		if ( ! date ) {
			date = parseDateByFormat( text, 'Y-m-d' );
		}

		return date ? formatDateByFormat( date, 'Y-m-d' ) : text;
	}

	function storageDateValue( field, value ) {
		var date = parseDateByFormat( value, 'Y-m-d' );
		var saveFormat = field.saveFormat || '';

		return date && saveFormat ? formatDateByFormat( date, saveFormat ) : value;
	}

	function normalizeGoogleMapValue( field, value ) {
		if ( value && typeof value === 'object' && ! Array.isArray( value ) ) {
			return {
				address: value.address || '',
				lat: value.lat || '',
				lng: value.lng || '',
				zoom: value.zoom || field.mapZoom || 14
			};
		}

		return {
			address: '',
			lat: field.mapCenterLat || '',
			lng: field.mapCenterLng || '',
			zoom: field.mapZoom || 14
		};
	}

	function googleMapQuery( map ) {
		var lat = map.lat == null ? '' : String( map.lat ).trim();
		var lng = map.lng == null ? '' : String( map.lng ).trim();
		var address = map.address == null ? '' : String( map.address ).trim();

		if ( lat && lng ) {
			return lat + ',' + lng;
		}

		return address;
	}

	function parseGoogleMapCoordinates( value ) {
		var text = String( value || '' ).trim();
		var decoded;
		var patterns;
		var i;
		var match;
		var lat;
		var lng;

		if ( ! text ) {
			return null;
		}

		try {
			decoded = window.decodeURIComponent( text );
		} catch ( e ) {
			decoded = text;
		}

		patterns = [
			/@(-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)/,
			/[?&](?:q|query|ll)=(-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)/,
			/^\s*(-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)\s*$/
		];

		for ( i = 0; i < patterns.length; i++ ) {
			match = decoded.match( patterns[ i ] );
			if ( ! match ) {
				continue;
			}
			lat = parseFloat( match[1] );
			lng = parseFloat( match[2] );
			if ( ! isNaN( lat ) && ! isNaN( lng ) && lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180 ) {
				return { lat: String( lat ), lng: String( lng ) };
			}
		}

		return null;
	}

	function GoogleMapField( props ) {
		var field = props.field;
		var map = normalizeGoogleMapValue( field, props.value );
		var query = googleMapQuery( map );
		var zoom = parseInt( map.zoom, 10 ) || parseInt( field.mapZoom, 10 ) || 14;
		var previewUrl = query ? 'https://maps.google.com/maps?q=' + encodeURIComponent( query ) + '&z=' + encodeURIComponent( String( zoom ) ) + '&output=embed' : '';
		var mapsUrl = query ? 'https://www.google.com/maps/search/?api=1&query=' + encodeURIComponent( query ) : '';

		function updateMap( patch ) {
			props.setMetaKey( field.metaKey, Object.assign( {}, map, patch ) );
		}

		return createElement(
			'div',
			{ className: 'wpps-cf-gutenberg-map' },
			createElement( TextControl, {
				label: props.label,
				help: props.help,
				value: map.address || '',
				placeholder: field.placeholder || __( 'Address', 'wppowersuite-lite' ),
				onChange: function ( next ) {
					var coords = parseGoogleMapCoordinates( next );
					updateMap( coords ? { address: next, lat: coords.lat, lng: coords.lng } : { address: next } );
				}
			} ),
			createElement(
				'div',
				{ className: 'wpps-cf-gutenberg-map__grid' },
				createElement( TextControl, {
					label: __( 'Latitude', 'wppowersuite-lite' ),
					value: map.lat || '',
					onChange: function ( next ) {
						updateMap( { lat: next } );
					}
				} ),
				createElement( TextControl, {
					label: __( 'Longitude', 'wppowersuite-lite' ),
					value: map.lng || '',
					onChange: function ( next ) {
						updateMap( { lng: next } );
					}
				} ),
				createElement( TextControl, {
					label: __( 'Zoom', 'wppowersuite-lite' ),
					type: 'number',
					value: String( zoom ),
					onChange: function ( next ) {
						updateMap( { zoom: next } );
					}
				} )
			),
			previewUrl
				? createElement(
					'div',
					{ className: 'wpps-cf-gutenberg-map__preview' },
					createElement( 'iframe', {
						title: props.label || __( 'Map preview', 'wppowersuite-lite' ),
						src: previewUrl,
						loading: 'lazy'
					} )
				)
				: createElement( 'p', { className: 'description' }, __( 'Enter an address or coordinates to preview this location.', 'wppowersuite-lite' ) ),
			mapsUrl
				? createElement(
					Button,
					{
						variant: 'link',
						href: mapsUrl,
						target: '_blank',
						rel: 'noopener noreferrer'
					},
					__( 'Open in Google Maps', 'wppowersuite-lite' )
				)
				: null
		);
	}

	function FieldControl( props ) {
		var field = props.field;
		var meta = props.meta;
		var setMetaKey = props.setMetaKey;
		var value = getMetaValue( meta, field );
		var type = field.type;
		var help = field.instructions || '';
		var label = field.label || field.name;
		var metaHasKey = Object.prototype.hasOwnProperty.call( meta, field.metaKey );

		if ( type === 'message' ) {
			return createElement(
				Notice,
				{ status: 'info', isDismissible: false, className: 'wpps-cf-gutenberg-message' },
				createElement( RawHTML, null, formatMessageHtml( field, label ) )
			);
		}

		if ( type === 'output' ) {
			return createElement(
				Notice,
				{ status: 'info', isDismissible: false, className: 'wpps-cf-gutenberg-message wpps-cf-gutenberg-output' },
				createElement( RawHTML, null, field.output || '' )
			);
		}

		if ( type === 'separator' ) {
			return createElement( 'hr', { className: 'wpps-cf-gutenberg-separator' } );
		}

		if ( type === 'toggle' ) {
			if ( field.toggleUi === false ) {
				return createElement( CheckboxControl, {
					label: field.toggleMessage || label,
					help: field.toggleMessage ? help : '',
					checked: value === '1' || value === 1 || value === true,
					onChange: function ( checked ) {
						setMetaKey( field.metaKey, checked ? '1' : '0' );
					}
				} );
			}

			return createElement( ToggleControl, {
				label: label,
				help: field.toggleMessage || help,
				checked: value === '1' || value === 1 || value === true,
				onChange: function ( checked ) {
					setMetaKey( field.metaKey, checked ? '1' : '0' );
				}
			} );
		}

		if ( type === 'textarea' ) {
			return createElement( TextareaControl, {
				label: label,
				help: help,
				value: value == null ? '' : String( value ),
				rows: field.rows || 4,
				maxLength: field.maxlength || undefined,
				onChange: function ( next ) {
					setMetaKey( field.metaKey, next );
				}
			} );
		}

		if ( type === 'select' ) {
			var selectOptions = ( field.allowNull ? [ { label: '—', value: '' } ] : [] ).concat(
				( field.choices || [] ).map( function ( choice ) {
					return { label: choice.label, value: choice.value };
				} )
			);
			if ( field.multiple ) {
				var selectedValues = Array.isArray( value ) ? value : ( value ? [ String( value ) ] : [] );
				return createElement(
					BaseControl,
					{ label: label, help: help },
					selectOptions.filter( function ( option ) {
						return option.value !== '';
					} ).map( function ( option ) {
						return createElement( CheckboxControl, {
							key: option.value,
							label: option.label,
							checked: selectedValues.indexOf( option.value ) !== -1,
							onChange: function ( checked ) {
								var next = selectedValues.slice();
								if ( checked ) {
									if ( next.indexOf( option.value ) === -1 ) {
										next.push( option.value );
									}
								} else {
									next = next.filter( function ( item ) {
										return item !== option.value;
									} );
								}
								setMetaKey( field.metaKey, next );
							}
						} );
					} )
				);
			}
			return createElement( SelectControl, {
				label: label,
				help: help,
				value: value == null ? '' : String( value ),
				options: selectOptions,
				onChange: function ( next ) {
					setMetaKey( field.metaKey, next );
				}
			} );
		}

		if ( type === 'radio' || type === 'button_group' ) {
			return createElement( RadioControl, {
				label: label,
				help: help,
				selected: value == null ? '' : String( value ),
				options: ( field.allowNull ? [ { label: '—', value: '' } ] : [] ).concat( ( field.choices || [] ).map( function ( choice ) {
					return { label: choice.label, value: choice.value };
				} ) ),
				onChange: function ( next ) {
					setMetaKey( field.metaKey, next );
				}
			} );
		}

		if ( type === 'checkbox' ) {
			var selected = Array.isArray( value ) ? value : [];
			return createElement(
				BaseControl,
				{ label: label, help: help },
				( field.choices || [] ).map( function ( choice ) {
					return createElement( CheckboxControl, {
						key: choice.value,
						label: choice.label,
						checked: selected.indexOf( choice.value ) !== -1,
						onChange: function ( checked ) {
							var next = selected.slice();
							if ( checked ) {
								if ( next.indexOf( choice.value ) === -1 ) {
									next.push( choice.value );
								}
							} else {
								next = next.filter( function ( item ) {
									return item !== choice.value;
								} );
							}
							setMetaKey( field.metaKey, next );
						}
					} );
				} )
			);
		}

		if ( type === 'image' || type === 'file' ) {
			var attachmentId = parseInt( value, 10 ) || 0;
			var mimeTypes = normalizeMediaMimeTypes( field.mimeTypes );
			if ( type === 'image' ) {
				mimeTypes = imageMimeTypesOnly( mimeTypes );
			}
			return createElement(
				BaseControl,
				{ label: label, help: help },
				createElement(
					'div',
					{ className: 'wpps-cf-gutenberg-media' },
					attachmentId
						? createElement( 'p', null, '#' + attachmentId )
						: createElement( 'p', { className: 'description' }, '—' ),
					createElement(
						Button,
						{
							variant: 'secondary',
							onClick: function () {
								openMediaFrame(
									{
										library: { type: mimeTypes.length ? mimeTypes : ( type === 'image' ? 'image' : '' ) }
									},
									function ( selection ) {
										var item = selection.first();
										if ( item ) {
											setMetaKey( field.metaKey, item.get( 'id' ) );
										}
									}
								);
							}
						},
						type === 'image' ? ( strings.selectImage || 'Select image' ) : ( strings.selectFile || 'Select file' )
					),
					attachmentId
						? createElement(
							Button,
							{
								variant: 'link',
								isDestructive: true,
								onClick: function () {
									setMetaKey( field.metaKey, 0 );
								}
							},
							strings.remove || 'Remove'
						)
						: null
				)
			);
		}

		if ( type === 'gallery' ) {
			return createElement( GalleryField, {
				field: field,
				value: value,
				setMetaKey: setMetaKey,
				label: label,
				help: help
			} );
		}

		if ( type === 'post_object' || type === 'relationship' || type === 'page_link' || type === 'term' || type === 'user' ) {
			return createElement( RelationalField, {
				field: field,
				value: value,
				setMetaKey: setMetaKey,
				label: label,
				help: help
			} );
		}

		if ( type === 'oembed' ) {
			return createElement( OembedField, {
				field: field,
				value: value,
				setMetaKey: setMetaKey,
				label: label,
				help: help
			} );
		}

		if ( type === 'google_map' ) {
			return createElement( GoogleMapField, {
				field: field,
				value: value,
				setMetaKey: setMetaKey,
				label: label,
				help: help
			} );
		}

		if ( type === 'color' ) {
			return createElement( ColorField, {
				field: field,
				value: value,
				setMetaKey: setMetaKey,
				label: label,
				help: help
			} );
		}

		if ( type === 'nav_menu' ) {
			return createElement( SelectControl, {
				label: label,
				help: help,
				value: value == null ? '' : String( value ),
				options: ( field.allowNull ? [ { label: '—', value: '' } ] : [] ).concat( mapRelOptions( field.options ) ),
				onChange: function ( next ) {
					setMetaKey( field.metaKey, next ? parseInt( next, 10 ) : 0 );
				}
			} );
		}

		if ( type === 'icon_picker' ) {
			return createElement( IconField, {
				field: field,
				value: value,
				setMetaKey: setMetaKey,
				label: label,
				help: help
			} );
		}

		if ( type === 'link' ) {
			var link = value && typeof value === 'object' ? value : { url: '', title: '', target: '' };
			return createElement(
				Fragment,
				null,
				createElement( TextControl, {
					label: label + ' — URL',
					value: link.url || '',
					onChange: function ( next ) {
						setMetaKey( field.metaKey, Object.assign( {}, link, { url: next } ) );
					}
				} ),
				createElement( TextControl, {
					label: label + ' — ' + __( 'Link text', 'wppowersuite-lite' ),
					value: link.title || '',
					onChange: function ( next ) {
						setMetaKey( field.metaKey, Object.assign( {}, link, { title: next } ) );
					}
				} ),
				createElement( ToggleControl, {
					label: label + ' — ' + __( 'Open in new tab', 'wppowersuite-lite' ),
					checked: link.target === '_blank',
					onChange: function ( checked ) {
						setMetaKey( field.metaKey, Object.assign( {}, link, { target: checked ? '_blank' : '' } ) );
					}
				} )
			);
		}

		var inputType = 'text';
		if ( type === 'number' || type === 'range' ) {
			inputType = 'number';
		} else if ( type === 'email' ) {
			inputType = 'email';
		} else if ( type === 'url' ) {
			inputType = 'url';
		} else if ( type === 'phone' ) {
			inputType = 'tel';
		} else if ( type === 'password' ) {
			inputType = 'password';
		} else if ( type === 'date' ) {
			inputType = 'date';
		} else if ( type === 'time' ) {
			inputType = 'time';
		} else if ( type === 'datetime' ) {
			inputType = 'datetime-local';
		}

		var controlValue = value == null ? '' : String( value );
		var defaultMetaValue = '';
		var shouldCommitCurrentDefault = false;
		if ( ! controlValue && ! metaHasKey && field.defaultCurrent && ( type === 'date' || type === 'time' || type === 'datetime' ) ) {
			controlValue = field.currentDefault || currentDefaultValue( type );
			defaultMetaValue = type === 'date' ? storageDateValue( field, controlValue ) : controlValue;
			shouldCommitCurrentDefault = true;
		}
		if ( type === 'date' ) {
			controlValue = normalizeDateInputValue( field, controlValue );
		}

		var textControlProps = {
			label: label,
			help: help,
			type: inputType,
			value: controlValue,
			placeholder: field.placeholder || undefined,
			maxLength: field.maxlength || undefined,
			min: field.min !== '' ? field.min : undefined,
			max: field.max !== '' ? field.max : undefined,
			step: field.step !== '' ? field.step : undefined,
			onChange: function ( next ) {
				setMetaKey( field.metaKey, type === 'date' ? storageDateValue( field, next ) : next );
			}
		};

		return createElement( CurrentDefaultTextControl, {
			field: field,
			controlProps: textControlProps,
			setMetaKey: setMetaKey,
			defaultMetaValue: defaultMetaValue,
			shouldCommitDefault: shouldCommitCurrentDefault
		} );
	}

	function GroupPanel( props ) {
		var group = props.group;
		var metaApi = usePostMeta();
		var fieldValues = buildFieldValues( group, metaApi.meta );
		var visibleFields = ( group.fields || [] ).filter( function ( field ) {
			return matchesConditional( field.conditional, fieldValues );
		} );

		return createElement(
			PluginDocumentSettingPanel,
			{
				name: 'wpps-cf-group-' + group.key,
				title: group.title,
				className: 'wpps-cf-gutenberg-panel'
			},
			! group.sidebarOnly
				? createElement(
					Notice,
					{ status: 'info', isDismissible: false },
					strings.advancedFields || 'These fields also appear in the meta box below the editor.'
				)
				: null,
			visibleFields.map( function ( field ) {
				return createElement(
					'div',
					{ key: field.key || field.metaKey, className: 'wpps-cf-gutenberg-field wpps-cf-gutenberg-field--' + field.type },
					createElement( FieldControl, {
						field: field,
						meta: metaApi.meta,
						setMetaKey: metaApi.setMetaKey
					} )
				);
			} )
		);
	}

	function CustomFieldsSidebar() {
		var postId = useSelect( function ( select ) {
			return select( 'core/editor' ).getCurrentPostId();
		}, [] );
		var postType = useSelect( function ( select ) {
			return select( 'core/editor' ).getCurrentPostType();
		}, [] );

		var schemaState = useState( initialSchema );
		var schema = schemaState[0];
		var setSchema = schemaState[1];

		useEffect( function () {
			if ( ! postType || ! config.restUrl ) {
				return;
			}
			var url = config.restUrl + encodeURIComponent( postType ) + '?post_id=' + encodeURIComponent( postId || 0 );
			window.fetch( url, {
				headers: {
					'X-WP-Nonce': config.restNonce || ''
				}
			} )
				.then( function ( response ) {
					return response.json();
				} )
				.then( function ( data ) {
					if ( data && Array.isArray( data.groups ) ) {
						setSchema( data );
					}
				} )
				.catch( function () {
					// Keep localized schema.
				} );
		}, [ postType, postId, setSchema ] );

		var groups = schema.groups || [];
		if ( ! groups.length ) {
			return null;
		}

		return createElement(
			Fragment,
			null,
			groups.map( function ( group ) {
				return createElement( GroupPanel, { key: group.key, group: group } );
			} )
		);
	}

	wp.plugins.registerPlugin( 'wpps-custom-fields-sidebar', {
		render: CustomFieldsSidebar,
		icon: 'list-view'
	} );
}( window.wp, window.wppsCfEditor ) );
