Example #1
0
				transform: ( { value, citation, ...attrs } ) => {
					// If there is no quote content, use the citation as the
					// content of the resulting heading. A nonexistent citation
					// will result in an empty heading.
					if ( value === '<p></p>' ) {
						return createBlock( 'core/heading', {
							content: citation,
						} );
					}

					const pieces = split( create( { html: value, multilineTag: 'p' } ), '\u2028' );
					const quotePieces = pieces.slice( 1 );

					return [
						createBlock( 'core/heading', {
							content: toHTMLString( { value: pieces[ 0 ] } ),
						} ),
						createBlock( 'core/quote', {
							...attrs,
							citation,
							value: toHTMLString( {
								value: quotePieces.length ? join( pieces.slice( 1 ), '\u2028' ) : create(),
								multilineTag: 'p',
							} ),
						} ),
					];
				},
Example #2
0
				transform: ( { value, citation } ) => {
					const paragraphs = [];
					if ( value && value !== '<p></p>' ) {
						paragraphs.push(
							...split( create( { html: value, multilineTag: 'p' } ), '\u2028' )
								.map( ( piece ) =>
									createBlock( 'core/paragraph', {
										content: toHTMLString( { value: piece } ),
									} )
								)
						);
					}
					if ( citation && citation !== '<p></p>' ) {
						paragraphs.push(
							createBlock( 'core/paragraph', {
								content: citation,
							} )
						);
					}

					if ( paragraphs.length === 0 ) {
						return createBlock( 'core/paragraph', {
							content: '',
						} );
					}
					return paragraphs;
				},
Example #3
0
				transform: ( attributes ) => {
					const validImages = filter( attributes, ( { id, url } ) => id && url );
					if ( validImages.length > 0 ) {
						return createBlock( 'core/gallery', {
							images: validImages.map( ( { id, url, alt, caption } ) => ( { id, url, alt, caption } ) ),
						} );
					}
					return createBlock( 'core/gallery' );
				},
Example #4
0
				transform: attributes => {
					const validImages = filter( attributes.images, ( { id, url } ) => id && url );
					if ( validImages.length > 0 ) {
						return createBlock( `jetpack/${ name }`, {
							images: validImages.map( ( { id, url, alt } ) => ( {
								id,
								url,
								alt,
							} ) ),
						} );
					}
					return createBlock( `jetpack/${ name }` );
				},
Example #5
0
								( before, after, ...blocks ) => {
									if ( ! blocks.length ) {
										blocks.push( createBlock( 'core/paragraph' ) );
									}

									if ( after.length ) {
										blocks.push( createBlock( 'core/list', {
											nodeName,
											values: after,
										} ) );
									}

									setAttributes( { values: before } );
									insertBlocksAfter( blocks );
								} :
Example #6
0
						( obj ) => {
							if ( this.unmounting ) {
								return;
							}
							// Some plugins only return HTML with no type info, so default this to 'rich'.
							let { type = 'rich' } = obj;
							// If we got a provider name from the API, use it for the slug, otherwise we use the title,
							// because not all embed code gives us a provider name.
							const { html, provider_name: providerName } = obj;
							const providerNameSlug = kebabCase( toLower( '' !== providerName ? providerName : title ) );
							// This indicates it's a WordPress embed, there aren't a set of URL patterns we can use to match WordPress URLs.
							if ( includes( html, 'class="wp-embedded-content" data-secret' ) ) {
								type = 'wp-embed';
								// If this is not the WordPress embed block, transform it into one.
								if ( this.props.name !== 'core-embed/wordpress' ) {
									this.props.onReplace( createBlock( 'core-embed/wordpress', { url } ) );
									return;
								}
							}
							if ( html ) {
								this.setState( { html, type, providerNameSlug } );
								setAttributes( { type, providerNameSlug } );
							} else if ( 'photo' === type ) {
								this.setState( { html: this.getPhotoHtml( obj ), type, providerNameSlug } );
								setAttributes( { type, providerNameSlug } );
							} else {
								// No html, no custom type that we support, so show the error state.
								this.setState( { error: true } );
							}
							this.setState( { fetching: false } );
						},
Example #7
0
				transform: ( { mediaAlt, mediaId, mediaUrl } ) => {
					return createBlock( 'core/image', {
						alt: mediaAlt,
						id: mediaId,
						url: mediaUrl,
					} );
				},
