Skip to content

Commit

Permalink
Editor: Extract LinkContainer from FormatToolbar (#9192)
Browse files Browse the repository at this point in the history
* Editor: Extract LinkContainer from FormatToolbar

* Format toolbar: Improve derived state handling

* Rich Text: Remove obsolete DEFAULT_CONTROLS constant
  • Loading branch information
gziolo authored Aug 22, 2018
1 parent 0e8c5e6 commit 649320e
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 130 deletions.
157 changes: 30 additions & 127 deletions packages/editor/src/components/rich-text/format-toolbar/index.js
Original file line number Diff line number Diff line change
@@ -1,59 +1,25 @@
/**
* External dependencies
*/
import { get } from 'lodash';

/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { Component } from '@wordpress/element';
import {
Fill,
IconButton,
ToggleControl,
Toolbar,
withSpokenMessages,
Popover,
ExternalLink,
} from '@wordpress/components';
import { ESCAPE, LEFT, RIGHT, UP, DOWN, BACKSPACE, ENTER, displayShortcut } from '@wordpress/keycodes';
import { ESCAPE, LEFT, RIGHT, UP, DOWN, BACKSPACE, ENTER } from '@wordpress/keycodes';
import { prependHTTP } from '@wordpress/url';

/**
* Internal dependencies
*/
import PositionedAtSelection from './positioned-at-selection';
import URLInput from '../../url-input';
import { filterURLForDisplay } from '../../../utils/url';

const FORMATTING_CONTROLS = [
{
icon: 'editor-bold',
title: __( 'Bold' ),
shortcut: displayShortcut.primary( 'b' ),
format: 'bold',
},
{
icon: 'editor-italic',
title: __( 'Italic' ),
shortcut: displayShortcut.primary( 'i' ),
format: 'italic',
},
{
icon: 'admin-links',
title: __( 'Link' ),
shortcut: displayShortcut.primary( 'k' ),
format: 'link',
},
{
icon: 'editor-strikethrough',
title: __( 'Strikethrough' ),
shortcut: displayShortcut.access( 'd' ),
format: 'strikethrough',
},
];

// Default controls shown if no `enabledControls` prop provided
const DEFAULT_CONTROLS = [ 'bold', 'italic', 'strikethrough', 'link' ];

// Stop the key event from propagating up to maybeStartTyping in BlockListBlock
const stopKeyPropagation = ( event ) => event.stopPropagation();
import { FORMATTING_CONTROLS } from '../formatting-controls';
import LinkContainer from './link-container';

/**
* Returns the Format Toolbar state given a set of props.
Expand All @@ -67,8 +33,8 @@ function computeDerivedState( props ) {
selectedNodeId: props.selectedNodeId,
settingsVisible: false,
opensInNewWindow: !! props.formats.link && !! props.formats.link.target,
linkValue: '',
isEditingLink: false,
linkValue: get( props, [ 'formats', 'link', 'value' ], '' ),
};
}

Expand Down Expand Up @@ -101,7 +67,8 @@ class FormatToolbar extends Component {
}
}
if ( [ LEFT, DOWN, RIGHT, UP, BACKSPACE, ENTER ].indexOf( event.keyCode ) > -1 ) {
stopKeyPropagation( event );
// Stop the key event from propagating up to maybeStartTyping in BlockListBlock.
event.stopPropagation();
}
}

Expand Down Expand Up @@ -141,13 +108,11 @@ class FormatToolbar extends Component {
}

addLink() {
this.setState( { linkValue: '' } );
this.props.onChange( { link: { isAdding: true } } );
}

dropLink() {
this.props.onChange( { link: null } );
this.setState( { linkValue: '' } );
}

editLink( event ) {
Expand All @@ -164,8 +129,6 @@ class FormatToolbar extends Component {
rel: this.state.opensInNewWindow ? 'noreferrer noopener' : null,
value,
} } );

this.setState( { linkValue: value, isEditingLink: false } );
if ( ! this.props.formats.link.value ) {
this.props.speak( __( 'Link added.' ), 'assertive' );
}
Expand All @@ -176,9 +139,11 @@ class FormatToolbar extends Component {
}

render() {
const { formats, enabledControls = DEFAULT_CONTROLS, customControls = [], selectedNodeId } = this.props;
const { formats, enabledControls = [], customControls = [], selectedNodeId } = this.props;
const { linkValue, settingsVisible, opensInNewWindow, isEditingLink } = this.state;
const isAddingLink = formats.link && formats.link.isAdding;
const isEditing = isAddingLink || isEditingLink;
const isPreviewing = ! isEditing && formats.link;

const toolbarControls = FORMATTING_CONTROLS.concat( customControls )
.filter( ( control ) => enabledControls.indexOf( control.format ) !== -1 )
Expand All @@ -202,88 +167,26 @@ class FormatToolbar extends Component {
};
} );

let linkUIToShow = 'none';
if ( isAddingLink || isEditingLink ) {
linkUIToShow = 'editing';
} else if ( formats.link ) {
linkUIToShow = 'previewing';
}

const linkSettings = settingsVisible && (
<div className="editor-format-toolbar__link-modal-line editor-format-toolbar__link-settings">
<ToggleControl
label={ __( 'Open in New Window' ) }
checked={ opensInNewWindow }
onChange={ this.setLinkTarget } />
</div>
);

return (
<div className="editor-format-toolbar">
<Toolbar controls={ toolbarControls } />

{ linkUIToShow !== 'none' && (
<Fill name="RichText.Siblings">
<PositionedAtSelection className="editor-format-toolbar__link-container">
<Popover
position="bottom center"
focusOnMount={ isAddingLink ? 'firstElement' : false }
key={ selectedNodeId /* Used to force rerender on change */ }
>
{ linkUIToShow === 'editing' && (
// Disable reason: KeyPress must be suppressed so the block doesn't hide the toolbar
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
<form
className="editor-format-toolbar__link-modal"
onKeyPress={ stopKeyPropagation }
onKeyDown={ this.onKeyDown }
onSubmit={ this.submitLink }>
<div className="editor-format-toolbar__link-modal-line">
<URLInput value={ linkValue } onChange={ this.onChangeLinkValue } />
<IconButton icon="editor-break" label={ __( 'Apply' ) } type="submit" />
<IconButton
className="editor-format-toolbar__link-settings-toggle"
icon="ellipsis"
label={ __( 'Link Settings' ) }
onClick={ this.toggleLinkSettingsVisibility }
aria-expanded={ settingsVisible }
/>
</div>
{ linkSettings }
</form>
/* eslint-enable jsx-a11y/no-noninteractive-element-interactions */
) }

