Skip to content

Commit

Permalink
Add Editable Permalinks (#5756)
Browse files Browse the repository at this point in the history
What a wild ride. Thanks for the memories.

Closes #5414, #1285.
  • Loading branch information
pento committed Apr 15, 2018
1 parent d0fa52e commit 4c90150
Show file tree
Hide file tree
Showing 12 changed files with 525 additions and 69 deletions.
4 changes: 4 additions & 0 deletions components/button/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ class Button extends Component {
this.ref = ref;
}

focus() {
this.ref.focus();
}

render() {
const {
href,
Expand Down
98 changes: 98 additions & 0 deletions editor/components/post-permalink/editor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/**
* WordPress dependencies
*/
import { withDispatch, withSelect } from '@wordpress/data';
import { Component, compose } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { Button } from '@wordpress/components';

/**
* Internal Dependencies
*/
import './style.scss';

class PostPermalinkEditor extends Component {
constructor( { permalinkParts } ) {
super( ...arguments );

this.state = {
editedPostName: permalinkParts.postName,
};

this.onSavePermalink = this.onSavePermalink.bind( this );
}

onSavePermalink( event ) {
const postName = this.state.editedPostName.replace( /\s+/g, '-' );

event.preventDefault();

this.props.onSave();

if ( ! postName || postName === this.props.postName ) {
return;
}

this.props.editPost( {
slug: postName,
} );

this.setState( {
editedPostName: postName,
} );
}

render() {
const { prefix, suffix } = this.props.permalinkParts;
const { editedPostName } = this.state;

/* eslint-disable jsx-a11y/no-autofocus */
// Autofocus is allowed here, as this mini-UI is only loaded when the user clicks to open it.
return (
<form
className="editor-post-permalink-editor"
onSubmit={ this.onSavePermalink }
>
<span>
<span className="editor-post-permalink-editor__prefix">
{ prefix }
</span>
<input
className="editor-post-permalink-editor__edit"
aria-label={ __( 'Edit post permalink' ) }
value={ editedPostName }
onChange={ ( event ) => this.setState( { editedPostName: event.target.value } ) }
required
autoFocus
/>
<span className="editor-post-permalink-editor__suffix">
{ suffix }
</span>
&lrm;
</span>
<Button
className="editor-post-permalink-editor__save"
isLarge
onClick={ this.onSavePermalink }
>
{ __( 'OK' ) }
</Button>
</form>
);
/* eslint-enable jsx-a11y/no-autofocus */
}
}

export default compose( [
withSelect( ( select ) => {
const { getPermalinkParts } = select( 'core/editor' );
return {
permalinkParts: getPermalinkParts(),
};
} ),
withDispatch( ( dispatch ) => {
const { editPost } = dispatch( 'core/editor' );
return { editPost };
} ),
] )( PostPermalinkEditor );

137 changes: 96 additions & 41 deletions editor/components/post-permalink/index.js
Original file line number Diff line number Diff line change
@@ -1,79 +1,134 @@
/**
* External dependencies
*/
import { connect } from 'react-redux';

/**
* WordPress dependencies
*/
import { Component } from '@wordpress/element';
import { withDispatch, withSelect } from '@wordpress/data';
import { Component, compose } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { Dashicon, ClipboardButton, Button } from '@wordpress/components';
import { Dashicon, Button, ClipboardButton, Tooltip } from '@wordpress/components';

/**
* Internal Dependencies
*/
import './style.scss';
import { isEditedPostNew, getEditedPostAttribute } from '../../store/selectors';
import PostPermalinkEditor from './editor.js';
import { getWPAdminURL } from '../../utils/url';

class PostPermalink extends Component {
constructor() {
super( ...arguments );

this.addVisibilityCheck = this.addVisibilityCheck.bind( this );
this.onVisibilityChange = this.onVisibilityChange.bind( this );

this.state = {
showCopyConfirmation: false,
iconClass: '',
isEditingPermalink: false,
};
this.onCopy = this.onCopy.bind( this );
this.onFinishCopy = this.onFinishCopy.bind( this );
}

componentWillUnmount() {
clearTimeout( this.dismissCopyConfirmation );
addVisibilityCheck() {
window.addEventListener( 'visibilitychange', this.onVisibilityChange );
}

onVisibilityChange() {
const { isEditable, refreshPost } = this.props;
// If the user just returned after having clicked the "Change Permalinks" button,
// fetch a new copy of the post from the server, just in case they enabled permalinks.
if ( ! isEditable && 'visible' === document.visibilityState ) {
refreshPost();
}
}

onCopy() {
this.setState( {
showCopyConfirmation: true,
} );
componentDidUpdate( prevProps, prevState ) {
// If we've just stopped editing the permalink, focus on the new permalink.
if ( prevState.isEditingPermalink && ! this.state.isEditingPermalink ) {
this.permalinkButton.focus();
}
}

onFinishCopy() {
this.setState( {
showCopyConfirmation: false,
} );
componentWillUnmount() {
window.removeEventListener( 'visibilitychange', this.addVisibilityCheck );
}

render() {
const { isNew, link } = this.props;
if ( isNew || ! link ) {
const { isNew, previewLink, isEditable, samplePermalink } = this.props;
const { iconClass, isEditingPermalink } = this.state;

if ( isNew || ! previewLink ) {
return null;
}

return (
<div className="editor-post-permalink">
<Dashicon icon="admin-links" />
<Tooltip text={ __( 'Copy the permalink to your clipboard' ) }>
<ClipboardButton
className="editor-post-permalink__copy"
text={ samplePermalink }
onCopy={ () => this.setState( { iconClass: 'is-copied' } ) }
>
<Dashicon icon="admin-links" className={ iconClass } />
</ClipboardButton>
</Tooltip>

<span className="editor-post-permalink__label">{ __( 'Permalink:' ) }</span>
<Button className="editor-post-permalink__link" href={ link } target="_blank">
{ decodeURI( link ) }
</Button>
<ClipboardButton
className="button"
text={ link }
onCopy={ this.onCopy }
onFinishCopy={ this.onFinishCopy }
>
{ this.state.showCopyConfirmation ? __( 'Copied!' ) : __( 'Copy' ) }
</ClipboardButton>

{ ! isEditingPermalink &&
<Button
className="editor-post-permalink__link"
href={ previewLink }
target="_blank"
ref={ ( permalinkButton ) => this.permalinkButton = permalinkButton }
>
{ decodeURI( samplePermalink ) }
&lrm;
</Button>
}

{ isEditingPermalink &&
<PostPermalinkEditor
onSave={ () => this.setState( { isEditingPermalink: false } ) }
/>
}

{ isEditable && ! isEditingPermalink &&
<Button
className="editor-post-permalink__edit"
isLarge
onClick={ () => this.setState( { isEditingPermalink: true } ) }
>
{ __( 'Edit' ) }
</Button>
}

{ ! isEditable &&
<Button
className="editor-post-permalink__change"
isLarge
href={ getWPAdminURL( 'options-permalink.php' ) }
onClick={ this.addVisibilityCheck }
target="_blank"
>
{ __( 'Change Permalinks' ) }
</Button>
}
</div>
);
}
}

export default connect(
( state ) => {
export default compose( [
withSelect( ( select ) => {
const { isEditedPostNew, isPermalinkEditable, getEditedPostPreviewLink, getPermalink } = select( 'core/editor' );
return {
isNew: isEditedPostNew( state ),
link: getEditedPostAttribute( state, 'link' ),
isNew: isEditedPostNew(),
previewLink: getEditedPostPreviewLink(),
isEditable: isPermalinkEditable(),
samplePermalink: getPermalink(),
};
}
)( PostPermalink );
} ),
withDispatch( ( dispatch ) => {
const { refreshPost } = dispatch( 'core/editor' );
return { refreshPost };
} ),
] )( PostPermalink );

41 changes: 40 additions & 1 deletion editor/components/post-permalink/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@
}
}

.editor-post-permalink__copy {
margin-top: 4px;
}

.editor-post-permalink__copy .is-copied {
opacity: 0.3;
}

.editor-post-permalink__label {
margin: 0 10px;
}
Expand All @@ -31,6 +39,37 @@
white-space: nowrap;

&:after {
@include long-content-fade( $size: 20% );
@include long-content-fade( $size: 20%, $edge: 1px );
}
}

.editor-post-permalink-editor {
width: 100%;
min-width: 20%;
display: inline-flex;
align-items: center;

// Higher specificity required to override core margin styles
.editor-post-permalink-editor__save {
margin-left: auto;
}
}

.editor-post-permalink-editor__prefix {
color: $dark-gray-300;
min-width: 20%;
overflow: hidden;
position: relative;
white-space: nowrap;
text-overflow: ellipsis;
}

.editor-post-permalink-editor__edit {
min-width: 20%;
margin: 0 5px;
}

.editor-post-permalink-editor__suffix {
color: $dark-gray-300;
margin-right: 10px;
}
8 changes: 6 additions & 2 deletions editor/components/post-title/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/
import Textarea from 'react-autosize-textarea';
import classnames from 'classnames';
import { get } from 'lodash';

/**
* WordPress dependencies
Expand Down Expand Up @@ -88,15 +89,14 @@ class PostTitle extends Component {
}

render() {
const { title, placeholder, instanceId } = this.props;
const { title, placeholder, instanceId, isPostTypeViewable } = this.props;
const { isSelected } = this.state;
const className = classnames( 'editor-post-title', { 'is-selected': isSelected } );
const decodedPlaceholder = decodeEntities( placeholder );

return (
<PostTypeSupportCheck supportKeys="title">
<div className={ className }>
{ isSelected && <PostPermalink /> }
<KeyboardShortcuts
shortcuts={ {
'mod+z': this.redirectHistory,
Expand All @@ -117,6 +117,7 @@ class PostTitle extends Component {
onKeyPress={ this.onUnselect }
/>
</KeyboardShortcuts>
{ isSelected && isPostTypeViewable && <PostPermalink /> }
</div>
</PostTypeSupportCheck>
);
Expand All @@ -125,9 +126,12 @@ class PostTitle extends Component {

const applyWithSelect = withSelect( ( select ) => {
const { getEditedPostAttribute } = select( 'core/editor' );
const { getPostType } = select( 'core' );
const postType = getPostType( getEditedPostAttribute( 'type' ) );

return {
title: getEditedPostAttribute( 'title' ),
isPostTypeViewable: get( postType, [ 'viewable' ], false ),
};
} );

Expand Down
6 changes: 6 additions & 0 deletions editor/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,12 @@ export function savePost() {
};
}

export function refreshPost() {
return {
type: 'REFRESH_POST',
};
}

export function trashPost( postId, postType ) {
return {
type: 'TRASH_POST',
Expand Down
Loading

0 comments on commit 4c90150

Please sign in to comment.