Example #8
0
				transform: ( { src, id } ) => (
					createBlock( 'core/media-text', {
						mediaId: id,
						mediaUrl: src,
						mediaType: 'video',
					} )
				),
Example #9
0
	/**
	 * Split handler for RichText value, namely when content is pasted or the
	 * user presses the Enter key.
	 *
	 * @param {?Array}     before Optional before value, to be used as content
	 *                            in place of what exists currently for the
	 *                            block. If undefined, the block is deleted.
	 * @param {?Array}     after  Optional after value, to be appended in a new
	 *                            paragraph block to the set of blocks passed
	 *                            as spread.
	 * @param {...WPBlock} blocks Optional blocks inserted between the before
	 *                            and after value blocks.
	 */
	splitBlock( before, after, ...blocks ) {
		const {
			attributes,
			insertBlocksAfter,
			setAttributes,
			onReplace,
		} = this.props;

		if ( after ) {
			// Append "After" content as a new paragraph block to the end of
			// any other blocks being inserted after the current paragraph.
			blocks.push( createBlock( name, { content: after } ) );
		}

		if ( blocks.length && insertBlocksAfter ) {
			insertBlocksAfter( blocks );
		}

		const { content } = attributes;
		if ( ! before ) {
			// If before content is omitted, treat as intent to delete block.
			onReplace( [] );
		} else if ( content !== before ) {
			// Only update content if it has in-fact changed. In case that user
			// has created a new paragraph at end of an existing one, the value
			// of before will be strictly equal to the current content.
			setAttributes( { content: before } );
		}
	}
Example #10
0
	onKeyDown( event ) {
		const { keyCode } = event;
		const { insertBlocksAfter } = this.props;
		if ( keyCode === ENTER ) {
			insertBlocksAfter( [ createBlock( getDefaultBlockName() ) ] );
		}
	}
Example #11
0
	/**
	 * Interprets keydown event intent to remove or insert after block if key
	 * event occurs on wrapper node. This can occur when the block has no text
	 * fields of its own, particularly after initial insertion, to allow for
	 * easy deletion and continuous writing flow to add additional content.
	 *
	 * @param {KeyboardEvent} event Keydown event.
	 */
	deleteOrInsertAfterWrapper( event ) {
		const { keyCode, target } = event;

		if ( target !== this.wrapperNode || this.props.isLocked ) {
			return;
		}

		switch ( keyCode ) {
			case ENTER:
				// Insert default block after current block if enter and event
				// not already handled by descendant.
				this.props.onInsertBlocks( [
					createBlock( 'core/paragraph' ),
				], this.props.order + 1 );
				event.preventDefault();
				break;

			case BACKSPACE:
			case DELETE:
				// Remove block on backspace.
				const { uid, onRemove, previousBlockUid, onSelect } = this.props;
				onRemove( uid );

				if ( previousBlockUid ) {
					onSelect( previousBlockUid, -1 );
				}
				event.preventDefault();
				break;
		}
	}
Example #12
0
							( before, after, ...blocks ) => {
								setAttributes( { content: before } );
								insertBlocksAfter( [
									...blocks,
									createBlock( 'core/paragraph', { content: after } ),
								] );
							} :
Example #13
0
	beforeAll( () => {
		registerBlockType( 'core/heading', {
			category: 'common',
			title: 'Heading',
			edit: () => { },
			save: () => { },
			attributes: {
				level: {
					type: 'number',
					default: 2,
				},
				content: {
					type: 'string',
				},
			},
		} );

		registerBlockType( 'core/paragraph', {
			category: 'common',
			title: 'Paragraph',
			edit: () => { },
			save: () => {},
		} );

		registerBlockType( 'core/columns', {
			category: 'common',
			title: 'Paragraph',
			edit: () => { },
			save: () => {},
		} );

		paragraph = createBlock( 'core/paragraph' );
		headingH1 = createBlock( 'core/heading', {
			content: 'Heading 1',
			level: 1,
		} );
		headingParent = createBlock( 'core/heading', {
			content: 'Heading parent',
			level: 2,
		} );
		headingChild = createBlock( 'core/heading', {
			content: 'Heading child',
			level: 3,
		} );

		nestedHeading = createBlock( 'core/columns', undefined, [ headingChild ] );
	} );
Example #14
0
				transform: ( { alt, url, id } ) => (
					createBlock( 'core/media-text', {
						mediaAlt: alt,
						mediaId: id,
						mediaUrl: url,
						mediaType: 'image',
					} )
				),
