-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Editable Permalinks #5756
Add Editable Permalinks #5756
Changes from all commits
b9c5934
4f1c501
8a451e2
8353e7b
6253a45
6d99339
e1e0830
8307f97
e7a3547
034a077
19aa275
a7da16a
a44c379
7a23f77
ab21ea5
a0b9205
8523412
4b0bce4
edbbe9e
d9a8440
6761c51
680b4b6
85c4bda
6660458
3778a32
4e83662
95b5ceb
d8f1ef2
7a2a12c
3ea1481
71a29c7
c135851
8bbcf6e
b697cae
2b0156c
b281331
6f796fb
02dea6c
a02c4a0
5e6baab
c401948
65cf88e
b0be34a
19f4f3a
aeb3a84
14d23ee
38e5444
380f40e
98c36f7
f63debc
daa4b8f
1f703b0
40eff87
0bf43de
5db2b58
6ded524
fb49a35
91e26fb
17392bd
48fbdec
8766dab
7e56148
6d3960f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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> | ||
‎ | ||
</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 ); | ||
|
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' ) }> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍🏻 |
||
<ClipboardButton | ||
className="editor-post-permalink__copy" | ||
text={ samplePermalink } | ||
onCopy={ () => this.setState( { iconClass: 'is-copied' } ) } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we don't need There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was the behaviour that @karmatosed chose, we can revisit it later. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think what @aduth means is that we maybe can replace this CSS: .editor-post-permalink__copy .is-copied {
opacity: 0.3;
} With: .editor-post-permalink__copy::focus .dashicon {
opacity: 0.3;
} And then remove There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
> | ||
<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 ) } | ||
‎ | ||
</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 ); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this
<span>
necessary?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep. To use the
‎
character (for forcing the correct layout in RTL languages), we should be able to place it next to each chunk of text, but I couldn't find a combination that worked. Placing it all inside a<span>
with the‎
at the end works.