diff --git a/editor/components/post-permalink/index.js b/editor/components/post-permalink/index.js index 830de1932ac3f..c89f8ac97019e 100644 --- a/editor/components/post-permalink/index.js +++ b/editor/components/post-permalink/index.js @@ -9,60 +9,197 @@ import { connect } from 'react-redux'; import { Component } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { Dashicon, ClipboardButton, Button } from '@wordpress/components'; +import { addQueryArgs } from '@wordpress/url'; /** * Internal Dependencies */ import './style.scss'; -import { isEditedPostNew, getEditedPostAttribute } from '../../store/selectors'; +import { + isEditedPostNew, + isCurrentPostPublished, + getEditedPostAttribute, +} from '../../store/selectors'; +import { editPost } from '../../store/actions'; + +/** + * Constants + */ +const REGEXP_NEWLINES = /[\r\n]+/g; class PostPermalink extends Component { constructor() { super( ...arguments ); this.state = { - showCopyConfirmation: false, + editingSlug: false, + permalink: '', }; + this.getSlug = this.getSlug.bind( this ); + this.onChangePermalink = this.onChangePermalink.bind( this ); + this.onEditPermalink = this.onEditPermalink.bind( this ); + this.onSavePermalink = this.onSavePermalink.bind( this ); this.onCopy = this.onCopy.bind( this ); - this.onFinishCopy = this.onFinishCopy.bind( this ); + } + + /** + * Returns a permalink for a given post slug. + * + * @param {string} slug The post slug to insert in into the permalink. + * + * @returns {string} The full permalink. + */ + getPermalink( slug ) { + let permalink = this.props.link; + const samplePermalink = this.props.samplePermalink; + if ( samplePermalink ) { + permalink = samplePermalink[ 0 ].replace( '%postname%', slug || samplePermalink[ 1 ] ); + } + + return permalink; + } + + /** + * Returns the slug for the current post. + * + * @returns {string} The slug. + */ + getSlug() { + const { actualSlug, isPublished, samplePermalink } = this.props; + + if ( isPublished ) { + return actualSlug; + } + + if ( samplePermalink ) { + return samplePermalink[ 1 ]; + } + + return ''; + } + + componentDidMount() { + this.setState( { + permalink: this.getPermalink(), + slug: this.getSlug(), + } ); } componentWillUnmount() { clearTimeout( this.dismissCopyConfirmation ); } - onCopy() { + componentWillReceiveProps() { + const slug = this.getSlug(); this.setState( { - showCopyConfirmation: true, + permalink: this.getPermalink( slug ), + slug: slug, + } ); + } + + /** + * Event handler for the slug input field being changed. + * + * @param {Object} event Change event + */ + onChangePermalink( event ) { + this.setState( { slug: event.target.value } ); + } + + /** + * Event handler for the Edit button being clicked. + */ + onEditPermalink() { + this.setState( { editingSlug: true } ); + } + + /** + * Event handler for the slug being saved. + */ + onSavePermalink() { + this.setState( { + editingSlug: false, + permalink: this.getPermalink( this.state.slug ), } ); + const newSlug = this.state.slug.replace( REGEXP_NEWLINES, ' ' ); + this.props.onUpdate( newSlug ); } - onFinishCopy() { + /** + * Event handler for the copy button to show feedback. + */ + onCopy() { this.setState( { - showCopyConfirmation: false, + showCopyConfirmation: true, } ); + + clearTimeout( this.dismissCopyConfirmation ); + this.dismissCopyConfirmation = setTimeout( () => { + this.setState( { + showCopyConfirmation: false, + } ); + }, 4000 ); } render() { const { isNew, link } = this.props; + const { editingSlug, permalink, slug } = this.state; if ( isNew || ! link ) { return null; } + const prefix = permalink.replace( /[^/]+\/?$/, '' ); return (
{ __( 'Permalink:' ) } - - - { this.state.showCopyConfirmation ? __( 'Copied!' ) : __( 'Copy' ) } - + { ! editingSlug && + + } + { editingSlug && +
+ + { prefix } + + + / + +
+ } + { ! editingSlug && + + { this.state.showCopyConfirmation ? __( 'Copied!' ) : __( 'Copy' ) } + + } + { ! editingSlug && + + }
); } @@ -72,8 +209,16 @@ export default connect( ( state ) => { return { isNew: isEditedPostNew( state ), + isPublished: isCurrentPostPublished( state ), link: getEditedPostAttribute( state, 'link' ), + samplePermalink: getEditedPostAttribute( state, 'sample_permalink' ), + actualSlug: getEditedPostAttribute( state, 'slug' ), }; + }, + { + onUpdate( slug ) { + return editPost( { slug } ); + }, } )( PostPermalink ); diff --git a/editor/components/post-permalink/style.scss b/editor/components/post-permalink/style.scss index 28452fa554f6a..9ba088830ee79 100644 --- a/editor/components/post-permalink/style.scss +++ b/editor/components/post-permalink/style.scss @@ -34,3 +34,18 @@ @include long-content-fade( $size: 20% ); } } + +.editor-post-permalink__slug { + color: $dark-gray-300; + font-weight: bold; + padding: 0; +} + +.editor-post-permalink__slug-form { + display: inline; + width: 100%; +} + +.editor-post-permalink__save { + float: right; +} diff --git a/editor/components/post-title/index.js b/editor/components/post-title/index.js index 8413c7210bc9c..7210e2da0604b 100644 --- a/editor/components/post-title/index.js +++ b/editor/components/post-title/index.js @@ -12,7 +12,7 @@ import { __ } from '@wordpress/i18n'; import { Component, compose } from '@wordpress/element'; import { keycodes } from '@wordpress/utils'; import { createBlock, getDefaultBlockName } from '@wordpress/blocks'; -import { withContext } from '@wordpress/components'; +import { Button, Dashicon, withContext } from '@wordpress/components'; /** * Internal dependencies @@ -110,6 +110,14 @@ class PostTitle extends Component { className={ className } tabIndex={ -1 /* Necessary for Firefox to include relatedTarget in blur event */ } > + { ! isSelected && + + } { isSelected && }