Example #15
0
				transform: ( blockAttributes ) => {
					const items = blockAttributes.map( ( { content } ) => content );
					const hasItems = ! items.every( isEmpty );
					return createBlock( 'core/list', {
						nodeName: 'UL',
						values: hasItems ? items.map( ( content, index ) => <li key={ index }>{ content }</li> ) : [],
					} );
				},
Example #16
0
				transform: ( { images } ) => {
					if ( images.length > 0 ) {
						return images.map( ( { id, url, alt } ) =>
							createBlock( 'core/image', { id, url, alt } )
						);
					}
					return createBlock( 'core/image' );
				},
Example #17
0
export function insertDefaultBlock( attributes, rootUID, index ) {
	const block = createBlock( getDefaultBlockName(), attributes );

	return {
		...insertBlock( block, index, rootUID ),
		isProvisional: true,
	};
}
Example #18
0
				transform: ( { values } ) => {
					return createBlock( 'core/quote', {
						value: compact( ( values.length === 1 ? values : initial( values ) )
							.map( ( value ) => get( value, [ 'props', 'children' ], null ) ) )
							.map( ( children ) => ( { children: <p>{ children }</p> } ) ),
						citation: ( values.length === 1 ? undefined : [ get( last( values ), [ 'props', 'children' ] ) ] ),
					} );
				},
Example #19
0
	onConfirmClick: () => {
		const { clientId } = ownProps;
		const { getBlockCount } = select( 'core/editor' );
		const { insertBlock } = wpDispatch( 'core/editor' );

		const nextChildPosition = getBlockCount( clientId );
		const block = createBlock( 'tribe/tickets-item', {} );
		insertBlock( block, nextChildPosition, clientId );
	},
Example #20
0
export const createUpgradedEmbedBlock = ( props, attributesFromPreview ) => {
	const { preview, name } = props;
	const { url } = props.attributes;

	if ( ! url ) {
		return;
	}

	const matchingBlock = findBlock( url );

	// WordPress blocks can work on multiple sites, and so don't have patterns,
	// so if we're in a WordPress block, assume the user has chosen it for a WordPress URL.
	if ( WORDPRESS_EMBED_BLOCK !== name && DEFAULT_EMBED_BLOCK !== matchingBlock ) {
		// At this point, we have discovered a more suitable block for this url, so transform it.
		if ( name !== matchingBlock ) {
			return createBlock( matchingBlock, { url } );
		}
	}

	if ( preview ) {
		const { html } = preview;

		// We can't match the URL for WordPress embeds, we have to check the HTML instead.
		if ( isFromWordPress( html ) ) {
			// If this is not the WordPress embed block, transform it into one.
			if ( WORDPRESS_EMBED_BLOCK !== name ) {
				return createBlock(
					WORDPRESS_EMBED_BLOCK,
					{
						url,
						// By now we have the preview, but when the new block first renders, it
						// won't have had all the attributes set, and so won't get the correct
						// type and it won't render correctly. So, we pass through the current attributes
						// here so that the initial render works when we switch to the WordPress
						// block. This only affects the WordPress block because it can't be
						// rendered in the usual Sandbox (it has a sandbox of its own) and it
						// relies on the preview to set the correct render type.
						...attributesFromPreview,
					}
				);
			}
		}
	}
};
Example #21
0
		onInsertBlock: ( item ) => {
			const { insertionPoint, selectedBlock } = ownProps;
			const { index, rootUID, layout } = insertionPoint;
			const { name, initialAttributes } = item;
			const insertedBlock = createBlock( name, { ...initialAttributes, layout } );
			if ( selectedBlock && isUnmodifiedDefaultBlock( selectedBlock ) ) {
				return dispatch( 'core/editor' ).replaceBlocks( selectedBlock.uid, insertedBlock );
			}
			return dispatch( 'core/editor' ).insertBlock( insertedBlock, index, rootUID );
		},
Example #22
0
				transform: ( attributes ) => {
					return createBlock( 'core/quote', {
						value: toHTMLString( {
							value: join( attributes.map( ( { content } ) =>
								create( { html: content } )
							), '\u2028' ),
							multilineTag: 'p',
						} ),
					} );
				},
Example #23
0
			...[ 2, 3, 4, 5, 6 ].map( ( level ) => ( {
				type: 'prefix',
				prefix: Array( level + 1 ).join( '#' ),
				transform( content ) {
					return createBlock( 'core/heading', {
						level,
						content,
					} );
				},
			} ) ),
