Skip to content

Commit

Permalink
Ellipsis: Add button to copy entire document
Browse files Browse the repository at this point in the history
* Scrap `copyAll` shenanigan, use ClipboardButton

* ClipboardButton: Accept additional onFinishCopy callback

* Add component EditorActions below ModeSwitcher

* Don't close menu on copy, show "Copied!" instead

* MenuItems: Make it more reusable

* Allow passing extra `classNames` to MenuItemsGroup, don't require a
`label`

* Change classes in MenuItemsToggle from `components-menu-items__toggle`
to `components-menu-items__button is-toggle`

* EllipsisMenu: Adopt areas Editor, Settings, Tools
  • Loading branch information
mcsf committed Jan 12, 2018
1 parent 6005b45 commit e81ab0a
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 17 deletions.
13 changes: 11 additions & 2 deletions components/clipboard-button/index.js
Expand Up @@ -38,6 +38,7 @@ class ClipboardButton extends Component {
componentWillUnmount() {
this.clipboard.destroy();
delete this.clipboard;
clearTimeout( this.onCopyTimeout );
}

bindContainer( container ) {
Expand All @@ -50,9 +51,17 @@ class ClipboardButton extends Component {
// kept within the rendered node.
args.clearSelection();

const { onCopy } = this.props;
const { onCopy, onFinishCopy } = this.props;
if ( onCopy ) {
onCopy();
// For convenience and consistency, ClipboardButton offers to call
// a secondary callback with delay. This is useful to reset
// consumers' state, e.g. to revert a label from "Copied" to
// "Copy".
if ( onFinishCopy ) {
clearTimeout( this.onCopyTimeout );
this.onCopyTimeout = setTimeout( onFinishCopy, 4000 );
}
}
}

Expand All @@ -68,7 +77,7 @@ class ClipboardButton extends Component {
render() {
// Disable reason: Exclude from spread props passed to Button
// eslint-disable-next-line no-unused-vars
const { className, children, onCopy, text, ...buttonProps } = this.props;
const { className, children, onCopy, onFinishCopy, text, ...buttonProps } = this.props;
const classes = classnames( 'components-clipboard-button', className );

return (
Expand Down
23 changes: 20 additions & 3 deletions components/menu-items/menu-items-group.js
@@ -1,3 +1,8 @@
/**
* External dependencies
*/
import classnames from 'classnames';

/**
* Internal dependencies
*/
Expand All @@ -6,11 +11,23 @@ import { NavigableMenu } from '../navigable-container';
import withInstanceId from '../higher-order/with-instance-id';
import MenuItemsToggle from './menu-items-toggle';

function MenuItemsGroup( { label, value, choices = [], onSelect, children, instanceId } ) {
function MenuItemsGroup( {
label,
value,
choices = [],
onSelect,
children,
instanceId,
className = '',
} ) {
const labelId = `components-choice-menu-label-${ instanceId }`;
const classNames = classnames( className, 'components-choice-menu' );

return (
<div className="components-choice-menu">
<div className="components-choice-menu__label" id={ labelId }>{ label }</div>
<div className={ classNames }>
{ label &&
<div className="components-choice-menu__label" id={ labelId }>{ label }</div>
}
<NavigableMenu orientation="vertical" aria-labelledby={ labelId }>
{ choices.map( ( item ) => {
const isSelected = value === item.value;
Expand Down
4 changes: 2 additions & 2 deletions components/menu-items/menu-items-toggle.js
Expand Up @@ -10,7 +10,7 @@ function MenuItemsToggle( { label, isSelected, onClick, shortcut } ) {
if ( isSelected ) {
return (
<IconButton
className="components-menu-items__toggle is-selected"
className="components-menu-items__button is-toggle is-selected"
icon="yes"
onClick={ onClick }
>
Expand All @@ -22,7 +22,7 @@ function MenuItemsToggle( { label, isSelected, onClick, shortcut } ) {

return (
<Button
className="components-menu-items__toggle"
className="components-menu-items__button is-toggle"
onClick={ onClick }
>
{ label }
Expand Down
4 changes: 2 additions & 2 deletions components/menu-items/style.scss
Expand Up @@ -8,8 +8,8 @@
color: $dark-gray-300;
}

.components-menu-items__toggle,
.components-menu-items__toggle.components-icon-button {
.components-menu-items__button,
.components-menu-items__button.components-icon-button {
width: 100%;
padding: 8px;
text-align: left;
Expand Down
19 changes: 12 additions & 7 deletions editor/components/post-permalink/index.js
Expand Up @@ -23,6 +23,7 @@ class PostPermalink extends Component {
showCopyConfirmation: false,
};
this.onCopy = this.onCopy.bind( this );
this.onFinishCopy = this.onFinishCopy.bind( this );
}

componentWillUnmount() {
Expand All @@ -33,13 +34,12 @@ class PostPermalink extends Component {
this.setState( {
showCopyConfirmation: true,
} );
}

clearTimeout( this.dismissCopyConfirmation );
this.dismissCopyConfirmation = setTimeout( () => {
this.setState( {
showCopyConfirmation: false,
} );
}, 4000 );
onFinishCopy() {
this.setState( {
showCopyConfirmation: false,
} );
}

render() {
Expand All @@ -55,7 +55,12 @@ class PostPermalink extends Component {
<Button className="editor-post-permalink__link" href={ link } target="_blank">
{ link }
</Button>
<ClipboardButton className="button" text={ link } onCopy={ this.onCopy }>
<ClipboardButton
className="button"
text={ link }
onCopy={ this.onCopy }
onFinishCopy={ this.onFinishCopy }
>
{ this.state.showCopyConfirmation ? __( 'Copied!' ) : __( 'Copy' ) }
</ClipboardButton>
</div>
Expand Down
51 changes: 51 additions & 0 deletions editor/edit-post/header/copy-content-button/index.js
@@ -0,0 +1,51 @@
/**
* External dependencies
*/
import { connect } from 'react-redux';

/**
* WordPress dependencies
*/
import { ClipboardButton } from '@wordpress/components';
import { Component } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import { getEditedPostContent } from '../../../store/selectors';

class CopyContentButton extends Component {
constructor() {
super( ...arguments );
this.state = { hasCopied: false };
this.onCopy = this.onCopy.bind( this );
this.onFinishCopy = this.onFinishCopy.bind( this );
}
onCopy() {
this.setState( { hasCopied: true } );
}
onFinishCopy() {
this.setState( { hasCopied: false } );
}
render() {
return (
<ClipboardButton
text={ this.props.editedPostContent }
className="components-menu-items__button"
onCopy={ this.onCopy }
onFinishCopy={ this.onFinishCopy }
>
{ this.state.hasCopied ?
__( 'Copied!' ) :
__( 'Copy All Content' ) }
</ClipboardButton>
);
}
}

export default connect(
( state ) => ( {
editedPostContent: getEditedPostContent( state ),
} )
)( CopyContentButton );
20 changes: 20 additions & 0 deletions editor/edit-post/header/editor-actions/index.js
@@ -0,0 +1,20 @@
/**
* WordPress dependencies
*/
import { MenuItemsGroup } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import CopyContentButton from '../copy-content-button';

export default function EditorActions() {
return (
<MenuItemsGroup className="editor-actions"
label={ __( 'Tools' ) }
>
<CopyContentButton />
</MenuItemsGroup>
);
}
3 changes: 3 additions & 0 deletions editor/edit-post/header/ellipsis-menu/index.js
Expand Up @@ -10,6 +10,7 @@ import { IconButton, Dropdown } from '@wordpress/components';
import './style.scss';
import ModeSwitcher from '../mode-switcher';
import FixedToolbarToggle from '../fixed-toolbar-toggle';
import EditorActions from '../editor-actions';

const element = (
<Dropdown
Expand All @@ -28,6 +29,8 @@ const element = (
<ModeSwitcher onSelect={ onClose } />
<div className="editor-ellipsis-menu__separator" />
<FixedToolbarToggle onToggle={ onClose } />
<div className="editor-ellipsis-menu__separator" />
<EditorActions />
</div>
) }
/>
Expand Down
2 changes: 1 addition & 1 deletion editor/edit-post/header/fixed-toolbar-toggle/index.js
Expand Up @@ -21,7 +21,7 @@ function FeatureToggle( { onToggle, active, onMobile } ) {
}
return (
<MenuItemsGroup
label={ __( 'Toolbar' ) }
label={ __( 'Settings' ) }
>
<MenuItemsToggle
label={ __( 'Fix toolbar to top' ) }
Expand Down

0 comments on commit e81ab0a

Please sign in to comment.