{ linkUIToShow === 'previewing' && (
// Disable reason: KeyPress must be suppressed so the block doesn't hide the toolbar
/* eslint-disable jsx-a11y/no-static-element-interactions */
<div
className="editor-format-toolbar__link-modal"
onKeyPress={ stopKeyPropagation }
>
<div className="editor-format-toolbar__link-modal-line">
<ExternalLink
className="editor-format-toolbar__link-value"
href={ formats.link.value }
>
{ formats.link.value && filterURLForDisplay( decodeURI( formats.link.value ) ) }
</ExternalLink>
<IconButton icon="edit" label={ __( 'Edit' ) } onClick={ this.editLink } />
<IconButton
className="editor-format-toolbar__link-settings-toggle"
icon="ellipsis"
label={ __( 'Link Settings' ) }
onClick={ this.toggleLinkSettingsVisibility }
aria-expanded={ settingsVisible }
/>
</div>
{ linkSettings }
</div>
/* eslint-enable jsx-a11y/no-static-element-interactions */
) }
</Popover>
</PositionedAtSelection>
</Fill>
{ ( isEditing || isPreviewing ) && (
<LinkContainer
editLink={ this.editLink }
formats={ formats }
isEditing={ isEditing }
isPreviewing={ isPreviewing }
linkValue={ linkValue }
onChangeLinkValue={ this.onChangeLinkValue }
onKeyDown={ this.onKeyDown }
opensInNewWindow={ opensInNewWindow }
selectedNodeId={ selectedNodeId }
setLinkTarget={ this.setLinkTarget }
settingsVisible={ settingsVisible }
submitLink={ this.submitLink }
toggleLinkSettingsVisibility={ this.toggleLinkSettingsVisibility }
/>
) }
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import {
ExternalLink,
Fill,
IconButton,
Popover,
ToggleControl,
} from '@wordpress/components';