Example #24
0
				transform: ( { value, citation } ) => {
					const items = value.map( ( p ) => get( p, [ 'children', 'props', 'children' ] ) );
					if ( ! isEmpty( citation ) ) {
						items.push( citation );
					}
					const hasItems = ! items.every( isEmpty );
					return createBlock( 'core/list', {
						nodeName: 'UL',
						values: hasItems ? items.map( ( content, index ) => <li key={ index }>{ content }</li> ) : [],
					} );
				},
Example #25
0
	( dispatch, ownProps ) => {
		return {
			ignoreInvalid() {
				const { block } = ownProps;
				const { name, attributes } = block;
				const nextBlock = createBlock( name, attributes );
				dispatch( replaceBlock( block.uid, nextBlock ) );
			},
			switchToBlockType( blockType ) {
				const { block } = ownProps;
				if ( blockType && block ) {
					const nextBlock = createBlock( blockType.name, {
						content: block.originalContent,
					} );

					dispatch( replaceBlock( block.uid, nextBlock ) );
				}
			},
		};
	}
Example #26
0
		it( 'should move the nested block down', () => {
			const movedBlock = createBlock( 'core/test-block' );
			const siblingBlock = createBlock( 'core/test-block' );
			const wrapperBlock = createBlock( 'core/test-block', {}, [ movedBlock, siblingBlock ] );
			const original = editor( undefined, {
				type: 'RESET_BLOCKS',
				blocks: [ wrapperBlock ],
			} );
			const state = editor( original, {
				type: 'MOVE_BLOCKS_DOWN',
				uids: [ movedBlock.uid ],
				rootUID: wrapperBlock.uid,
			} );

			expect( state.present.blockOrder ).toEqual( {
				'': [ wrapperBlock.uid ],
				[ wrapperBlock.uid ]: [ siblingBlock.uid, movedBlock.uid ],
				[ movedBlock.uid ]: [],
				[ siblingBlock.uid ]: [],
			} );
		} );
Example #27
0
		it( 'should replace the nested block', () => {
			const nestedBlock = createBlock( 'core/test-block' );
			const wrapperBlock = createBlock( 'core/test-block', {}, [ nestedBlock ] );
			const replacementBlock = createBlock( 'core/test-block' );
			const original = editor( undefined, {
				type: 'RESET_BLOCKS',
				blocks: [ wrapperBlock ],
			} );

			const state = editor( original, {
				type: 'REPLACE_BLOCKS',
				uids: [ nestedBlock.uid ],
				blocks: [ replacementBlock ],
			} );

			expect( state.present.blockOrder ).toEqual( {
				'': [ wrapperBlock.uid ],
				[ wrapperBlock.uid ]: [ replacementBlock.uid ],
				[ replacementBlock.uid ]: [],
			} );
		} );
Example #28
0
		it( 'should cascade remove to include inner blocks', () => {
			const block = createBlock( 'core/test-block', {}, [
				createBlock( 'core/test-block', {}, [
					createBlock( 'core/test-block' ),
				] ),
			] );

			const original = editor( undefined, {
				type: 'RESET_BLOCKS',
				blocks: [ block ],
			} );

			const state = editor( original, {
				type: 'REMOVE_BLOCKS',
				uids: [ block.uid ],
			} );

			expect( state.present.blocksByUid ).toEqual( {} );
			expect( state.present.blockOrder ).toEqual( {
				'': [],
			} );
		} );
Example #29
0
	withDispatch( ( dispatch, ownProps ) => {
		const { uid, rootUID, layout } = ownProps;

		return {
			onInsert( { name, initialAttributes } ) {
				const block = createBlock( name, { ...initialAttributes, layout } );
				if ( uid ) {
					dispatch( 'core/editor' ).replaceBlocks( uid, block );
				} else {
					dispatch( 'core/editor' ).insertBlock( block, undefined, rootUID );
				}
			},
		};
	} ),
export default withDispatch( ( dispatch, { block } ) => {
	const { replaceBlock } = dispatch( 'core/editor' );
	return {
		convertToHTML() {
			replaceBlock( block.uid, createBlock( 'core/html', {
				content: block.originalContent,
			} ) );
		},
		convertToBlocks() {
			replaceBlock( block.uid, rawHandler( {
				HTML: block.originalContent,
				mode: 'BLOCKS',
			} ) );
		},
	};
} )( InvalidBlockWarning );