/**
* Internal dependencies
*/
import PositionedAtSelection from './positioned-at-selection';
import URLInput from '../../url-input';
import { filterURLForDisplay } from '../../../utils/url';

const stopKeyPropagation = ( event ) => event.stopPropagation();

const LinkContainer = ( props ) => {
const {
editLink,
formats,
isEditing,
isPreviewing,
linkValue,
onChangeLinkValue,
onKeyDown,
opensInNewWindow,
selectedNodeId,
setLinkTarget,
settingsVisible,
submitLink,
toggleLinkSettingsVisibility,
} = props;

const linkSettings = settingsVisible && (
<div className="editor-format-toolbar__link-modal-line editor-format-toolbar__link-settings">
<ToggleControl
label={ __( 'Open in New Window' ) }
checked={ opensInNewWindow }
onChange={ setLinkTarget } />
</div>
);

return (
<Fill name="RichText.Siblings">
<PositionedAtSelection className="editor-format-toolbar__link-container">
<Popover
position="bottom center"
focusOnMount={ isEditing ? 'firstElement' : false }
key={ selectedNodeId /* Used to force rerender on change */ }
>
{ isEditing && (
// Disable reason: KeyPress must be suppressed so the block doesn't hide the toolbar
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
<form
className="editor-format-toolbar__link-modal"
onKeyPress={ stopKeyPropagation }
onKeyDown={ onKeyDown }
onSubmit={ submitLink }>
<div className="editor-format-toolbar__link-modal-line">
<URLInput value={ linkValue } onChange={ onChangeLinkValue } />
<IconButton icon="editor-break" label={ __( 'Apply' ) } type="submit" />
<IconButton
className="editor-format-toolbar__link-settings-toggle"
icon="ellipsis"
label={ __( 'Link Settings' ) }
onClick={ toggleLinkSettingsVisibility }
aria-expanded={ settingsVisible }
/>
</div>
{ linkSettings }
</form>
/* eslint-enable jsx-a11y/no-noninteractive-element-interactions */
) }

{ isPreviewing && (
// Disable reason: KeyPress must be suppressed so the block doesn't hide the toolbar
/* eslint-disable jsx-a11y/no-static-element-interactions */
<div
className="editor-format-toolbar__link-modal"
onKeyPress={ stopKeyPropagation }
>
<div className="editor-format-toolbar__link-modal-line">
<ExternalLink
className="editor-format-toolbar__link-value"
href={ formats.link.value }
>
{ formats.link.value && filterURLForDisplay( decodeURI( formats.link.value ) ) }
</ExternalLink>
<IconButton icon="edit" label={ __( 'Edit' ) } onClick={ editLink } />
<IconButton
className="editor-format-toolbar__link-settings-toggle"
icon="ellipsis"
label={ __( 'Link Settings' ) }
onClick={ toggleLinkSettingsVisibility }
aria-expanded={ settingsVisible }
/>
</div>
{ linkSettings }
</div>
/* eslint-enable jsx-a11y/no-static-element-interactions */
) }
</Popover>
</PositionedAtSelection>
</Fill>
);
};

export default LinkContainer;
32 changes: 32 additions & 0 deletions packages/editor/src/components/rich-text/formatting-controls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { displayShortcut } from '@wordpress/keycodes';

export const FORMATTING_CONTROLS = [
{
icon: 'editor-bold',
title: __( 'Bold' ),
shortcut: displayShortcut.primary( 'b' ),
format: 'bold',
},
{
icon: 'editor-italic',
title: __( 'Italic' ),
shortcut: displayShortcut.primary( 'i' ),
format: 'italic',
},
{
icon: 'admin-links',
title: __( 'Link' ),
shortcut: displayShortcut.primary( 'k' ),
format: 'link',
},
{
icon: 'editor-strikethrough',
title: __( 'Strikethrough' ),
shortcut: displayShortcut.access( 'd' ),
format: 'strikethrough',
},
];
Loading

0 comments on commit 649320e

Please sign in to comment.